Merge "New Autofill API: SaveCallback.onSuccess(IntentSender)."
diff --git a/Android.mk b/Android.mk
index b3db409..f7b6f46 100644
--- a/Android.mk
+++ b/Android.mk
@@ -694,6 +694,7 @@
 	frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \
+	frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \
 	frameworks/base/location/java/android/location/Location.aidl \
@@ -1562,7 +1563,8 @@
     tools/streaming_proto/stream.proto \
     cmds/am/proto/instrumentation_data.proto \
     $(call all-proto-files-under, core/proto) \
-    $(call all-proto-files-under, libs/incident/proto)
+    $(call all-proto-files-under, libs/incident/proto) \
+    $(call all-proto-files-under, cmds/statsd/src)
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 # ====  java proto device library (for test only)  ==============================
diff --git a/api/system-current.txt b/api/system-current.txt
index 9dca6f7..990df9b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4131,6 +4131,39 @@
 
 }
 
+package android.telephony.data {
+
+  public final class DataProfile implements android.os.Parcelable {
+    ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
+    ctor public DataProfile(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.String getApn();
+    method public int getAuthType();
+    method public int getBearerBitmap();
+    method public int getMaxConns();
+    method public int getMaxConnsTime();
+    method public int getMtu();
+    method public java.lang.String getMvnoMatchData();
+    method public java.lang.String getMvnoType();
+    method public java.lang.String getPassword();
+    method public int getProfileId();
+    method public java.lang.String getProtocol();
+    method public java.lang.String getRoamingProtocol();
+    method public int getSupportedApnTypesBitmap();
+    method public int getType();
+    method public java.lang.String getUserName();
+    method public int getWaitTime();
+    method public boolean isEnabled();
+    method public boolean isModemCognitive();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
+    field public static final int TYPE_3GPP = 1; // 0x1
+    field public static final int TYPE_3GPP2 = 2; // 0x2
+    field public static final int TYPE_COMMON = 0; // 0x0
+  }
+
+}
+
 package android.telephony.ims {
 
   public class ImsService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index 2e6c40a..8647ed3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -75,9 +75,11 @@
     method public int describeContents();
     method public int getActivityType();
     method public android.graphics.Rect getAppBounds();
+    method public android.graphics.Rect getBounds();
     method public int getWindowingMode();
     method public void setActivityType(int);
     method public void setAppBounds(android.graphics.Rect);
+    method public void setBounds(android.graphics.Rect);
     method public void setTo(android.app.WindowConfiguration);
     method public void setWindowingMode(int);
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 4d975fc..1f15c5e 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -57,7 +57,8 @@
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
     src/stats_util.cpp \
-    src/guardrail/MemoryLeakTrackUtil.cpp
+    src/guardrail/MemoryLeakTrackUtil.cpp \
+    src/guardrail/StatsdStats.cpp
 
 statsd_common_c_includes := \
     $(LOCAL_PATH)/src \
@@ -167,7 +168,8 @@
     tests/metrics/MaxDurationTracker_test.cpp \
     tests/metrics/CountMetricProducer_test.cpp \
     tests/metrics/EventMetricProducer_test.cpp \
-    tests/metrics/ValueMetricProducer_test.cpp
+    tests/metrics/ValueMetricProducer_test.cpp \
+    tests/guardrail/StatsdStats_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index b5cb20c..2690c7e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 #include "statslog.h"
 
@@ -21,6 +22,7 @@
 #include <dirent.h>
 #include "StatsLogProcessor.h"
 #include "android-base/stringprintf.h"
+#include "guardrail/StatsdStats.h"
 #include "metrics/CountMetricProducer.h"
 #include "stats_util.h"
 #include "storage/StorageManager.h"
@@ -82,6 +84,7 @@
 
 // TODO: what if statsd service restarts? How do we know what logs are already processed before?
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
+    StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
         pair.second->onLogEvent(msg);
@@ -106,23 +109,26 @@
 }
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
+    ALOGD("Updated configuration for key %s", key.ToString().c_str());
+    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config);
+
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
         it->second->finish();
+    } else if (mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
+        ALOGE("Can't accept more configs!");
+        return;
     }
 
-    ALOGD("Updated configuration for key %s", key.ToString().c_str());
-
-    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
         newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
         mMetricsManagers[key] = std::move(newMetricsManager);
         // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
-        ALOGD("StatsdConfig valid");
+        VLOG("StatsdConfig valid");
     } else {
         // If there is any error in the config, don't use it.
-        ALOGD("StatsdConfig NOT valid");
+        ALOGE("StatsdConfig NOT valid");
     }
 }
 
@@ -204,6 +210,7 @@
         mMetricsManagers.erase(it);
         mUidMap->OnConfigRemoved(key);
     }
+    StatsdStats::getInstance().noteConfigRemoved(key);
 
     std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
     mLastBroadcastTimes.erase(key);
@@ -223,12 +230,14 @@
             }
         }
         mLastBroadcastTimes[key] = timestampNs;
-        ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
+        VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
         mSendBroadcast(key);
+        StatsdStats::getInstance().noteBroadcastSent(key);
     } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
         // We ignore the return value so we force each metric producer to clear its contents.
         metricsManager->onDumpReport();
-        ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
+        StatsdStats::getInstance().noteDataDrop(key);
+        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
     }
 }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ec2650e..8b64f0d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -22,6 +22,7 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigManager.h"
 #include "guardrail/MemoryLeakTrackUtil.h"
+#include "guardrail/StatsdStats.h"
 #include "storage/DropboxReader.h"
 #include "storage/StorageManager.h"
 
@@ -222,7 +223,7 @@
         }
 
         if (!args[0].compare(String8("print-stats"))) {
-            return cmd_print_stats(out);
+            return cmd_print_stats(out, args);
         }
 
         if (!args[0].compare(String8("clear-config"))) {
@@ -305,8 +306,9 @@
     fprintf(out, "  NAME          The name of the configuration\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmd stats print-stats\n");
+    fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n");
     fprintf(out, "  Prints some basic stats.\n");
+    fprintf(out, "  reset: 1 to reset the statsd stats. default=0.\n");
 }
 
 status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
@@ -474,12 +476,20 @@
     }
 }
 
-status_t StatsService::cmd_print_stats(FILE* out) {
+status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
     vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
     for (const ConfigKey& key : configs) {
         fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
                 mProcessor->GetMetricsSize(key));
     }
+    fprintf(out, "Detailed statsd stats in logcat...");
+    StatsdStats& statsdStats = StatsdStats::getInstance();
+    bool reset = false;
+    if (args.size() > 1) {
+        reset = strtol(args[1].string(), NULL, 10);
+    }
+    vector<int8_t> output;
+    statsdStats.dumpStats(&output, reset);
     return NO_ERROR;
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 6b5c156..a32595a 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -134,7 +134,7 @@
     /**
      * Prints some basic stats to std out.
      */
-    status_t cmd_print_stats(FILE* out);
+    status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
 
     /**
      * Print the event log.
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index d8099b3..50cd130 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "SimpleConditionTracker.h"
+#include "guardrail/StatsdStats.h"
 
 #include <log/logprint.h>
 
@@ -32,9 +33,10 @@
 using std::vector;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const string& name, const int index, const SimpleCondition& simpleCondition,
+        const ConfigKey& key, const string& name, const int index,
+        const SimpleCondition& simpleCondition,
         const unordered_map<string, int>& trackerNameIndexMap)
-    : ConditionTracker(name, index) {
+    : ConditionTracker(name, index), mConfigKey(key) {
     VLOG("creating SimpleConditionTracker %s", mName.c_str());
     mCountNesting = simpleCondition.count_nesting();
 
@@ -126,6 +128,24 @@
     conditionCache[mIndex] = ConditionState::kFalse;
 }
 
+bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
+        // if the condition is not sliced or the key is not new, we are good!
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mSlicedConditionState.size() + 1;
+        StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
                                                   bool matchStart,
                                                   std::vector<ConditionState>& conditionCache,
@@ -133,6 +153,12 @@
     bool changed = false;
     auto outputIt = mSlicedConditionState.find(outputKey);
     ConditionState newCondition;
+    if (hitGuardRail(outputKey)) {
+        conditionChangedCache[mIndex] = false;
+        // Tells the caller it's evaluated.
+        conditionCache[mIndex] = ConditionState::kUnknown;
+        return;
+    }
     if (outputIt == mSlicedConditionState.end()) {
         // We get a new output key.
         newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index c287d8f..d21afd1 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest_prod.h>
 #include "ConditionTracker.h"
+#include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
@@ -28,7 +29,7 @@
 
 class SimpleConditionTracker : public virtual ConditionTracker {
 public:
-    SimpleConditionTracker(const std::string& name, const int index,
+    SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
                            const SimpleCondition& simpleCondition,
                            const std::unordered_map<std::string, int>& trackerNameIndexMap);
 
@@ -50,6 +51,7 @@
                         std::vector<ConditionState>& conditionCache) const override;
 
 private:
+    const ConfigKey mConfigKey;
     // The index of the LogEventMatcher which defines the start.
     int mStartLogMatcherIndex;
 
@@ -75,6 +77,8 @@
                               std::vector<ConditionState>& conditionCache,
                               std::vector<bool>& changedCache);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
new file mode 100644
index 0000000..815e03f
--- /dev/null
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2017, 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.
+ */
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsdStats.h"
+
+#include <android/util/ProtoOutputStream.h>
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
+using std::lock_guard;
+using std::map;
+using std::string;
+using std::vector;
+
+const int FIELD_ID_BEGIN_TIME = 1;
+const int FIELD_ID_END_TIME = 2;
+const int FIELD_ID_CONFIG_STATS = 3;
+const int FIELD_ID_MATCHER_STATS = 4;
+const int FIELD_ID_CONDITION_STATS = 5;
+const int FIELD_ID_METRIC_STATS = 6;
+const int FIELD_ID_ATOM_STATS = 7;
+
+const int FIELD_ID_MATCHER_STATS_NAME = 1;
+const int FIELD_ID_MATCHER_STATS_COUNT = 2;
+
+const int FIELD_ID_CONDITION_STATS_NAME = 1;
+const int FIELD_ID_CONDITION_STATS_COUNT = 2;
+
+const int FIELD_ID_METRIC_STATS_NAME = 1;
+const int FIELD_ID_METRIC_STATS_COUNT = 2;
+
+const int FIELD_ID_ATOM_STATS_TAG = 1;
+const int FIELD_ID_ATOM_STATS_COUNT = 2;
+
+// TODO: add stats for pulled atoms.
+StatsdStats::StatsdStats() {
+    mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
+    mStartTime = time(nullptr);
+}
+
+StatsdStats& StatsdStats::getInstance() {
+    static StatsdStats statsInstance;
+    return statsInstance;
+}
+
+void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
+                                     int matchersCount, int alertsCount, bool isValid) {
+    lock_guard<std::mutex> lock(mLock);
+    int32_t nowTimeSec = time(nullptr);
+
+    // If there is an existing config for the same key, icebox the old config.
+    noteConfigRemovedInternalLocked(key);
+
+    StatsdStatsReport_ConfigStats configStats;
+    configStats.set_uid(key.GetUid());
+    configStats.set_name(key.GetName());
+    configStats.set_creation_time_sec(nowTimeSec);
+    configStats.set_metric_count(metricsCount);
+    configStats.set_condition_count(conditionsCount);
+    configStats.set_matcher_count(matchersCount);
+    configStats.set_alert_count(alertsCount);
+    configStats.set_is_valid(isValid);
+
+    if (isValid) {
+        mConfigStats[key] = configStats;
+    } else {
+        configStats.set_deletion_time_sec(nowTimeSec);
+        mIceBox.push_back(configStats);
+    }
+}
+
+void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
+    auto it = mConfigStats.find(key);
+    if (it != mConfigStats.end()) {
+        int32_t nowTimeSec = time(nullptr);
+        it->second.set_deletion_time_sec(nowTimeSec);
+        // Add condition stats, metrics stats, matcher stats
+        addSubStatsToConfig(key, it->second);
+        // Remove them after they are added to the config stats.
+        mMatcherStats.erase(key);
+        mMetricsStats.erase(key);
+        mConditionStats.erase(key);
+        mIceBox.push_back(it->second);
+    }
+}
+
+void StatsdStats::noteConfigRemoved(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    noteConfigRemovedInternalLocked(key);
+}
+
+void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    auto it = mConfigStats.find(key);
+    if (it == mConfigStats.end()) {
+        ALOGE("Config key %s not found!", key.ToString().c_str());
+        return;
+    }
+
+    it->second.add_broadcast_sent_time_sec(time(nullptr));
+}
+
+void StatsdStats::noteDataDrop(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    auto it = mConfigStats.find(key);
+    if (it == mConfigStats.end()) {
+        ALOGE("Config key %s not found!", key.ToString().c_str());
+        return;
+    }
+
+    it->second.add_data_drop_time_sec(time(nullptr));
+}
+
+void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto& conditionSizeMap = mConditionStats[key];
+    if (size > conditionSizeMap[name]) {
+        conditionSizeMap[name] = size;
+    }
+}
+
+void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto& metricsDimensionMap = mMetricsStats[key];
+    if (size > metricsDimensionMap[name]) {
+        metricsDimensionMap[name] = size;
+    }
+}
+
+void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) {
+    lock_guard<std::mutex> lock(mLock);
+    auto& matcherStats = mMatcherStats[key];
+    matcherStats[name]++;
+}
+
+void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
+    lock_guard<std::mutex> lock(mLock);
+
+    if (timeSec < mStartTime) {
+        return;
+    }
+
+    if (atomId > android::util::kMaxPushedAtomId) {
+        ALOGW("not interested in atom %d", atomId);
+        return;
+    }
+
+    mPushedAtomStats[atomId]++;
+}
+
+void StatsdStats::reset() {
+    lock_guard<std::mutex> lock(mLock);
+    resetInternalLocked();
+}
+
+void StatsdStats::resetInternalLocked() {
+    // Reset the historical data, but keep the active ConfigStats
+    mStartTime = time(nullptr);
+    mIceBox.clear();
+    mConditionStats.clear();
+    mMetricsStats.clear();
+    std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
+    mMatcherStats.clear();
+}
+
+void StatsdStats::addSubStatsToConfig(const ConfigKey& key,
+                                      StatsdStatsReport_ConfigStats& configStats) {
+    // Add matcher stats
+    if (mMatcherStats.find(key) != mMatcherStats.end()) {
+        const auto& matcherStats = mMatcherStats[key];
+        for (const auto& stats : matcherStats) {
+            auto output = configStats.add_matcher_stats();
+            output->set_name(stats.first);
+            output->set_matched_times(stats.second);
+            VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second);
+        }
+    }
+    // Add condition stats
+    if (mConditionStats.find(key) != mConditionStats.end()) {
+        const auto& conditionStats = mConditionStats[key];
+        for (const auto& stats : conditionStats) {
+            auto output = configStats.add_condition_stats();
+            output->set_name(stats.first);
+            output->set_max_tuple_counts(stats.second);
+            VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second);
+        }
+    }
+    // Add metrics stats
+    if (mMetricsStats.find(key) != mMetricsStats.end()) {
+        const auto& conditionStats = mMetricsStats[key];
+        for (const auto& stats : conditionStats) {
+            auto output = configStats.add_metric_stats();
+            output->set_name(stats.first);
+            output->set_max_tuple_counts(stats.second);
+            VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second);
+        }
+    }
+}
+
+void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
+    lock_guard<std::mutex> lock(mLock);
+
+    if (DEBUG) {
+        time_t t = time(nullptr);
+        struct tm* tm = localtime(&t);
+        char timeBuffer[80];
+        strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
+        VLOG("=================StatsdStats dump begins====================");
+        VLOG("Stats collection start second: %s", timeBuffer);
+    }
+    ProtoOutputStream proto;
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTime);
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
+
+    VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size());
+    for (const auto& configStats : mIceBox) {
+        const int numBytes = configStats.ByteSize();
+        vector<char> buffer(numBytes);
+        configStats.SerializeToArray(&buffer[0], numBytes);
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
+                    buffer.size());
+
+        // surround the whole block with DEBUG, so that compiler can strip out the code
+        // in production.
+        if (DEBUG) {
+            VLOG("*****ICEBOX*****");
+            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+                 "#matcher=%d, #alert=%d,  #valid=%d",
+                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.deletion_time_sec(), configStats.metric_count(),
+                 configStats.condition_count(), configStats.matcher_count(),
+                 configStats.alert_count(), configStats.is_valid());
+
+            for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+                VLOG("\tbroadcast time: %d", broadcastTime);
+            }
+
+            for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+                VLOG("\tdata drop time: %d", dataDropTime);
+            }
+        }
+    }
+
+    for (auto& pair : mConfigStats) {
+        auto& configStats = pair.second;
+        if (DEBUG) {
+            VLOG("********Active Configs***********");
+            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+                 "#matcher=%d, #alert=%d,  #valid=%d",
+                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.deletion_time_sec(), configStats.metric_count(),
+                 configStats.condition_count(), configStats.matcher_count(),
+                 configStats.alert_count(), configStats.is_valid());
+            for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+                VLOG("\tbroadcast time: %d", broadcastTime);
+            }
+
+            for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+                VLOG("\tdata drop time: %d", dataDropTime);
+            }
+        }
+
+        addSubStatsToConfig(pair.first, configStats);
+
+        const int numBytes = configStats.ByteSize();
+        vector<char> buffer(numBytes);
+        configStats.SerializeToArray(&buffer[0], numBytes);
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
+                    buffer.size());
+        // reset the sub stats, the source of truth is in the individual map
+        // they will be repopulated when dumpStats() is called again.
+        configStats.clear_matcher_stats();
+        configStats.clear_condition_stats();
+        configStats.clear_metric_stats();
+    }
+
+    VLOG("********Atom stats***********");
+    const size_t atomCounts = mPushedAtomStats.size();
+    for (size_t i = 2; i < atomCounts; i++) {
+        if (mPushedAtomStats[i] > 0) {
+            long long token =
+                    proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
+            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
+            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
+            proto.end(token);
+
+            VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+        }
+    }
+
+    output->clear();
+    size_t bufferSize = proto.size();
+    output->resize(bufferSize);
+
+    size_t pos = 0;
+    auto it = proto.data();
+    while (it.readBuffer() != NULL) {
+        size_t toRead = it.currentToRead();
+        std::memcpy(&((*output)[pos]), it.readBuffer(), toRead);
+        pos += toRead;
+        it.rp()->move(toRead);
+    }
+
+    if (reset) {
+        resetInternalLocked();
+    }
+
+    VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
+    VLOG("=================StatsdStats dump ends====================");
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
new file mode 100644
index 0000000..73ce279
--- /dev/null
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017, 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.
+ */
+#pragma once
+
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Keeps track of stats of statsd.
+// Single instance shared across the process. All methods are thread safe.
+class StatsdStats {
+public:
+    static StatsdStats& getInstance();
+    ~StatsdStats(){};
+
+    // TODO: set different limit if the device is low ram.
+    const static int kDimensionKeySizeSoftLimit = 300;
+    const static int kDimensionKeySizeHardLimit = 500;
+
+    const static int kMaxConfigCount = 10;
+    const static int kMaxConditionCountPerConfig = 200;
+    const static int kMaxMetricCountPerConfig = 300;
+    const static int kMaxMatcherCountPerConfig = 500;
+
+    /**
+     * Report a new config has been received and report the static stats about the config.
+     *
+     * The static stats include: the count of metrics, conditions, matchers, and alerts.
+     * If the config is not valid, this config stats will be put into icebox immediately.
+     */
+    void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
+                            int matchersCount, int alertCount, bool isValid);
+    /**
+     * Report a config has been removed.
+     */
+    void noteConfigRemoved(const ConfigKey& key);
+
+    /**
+     * Report a broadcast has been sent to a config owner to collect the data.
+     */
+    void noteBroadcastSent(const ConfigKey& key);
+
+    /**
+     * Report a config's metrics data has been dropped.
+     */
+    void noteDataDrop(const ConfigKey& key);
+
+    /**
+     * Report the size of output tuple of a condition.
+     *
+     * Note: only report when the condition has an output dimension, and the tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this condition belongs to.
+     * [name]: The name of the condition.
+     * [size]: The output tuple size.
+     */
+    void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size);
+
+    /**
+     * Report the size of output tuple of a metric.
+     *
+     * Note: only report when the metric has an output dimension, and the tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this metric belongs to.
+     * [name]: The name of the metric.
+     * [size]: The output tuple size.
+     */
+    void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size);
+
+    /**
+     * Report a matcher has been matched.
+     *
+     * [key]: The config key that this matcher belongs to.
+     * [name]: The name of the matcher.
+     */
+    void noteMatcherMatched(const ConfigKey& key, const std::string& name);
+
+    /**
+     * Report an atom event has been logged.
+     */
+    void noteAtomLogged(int atomId, int32_t timeSec);
+
+    /**
+     * Reset the historical stats. Including all stats in icebox, and the tracked stats about
+     * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
+     * to collect stats after reset() has been called.
+     */
+    void reset();
+
+    /**
+     * Output the stats in protobuf binary format to [buffer].
+     *
+     * [reset]: whether to clear the historical stats after the call.
+     */
+    void dumpStats(std::vector<int8_t>* buffer, bool reset);
+
+private:
+    StatsdStats();
+
+    mutable std::mutex mLock;
+
+    int32_t mStartTime;
+
+    // The stats about the configs that are still in use.
+    std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
+
+    // Stores the stats for the configs that are no longer in use.
+    std::vector<const StatsdStatsReport_ConfigStats> mIceBox;
+
+    // Stores the number of output tuple of condition trackers when it's bigger than
+    // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+    // it means some data has been dropped.
+    std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats;
+
+    // Stores the number of output tuple of metric producers when it's bigger than
+    // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+    // it means some data has been dropped.
+    std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats;
+
+    // Stores the number of times a pushed atom is logged.
+    // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
+    // out of that range will be dropped (it's either pulled atoms or test atoms).
+    // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
+    std::vector<int> mPushedAtomStats;
+
+    // Stores how many times a matcher have been matched.
+    std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats;
+
+    void noteConfigRemovedInternalLocked(const ConfigKey& key);
+
+    void resetInternalLocked();
+
+    void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index ffbf248..fea3e9b 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -69,6 +69,10 @@
         return mTagIds;
     }
 
+    const std::string& getName() const {
+        return mName;
+    }
+
 protected:
     // Name of this matching. We don't really need the name, but it makes log message easy to debug.
     const std::string mName;
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index b2c88a0..ad37b01 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -19,8 +19,6 @@
 
 #include "SimpleLogMatchingTracker.h"
 
-#include <log/logprint.h>
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 149b9c1..ce60eb9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "CountMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <limits.h>
@@ -63,10 +64,11 @@
 
 // TODO: add back AnomalyTracker.
 
-CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
+CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -180,6 +182,26 @@
     mCondition = conditionMet;
 }
 
+bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
+        return false;
+    }
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
 void CountMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -195,6 +217,11 @@
     auto it = mCurrentSlicedCounter->find(eventKey);
 
     if (it == mCurrentSlicedCounter->end()) {
+        // ===========GuardRail==============
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
+
         // create a counter for the new key
         (*mCurrentSlicedCounter)[eventKey] = 1;
     } else {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 293e5b9..f78a199 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -42,8 +42,9 @@
 class CountMetricProducer : public MetricProducer {
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
-    CountMetricProducer(const CountMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
+    CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const uint64_t startTimeNs);
 
     virtual ~CountMetricProducer();
 
@@ -84,6 +85,8 @@
 
     static const size_t kBucketSize = sizeof(CountBucket{});
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 0ee7dcd..a0374c0 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -18,6 +18,7 @@
 
 #include "Log.h"
 #include "DurationMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <limits.h>
@@ -60,14 +61,14 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_DURATION = 3;
 
-DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
+DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
                                                const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -113,13 +114,13 @@
         const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
-            return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                     mNested, mCurrentBucketStartTimeNs,
-                                                     mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<OringDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
         case DurationMetric_AggregationType_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                   mNested, mCurrentBucketStartTimeNs,
-                                                   mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<MaxDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
     }
 }
 
@@ -130,6 +131,7 @@
 
 void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+    flushIfNeeded(eventTime);
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& pair : mCurrentSlicedDuration) {
         pair.second->onSlicedConditionMayChange(eventTime);
@@ -139,6 +141,7 @@
 void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
+    flushIfNeeded(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
     for (auto& pair : mCurrentSlicedDuration) {
@@ -236,6 +239,26 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
+bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    // the key is not new, we are good.
+    if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void DurationMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKeys, bool condition,
@@ -252,6 +275,9 @@
     HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
     }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index ebd5e8d..5b5373e 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -37,9 +37,9 @@
 
 class DurationMetricProducer : public MetricProducer {
 public:
-    DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
-                           const size_t startIndex, const size_t stopIndex,
-                           const size_t stopAllIndex, const bool nesting,
+    DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
+                           const int conditionIndex, const size_t startIndex,
+                           const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
                            const sp<ConditionWizard>& wizard,
                            const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
 
@@ -98,6 +98,7 @@
 
     std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
                                                            std::vector<DurationBucket>& bucket);
+    bool hitGuardRail(const HashableDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
 };
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 567b4c7..95a18f7 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -51,10 +51,11 @@
 const int FIELD_ID_TIMESTAMP_NANOS = 1;
 const int FIELD_ID_ATOMS = 2;
 
-EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
+EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 5afcebd..33a9510 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -34,8 +34,9 @@
 class EventMetricProducer : public MetricProducer {
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
-    EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
+    EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const uint64_t startTimeNs);
 
     virtual ~EventMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index be030d8..1791654 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "GaugeMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <cutils/log.h>
@@ -62,10 +63,11 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_GAUGE = 3;
 
-GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
+GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const int64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mPullTagId(pullTagId) {
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -225,6 +227,26 @@
     }
 }
 
+bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void GaugeMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -244,12 +266,15 @@
         flushIfNeeded(eventTimeNs);
     }
 
-    // For gauge metric, we just simply use the first guage in the given bucket.
+    // For gauge metric, we just simply use the first gauge in the given bucket.
     if (!mCurrentSlicedBucket->empty()) {
         return;
     }
     const long gauge = getGauge(event);
     if (gauge >= 0) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         (*mCurrentSlicedBucket)[eventKey] = gauge;
     }
     for (auto& tracker : mAnomalyTrackers) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 4b7654b4..f344303 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -47,9 +47,9 @@
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent
     // for all metrics.
-    GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const int64_t startTimeNs);
+    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const int64_t startTimeNs);
 
     virtual ~GaugeMetricProducer();
 
@@ -100,6 +100,8 @@
 
     int64_t getGauge(const LogEvent& event);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     static const size_t kBucketSize = sizeof(GaugeBucket{});
 
     FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ddccf9a..b22ff6f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -19,6 +19,7 @@
 
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
+#include "config/ConfigKey.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
 
@@ -35,9 +36,10 @@
 // be a no-op.
 class MetricProducer : public virtual PackageInfoListener {
 public:
-    MetricProducer(const int64_t startTimeNs, const int conditionIndex,
+    MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex,
                    const sp<ConditionWizard>& wizard)
-        : mStartTimeNs(startTimeNs),
+        : mConfigKey(key),
+          mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
           mCurrentBucketNum(0),
           mCondition(conditionIndex >= 0 ? false : true),
@@ -83,6 +85,8 @@
     }
 
 protected:
+    const ConfigKey mConfigKey;
+
     const uint64_t mStartTimeNs;
 
     uint64_t mCurrentBucketStartTimeNs;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 5916b040..c866951 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -20,6 +20,7 @@
 #include "CountMetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
+#include "guardrail/StatsdStats.h"
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/SimpleLogMatchingTracker.h"
 #include "metrics_manager_util.h"
@@ -36,10 +37,24 @@
 namespace os {
 namespace statsd {
 
-MetricsManager::MetricsManager(const StatsdConfig& config) {
-    mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
-                                    mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
-                                    mTrackerToMetricMap, mTrackerToConditionMap);
+MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) {
+    mConfigValid =
+            initStatsdConfig(key, config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+                             mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
+                             mTrackerToMetricMap, mTrackerToConditionMap);
+
+    // TODO: add alert size.
+    // no matter whether this config is valid, log it in the stats.
+    StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
+                                                  mAllConditionTrackers.size(),
+                                                  mAllLogEntryMatchers.size(), 0, mConfigValid);
+    // Guardrail. Reject the config if it's too big.
+    if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
+        mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
+        mAllLogEntryMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
+        ALOGE("This config is too big! Reject!");
+        mConfigValid = false;
+    }
 }
 
 MetricsManager::~MetricsManager() {
@@ -137,6 +152,8 @@
     // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
     for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
         if (matcherCache[i] == MatchingState::kMatched) {
+            StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
+                                                          mAllLogEntryMatchers[i]->getName());
             auto pair = mTrackerToMetricMap.find(i);
             if (pair != mTrackerToMetricMap.end()) {
                 auto& metricList = pair->second;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index a6054e3..517831d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -19,6 +19,7 @@
 #include "anomaly/AnomalyMonitor.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionTracker.h"
+#include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "logd/LogEvent.h"
 #include "matchers/LogMatchingTracker.h"
@@ -33,7 +34,7 @@
 // A MetricsManager is responsible for managing metrics from one single config source.
 class MetricsManager {
 public:
-    MetricsManager(const StatsdConfig& config);
+    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config);
 
     ~MetricsManager();
 
@@ -57,6 +58,8 @@
     size_t byteSize();
 
 private:
+    const ConfigKey mConfigKey;
+
     // All event tags that are interesting to my metrics.
     std::set<int> mTagIds;
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cc02c69..66c8419 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "ValueMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 
 #include <cutils/log.h>
 #include <limits.h>
@@ -67,11 +68,12 @@
 static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
 
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId) {
@@ -103,10 +105,11 @@
 }
 
 // for testing
-ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs)
-    : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
+    : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
                           make_shared<StatsPullerManager>()) {
 }
 
@@ -238,6 +241,27 @@
     }
 }
 
+bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
+        return false;
+    }
+    if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void ValueMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -249,6 +273,9 @@
         return;
     }
 
+    if (hitGuardRail(eventKey)) {
+        return;
+    }
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
     long value = get_value(event);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 24c76f2..a024bd8 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -38,9 +38,9 @@
 
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const uint64_t startTimeNs);
+    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const uint64_t startTimeNs);
 
     virtual ~ValueMetricProducer();
 
@@ -77,9 +77,9 @@
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
 
     // for testing
-    ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const uint64_t startTimeNs,
+    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const uint64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     Mutex mLock;
@@ -104,6 +104,8 @@
 
     long get_value(const LogEvent& event);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
     FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 7ce7f02..834f7f5 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -19,6 +19,7 @@
 
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
+#include "config/ConfigKey.h"
 #include "stats_util.h"
 
 namespace android {
@@ -59,11 +60,14 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                    int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                    uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
+    DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
+                    sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                     std::vector<DurationBucket>& bucket)
-        : mEventKey(eventKey),
+        : mConfigKey(key),
+          mName(name),
+          mEventKey(eventKey),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
@@ -138,6 +142,10 @@
             }
         }
     }
+    // A reference to the DurationMetricProducer's config key.
+    const ConfigKey& mConfigKey;
+
+    const std::string mName;
 
     HashableDimensionKey mEventKey;
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 06e743d..4b346dd 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -18,24 +18,50 @@
 
 #include "Log.h"
 #include "MaxDurationTracker.h"
+#include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
+MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name,
+                                       const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                        std::vector<DurationBucket>& bucket)
-    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
-                      anomalyTrackers, bucket) {
+    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+                      bucketSizeNs, anomalyTrackers, bucket) {
+}
+
+bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    if (mInfos.find(newKey) != mInfos.end()) {
+        // if the key existed, we are good!
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mInfos.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
 }
 
 void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                    const uint64_t eventTime, const ConditionKey& conditionKey) {
-    flushIfNeeded(eventTime);
     // this will construct a new DurationInfo if this key didn't exist.
+    if (hitGuardRail(key)) {
+        return;
+    }
+
     DurationInfo& duration = mInfos[key];
     duration.conditionKeys = conditionKey;
     VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
@@ -63,7 +89,6 @@
 
 void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
                                   bool forceStop) {
-    flushIfNeeded(eventTime);
     declareAnomalyIfAlarmExpired(eventTime);
     VLOG("MaxDuration: key %s stop", key.c_str());
     if (mInfos.find(key) == mInfos.end()) {
@@ -85,7 +110,6 @@
                      (long long)duration.lastStartTime, (long long)eventTime,
                      (long long)durationTime);
                 duration.lastDuration = duration.lastDuration + durationTime;
-                duration.lastStartTime = -1;
                 VLOG("  record duration: %lld ", (long long)duration.lastDuration);
             }
             break;
@@ -223,7 +247,6 @@
 
 void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                                               const uint64_t timestamp) {
-    flushIfNeeded(timestamp);
     declareAnomalyIfAlarmExpired(timestamp);
     auto it = mInfos.find(key);
     if (it == mInfos.end()) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 0f79ffe..e0d1466 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,7 +28,8 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+    MaxDurationTracker(const ConfigKey& key, const string& name,
+                       const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                        uint64_t bucketSizeNs,
                        const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
@@ -53,6 +54,9 @@
     void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                               const uint64_t timestamp);
 
+    // return true if we should not allow newKey to be tracked because we are above the threshold
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
     FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
     FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 29b6c89..22c33d6 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -16,6 +16,7 @@
 #define DEBUG true
 #include "Log.h"
 #include "OringDurationTracker.h"
+#include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
@@ -23,22 +24,45 @@
 
 using std::pair;
 
-OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
+OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& name,
+                                           const HashableDimensionKey& eventKey,
                                            sp<ConditionWizard> wizard, int conditionIndex,
                                            bool nesting, uint64_t currentBucketStartNs,
                                            uint64_t bucketSizeNs,
                                            const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                            std::vector<DurationBucket>& bucket)
-    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
-                      anomalyTrackers, bucket),
+    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+                      bucketSizeNs, anomalyTrackers, bucket),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
 }
 
+bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) {
+        return false;
+    }
+    if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mConditionKeyMap.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                      const uint64_t eventTime, const ConditionKey& conditionKey) {
-    flushIfNeeded(eventTime);
+    if (hitGuardRail(key)) {
+        return;
+    }
     if (condition) {
         if (mStarted.size() == 0) {
             mLastStartTime = eventTime;
@@ -59,7 +83,6 @@
 
 void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
                                     const bool stopAll) {
-    flushIfNeeded(timestamp);
     declareAnomalyIfAlarmExpired(timestamp);
     VLOG("Oring: %s stop", key.c_str());
     auto it = mStarted.find(key);
@@ -72,7 +95,6 @@
         if (mStarted.empty()) {
             mDuration += (timestamp - mLastStartTime);
             detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
-            mLastStartTime = -1;
             VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
                  (long long)mDuration);
         }
@@ -92,7 +114,6 @@
 }
 
 void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
-    flushIfNeeded(timestamp);
     declareAnomalyIfAlarmExpired(timestamp);
     if (!mStarted.empty()) {
         mDuration += (timestamp - mLastStartTime);
@@ -105,7 +126,6 @@
     mStarted.clear();
     mPaused.clear();
     mConditionKeyMap.clear();
-    mLastStartTime = -1;
 }
 
 bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
@@ -122,7 +142,6 @@
     // Process the current bucket.
     if (mStarted.size() > 0) {
         mDuration += (current_info.mBucketEndNs - mLastStartTime);
-        mLastStartTime = current_info.mBucketEndNs;
     }
     if (mDuration > 0) {
         current_info.mDuration = mDuration;
@@ -138,17 +157,15 @@
             info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
             info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-            mLastStartTime = info.mBucketEndNs;
-            if (info.mDuration > 0) {
                 mBucket.push_back(info);
                 addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
                 VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
-            }
         }
     }
     mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
 
+    mLastStartTime = mCurrentBucketStartTimeNs;
     mDuration = 0;
 
     // if all stopped, then tell owner it's safe to remove this tracker.
@@ -156,7 +173,6 @@
 }
 
 void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
-    flushIfNeeded(timestamp);
     declareAnomalyIfAlarmExpired(timestamp);
     vector<pair<HashableDimensionKey, int>> startedToPaused;
     vector<pair<HashableDimensionKey, int>> pausedToStarted;
@@ -179,8 +195,7 @@
         }
 
         if (mStarted.empty()) {
-            mDuration = (timestamp - mLastStartTime);
-            mLastStartTime = -1;
+            mDuration += (timestamp - mLastStartTime);
             VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
                  (long long)mDuration);
             detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
@@ -222,13 +237,12 @@
 }
 
 void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
-    flushIfNeeded(timestamp);
     declareAnomalyIfAlarmExpired(timestamp);
     if (condition) {
         if (!mPaused.empty()) {
             VLOG("Condition true, all started");
             if (mStarted.empty()) {
-                mLastStartTime = -1;
+                mLastStartTime = timestamp;
             }
             if (mStarted.empty() && !mPaused.empty()) {
                 startAnomalyAlarm(timestamp);
@@ -239,8 +253,7 @@
     } else {
         if (!mStarted.empty()) {
             VLOG("Condition false, all paused");
-            mDuration = (timestamp - mLastStartTime);
-            mLastStartTime = -1;
+            mDuration += (timestamp - mLastStartTime);
             mPaused.insert(mStarted.begin(), mStarted.end());
             mStarted.clear();
             detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index ef32fdb..a8404a9 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,7 +27,8 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+    OringDurationTracker(const ConfigKey& key, const string& name,
+                         const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                          uint64_t bucketSizeNs,
                          const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
@@ -58,6 +59,9 @@
     int64_t mLastStartTime;
     std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
 
+    // return true if we should not allow newKey to be tracked because we are above the threshold
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
     FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
     FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4660263..1b9efc5 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -134,7 +134,8 @@
     return true;
 }
 
-bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+                    const unordered_map<string, int>& logTrackerMap,
                     unordered_map<string, int>& conditionTrackerMap,
                     vector<sp<ConditionTracker>>& allConditionTrackers,
                     unordered_map<int, std::vector<int>>& trackerToConditionMap) {
@@ -149,7 +150,7 @@
         switch (condition.contents_case()) {
             case Condition::ContentsCase::kSimpleCondition: {
                 allConditionTrackers.push_back(new SimpleConditionTracker(
-                        condition.name(), index, condition.simple_condition(), logTrackerMap));
+                        key, condition.name(), index, condition.simple_condition(), logTrackerMap));
                 break;
             }
             case Condition::ContentsCase::kCombination: {
@@ -184,7 +185,8 @@
     return true;
 }
 
-bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
+                 const unordered_map<string, int>& logTrackerMap,
                  const unordered_map<string, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -230,7 +232,7 @@
         }
 
         sp<MetricProducer> countProducer =
-                new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
+                new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -298,8 +300,8 @@
         }
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
-                metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
-                nesting, wizard, internalDimension, startTimeNs);
+                key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
+                trackerIndices[2], nesting, wizard, internalDimension, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -332,7 +334,7 @@
         }
 
         sp<MetricProducer> eventMetric =
-                new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
+                new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
 
         allMetricProducers.push_back(eventMetric);
     }
@@ -378,8 +380,8 @@
             }
         }
 
-        sp<MetricProducer> valueProducer =
-                new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
+        sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
+                                                                   wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -424,8 +426,8 @@
             }
         }
 
-        sp<MetricProducer> gaugeProducer =
-                new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
+        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(key, metric, conditionIndex,
+                                                                   wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(gaugeProducer);
     }
     return true;
@@ -451,7 +453,7 @@
     return true;
 }
 
-bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -469,13 +471,13 @@
     }
     ALOGD("initLogMatchingTrackers succeed...");
 
-    if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
+    if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
                         trackerToConditionMap)) {
         ALOGE("initConditionTrackers failed");
         return false;
     }
 
-    if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
+    if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
                      allConditionTrackers, allMetricProducers, conditionToMetricMap,
                      trackerToMetricMap, metricProducerMap)) {
         ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 7d7e0c3..e7cbd53 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -36,6 +36,7 @@
 
 // Initialize the LogMatchingTrackers.
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input StatsdConfig
 // output:
 // [logTrackerMap]: this map should contain matcher name to index mapping
@@ -48,6 +49,7 @@
 
 // Initialize ConditionTrackers
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input config
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // output:
@@ -55,7 +57,7 @@
 // [allConditionTrackers]: stores the sp to all the ConditionTrackers
 // [trackerToConditionMap]: contain the mapping from index of
 //                        log tracker to condition trackers that use the log tracker
-bool initConditions(const StatsdConfig& config,
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
                     const std::unordered_map<std::string, int>& logTrackerMap,
                     std::unordered_map<std::string, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -64,6 +66,7 @@
 
 // Initialize MetricProducers.
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input config
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // [conditionTrackerMap]: condition name to index mapping
@@ -73,7 +76,8 @@
 //                          the list of MetricProducer index
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
-        const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap,
+        const ConfigKey& key, const StatsdConfig& config,
+        const std::unordered_map<std::string, int>& logTrackerMap,
         const std::unordered_map<std::string, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
@@ -84,7 +88,7 @@
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 4783cd89..81f8eb6 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -172,3 +172,51 @@
 
   repeated ConfigMetricsReport reports = 2;
 }
+
+message StatsdStatsReport {
+    optional int32 stats_begin_time_sec = 1;
+
+    optional int32 stats_end_time_sec = 2;
+
+    message MatcherStats {
+        optional string name = 1;
+        optional int32 matched_times = 2;
+    }
+
+    message ConditionStats {
+        optional string name = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message MetricStats {
+        optional string name = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message ConfigStats {
+        optional int32 uid = 1;
+        optional string name = 2;
+        optional int32 creation_time_sec = 3;
+        optional int32 deletion_time_sec = 4;
+        optional int32 metric_count = 5;
+        optional int32 condition_count = 6;
+        optional int32 matcher_count = 7;
+        optional int32 alert_count = 8;
+        optional bool is_valid = 9;
+
+        repeated int32 broadcast_sent_time_sec = 10;
+        repeated int32 data_drop_time_sec = 11;
+        repeated MatcherStats matcher_stats = 12;
+        repeated ConditionStats condition_stats = 13;
+        repeated MetricStats metric_stats = 14;
+    }
+
+    repeated ConfigStats config_stats = 3;
+
+    message AtomStats {
+        optional int32 tag = 1;
+        optional int32 count = 2;
+    }
+
+    repeated AtomStats atom_stats = 7;
+}
\ No newline at end of file
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 618aef6..696fddf 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -62,7 +62,8 @@
 }
 
 TEST(ConfigManagerTest, TestFakeConfig) {
-    auto metricsManager = std::make_unique<MetricsManager>(build_fake_config());
+    auto metricsManager =
+            std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config());
     EXPECT_TRUE(metricsManager->isConfigValid());
 }
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 3dd4e70..2adec94 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -40,6 +40,8 @@
 
 // TODO: ADD MORE TEST CASES.
 
+const ConfigKey kConfigKey(0, "test");
+
 StatsdConfig buildGoodConfig() {
     StatsdConfig config;
     config.set_name("12345");
@@ -254,9 +256,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                 allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                 trackerToMetricMap, trackerToConditionMap));
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                 allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                 conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
 }
@@ -272,9 +274,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -288,9 +290,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -303,9 +305,9 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleConditionDependency) {
@@ -319,9 +321,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -335,9 +337,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 #else
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 11fb011..92b4ffc 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -28,6 +28,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid) {
     SimpleCondition simpleCondition;
@@ -76,8 +78,8 @@
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
-    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
-                                            trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
 
@@ -158,8 +160,9 @@
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
-    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON",
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
 
@@ -227,8 +230,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid = 111;
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -308,8 +312,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid1 = 111;
     string uid1_wl1 = "wl1_1";
     int uid2 = 222;
@@ -392,8 +397,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid1 = 111;
     int uid2 = 222;
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
new file mode 100644
index 0000000..286f6bd
--- /dev/null
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2017 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 "src/guardrail/StatsdStats.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(StatsdStatsTest, TestConfigAdd) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestConfigRemove) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestMatcherReport) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestConditionReport) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestAtomLog) {
+    // TODO: implement
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 35e08af..df74364 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -32,6 +32,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -48,7 +50,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
 
     // 2 events in bucket 1.
@@ -106,7 +108,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs);
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -161,7 +163,7 @@
 
     EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
 
-    CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
@@ -201,7 +203,7 @@
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
     countProducer.addAnomalyTracker(anomalyTracker);
 
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 18d177c..724ad59 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -32,6 +32,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(EventMetricProducerTest, TestNoCondition) {
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -45,7 +47,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -69,7 +71,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -111,7 +113,7 @@
 
     EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
 
-    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 9e169bb..0763c94 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -36,6 +36,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -45,8 +47,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     tracker.noteStart("1", true, bucketStartTimeNs, key1);
     // Event starts again. This would not change anything as it already starts.
@@ -72,13 +74,14 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
 
     // Another event starts in this bucket.
     tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40);
     tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
     EXPECT_TRUE(tracker.mInfos.empty());
     EXPECT_EQ(1u, buckets.size());
@@ -99,8 +102,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     // The event starts.
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -108,20 +111,9 @@
     // Starts again. Does not change anything.
     tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
 
-    // Flushes at early 2nd bucket. The event still does not stop yet.
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
-
-    // Flushes at the end of the 2nd bucket. The event still does not stop yet.
-    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs));
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
-    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
-
     // The event stops at early 4th bucket.
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20);
     tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
-    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 21);
     EXPECT_EQ(3u, buckets.size());
     EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
     EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
@@ -137,8 +129,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     // 2 starts
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -178,33 +170,19 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
-    EXPECT_TRUE(tracker.mInfos.empty());
-    EXPECT_EQ(6LL, tracker.mDuration);
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStart("2:maps", false, eventStartTimeNs + 3 * bucketSizeNs + 10, key1);
-    EXPECT_EQ(1u, tracker.mInfos.size());
-    for (const auto& itr : tracker.mInfos) {
-        EXPECT_EQ(DurationState::kPaused, itr.second.state);
-        EXPECT_EQ(0LL, itr.second.lastDuration);
-    }
-    EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
-    EXPECT_EQ(6ULL, buckets[2].mDuration);
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(5ULL, buckets[0].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
@@ -224,8 +202,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
-                               {anomalyTracker}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {anomalyTracker}, buckets);
 
     tracker.noteStart("1", true, eventStartTimeNs, key1);
     tracker.noteStop("1", eventStartTimeNs + 10, false);
@@ -233,6 +211,7 @@
     EXPECT_EQ(10LL, tracker.mDuration);
 
     tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC);
     tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
     EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index f4edffd..63a0e2b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -34,6 +34,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -47,8 +49,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -73,8 +75,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -99,8 +101,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -125,15 +127,15 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
 
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
     EXPECT_EQ(2u, buckets.size());
     EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
     EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
@@ -162,17 +164,52 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
-    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
-    tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + durationTimeNs);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(5ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    ConditionKey key1;
+    key1["APP_BACKGROUND"] = "1:maps|";
+
+    EXPECT_CALL(*wizard, query(_, key1))
+            .Times(2)
+            .WillOnce(Return(ConditionState::kFalse))
+            .WillOnce(Return(ConditionState::kTrue));
+
+    vector<DurationBucket> buckets;
+
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    uint64_t durationTimeNs = 2 * 1000;
+
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
+
+    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    // condition to false; record duration 5n
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+    // condition to true.
+    tracker.onSlicedConditionMayChange(eventStartTimeNs + 1000);
+    // 2nd duration: 1000ns
+    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1u, buckets.size());
+    EXPECT_EQ(1005ULL, buckets[0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
@@ -190,8 +227,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
@@ -224,8 +261,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
-                                 {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {anomalyTracker}, buckets);
 
     // Nothing in the past bucket.
     tracker.noteStart("", true, eventStartTimeNs, key1);
@@ -242,6 +279,7 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
 
     uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
+    tracker.flushIfNeeded(event1StopTimeNs);
     tracker.noteStop("1", event1StopTimeNs, false);
     EXPECT_EQ(1u, buckets.size());
     EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
@@ -283,14 +321,13 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
-                                 bucketSizeNs, {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/,
+                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets);
 
     tracker.noteStart("", true, eventStartTimeNs, key1);
     tracker.noteStop("", eventStartTimeNs + 10, false);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
     EXPECT_TRUE(tracker.mStarted.empty());
-    EXPECT_EQ(-1LL, tracker.mLastStartTime);
     EXPECT_EQ(10LL, tracker.mDuration);
 
     EXPECT_EQ(0u, tracker.mStarted.size());
@@ -299,6 +336,7 @@
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
     EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25);
     tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
     EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
     EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1ed3636..9d78466 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "metrics_test_helper.h"
 #include "src/metrics/ValueMetricProducer.h"
+#include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -22,11 +22,11 @@
 
 using namespace testing;
 using android::sp;
+using std::make_shared;
 using std::set;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
-using std::shared_ptr;
-using std::make_shared;
 
 #ifdef __ANDROID__
 
@@ -34,6 +34,7 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
 /*
  * Tests pulled atoms with no conditions
  */
@@ -42,7 +43,7 @@
     int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 
     ValueMetric metric;
     metric.set_name("1");
@@ -54,12 +55,13 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     // TODO: pending refactor of StatsPullerManager
     // For now we still need this so that it doesn't do real pulling.
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
-    ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      tagId, bucketStartTimeNs, pullerManager);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -144,41 +146,43 @@
     int tagId = 1;
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<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) {
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                int64_t bucketStartTimeNs = 10000000000;
+                int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
-        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-        data->clear();
-        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-        event->write(1);
-        event->write(100);
-        event->init();
-        data->push_back(event);
-        return true;
-    }))
-    .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+                int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+                int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(1);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                int64_t bucketStartTimeNs = 10000000000;
+                int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
-        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-        data->clear();
-        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-        event->write(1);
-        event->write(120);
-        event->init();
-        data->push_back(event);
-        return true;
-    }));
+                int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+                int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+                event->write(1);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
-    ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
 
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
 
@@ -241,10 +245,11 @@
     int tagId = 1;
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
 
-    ValueMetricProducer valueProducer(metric, -1, wizard,-1,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      pullerManager);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 2c1fad1..80399ae 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -40,6 +40,13 @@
  */
 @TestApi
 public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
+    /**
+     * bounds that can differ from app bounds, which may include things such as insets.
+     *
+     * TODO: Investigate combining with {@link mAppBounds}. Can the latter be a product of the
+     * former?
+     */
+    private Rect mBounds = new Rect();
 
     /**
      * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
@@ -117,22 +124,26 @@
     })
     public @interface ActivityType {}
 
+    /** Bit that indicates that the {@link #mBounds} changed.
+     * @hide */
+    public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
     /** Bit that indicates that the {@link #mAppBounds} changed.
      * @hide */
-    public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 0;
+    public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1;
     /** Bit that indicates that the {@link #mWindowingMode} changed.
      * @hide */
-    public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 1;
+    public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 2;
     /** Bit that indicates that the {@link #mActivityType} changed.
      * @hide */
-    public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 2;
+    public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
 
     /** @hide */
     @IntDef(flag = true,
             value = {
+                    WINDOW_CONFIG_BOUNDS,
                     WINDOW_CONFIG_APP_BOUNDS,
                     WINDOW_CONFIG_WINDOWING_MODE,
-                    WINDOW_CONFIG_ACTIVITY_TYPE,
+                    WINDOW_CONFIG_ACTIVITY_TYPE
             })
     public @interface WindowConfig {}
 
@@ -151,12 +162,14 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mBounds, flags);
         dest.writeParcelable(mAppBounds, flags);
         dest.writeInt(mWindowingMode);
         dest.writeInt(mActivityType);
     }
 
     private void readFromParcel(Parcel source) {
+        mBounds = source.readParcelable(Rect.class.getClassLoader());
         mAppBounds = source.readParcelable(Rect.class.getClassLoader());
         mWindowingMode = source.readInt();
         mActivityType = source.readInt();
@@ -181,6 +194,19 @@
     };
 
     /**
+     * Sets the bounds to the provided {@link Rect}.
+     * @param rect the new bounds value.
+     */
+    public void setBounds(Rect rect) {
+        if (rect == null) {
+            mBounds.setEmpty();
+            return;
+        }
+
+        mBounds.set(rect);
+    }
+
+    /**
      * Set {@link #mAppBounds} to the input Rect.
      * @param rect The rect value to set {@link #mAppBounds} to.
      * @see #getAppBounds()
@@ -212,6 +238,11 @@
         return mAppBounds;
     }
 
+    /** @see #setBounds(Rect) */
+    public Rect getBounds() {
+        return mBounds;
+    }
+
     public void setWindowingMode(@WindowingMode int windowingMode) {
         mWindowingMode = windowingMode;
     }
@@ -244,6 +275,7 @@
     }
 
     public void setTo(WindowConfiguration other) {
+        setBounds(other.mBounds);
         setAppBounds(other.mAppBounds);
         setWindowingMode(other.mWindowingMode);
         setActivityType(other.mActivityType);
@@ -258,6 +290,7 @@
     /** @hide */
     public void setToDefaults() {
         setAppBounds(null);
+        setBounds(null);
         setWindowingMode(WINDOWING_MODE_UNDEFINED);
         setActivityType(ACTIVITY_TYPE_UNDEFINED);
     }
@@ -272,6 +305,11 @@
      */
     public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) {
         int changed = 0;
+        // Only allow override if bounds is not empty
+        if (!delta.mBounds.isEmpty() && !delta.mBounds.equals(mBounds)) {
+            changed |= WINDOW_CONFIG_BOUNDS;
+            setBounds(delta.mBounds);
+        }
         if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
             changed |= WINDOW_CONFIG_APP_BOUNDS;
             setAppBounds(delta.mAppBounds);
@@ -303,6 +341,10 @@
     public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) {
         long changes = 0;
 
+        if (!mBounds.equals(other.mBounds)) {
+            changes |= WINDOW_CONFIG_BOUNDS;
+        }
+
         // Make sure that one of the values is not null and that they are not equal.
         if ((compareUndefined || other.mAppBounds != null)
                 && mAppBounds != other.mAppBounds
@@ -340,6 +382,16 @@
             n = mAppBounds.bottom - that.mAppBounds.bottom;
             if (n != 0) return n;
         }
+
+        n = mBounds.left - that.mBounds.left;
+        if (n != 0) return n;
+        n = mBounds.top - that.mBounds.top;
+        if (n != 0) return n;
+        n = mBounds.right - that.mBounds.right;
+        if (n != 0) return n;
+        n = mBounds.bottom - that.mBounds.bottom;
+        if (n != 0) return n;
+
         n = mWindowingMode - that.mWindowingMode;
         if (n != 0) return n;
         n = mActivityType - that.mActivityType;
@@ -367,6 +419,8 @@
         if (mAppBounds != null) {
             result = 31 * result + mAppBounds.hashCode();
         }
+        result = 31 * result + mBounds.hashCode();
+
         result = 31 * result + mWindowingMode;
         result = 31 * result + mActivityType;
         return result;
@@ -375,7 +429,8 @@
     /** @hide */
     @Override
     public String toString() {
-        return "{mAppBounds=" + mAppBounds
+        return "{ mBounds=" + mBounds
+                + " mAppBounds=" + mAppBounds
                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
                 + " mActivityType=" + activityTypeToString(mActivityType) + "}";
     }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index c5f2272..ddc5760 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -58,6 +58,16 @@
     public @interface SliceHint{ }
 
     /**
+     * The meta-data key that allows an activity to easily be linked directly to a slice.
+     * <p>
+     * An activity can be statically linked to a slice uri by including a meta-data item
+     * for this key that contains a valid slice uri for the same application declaring
+     * the activity.
+     * @hide
+     */
+    public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
+
+    /**
      * Hint that this content is a title of other content in the slice. This can also indicate that
      * the content should be used in the shortcut representation of the slice (icon, label, action),
      * normally this should be indicated by adding the hint on the action containing that content.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 999ba73..e4310e1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4373,7 +4373,7 @@
     <string name="package_deleted_device_owner">Deleted by your admin</string>
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
-    <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+    <string name="battery_saver_description">To help improve battery life, Battery Saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery Saver turns off automatically when your device is charging.</string>
 
     <!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
     <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 508c788..ca85445 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -67,7 +67,7 @@
     <string name="battery_low_percent_format"><xliff:g id="percentage">%s</xliff:g> remaining</string>
 
     <!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]-->
-    <string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery saver is on.</string>
+    <string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery Saver is on.</string>
 
     <!-- A message that appears when a USB charger is plugged in and the device does not
     support charging on it.  That is, a charger that fits into the USB port and goes into
@@ -86,13 +86,13 @@
     <string name="battery_low_why">Settings</string>
 
     <!-- Battery saver confirmation dialog title [CHAR LIMIT=NONE]-->
-    <string name="battery_saver_confirmation_title">Turn on battery saver?</string>
+    <string name="battery_saver_confirmation_title">Turn on Battery Saver?</string>
 
     <!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]-->
     <string name="battery_saver_confirmation_ok">Turn on</string>
 
     <!-- Battery saver notification action [CHAR LIMIT=NONE]-->
-    <string name="battery_saver_start_action">Turn on battery saver</string>
+    <string name="battery_saver_start_action">Turn on Battery Saver</string>
 
     <!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] -->
     <string name="status_bar_settings_settings_button">Settings</string>
@@ -977,13 +977,13 @@
     <string name="user_remove_user_remove">Remove</string>
 
     <!-- Battery saver notification title. [CHAR LIMIT=60]-->
-    <string name="battery_saver_notification_title">Battery saver is on</string>
+    <string name="battery_saver_notification_title">Battery Saver is on</string>
 
     <!-- Battery saver notification text. [CHAR LIMIT=60] -->
     <string name="battery_saver_notification_text">Reduces performance and background data</string>
 
     <!-- Battery saver notification action text. [CHAR LIMIT=60] -->
-    <string name="battery_saver_notification_action_text">Turn off battery saver</string>
+    <string name="battery_saver_notification_action_text">Turn off Battery Saver</string>
 
     <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
     <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string>
@@ -1520,10 +1520,10 @@
     <string name="battery_panel_title">Battery usage</string>
 
     <!-- Summary of battery saver not available [CHAR LIMIT=NONE] -->
-    <string name="battery_detail_charging_summary">Battery saver not available during charging</string>
+    <string name="battery_detail_charging_summary">Battery Saver not available during charging</string>
 
     <!-- Title of switch for battery saver [CHAR LIMIT=NONE] -->
-    <string name="battery_detail_switch_title">Battery saver</string>
+    <string name="battery_detail_switch_title">Battery Saver</string>
 
     <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
     <string name="battery_detail_switch_summary">Reduces performance and background data</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index f6fab86..eb2d12e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -319,32 +319,39 @@
         mBackgroundExecutor.submit(new Runnable() {
             @Override
             public void run() {
+                boolean result = false;
                 try {
-                    ActivityManager.getService().startActivityFromRecents(taskKey.id,
-                            finalOptions == null ? null : finalOptions.toBundle());
-                    if (resultCallback != null) {
-                        resultCallbackHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                resultCallback.accept(true);
-                            }
-                        });
-                    }
+                    result = startActivityFromRecents(taskKey.id, finalOptions);
                 } catch (Exception e) {
-                    if (resultCallback != null) {
-                        resultCallbackHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                resultCallback.accept(false);
-                            }
-                        });
-                    }
+                    // Fall through
+                }
+                final boolean finalResult = result;
+                if (resultCallback != null) {
+                    resultCallbackHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            resultCallback.accept(finalResult);
+                        }
+                    });
                 }
             }
         });
     }
 
     /**
+     * Starts a task from Recents synchronously.
+     */
+    public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
+        try {
+            Bundle optsBundle = options == null ? null : options.toBundle();
+            ActivityManager.getService().startActivityFromRecents(taskId, optsBundle);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
      * Registers a task stack listener with the system.
      * This should be called on the main thread.
      */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
new file mode 100644
index 0000000..705a215
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+
+import android.app.ActivityOptions;
+
+/**
+ * Wrapper around internal ActivityOptions creation.
+ */
+public abstract class ActivityOptionsCompat {
+
+    /**
+     * @return ActivityOptions for starting a task in split screen.
+     */
+    public static ActivityOptions makeSplitScreenOptions(boolean dockTopLeft) {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        options.setSplitScreenCreateMode(dockTopLeft
+                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+        return options;
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 1477558..225dbb4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -19,8 +19,15 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
 import android.view.WindowManagerGlobal;
 
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+
 public class WindowManagerWrapper {
 
     private static final String TAG = "WindowManagerWrapper";
@@ -38,8 +45,24 @@
         try {
             WindowManagerGlobal.getWindowManagerService().getStableInsets(DEFAULT_DISPLAY,
                     outStableInsets);
-        } catch (Exception e) {
-            e.printStackTrace();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get stable insets", e);
+        }
+    }
+
+    /**
+     * Overrides a pending app transition.
+     */
+    public void overridePendingAppTransitionMultiThumbFuture(
+            AppTransitionAnimationSpecsFuture animationSpecFuture,
+            Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) {
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                    .overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
+                            RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
+                                    animStartedCallback), scaleUp);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index d89bab7..cf3cae5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -21,12 +21,10 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
@@ -49,9 +47,6 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.IRemoteCallback;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -63,7 +58,6 @@
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.view.Display;
-import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IDockedStackListener;
 import android.view.IWindowManager;
 import android.view.WindowManager;
@@ -78,12 +72,9 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.policy.UserInfoController;
 
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Acts as a shim around the real system services that we need to access data from, and provides
@@ -268,22 +259,6 @@
         return mIsSafeMode;
     }
 
-    /** Docks a task to the side of the screen and starts it. */
-    public boolean startTaskInDockedMode(int taskId, int createMode) {
-        if (mIam == null) return false;
-
-        try {
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setSplitScreenCreateMode(createMode);
-            options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-            mIam.startActivityFromRecents(taskId, options.toBundle());
-            return true;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e);
-        }
-        return false;
-    }
-
     /** Moves an already resumed task to the side of the screen to initiate split screen. */
     public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
             Rect initialBounds) {
@@ -540,16 +515,6 @@
         }
     }
 
-    public void overridePendingAppTransitionMultiThumbFuture(
-            IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
-            boolean scaleUp) {
-        try {
-            mIwm.overridePendingAppTransitionMultiThumbFuture(future, animStartedListener, scaleUp);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to override transition: " + e);
-        }
-    }
-
     /**
      * Updates the visibility of recents.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1440fc1..e3ed1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,13 +16,14 @@
 
 package com.android.systemui.recents.views;
 
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
-import android.app.ActivityOptions.OnAnimationStartedListener;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -33,11 +34,11 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.IRemoteCallback;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
-import android.view.AppTransitionAnimationSpec;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -86,13 +87,15 @@
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -608,16 +611,17 @@
             // rect to its final layout-space rect
             Utilities.setViewFrameFromTranslation(event.taskView);
 
-            // Dock the task and launch it
-            SystemServicesProxy ssp = Recents.getSystemServices();
-            if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) {
+            final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(
+                    dockState.createMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+            if (ActivityManagerWrapper.getInstance().startActivityFromRecents(event.task.key.id,
+                    options)) {
                 final Runnable animStartedListener = () -> {
                     EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
-                    // Remove the task and don't bother relaying out, as all the tasks will be
-                    // relaid out when the stack changes on the multiwindow change event
+                    // Remove the task and don't bother relaying out, as all the tasks
+                    // will be relaid out when the stack changes on the multiwindow
+                    // change event
                     getStack().removeTask(event.task, null, true /* fromDockGesture */);
                 };
-
                 final Rect taskRect = getTaskRect(event.taskView);
                 AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
                         getHandler()) {
@@ -626,10 +630,8 @@
                         return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
                     }
                 };
-                ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(),
-                        RecentsTransition.wrapStartedListener(getHandler(), animStartedListener),
-                        true /* scaleUp */);
-
+                WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+                        future, animStartedListener, getHandler(), true /* scaleUp */);
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
                         event.task.getTopComponent().flattenToShortString());
             } else {
@@ -1032,11 +1034,9 @@
                 if (taskIndex > -1) {
                     taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
                 }
-                EventBus.getDefault().send(new LaunchTaskSucceededEvent(
-                        taskIndexFromFront));
+                EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
             } else {
-                Log.e(TAG, mContext.getString(R.string.recents_launch_error_message,
-                        task.title));
+                Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, task.title));
 
                 // Dismiss the task if we fail to launch it
                 if (taskView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 1cda301..da79884 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -20,6 +20,8 @@
 import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.os.UserHandle.USER_CURRENT;
 
+import static com.android.systemui.statusbar.phone.NavigationBarGestureHelper.DRAG_MODE_NONE;
+
 import android.app.ActivityManager;
 import android.content.res.Configuration;
 import android.os.RemoteException;
@@ -36,6 +38,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
 
 import java.util.List;
 
@@ -89,20 +92,11 @@
         try {
             int dockSide = mWindowManagerService.getDockedStackSide();
             if (dockSide == WindowManager.DOCKED_INVALID) {
-                // If there is no window docked, we dock the top-most window.
+                // Split the screen
                 Recents recents = getComponent(Recents.class);
-                int dockMode = (shortcutCode == SC_DOCK_LEFT)
+                recents.splitPrimaryTask(DRAG_MODE_NONE, (shortcutCode == SC_DOCK_LEFT)
                         ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                        : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-                List<ActivityManager.RecentTaskInfo> taskList =
-                        ActivityManagerWrapper.getInstance().getRecentTasks(1, USER_CURRENT);
-                recents.showRecentApps(
-                        false /* triggeredFromAltTab */,
-                        false /* fromHome */);
-                if (!taskList.isEmpty()) {
-                    SystemServicesProxy.getInstance(mContext).startTaskInDockedMode(
-                            taskList.get(0).id, dockMode);
-                }
+                        : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
             } else {
                 // If there is already a docked window, we respond by resizing the docking pane.
                 DividerView dividerView = getComponent(Divider.class).getView();
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 83a1158..32af29d 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -173,6 +173,7 @@
 
   // Selection invocation methods. Used as sub-type for TEXT_SELECTION_SESSION events.
   enum TextSelectionInvocationMethod {
+    TEXT_SELECTION_INVOCATION_UNKNOWN = 0;
     TEXT_SELECTION_INVOCATION_MANUAL = 1;
     TEXT_SELECTION_INVOCATION_LINK = 2;
   }
@@ -2805,316 +2806,524 @@
     // OS: O
     TEXT_LONGPRESS = 629;
 
-    // OBSOLETE
+    // ACTION: An app requested an unknown permission
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_UNKNOWN = 630;
 
-    // OBSOLETE
+    // ACTION: An app was granted an unknown permission
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_UNKNOWN = 631;
 
-    // OBSOLETE
+    // ACTION: An app requested an unknown permission and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_UNKNOWN = 632;
 
-    // OBSOLETE
+    // ACTION: An unknown permission was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_UNKNOWN = 633;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CALENDAR
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_CALENDAR = 634;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_CALENDAR
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_CALENDAR = 635;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CALENDAR and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_CALENDAR = 636;
 
-    // OBSOLETE
+    // ACTION: The permission READ_CALENDAR was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_CALENDAR = 637;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CALENDAR
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_WRITE_CALENDAR = 638;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission WRITE_CALENDAR
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_WRITE_CALENDAR = 639;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CALENDAR and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_WRITE_CALENDAR = 640;
 
-    // OBSOLETE
+    // ACTION: The permission WRITE_CALENDAR was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_WRITE_CALENDAR = 641;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission CAMERA
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_CAMERA = 642;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission CAMERA
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_CAMERA = 643;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission CAMERA and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_CAMERA = 644;
 
-    // OBSOLETE
+    // ACTION: The permission CAMERA was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_CAMERA = 645;
 
-    // AOBSOLETE
+    // ACTION: An app requested the permission READ_CONTACTS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_CONTACTS = 646;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_CONTACTS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_CONTACTS = 647;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CONTACTS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_CONTACTS = 648;
 
-    // OBSOLETE
+    // ACTION: The permission READ_CONTACTS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_CONTACTS = 649;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CONTACTS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_WRITE_CONTACTS = 650;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission WRITE_CONTACTS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_WRITE_CONTACTS = 651;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CONTACTS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_WRITE_CONTACTS = 652;
 
-    // OBSOLETE
+    // ACTION: The permission WRITE_CONTACTS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_WRITE_CONTACTS = 653;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission GET_ACCOUNTS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_GET_ACCOUNTS = 654;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission GET_ACCOUNTS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_GET_ACCOUNTS = 655;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission GET_ACCOUNTS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_GET_ACCOUNTS = 656;
 
-    // OBSOLETE
+    // ACTION: The permission GET_ACCOUNTS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_GET_ACCOUNTS = 657;
 
-    // AOBSOLETE
+    // ACTION: An app requested the permission ACCESS_FINE_LOCATION
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 658;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission ACCESS_FINE_LOCATION
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_ACCESS_FINE_LOCATION = 659;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission ACCESS_FINE_LOCATION and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_ACCESS_FINE_LOCATION = 660;
 
-    // OBSOLETE
+    // ACTION: The permission ACCESS_FINE_LOCATION was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_ACCESS_FINE_LOCATION = 661;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission ACCESS_COARSE_LOCATION
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_ACCESS_COARSE_LOCATION = 662;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission ACCESS_COARSE_LOCATION
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_ACCESS_COARSE_LOCATION = 663;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission ACCESS_COARSE_LOCATION and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_ACCESS_COARSE_LOCATION = 664;
 
-    // OBSOLETE
+    // ACTION: The permission ACCESS_COARSE_LOCATION was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_ACCESS_COARSE_LOCATION = 665;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECORD_AUDIO
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_RECORD_AUDIO = 666;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission RECORD_AUDIO
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_RECORD_AUDIO = 667;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECORD_AUDIO and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_RECORD_AUDIO = 668;
 
-    // OBSOLETE
+    // ACTION: The permission RECORD_AUDIO was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_RECORD_AUDIO = 669;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_PHONE_STATE
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_PHONE_STATE = 670;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_PHONE_STATE
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_PHONE_STATE = 671;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_PHONE_STATE and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_PHONE_STATE = 672;
 
-    // OBSOLETE
+    // ACTION: The permission READ_PHONE_STATE was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_PHONE_STATE = 673;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission CALL_PHONE
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_CALL_PHONE = 674;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission CALL_PHONE
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_CALL_PHONE = 675;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission CALL_PHONE and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_CALL_PHONE = 676;
 
-    // OBSOLETE
+    // ACTION: The permission CALL_PHONE was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_CALL_PHONE = 677;
 
-    // AOBSOLETE
+    // ACTION: An app requested the permission READ_CALL_LOG
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_CALL_LOG = 678;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_CALL_LOG
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_CALL_LOG = 679;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CALL_LOG and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_CALL_LOG = 680;
 
-    // OBSOLETE
+    // ACTION: The permission READ_CALL_LOG was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_CALL_LOG = 681;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CALL_LOG
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_WRITE_CALL_LOG = 682;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission WRITE_CALL_LOG
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_WRITE_CALL_LOG = 683;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_CALL_LOG and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_WRITE_CALL_LOG = 684;
 
-    // OBSOLETE
+    // ACTION: The permission WRITE_CALL_LOG was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_WRITE_CALL_LOG = 685;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission ADD_VOICEMAIL
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_ADD_VOICEMAIL = 686;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission ADD_VOICEMAIL
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_ADD_VOICEMAIL = 687;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission ADD_VOICEMAIL and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_ADD_VOICEMAIL = 688;
 
-    // OBSOLETE
+    // ACTION: The permission ADD_VOICEMAIL was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_ADD_VOICEMAIL = 689;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission USE_SIP
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_USE_SIP = 690;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission USE_SIP
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_USE_SIP = 691;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission USE_SIP and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_USE_SIP = 692;
 
-    // OBSOLETE
+    // ACTION: The permission USE_SIP was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_USE_SIP = 693;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission PROCESS_OUTGOING_CALLS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_PROCESS_OUTGOING_CALLS = 694;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission PROCESS_OUTGOING_CALLS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_PROCESS_OUTGOING_CALLS = 695;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission PROCESS_OUTGOING_CALLS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_PROCESS_OUTGOING_CALLS = 696;
 
-    // OBSOLETE
+    // ACTION: The permission PROCESS_OUTGOING_CALLS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_PROCESS_OUTGOING_CALLS = 697;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CELL_BROADCASTS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_CELL_BROADCASTS = 698;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_CELL_BROADCASTS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_CELL_BROADCASTS = 699;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_CELL_BROADCASTS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_CELL_BROADCASTS = 700;
 
-    // OBSOLETE
+    // ACTION: The permission READ_CELL_BROADCASTS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_CELL_BROADCASTS = 701;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission BODY_SENSORS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_BODY_SENSORS = 702;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission BODY_SENSORS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_BODY_SENSORS = 703;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission BODY_SENSORS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_BODY_SENSORS = 704;
 
-    // OBSOLETE
+    // ACTION: The permission BODY_SENSORS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_BODY_SENSORS = 705;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission SEND_SMS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_SEND_SMS = 706;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission SEND_SMS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_SEND_SMS = 707;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission SEND_SMS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_SEND_SMS = 708;
 
-    // OBSOLETE
+    // ACTION: The permission SEND_SMS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_SEND_SMS = 709;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_SMS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_RECEIVE_SMS = 710;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission RECEIVE_SMS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_RECEIVE_SMS = 711;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_SMS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_RECEIVE_SMS = 712;
 
-    // OBSOLETE
+    // ACTION: The permission RECEIVE_SMS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_RECEIVE_SMS = 713;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_SMS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_SMS = 714;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_SMS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_SMS = 715;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_SMS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_SMS = 716;
 
-    // OBSOLETE
+    // ACTION: The permission READ_SMS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_SMS = 717;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_WAP_PUSH
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_RECEIVE_WAP_PUSH = 718;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission RECEIVE_WAP_PUSH
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_RECEIVE_WAP_PUSH = 719;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_WAP_PUSH and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_RECEIVE_WAP_PUSH = 720;
 
-    // OBSOLETE
+    // ACTION: The permission RECEIVE_WAP_PUSH was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_RECEIVE_WAP_PUSH = 721;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_MMS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_RECEIVE_MMS = 722;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission RECEIVE_MMS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_RECEIVE_MMS = 723;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission RECEIVE_MMS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_RECEIVE_MMS = 724;
 
-    // OBSOLETE
+    // ACTION: The permission RECEIVE_MMS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_RECEIVE_MMS = 725;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_EXTERNAL_STORAGE
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 726;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_EXTERNAL_STORAGE
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_EXTERNAL_STORAGE = 727;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_EXTERNAL_STORAGE and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_EXTERNAL_STORAGE = 728;
 
-    // OBSOLETE
+    // ACTION: The permission READ_EXTERNAL_STORAGE was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_EXTERNAL_STORAGE = 729;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_EXTERNAL_STORAGE
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 730;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission WRITE_EXTERNAL_STORAGE
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_WRITE_EXTERNAL_STORAGE = 731;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission WRITE_EXTERNAL_STORAGE and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_WRITE_EXTERNAL_STORAGE = 732;
 
-    // OBSOLETE
+    // ACTION: The permission WRITE_EXTERNAL_STORAGE was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_WRITE_EXTERNAL_STORAGE = 733;
 
     // ACTION: Logged when a provisioning session has started
@@ -3123,16 +3332,24 @@
     // ACTION: Logged when a provisioning session has completed
     PROVISIONING_SESSION_COMPLETED = 735;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_PHONE_NUMBERS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REQUEST_READ_PHONE_NUMBERS = 736;
 
-    // OBSOLETE
+    // ACTION: An app was granted the permission READ_PHONE_NUMBERS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_GRANT_READ_PHONE_NUMBERS = 737;
 
-    // OBSOLETE
+    // ACTION: An app requested the permission READ_PHONE_NUMBERS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_DENIED_READ_PHONE_NUMBERS = 738;
 
-    // OBSOLETE
+    // ACTION: The permission READ_PHONE_NUMBERS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBERS = 739;
 
     // ACTION: QS Brightness Slider (with auto brightness disabled, and VR enabled)
@@ -3624,52 +3841,84 @@
     // CATEGORY: SETTINGS
     SETTINGS_LOCK_SCREEN_PREFERENCES = 882;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_REQUEST_ACCESS_NOTIFICATIONS = 883;
 
-    // OBSOLETE
+    // ACTION: An app was granted the app-op permission ACCESS_NOTIFICATIONS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_GRANT_ACCESS_NOTIFICATIONS = 884;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_DENIED_ACCESS_NOTIFICATIONS = 885;
 
-    // OBSOLETE
+    // ACTION: The app-op permission ACCESS_NOTIFICATIONS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_APPOP_REVOKE_ACCESS_NOTIFICATIONS = 886;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_REQUEST_SYSTEM_ALERT_WINDOW = 887;
 
-    // OBSOLETE
+    // ACTION: An app was granted the app-op permission SYSTEM_ALERT_WINDOW
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_GRANT_SYSTEM_ALERT_WINDOW = 888;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_DENIED_SYSTEM_ALERT_WINDOW = 889;
 
-    // OBSOLETE
+    // ACTION: The app-op permission SYSTEM_ALERT_WINDOW was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_APPOP_REVOKE_SYSTEM_ALERT_WINDOW = 890;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_REQUEST_WRITE_SETTINGS = 891;
 
-    // OBSOLETE
+    // ACTION: An app was granted the app-op permission REQUEST_WRITE_SETTINGS
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_GRANT_WRITE_SETTINGS = 892;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_DENIED_WRITE_SETTINGS = 893;
 
-    // OBSOLETE
+    // ACTION: The app-op permission REQUEST_WRITE_SETTINGS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_APPOP_REVOKE_WRITE_SETTINGS = 894;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_REQUEST_REQUEST_INSTALL_PACKAGES = 895;
 
-    // OBSOLETE
+    // ACTION: An app was granted the app-op permission REQUEST_INSTALL_PACKAGES
+    // PACKAGE: The package name of the app that was granted the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_GRANT_REQUEST_INSTALL_PACKAGES = 896;
 
-    // OBSOLETE
+    // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    // OBSOLETE as of Android P
     ACTION_APPOP_DENIED_REQUEST_INSTALL_PACKAGES = 897;
 
-    // OBSOLETE
+    // ACTION: The app-op permission REQUEST_INSTALL_PACKAGES was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    // OBSOLETE as of Android P
     ACTION_APPOP_REVOKE_REQUEST_INSTALL_PACKAGES = 898;
 
     // ACTION: Phase 1 of instant application resolution occurred
@@ -4764,6 +5013,8 @@
     // OS: P
     FIELD_SELECTION_WIDGET_VERSION = 1262;
 
+    // ---- End P Constants, all P constants go above this line ----
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 2289f85..3dbee42 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -42,6 +42,8 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -89,6 +91,9 @@
     private ActivityStack mPinnedStack = null;
     private ActivityStack mSplitScreenPrimaryStack = null;
 
+    // Used in updating the display size
+    private Point mTmpDisplaySize = new Point();
+
     ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
         mSupervisor = supervisor;
         mDisplayId = displayId;
@@ -97,6 +102,13 @@
             throw new IllegalStateException("Display does not exist displayId=" + displayId);
         }
         mDisplay = display;
+
+        updateBounds();
+    }
+
+    void updateBounds() {
+        mDisplay.getSize(mTmpDisplaySize);
+        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
     }
 
     void addChild(ActivityStack stack, int position) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae91b82..083d306 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2494,7 +2494,7 @@
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
             ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
             ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
-                    DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+                    DUMP_FLAG_PRIORITY_HIGH);
             ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
             ServiceManager.addService("dbinfo", new DbBinder(this));
             if (MONITOR_CPU_USAGE) {
@@ -2552,9 +2552,15 @@
         private final PriorityDump.PriorityDumper mPriorityDumper =
                 new PriorityDump.PriorityDumper() {
             @Override
-            public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args,
+            public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
                     boolean asProto) {
                 if (asProto) return;
+                mActivityManagerService.dumpApplicationMemoryUsage(fd,
+                        pw, "  ", new String[] {"-a"}, false, null);
+            }
+            @Override
+            public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
+                if (asProto) return;
                 mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, "  ", args, false, null);
             }
         };
@@ -8092,7 +8098,7 @@
                     return false;
                 }
                 // An activity is consider to be in multi-window mode if its task isn't fullscreen.
-                return !r.getTask().mFullscreen;
+                return r.inMultiWindowMode();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -10111,8 +10117,8 @@
                 } else {
                     // Task isn't in window manager yet since it isn't associated with a stack.
                     // Return the persist value from activity manager
-                    if (task.mBounds != null) {
-                        rect.set(task.mBounds);
+                    if (!task.matchParentBounds()) {
+                        rect.set(task.getBounds());
                     } else if (task.mLastNonFullscreenBounds != null) {
                         rect.set(task.mLastNonFullscreenBounds);
                     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a2c46f1..11590d6 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -165,7 +165,6 @@
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.XmlUtils;
@@ -343,12 +342,6 @@
     // on the window.
     int mRotationAnimationHint = -1;
 
-    // The bounds of this activity. Mainly used for aspect-ratio compatibility.
-    // TODO(b/36505427): Every level on ConfigurationContainer now has bounds information, which
-    // directly affects the configuration. We should probably move this into that class and have it
-    // handle calculating override configuration from the bounds.
-    private final Rect mBounds = new Rect();
-
     private boolean mShowWhenLocked;
     private boolean mTurnScreenOn;
 
@@ -414,8 +407,8 @@
         if (!getOverrideConfiguration().equals(EMPTY)) {
             pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration());
         }
-        if (!mBounds.isEmpty()) {
-            pw.println(prefix + "mBounds=" + mBounds);
+        if (!matchParentBounds()) {
+            pw.println(prefix + "bounds=" + getBounds());
         }
         if (resultTo != null || resultWho != null) {
             pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
@@ -648,7 +641,7 @@
         }
 
         // An activity is considered to be in multi-window mode if its task isn't fullscreen.
-        final boolean inMultiWindowMode = !task.mFullscreen;
+        final boolean inMultiWindowMode = task.inMultiWindowMode();
         if (inMultiWindowMode != mLastReportedMultiWindowMode) {
             mLastReportedMultiWindowMode = inMultiWindowMode;
             scheduleMultiWindowModeChanged(getConfiguration());
@@ -966,14 +959,14 @@
                 (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
                 task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
                 appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L, mBounds);
+                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
 
         task.addActivityToTop(this);
 
         // When an activity is started directly into a split-screen fullscreen stack, we need to
         // update the initial multi-window modes so that the callbacks are scheduled correctly when
         // the user leaves that mode.
-        mLastReportedMultiWindowMode = !task.mFullscreen;
+        mLastReportedMultiWindowMode = inMultiWindowMode();
         mLastReportedPictureInPictureMode = inPinnedWindowingMode();
     }
 
@@ -2172,33 +2165,25 @@
         mLastReportedConfiguration.setConfiguration(global, override);
     }
 
-    @Override
-    public void onOverrideConfigurationChanged(Configuration newConfig) {
-        final Configuration currentConfig = getOverrideConfiguration();
-        if (currentConfig.equals(newConfig)) {
-            return;
-        }
-        super.onOverrideConfigurationChanged(newConfig);
-        if (mWindowContainerController == null) {
-            return;
-        }
-        mWindowContainerController.onOverrideConfigurationChanged(newConfig, mBounds);
-    }
-
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private void updateOverrideConfiguration() {
         mTmpConfig.unset();
         computeBounds(mTmpBounds);
-        if (mTmpBounds.equals(mBounds)) {
+
+        if (mTmpBounds.equals(getOverrideBounds())) {
             return;
         }
 
-        mBounds.set(mTmpBounds);
+        setBounds(mTmpBounds);
+
+        final Rect updatedBounds = getOverrideBounds();
+
         // Bounds changed...update configuration to match.
-        if (!mBounds.isEmpty()) {
-            task.computeOverrideConfiguration(mTmpConfig, mBounds, null /* insetBounds */,
+        if (!matchParentBounds()) {
+            task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
                     false /* overrideWidth */, false /* overrideHeight */);
         }
+
         onOverrideConfigurationChanged(mTmpConfig);
     }
 
@@ -2225,7 +2210,7 @@
         outBounds.setEmpty();
         final float maxAspectRatio = info.maxAspectRatio;
         final ActivityStack stack = getStack();
-        if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0
+        if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
                 || isInVrUiMode(getConfiguration())) {
             // We don't set override configuration if that activity task isn't fullscreen. I.e. the
             // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
@@ -2256,11 +2241,11 @@
         if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
             // The display matches or is less than the activity aspect ratio, so nothing else to do.
             // Return the existing bounds. If this method is running for the first time,
-            // {@link mBounds} will be empty (representing no override). If the method has run
-            // before, then effect of {@link mBounds} will already have been applied to the
+            // {@link #getOverrideBounds()} will be empty (representing no override). If the method has run
+            // before, then effect of {@link #getOverrideBounds()} will already have been applied to the
             // value returned from {@link getConfiguration}. Refer to
             // {@link TaskRecord#computeOverrideConfiguration}.
-            outBounds.set(mBounds);
+            outBounds.set(getOverrideBounds());
             return;
         }
 
@@ -2272,12 +2257,6 @@
         outBounds.offsetTo(left, 0 /* top */);
     }
 
-    /** Get bounds of the activity. */
-    @VisibleForTesting
-    Rect getBounds() {
-        return new Rect(mBounds);
-    }
-
     /**
      * Make sure the given activity matches the current configuration. Returns false if the activity
      * had to be destroyed.  Returns true if the configuration is the same, or the activity will
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6985d6e..4816998 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -156,7 +156,6 @@
  */
 class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
         implements StackWindowListener {
-
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -322,11 +321,6 @@
      */
     boolean mForceHidden = false;
 
-    // Whether or not this stack covers the entire screen; by default stacks are fullscreen
-    boolean mFullscreen = true;
-    // Current bounds of the stack or null if fullscreen.
-    Rect mBounds = null;
-
     private boolean mUpdateBoundsDeferred;
     private boolean mUpdateBoundsDeferredCalled;
     private final Rect mDeferredBounds = new Rect();
@@ -342,8 +336,6 @@
     /** The attached Display's unique identifier, or -1 if detached */
     int mDisplayId;
 
-    /** Temp variables used during override configuration update. */
-    private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
     private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
     private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
     private final Rect mTmpRect2 = new Rect();
@@ -574,7 +566,7 @@
                 }
             }
 
-            if (!Objects.equals(mBounds, mTmpRect2)) {
+            if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
                 resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
             }
         } finally {
@@ -641,9 +633,7 @@
      */
     private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
         mDisplayId = activityDisplay.mDisplayId;
-        mBounds = bounds != null ? new Rect(bounds) : null;
-        mFullscreen = mBounds == null;
-
+        setBounds(bounds);
         onParentChanged();
 
         activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
@@ -651,7 +641,7 @@
             // If we created a docked stack we want to resize it so it resizes all other stacks
             // in the system.
             mStackSupervisor.resizeDockedStackLocked(
-                    mBounds, null, null, null, null, PRESERVE_WINDOWS);
+                    getOverrideBounds(), null, null, null, null, PRESERVE_WINDOWS);
         }
     }
 
@@ -766,8 +756,9 @@
         return false;
     }
 
-    void setBounds(Rect bounds) {
-        mBounds = mFullscreen ? null : new Rect(bounds);
+    @Override
+    public int setBounds(Rect bounds) {
+        return super.setBounds(!inMultiWindowMode() ? null : bounds);
     }
 
     ActivityRecord topRunningActivityLocked() {
@@ -2495,7 +2486,7 @@
             // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
             // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
             final boolean lastActivityTranslucent = lastStack != null
-                    && (!lastStack.mFullscreen
+                    && (lastStack.inMultiWindowMode()
                     || (lastStack.mLastPausedActivity != null
                     && !lastStack.mLastPausedActivity.fullscreen));
 
@@ -2739,8 +2730,7 @@
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         mTaskHistory.remove(task);
         mTaskHistory.add(position, task);
-        mWindowContainerController.positionChildAt(task.getWindowContainerController(), position,
-                task.mBounds, task.getOverrideConfiguration());
+        mWindowContainerController.positionChildAt(task.getWindowContainerController(), position);
         updateTaskMovement(task, true);
     }
 
@@ -4602,8 +4592,6 @@
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
     // Need to consolidate those calls points into this resize method so anyone can call directly.
     void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
-        bounds = TaskRecord.validateBounds(bounds);
-
         if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
             return;
         }
@@ -4613,7 +4601,6 @@
         final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
 
         mTmpBounds.clear();
-        mTmpConfigs.clear();
         mTmpInsetBounds.clear();
 
         for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
@@ -4624,7 +4611,7 @@
                     // For freeform stack we don't adjust the size of the tasks to match that
                     // of the stack, but we do try to make sure the tasks are still contained
                     // with the bounds of the stack.
-                    mTmpRect2.set(task.mBounds);
+                    mTmpRect2.set(task.getOverrideBounds());
                     fitWithinBounds(mTmpRect2, bounds);
                     task.updateOverrideConfiguration(mTmpRect2);
                 } else {
@@ -4632,15 +4619,13 @@
                 }
             }
 
-            mTmpConfigs.put(task.taskId, task.getOverrideConfiguration());
-            mTmpBounds.put(task.taskId, task.mBounds);
+            mTmpBounds.put(task.taskId, task.getOverrideBounds());
             if (tempTaskInsetBounds != null) {
                 mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
             }
         }
 
-        mFullscreen = mWindowContainerController.resize(bounds, mTmpConfigs, mTmpBounds,
-                mTmpInsetBounds);
+        mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
         setBounds(bounds);
     }
 
@@ -4655,7 +4640,7 @@
      * @param stackBounds Bounds within which the other bounds should remain.
      */
     private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
-        if (stackBounds == null || stackBounds.contains(bounds)) {
+        if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
             return;
         }
 
@@ -4873,8 +4858,7 @@
                 pw.println("");
             }
             pw.println(prefix + "Task id #" + task.taskId);
-            pw.println(prefix + "mFullscreen=" + task.mFullscreen);
-            pw.println(prefix + "mBounds=" + task.mBounds);
+            pw.println(prefix + "mBounds=" + task.getOverrideBounds());
             pw.println(prefix + "mMinWidth=" + task.mMinWidth);
             pw.println(prefix + "mMinHeight=" + task.mMinHeight);
             pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
@@ -4981,7 +4965,7 @@
             if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
                     && mStackSupervisor.isFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
-                if (mFullscreen || !adjustFocusToNextFocusableStack(myReason)) {
+                if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) {
                     mStackSupervisor.moveHomeStackToFront(myReason);
                 }
             }
@@ -5011,8 +4995,8 @@
         final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
                 .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
         if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
-                && mBounds != null && task.isResizeable() && !isLockscreenShown) {
-            task.updateOverrideConfiguration(mBounds);
+                && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
+            task.updateOverrideConfiguration(getOverrideBounds());
         }
         task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
         return task;
@@ -5174,10 +5158,13 @@
             mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
         }
         proto.write(DISPLAY_ID, mDisplayId);
-        if (mBounds != null) {
-            mBounds.writeToProto(proto, BOUNDS);
+        if (!matchParentBounds()) {
+            final Rect bounds = getOverrideBounds();
+            bounds.writeToProto(proto, BOUNDS);
         }
-        proto.write(FULLSCREEN, mFullscreen);
+
+        // TODO: Remove, no longer needed with windowingMode.
+        proto.write(FULLSCREEN, matchParentBounds());
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ddde4bc..5525cdb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2118,7 +2118,7 @@
         }
 
         if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
-            final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
+            final Rect bounds = options.getLaunchBounds();
             task.updateOverrideConfiguration(bounds);
 
             ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
@@ -2626,7 +2626,8 @@
 
             // TODO: Checking for isAttached might not be needed as if the user passes in null
             // dockedBounds then they want the docked stack to be dismissed.
-            if (stack.mFullscreen || (dockedBounds == null && !stack.isAttached())) {
+            if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                    || (dockedBounds == null && !stack.isAttached())) {
                 // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
@@ -3058,7 +3059,7 @@
             // Resize the pinned stack to match the current size of the task the activity we are
             // going to be moving is currently contained in. We do this to have the right starting
             // animation bounds for the pinned stack to the desired bounds the caller wants.
-            resizeStackLocked(stack, task.mBounds, null /* tempTaskBounds */,
+            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
                     null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                     true /* allowResizeInDockedMode */, !DEFER_RESUME);
 
@@ -3782,9 +3783,8 @@
                 pw.println("  Stack #" + stack.mStackId
                         + ": type=" + activityTypeToString(stack.getActivityType())
                         + " mode=" + windowingModeToString(stack.getWindowingMode()));
-                pw.println("  mFullscreen=" + stack.mFullscreen);
                 pw.println("  isSleeping=" + stack.shouldSleepActivities());
-                pw.println("  mBounds=" + stack.mBounds);
+                pw.println("  mBounds=" + stack.getOverrideBounds());
 
                 printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
                         needSep);
@@ -4054,6 +4054,7 @@
     private void handleDisplayChanged(int displayId) {
         synchronized (mService) {
             ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+            // TODO: The following code block should be moved into {@link ActivityDisplay}.
             if (activityDisplay != null) {
                 // The window policy is responsible for stopping activities on the default display
                 if (displayId != Display.DEFAULT_DISPLAY) {
@@ -4067,7 +4068,8 @@
                         activityDisplay.mOffToken = null;
                     }
                 }
-                // TODO: Update the bounds.
+
+                activityDisplay.updateBounds();
             }
             mWindowManager.onDisplayChanged(displayId);
         }
@@ -4289,7 +4291,7 @@
             return;
         }
 
-        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
+        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds());
     }
 
     void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
diff --git a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
index d5f9cf3..793884d 100644
--- a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
@@ -47,10 +47,10 @@
             return RESULT_SKIP;
         }
 
-        final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
+        final Rect bounds = options.getLaunchBounds();
 
         // Bounds weren't valid.
-        if (bounds == null) {
+        if (bounds == null || bounds.isEmpty()) {
             return RESULT_SKIP;
         }
 
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index c958fca..d89568e 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -88,7 +88,7 @@
 
         final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks();
 
-        updateAvailableRect(task, mAvailableRect);
+        mAvailableRect.set(task.getParent().getBounds());
 
         if (layout == null) {
             positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
@@ -123,17 +123,6 @@
         return RESULT_CONTINUE;
     }
 
-    private void updateAvailableRect(TaskRecord task, Rect availableRect) {
-        final Rect stackBounds = task.getStack().mBounds;
-
-        if (stackBounds != null) {
-            availableRect.set(stackBounds);
-        } else {
-            task.getStack().getDisplay().mDisplay.getSize(mDisplaySize);
-            availableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
-        }
-    }
-
     @VisibleForTesting
     static int getFreeformStartLeft(Rect bounds) {
         return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR;
@@ -294,9 +283,9 @@
 
     private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) {
         for (int i = tasks.size() - 1; i >= 0; i--) {
-            TaskRecord task = tasks.get(i);
-            if (!task.mActivities.isEmpty() && task.mBounds != null) {
-                Rect bounds = task.mBounds;
+            final TaskRecord task = tasks.get(i);
+            if (!task.mActivities.isEmpty() && !task.matchParentBounds()) {
+                final Rect bounds = task.getOverrideBounds();
                 if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds)
                         || closeLeftBottomCorner(proposal, bounds)
                         || closeRightBottomCorner(proposal, bounds)) {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index d35c37b..6e6d7e9 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -1426,9 +1426,6 @@
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
     ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
-        // Update the task description to reflect any changes in the task stack
-        tr.updateTaskDescription();
-
         // Compose the recent task info
         ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
         rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
@@ -1444,8 +1441,8 @@
         rti.affiliatedTaskId = tr.mAffiliatedTaskId;
         rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
         rti.numActivities = 0;
-        if (tr.mBounds != null) {
-            rti.bounds = new Rect(tr.mBounds);
+        if (!tr.matchParentBounds()) {
+            rti.bounds = new Rect(tr.getOverrideBounds());
         }
         rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
         rti.resizeMode = tr.mResizeMode;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a9c6eee..365990f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -19,9 +19,6 @@
 import static android.app.ActivityManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -293,11 +290,6 @@
 
     final ActivityManagerService mService;
 
-    // Whether or not this task covers the entire screen; by default tasks are fullscreen.
-    boolean mFullscreen = true;
-
-    // Bounds of the Task. null for fullscreen tasks.
-    Rect mBounds = null;
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
     private final Rect mTmpRect = new Rect();
@@ -480,68 +472,76 @@
     }
 
     boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
-        if (!isResizeable()) {
-            Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
-            return true;
-        }
+        mService.mWindowManager.deferSurfaceLayout();
 
-        // If this is a forced resize, let it go through even if the bounds is not changing,
-        // as we might need a relayout due to surface size change (to/from fullscreen).
-        final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
-        if (Objects.equals(mBounds, bounds) && !forced) {
-            // Nothing to do here...
-            return true;
-        }
-        bounds = validateBounds(bounds);
-
-        if (mWindowContainerController == null) {
-            // Task doesn't exist in window manager yet (e.g. was restored from recents).
-            // All we can do for now is update the bounds so it can be used when the task is
-            // added to window manager.
-            updateOverrideConfiguration(bounds);
-            if (!inFreeformWindowingMode()) {
-                // re-restore the task so it can have the proper stack association.
-                mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
+        try {
+            if (!isResizeable()) {
+                Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
+                return true;
             }
-            return true;
-        }
 
-        if (!canResizeToBounds(bounds)) {
-            throw new IllegalArgumentException("resizeTask: Can not resize task=" + this
-                    + " to bounds=" + bounds + " resizeMode=" + mResizeMode);
-        }
+            // If this is a forced resize, let it go through even if the bounds is not changing,
+            // as we might need a relayout due to surface size change (to/from fullscreen).
+            final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
+            if (equivalentOverrideBounds(bounds) && !forced) {
+                // Nothing to do here...
+                return true;
+            }
 
-        // Do not move the task to another stack here.
-        // This method assumes that the task is already placed in the right stack.
-        // we do not mess with that decision and we only do the resize!
+            if (mWindowContainerController == null) {
+                // Task doesn't exist in window manager yet (e.g. was restored from recents).
+                // All we can do for now is update the bounds so it can be used when the task is
+                // added to window manager.
+                updateOverrideConfiguration(bounds);
+                if (!inFreeformWindowingMode()) {
+                    // re-restore the task so it can have the proper stack association.
+                    mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
+                }
+                return true;
+            }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
+            if (!canResizeToBounds(bounds)) {
+                throw new IllegalArgumentException("resizeTask: Can not resize task=" + this
+                        + " to bounds=" + bounds + " resizeMode=" + mResizeMode);
+            }
 
-        final boolean updatedConfig = updateOverrideConfiguration(bounds);
-        // This variable holds information whether the configuration didn't change in a significant
-        // way and the activity was kept the way it was. If it's false, it means the activity had
-        // to be relaunched due to configuration change.
-        boolean kept = true;
-        if (updatedConfig) {
-            final ActivityRecord r = topRunningActivityLocked();
-            if (r != null && !deferResume) {
-                kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindow);
-                mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
-                if (!kept) {
-                    mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            // Do not move the task to another stack here.
+            // This method assumes that the task is already placed in the right stack.
+            // we do not mess with that decision and we only do the resize!
+
+            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
+
+            final boolean updatedConfig = updateOverrideConfiguration(bounds);
+            // This variable holds information whether the configuration didn't change in a significant
+
+            // way and the activity was kept the way it was. If it's false, it means the activity
+            // had
+            // to be relaunched due to configuration change.
+            boolean kept = true;
+            if (updatedConfig) {
+                final ActivityRecord r = topRunningActivityLocked();
+                if (r != null && !deferResume) {
+                    kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */,
+                            preserveWindow);
+                    mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
+                            !PRESERVE_WINDOWS);
+                    if (!kept) {
+                        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                    }
                 }
             }
-        }
-        mWindowContainerController.resize(mBounds, getOverrideConfiguration(), kept, forced);
+            mWindowContainerController.resize(kept, forced);
 
-        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        return kept;
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            return kept;
+        } finally {
+            mService.mWindowManager.continueSurfaceLayout();
+        }
     }
 
     // TODO: Investigate combining with the resize() method above.
     void resizeWindowContainer() {
-        mWindowContainerController.resize(mBounds, getOverrideConfiguration(), false /* relayout */,
-                false /* forced */);
+        mWindowContainerController.resize(false /* relayout */, false /* forced */);
     }
 
     void getWindowContainerBounds(Rect bounds) {
@@ -686,16 +686,17 @@
             // Make sure the task has the appropriate bounds/size for the stack it is in.
             final boolean toStackSplitScreenPrimary =
                     toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+            final Rect configBounds = getOverrideBounds();
             if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
                     || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
-                    && !Objects.equals(mBounds, toStack.mBounds)) {
-                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                    && !Objects.equals(configBounds, toStack.getOverrideBounds())) {
+                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
                         deferResume);
             } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
                 Rect bounds = getLaunchBounds();
                 if (bounds == null) {
                     mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
-                    bounds = mBounds;
+                    bounds = configBounds;
                 }
                 kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
             } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
@@ -704,7 +705,7 @@
                     // mode
                     mService.mStackSupervisor.moveRecentsStackToFront(reason);
                 }
-                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
                         deferResume);
             }
         } finally {
@@ -1501,8 +1502,10 @@
             return true;
         }
         final boolean landscape = bounds.width() > bounds.height();
+        final Rect configBounds = getOverrideBounds();
         if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
-            return mBounds == null || landscape == (mBounds.width() > mBounds.height());
+            return configBounds.isEmpty()
+                    || landscape == (configBounds.width() > configBounds.height());
         }
         return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape)
                 && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape);
@@ -1623,6 +1626,9 @@
         final int effectiveRootIndex = findEffectiveRootIndex();
         final ActivityRecord r = mActivities.get(effectiveRootIndex);
         setIntent(r);
+
+        // Update the task description when the activities change
+        updateTaskDescription();
     }
 
     void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
@@ -1920,8 +1926,9 @@
             return;
         }
 
+        final Rect configBounds = getOverrideBounds();
         if (adjustWidth) {
-            if (mBounds != null && bounds.right == mBounds.right) {
+            if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
                 bounds.left = bounds.right - minWidth;
             } else {
                 // Either left bounds match, or neither match, or the previous bounds were
@@ -1930,7 +1937,7 @@
             }
         }
         if (adjustHeight) {
-            if (mBounds != null && bounds.bottom == mBounds.bottom) {
+            if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
                 bounds.top = bounds.bottom - minHeight;
             } else {
                 // Either top bounds match, or neither match, or the previous bounds were
@@ -1977,38 +1984,37 @@
      * @return True if the override configuration was updated.
      */
     boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
-        if (Objects.equals(mBounds, bounds)) {
+        if (equivalentOverrideBounds(bounds)) {
             return false;
         }
+        final Rect currentBounds = getOverrideBounds();
+
         mTmpConfig.setTo(getOverrideConfiguration());
-        final boolean oldFullscreen = mFullscreen;
+        final boolean oldMatchParentBounds = matchParentBounds();
         final Configuration newConfig = getOverrideConfiguration();
 
-        mFullscreen = bounds == null;
+        final boolean matchParentBounds = bounds == null || bounds.isEmpty();
         final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
-        if (mFullscreen) {
-            if (mBounds != null && persistBounds) {
-                mLastNonFullscreenBounds = mBounds;
+        if (matchParentBounds) {
+            if (!currentBounds.isEmpty() && persistBounds) {
+                mLastNonFullscreenBounds = currentBounds;
             }
-            mBounds = null;
+            setBounds(null);
             newConfig.unset();
         } else {
             mTmpRect.set(bounds);
             adjustForMinimalTaskDimensions(mTmpRect);
-            if (mBounds == null) {
-                mBounds = new Rect(mTmpRect);
-            } else {
-                mBounds.set(mTmpRect);
-            }
+            setBounds(mTmpRect);
+
             if (mStack == null || persistBounds) {
-                mLastNonFullscreenBounds = mBounds;
+                mLastNonFullscreenBounds = getOverrideBounds();
             }
             computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
         onOverrideConfigurationChanged(newConfig);
 
-        if (mFullscreen != oldFullscreen) {
+        if (matchParentBounds != oldMatchParentBounds) {
             mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
         }
 
@@ -2053,23 +2059,19 @@
         final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
         final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
         config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
-
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
-        final Rect bounds = validateBounds(getLaunchBounds());
+        final Rect bounds = getLaunchBounds();
         updateOverrideConfiguration(bounds);
-        if (bounds != null) {
-            bounds.set(mBounds);
+        if (bounds != null && !bounds.isEmpty()) {
+            // TODO: Review if we actually want to do this - we are setting the launch bounds
+            // directly here.
+            bounds.set(getOverrideBounds());
         }
         return bounds;
     }
 
-    static Rect validateBounds(Rect bounds) {
-        // TODO: Not needed once we have bounds in WindowConfiguration.
-        return (bounds != null && bounds.isEmpty()) ? null : bounds;
-    }
-
     /** Updates the task's bounds and override configuration to match what is expected for the
      * input stack. */
     void updateOverrideConfigurationForStack(ActivityStack inStack) {
@@ -2082,7 +2084,7 @@
                 throw new IllegalArgumentException("Can not position non-resizeable task="
                         + this + " in stack=" + inStack);
             }
-            if (mBounds != null) {
+            if (!matchParentBounds()) {
                 return;
             }
             if (mLastNonFullscreenBounds != null) {
@@ -2091,7 +2093,7 @@
                 mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
             }
         } else {
-            updateOverrideConfiguration(inStack.mBounds);
+            updateOverrideConfiguration(inStack.getOverrideBounds());
         }
     }
 
@@ -2105,9 +2107,9 @@
         if (!isActivityTypeStandardOrUndefined()
                 || windowingMode == WINDOWING_MODE_FULLSCREEN
                 || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
-            return isResizeable() ? mStack.mBounds : null;
+            return isResizeable() ? mStack.getOverrideBounds() : null;
         } else if (!getWindowConfiguration().persistTaskBounds()) {
-            return mStack.mBounds;
+            return mStack.getOverrideBounds();
         }
         return mLastNonFullscreenBounds;
     }
@@ -2287,9 +2289,12 @@
         }
         proto.write(ACTIVITY_TYPE, getActivityType());
         proto.write(RESIZE_MODE, mResizeMode);
-        proto.write(FULLSCREEN, mFullscreen);
-        if (mBounds != null) {
-            mBounds.writeToProto(proto, BOUNDS);
+        // TODO: Remove, no longer needed with windowingMode.
+        proto.write(FULLSCREEN, matchParentBounds());
+
+        if (!matchParentBounds()) {
+            final Rect bounds = getOverrideBounds();
+            bounds.writeToProto(proto, BOUNDS);
         }
         proto.write(MIN_WIDTH, mMinWidth);
         proto.write(MIN_HEIGHT, mMinHeight);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index a528ecd..00a0d3d 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -181,13 +181,12 @@
             IApplicationToken token, AppWindowContainerListener listener, int index,
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            Rect bounds) {
+            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
         this(taskController, token, listener, index, requestedOrientation, fullscreen,
                 showForAllUsers,
                 configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
                 targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
-                WindowManagerService.getInstance(), bounds);
+                WindowManagerService.getInstance());
     }
 
     public AppWindowContainerController(TaskWindowContainerController taskController,
@@ -195,7 +194,7 @@
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            WindowManagerService service, Rect bounds) {
+            WindowManagerService service) {
         super(listener, service);
         mHandler = new H(service.mH.getLooper());
         mToken = token;
@@ -216,7 +215,7 @@
             atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
                     requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
-                    alwaysFocusable, this, bounds);
+                    alwaysFocusable, this);
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
                     + " controller=" + taskController + " at " + index);
             task.addChild(atoken, index);
@@ -228,11 +227,11 @@
             boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
             boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
             int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-            boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
+            boolean alwaysFocusable, AppWindowContainerController controller) {
         return new AppWindowToken(service, token, voiceInteraction, dc,
                 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
                 rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                controller, bounds);
+                controller);
     }
 
     public void removeContainer(int displayId) {
@@ -299,17 +298,6 @@
         }
     }
 
-    // TODO(b/36505427): Maybe move to WindowContainerController so other sub-classes can use it as
-    // a generic way to set override config. Need to untangle current ways the override config is
-    // currently set for tasks and displays before we are doing that though.
-    public void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
-        synchronized(mWindowMap) {
-            if (mContainer != null) {
-                mContainer.onOverrideConfigurationChanged(overrideConfiguration, bounds);
-            }
-        }
-    }
-
     public void setDisablePreviewScreenshots(boolean disable) {
         synchronized (mWindowMap) {
             if (mContainer == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 627f8ce..c39ce98 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -175,11 +175,6 @@
     private boolean mLastContainsShowWhenLockedWindow;
     private boolean mLastContainsDismissKeyguardWindow;
 
-    // The bounds of this activity. Mainly used for aspect-ratio compatibility.
-    // TODO(b/36505427): Every level on WindowContainer now has bounds information, which directly
-    // affects the configuration. We should probably move this into that class.
-    private final Rect mBounds = new Rect();
-
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
     ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
 
@@ -196,8 +191,8 @@
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
             boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
             int configChanges, boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller, Rect bounds) {
-        this(service, token, voiceInteraction, dc, fullscreen, bounds);
+            AppWindowContainerController controller) {
+        this(service, token, voiceInteraction, dc, fullscreen);
         setController(controller);
         mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
         mShowForAllUsers = showForAllUsers;
@@ -214,7 +209,7 @@
     }
 
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
-            DisplayContent dc, boolean fillsParent, Rect bounds) {
+            DisplayContent dc, boolean fillsParent) {
         super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true, dc,
                 false /* ownerCanManageAppTokens */);
         appToken = token;
@@ -222,27 +217,6 @@
         mFillsParent = fillsParent;
         mInputApplicationHandle = new InputApplicationHandle(this);
         mAppAnimator = new AppWindowAnimator(this, service);
-        if (bounds != null) {
-            mBounds.set(bounds);
-        }
-    }
-
-    void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
-        onOverrideConfigurationChanged(overrideConfiguration);
-        if (mBounds.equals(bounds)) {
-            return;
-        }
-        // TODO(b/36505427): If bounds is in WC, then we can automatically call onResize() when set.
-        mBounds.set(bounds);
-        onResize();
-    }
-
-    void getBounds(Rect outBounds) {
-        outBounds.set(mBounds);
-    }
-
-    boolean hasBounds() {
-        return !mBounds.isEmpty();
     }
 
     void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index b534b8a..d340923 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -36,6 +36,7 @@
 import android.annotation.CallSuper;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
@@ -46,6 +47,11 @@
  * hierarchy.
  */
 public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
+    /**
+     * {@link #Rect} returned from {@link #getOverrideBounds()} to prevent original value from being
+     * set directly.
+     */
+    private Rect mReturnBounds = new Rect();
 
     /** Contains override configuration settings applied to this configuration container. */
     private Configuration mOverrideConfiguration = new Configuration();
@@ -71,6 +77,16 @@
     // TODO: Can't have ag/2592611 soon enough!
     private final Configuration mTmpConfig = new Configuration();
 
+    // Used for setting bounds
+    private final Rect mTmpRect = new Rect();
+
+    static final int BOUNDS_CHANGE_NONE = 0;
+    // Return value from {@link setBounds} indicating the position of the override bounds changed.
+    static final int BOUNDS_CHANGE_POSITION = 1;
+    // Return value from {@link setBounds} indicating the size of the override bounds changed.
+    static final int BOUNDS_CHANGE_SIZE = 1 << 1;
+
+
     /**
      * Returns full configuration applied to this configuration container.
      * This method should be used for getting settings applied in each particular level of the
@@ -148,6 +164,118 @@
         }
     }
 
+    /**
+     * Indicates whether this container has not specified any bounds different from its parent. In
+     * this case, it will inherit the bounds of the first ancestor which specifies a bounds.
+     * @return {@code true} if no explicit bounds have been set at this container level.
+     *         {@code false} otherwise.
+     */
+    public boolean matchParentBounds() {
+        return getOverrideBounds().isEmpty();
+    }
+
+    /**
+     * Returns whether the bounds specified is considered the same as the existing override bounds.
+     * This is either when the two bounds are equal or the override bounds is empty and the
+     * specified bounds is null.
+     *
+     * @return {@code true} if the bounds are equivalent, {@code false} otherwise
+     */
+    public boolean equivalentOverrideBounds(Rect bounds) {
+        return equivalentBounds(getOverrideBounds(),  bounds);
+    }
+
+    /**
+     * Returns whether the two bounds are equal to each other or are a combination of null or empty.
+     */
+    public static boolean equivalentBounds(Rect bounds, Rect other) {
+        return bounds == other
+                || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null)))
+                || (other != null && other.isEmpty() && bounds == null);
+    }
+
+    /**
+     * Returns the effective bounds of this container, inheriting the first non-empty bounds set in
+     * its ancestral hierarchy, including itself.
+     * @return
+     */
+    public Rect getBounds() {
+        mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
+        return mReturnBounds;
+    }
+
+    public void getBounds(Rect outBounds) {
+        outBounds.set(getBounds());
+    }
+
+    /**
+     * Returns the current bounds explicitly set on this container. The {@link Rect} handed back is
+     * shared for all calls to this method and should not be modified.
+     */
+    public Rect getOverrideBounds() {
+        mReturnBounds.set(getOverrideConfiguration().windowConfiguration.getBounds());
+
+        return mReturnBounds;
+    }
+
+    /**
+     * Sets the passed in {@link Rect} to the current bounds.
+     * @see {@link #getOverrideBounds()}.
+     */
+    public void getOverrideBounds(Rect outBounds) {
+        outBounds.set(getOverrideBounds());
+    }
+
+    /**
+     * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor.
+     * This value will be reported when {@link #getBounds()} and {@link #getOverrideBounds()}. If
+     * an empty {@link Rect} or null is specified, this container will be considered to match its
+     * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent.
+     * @param bounds The bounds defining the container size.
+     * @return a bitmask representing the types of changes made to the bounds.
+     */
+    public int setBounds(Rect bounds) {
+        int boundsChange = diffOverrideBounds(bounds);
+
+        if (boundsChange == BOUNDS_CHANGE_NONE) {
+            return boundsChange;
+        }
+
+
+        mTmpConfig.setTo(getOverrideConfiguration());
+        mTmpConfig.windowConfiguration.setBounds(bounds);
+        onOverrideConfigurationChanged(mTmpConfig);
+
+        return boundsChange;
+    }
+
+    public int setBounds(int left, int top, int right, int bottom) {
+        mTmpRect.set(left, top, right, bottom);
+        return setBounds(mTmpRect);
+    }
+
+    int diffOverrideBounds(Rect bounds) {
+        if (equivalentOverrideBounds(bounds)) {
+            return BOUNDS_CHANGE_NONE;
+        }
+
+        int boundsChange = BOUNDS_CHANGE_NONE;
+
+        final Rect existingBounds = getOverrideBounds();
+
+        if (bounds == null || existingBounds.left != bounds.left
+                || existingBounds.top != bounds.top) {
+            boundsChange |= BOUNDS_CHANGE_POSITION;
+        }
+
+        if (bounds == null || existingBounds.width() != bounds.width()
+                || existingBounds.height() != bounds.height()) {
+            boundsChange |= BOUNDS_CHANGE_SIZE;
+        }
+
+        return boundsChange;
+    }
+
     public WindowConfiguration getWindowConfiguration() {
         return mFullConfiguration.windowConfiguration;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 44dfa07..3fb3773 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -314,6 +314,9 @@
     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();
+
     WindowManagerService mService;
 
     /** Remove this display when animation on it has completed. */
@@ -1223,6 +1226,8 @@
             mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
                     mCompatDisplayMetrics);
         }
+
+        updateBounds();
         return mDisplayInfo;
     }
 
@@ -1541,8 +1546,17 @@
         // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
         mService.reconfigureDisplayLocked(this);
 
-        getDockedDividerController().onConfigurationChanged();
-        getPinnedStackController().onConfigurationChanged();
+        final DockedStackDividerController dividerController = getDockedDividerController();
+
+        if (dividerController != null) {
+            getDockedDividerController().onConfigurationChanged();
+        }
+
+        final PinnedStackController pinnedStackController = getPinnedStackController();
+
+        if (pinnedStackController != null) {
+            getPinnedStackController().onConfigurationChanged();
+        }
     }
 
     /**
@@ -1681,33 +1695,6 @@
         mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
     }
 
-    void getLogicalDisplayRect(Rect out) {
-        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
-        final int orientation = mDisplayInfo.rotation;
-        boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
-        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
-        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-        int width = mDisplayInfo.logicalWidth;
-        int left = (physWidth - width) / 2;
-        int height = mDisplayInfo.logicalHeight;
-        int top = (physHeight - height) / 2;
-        out.set(left, top, left + width, top + height);
-    }
-
-    private void getLogicalDisplayRect(Rect out, int orientation) {
-        getLogicalDisplayRect(out);
-
-        // Rotate the Rect if needed.
-        final int currentRotation = mDisplayInfo.rotation;
-        final int rotationDelta = deltaRotation(currentRotation, orientation);
-        if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
-            createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix);
-            mTmpRectF.set(out);
-            mTmpMatrix.mapRect(mTmpRectF);
-            mTmpRectF.round(out);
-        }
-    }
-
     /**
      * If display metrics changed, overrides are not set and it's not just a rotation - update base
      * values.
@@ -1775,6 +1762,8 @@
         }
 
         mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+
+        updateBounds();
     }
 
     void getContentRect(Rect out) {
@@ -2104,7 +2093,7 @@
     }
 
     void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
-        getLogicalDisplayRect(mTmpRect, newRotation);
+        getBounds(mTmpRect, newRotation);
 
         // Compute a transform matrix to undo the coordinate space transformation,
         // and present the window at the same physical position it previously occupied.
@@ -2881,6 +2870,44 @@
         return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
     }
 
+    private void updateBounds() {
+        calculateBounds(mTmpBounds);
+        setBounds(mTmpBounds);
+    }
+
+    // Determines the current display bounds based on the current state
+    private void calculateBounds(Rect out) {
+        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
+        final int orientation = mDisplayInfo.rotation;
+        boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
+        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
+        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
+        int width = mDisplayInfo.logicalWidth;
+        int left = (physWidth - width) / 2;
+        int height = mDisplayInfo.logicalHeight;
+        int top = (physHeight - height) / 2;
+        out.set(left, top, left + width, top + height);
+    }
+
+    @Override
+    public void getBounds(Rect out) {
+        calculateBounds(out);
+    }
+
+    private void getBounds(Rect out, int orientation) {
+        getBounds(out);
+
+        // Rotate the Rect if needed.
+        final int currentRotation = mDisplayInfo.rotation;
+        final int rotationDelta = deltaRotation(currentRotation, orientation);
+        if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
+            createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix);
+            mTmpRectF.set(out);
+            mTmpMatrix.mapRect(mTmpRectF);
+            mTmpRectF.round(out);
+        }
+    }
+
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8308417..a37598e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -655,8 +655,8 @@
     }
 
     private boolean isWithinDisplay(Task task) {
-        task.mStack.getBounds(mTmpRect);
-        mDisplayContent.getLogicalDisplayRect(mTmpRect2);
+        task.getBounds(mTmpRect);
+        mDisplayContent.getBounds(mTmpRect2);
         return mTmpRect.intersect(mTmpRect2);
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 18b0f97..7e29a3a 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -661,8 +661,8 @@
             if (w.inPinnedWindowingMode()) {
                 if (mAddPipInputConsumerHandle
                         && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
-                    // Update the bounds of the Pip input consumer to match the Pinned stack
-                    w.getStack().getBounds(pipTouchableBounds);
+                    // Update the bounds of the Pip input consumer to match the window bounds.
+                    w.getBounds(pipTouchableBounds);
                     pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
                     addInputWindowHandle(pipInputConsumer.mWindowHandle);
                     mAddPipInputConsumerHandle = false;
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 41f076d..b021a72 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -106,7 +106,7 @@
                 } else {
                     // Otherwise, use the display bounds
                     toBounds = new Rect();
-                    mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
+                    mContainer.getDisplayContent().getBounds(toBounds);
                 }
             } else if (fromFullscreen) {
                 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 70bf15c..5a39de5 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -28,7 +28,6 @@
 
 import android.content.Context;
 import android.graphics.Matrix;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -230,7 +229,7 @@
         mService = service;
         mContext = context;
         mDisplayContent = displayContent;
-        displayContent.getLogicalDisplayRect(mOriginalDisplayRect);
+        displayContent.getBounds(mOriginalDisplayRect);
 
         // Screenshot does NOT include rotation!
         final Display display = displayContent.getDisplay();
@@ -312,7 +311,7 @@
             float x = mTmpFloats[Matrix.MTRANS_X];
             float y = mTmpFloats[Matrix.MTRANS_Y];
             if (mForceDefaultOrientation) {
-                mDisplayContent.getLogicalDisplayRect(mCurrentDisplayRect);
+                mDisplayContent.getBounds(mCurrentDisplayRect);
                 x -= mCurrentDisplayRect.left;
                 y -= mCurrentDisplayRect.top;
             }
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 95c1d53..c2a4be5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -111,8 +111,7 @@
         }
     }
 
-    public void positionChildAt(TaskWindowContainerController child, int position, Rect bounds,
-            Configuration overrideConfig) {
+    public void positionChildAt(TaskWindowContainerController child, int position) {
         synchronized (mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
                     + " at " + position);
@@ -126,7 +125,7 @@
                         "positionChildAt: could not find stack for task=" + mContainer);
                 return;
             }
-            child.mContainer.positionAt(position, bounds, overrideConfig);
+            child.mContainer.positionAt(position);
             mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
         }
     }
@@ -178,24 +177,22 @@
      * Re-sizes a stack and its containing tasks.
      *
      * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param configs Configurations for tasks in the resized stack, keyed by task id.
      * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
-     * @return True if the stack is now fullscreen.
+     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
      */
-    public boolean resize(Rect bounds, SparseArray<Configuration> configs,
-            SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) {
+    public void resize(Rect bounds, SparseArray<Rect> taskBounds,
+            SparseArray<Rect> taskTempInsetBounds) {
         synchronized (mWindowMap) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
             }
             // We might trigger a configuration change. Save the current task bounds for freezing.
             mContainer.prepareFreezingTaskBounds();
-            if (mContainer.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
+            if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
                     && mContainer.isVisible()) {
                 mContainer.getDisplayContent().setLayoutNeeded();
                 mService.mWindowPlacerLocked.performSurfacePlacement();
             }
-            return mContainer.getRawFullscreen();
         }
     }
 
@@ -227,7 +224,7 @@
 
     public void getRawBounds(Rect outBounds) {
         synchronized (mWindowMap) {
-            if (mContainer.getRawFullscreen()) {
+            if (mContainer.matchParentBounds()) {
                 outBounds.setEmpty();
             } else {
                 mContainer.getRawBounds(outBounds);
@@ -275,6 +272,7 @@
 
             final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
 
+            config.windowConfiguration.setBounds(bounds);
             config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
             boolean intersectParentBounds = false;
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f70845e..8aa129a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -53,12 +53,6 @@
 
 class Task extends WindowContainer<AppWindowToken> {
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
-    // Return value from {@link setBounds} indicating no change was made to the Task bounds.
-    private static final int BOUNDS_CHANGE_NONE = 0;
-    // Return value from {@link setBounds} indicating the position of the Task bounds changed.
-    private static final int BOUNDS_CHANGE_POSITION = 1;
-    // Return value from {@link setBounds} indicating the size of the Task bounds changed.
-    private static final int BOUNDS_CHANGE_SIZE = 1 << 1;
 
     // TODO: Track parent marks like this in WindowContainer.
     TaskStack mStack;
@@ -67,8 +61,6 @@
     private boolean mDeferRemoval = false;
     final WindowManagerService mService;
 
-    // Content limits relative to the DisplayContent this sits in.
-    private Rect mBounds = new Rect();
     final Rect mPreparedFrozenBounds = new Rect();
     final Configuration mPreparedFrozenMergedConfig = new Configuration();
 
@@ -78,13 +70,12 @@
     // Device rotation as of the last time {@link #mBounds} was set.
     private int mRotation;
 
-    // Whether mBounds is fullscreen
-    private boolean mFillsParent = true;
-
     // For comparison with DisplayContent bounds.
     private Rect mTmpRect = new Rect();
     // For handling display rotations.
     private Rect mTmpRect2 = new Rect();
+    // For retrieving dim bounds
+    private Rect mTmpRect3 = new Rect();
 
     // Resize mode of the task. See {@link ActivityInfo#resizeMode}
     private int mResizeMode;
@@ -108,8 +99,8 @@
     private Dimmer mDimmer = new Dimmer(this);
     private final Rect mTmpDimBoundsRect = new Rect();
 
-    Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
-            int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
+    Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
+            boolean supportsPictureInPicture, TaskDescription taskDescription,
             TaskWindowContainerController controller) {
         mTaskId = taskId;
         mStack = stack;
@@ -118,7 +109,7 @@
         mResizeMode = resizeMode;
         mSupportsPictureInPicture = supportsPictureInPicture;
         setController(controller);
-        setBounds(bounds, getOverrideConfiguration());
+        setBounds(getOverrideBounds());
         mTaskDescription = taskDescription;
 
         // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
@@ -227,9 +218,8 @@
     }
 
     /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */
-    void positionAt(int position, Rect bounds, Configuration overrideConfig) {
+    void positionAt(int position) {
         mStack.positionChildAt(position, this, false /* includingParents */);
-        resizeLocked(bounds, overrideConfig, false /* force */);
     }
 
     @Override
@@ -271,48 +261,37 @@
         }
     }
 
-    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
-    // TODO: There is probably not a need to pass in overrideConfig anymore since any change to it
-    // will be automatically propagated from the AM. Also, mBound is going to be in
-    // WindowConfiguration long term.
-    private int setBounds(Rect bounds, Configuration overrideConfig) {
-        if (overrideConfig == null) {
-            overrideConfig = EMPTY;
+    public int setBounds(Rect bounds, boolean forceResize) {
+        final int boundsChanged = setBounds(bounds);
+
+        if (forceResize && (boundsChanged & BOUNDS_CHANGE_SIZE) != BOUNDS_CHANGE_SIZE) {
+            onResize();
+            return BOUNDS_CHANGE_SIZE | boundsChanged;
         }
 
-        boolean oldFullscreen = mFillsParent;
+        return boundsChanged;
+    }
+
+    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
+    @Override
+    public int setBounds(Rect bounds) {
         int rotation = Surface.ROTATION_0;
         final DisplayContent displayContent = mStack.getDisplayContent();
         if (displayContent != null) {
-            displayContent.getLogicalDisplayRect(mTmpRect);
             rotation = displayContent.getDisplayInfo().rotation;
-            mFillsParent = bounds == null;
-            if (mFillsParent) {
-                bounds = mTmpRect;
-            }
-        }
-
-        if (bounds == null) {
+        } else if (bounds == null) {
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return BOUNDS_CHANGE_NONE;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) {
+
+        if (equivalentOverrideBounds(bounds)) {
             return BOUNDS_CHANGE_NONE;
         }
 
-        int boundsChange = BOUNDS_CHANGE_NONE;
-        if (mBounds.left != bounds.left || mBounds.top != bounds.top) {
-            boundsChange |= BOUNDS_CHANGE_POSITION;
-        }
-        if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
-            boundsChange |= BOUNDS_CHANGE_SIZE;
-        }
-
-        mBounds.set(bounds);
+        final int boundsChange = super.setBounds(bounds);
 
         mRotation = rotation;
 
-        onOverrideConfigurationChanged(overrideConfig);
         return boundsChange;
     }
 
@@ -360,28 +339,12 @@
         return isResizeable();
     }
 
-    boolean resizeLocked(Rect bounds, Configuration overrideConfig, boolean forced) {
-        int boundsChanged = setBounds(bounds, overrideConfig);
-        if (forced) {
-            boundsChanged |= BOUNDS_CHANGE_SIZE;
-        }
-        if (boundsChanged == BOUNDS_CHANGE_NONE) {
-            return false;
-        }
-        if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
-            onResize();
-        } else {
-            onMovedByResize();
-        }
-        return true;
-    }
-
     /**
      * Prepares the task bounds to be frozen with the current size. See
      * {@link AppWindowToken#freezeBounds}.
      */
     void prepareFreezingBounds() {
-        mPreparedFrozenBounds.set(mBounds);
+        mPreparedFrozenBounds.set(getBounds());
         mPreparedFrozenMergedConfig.setTo(getConfiguration());
     }
 
@@ -407,30 +370,30 @@
             mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
         }
         setTempInsetBounds(tempInsetBounds);
-        resizeLocked(mTmpRect2, getOverrideConfiguration(), false /* forced */);
+        setBounds(mTmpRect2, false /* forced */);
     }
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
     private boolean useCurrentBounds() {
         final DisplayContent displayContent = getDisplayContent();
-        return mFillsParent
+        return matchParentBounds()
                 || !inSplitScreenSecondaryWindowingMode()
                 || displayContent == null
                 || displayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null;
     }
 
-    /** Original bounds of the task if applicable, otherwise fullscreen rect. */
-    void getBounds(Rect out) {
+    @Override
+    public void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
             // since it is already what we want to represent to the rest of the system.
-            out.set(mBounds);
+            super.getBounds(out);
             return;
         }
 
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
         // not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
-        mStack.getDisplayContent().getLogicalDisplayRect(out);
+        mStack.getDisplayContent().getBounds(out);
     }
 
     /**
@@ -490,7 +453,7 @@
                 return;
             }
 
-            if (!mFillsParent) {
+            if (!matchParentBounds()) {
                 // When minimizing the docked stack when going home, we don't adjust the task bounds
                 // so we need to intersect the task bounds with the stack bounds here.
                 //
@@ -501,11 +464,11 @@
                     mStack.getBounds(out);
                 } else {
                     mStack.getBounds(mTmpRect);
-                    mTmpRect.intersect(mBounds);
+                    mTmpRect.intersect(getBounds());
                 }
                 out.set(mTmpRect);
             } else {
-                out.set(mBounds);
+                out.set(getBounds());
             }
             return;
         }
@@ -513,7 +476,7 @@
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
         // not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
         if (displayContent != null) {
-            displayContent.getLogicalDisplayRect(out);
+            displayContent.getBounds(out);
         }
     }
 
@@ -541,10 +504,10 @@
         if (displayContent == null) {
             return;
         }
-        if (mFillsParent) {
+        if (matchParentBounds()) {
             // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a
             // problem once we move mBounds into WindowConfiguration.
-            setBounds(null, getOverrideConfiguration());
+            setBounds(null);
             return;
         }
         final int newRotation = displayContent.getDisplayInfo().rotation;
@@ -557,18 +520,18 @@
         //   task bounds so it stays in the same place.
         // - Rotate the bounds and notify activity manager if the task can be resized independently
         //   from its stack. The stack will take care of task rotation for the other case.
-        mTmpRect2.set(mBounds);
+        mTmpRect2.set(getBounds());
 
         if (!getWindowConfiguration().canResizeTask()) {
-            setBounds(mTmpRect2, getOverrideConfiguration());
+            setBounds(mTmpRect2);
             return;
         }
 
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
-        if (setBounds(mTmpRect2, getOverrideConfiguration()) != BOUNDS_CHANGE_NONE) {
+        if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) {
             final TaskWindowContainerController controller = getController();
             if (controller != null) {
-                controller.requestResize(mBounds, RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+                controller.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
             }
         }
     }
@@ -632,7 +595,7 @@
 
     boolean isFullscreen() {
         if (useCurrentBounds()) {
-            return mFillsParent;
+            return matchParentBounds();
         }
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
@@ -661,7 +624,7 @@
 
     @Override
     boolean fillsParent() {
-        return mFillsParent || !getWindowConfiguration().canResizeTask();
+        return matchParentBounds() || !getWindowConfiguration().canResizeTask();
     }
 
     @Override
@@ -711,8 +674,8 @@
             final AppWindowToken appWindowToken = mChildren.get(i);
             appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, trim);
         }
-        proto.write(FILLS_PARENT, mFillsParent);
-        mBounds.writeToProto(proto, BOUNDS);
+        proto.write(FILLS_PARENT, matchParentBounds());
+        getBounds().writeToProto(proto, BOUNDS);
         mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
         proto.end(token);
     }
@@ -721,8 +684,7 @@
         final String doublePrefix = prefix + "  ";
 
         pw.println(prefix + "taskId=" + mTaskId);
-        pw.println(doublePrefix + "mFillsParent=" + mFillsParent);
-        pw.println(doublePrefix + "mBounds=" + mBounds.toShortString());
+        pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
         pw.println(doublePrefix + "mdr=" + mDeferRemoval);
         pw.println(doublePrefix + "appTokens=" + mChildren);
         pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e3c7515..41915a3 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -18,9 +18,7 @@
 
 import static android.graphics.Color.WHITE;
 import static android.graphics.Color.alpha;
-import static android.view.SurfaceControl.HIDDEN;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
 import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8a930ef..4a3a3fc 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -36,7 +36,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
 import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
 import static com.android.server.wm.proto.StackProto.BOUNDS;
 import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
@@ -88,9 +87,6 @@
     private Rect mTmpRect2 = new Rect();
     private Rect mTmpRect3 = new Rect();
 
-    /** Content limits relative to the DisplayContent this sits in. */
-    private Rect mBounds = new Rect();
-
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
@@ -100,9 +96,6 @@
      */
     private final Rect mFullyAdjustedImeBounds = new Rect();
 
-    /** Whether mBounds is fullscreen */
-    private boolean mFillsParent = true;
-
     // Device rotation as of the last time {@link #mBounds} was set.
     private int mRotation;
 
@@ -180,27 +173,20 @@
     /**
      * Set the bounds of the stack and its containing tasks.
      * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param configs Configuration for individual tasks, keyed by task id.
      * @param taskBounds Bounds for individual tasks, keyed by task id.
+     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
      * @return True if the stack bounds was changed.
      * */
     boolean setBounds(
-            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
-            SparseArray<Rect> taskTempInsetBounds) {
+            Rect stackBounds, SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) {
         setBounds(stackBounds);
 
         // Update bounds of containing tasks.
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task task = mChildren.get(taskNdx);
-            Configuration config = configs.get(task.mTaskId);
-            if (config != null) {
-                Rect bounds = taskBounds.get(task.mTaskId);
-                task.resizeLocked(bounds, config, false /* forced */);
-                task.setTempInsetBounds(taskTempInsetBounds != null ?
-                        taskTempInsetBounds.get(task.mTaskId) : null);
-            } else {
-                Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
-            }
+            task.setBounds(taskBounds.get(task.mTaskId), false /* forced */);
+            task.setTempInsetBounds(taskTempInsetBounds != null ?
+                    taskTempInsetBounds.get(task.mTaskId) : null);
         }
         return true;
     }
@@ -227,20 +213,20 @@
         final boolean adjusted = !mAdjustedBounds.isEmpty();
         Rect insetBounds = null;
         if (adjusted && isAdjustedForMinimizedDockedStack()) {
-            insetBounds = mBounds;
+            insetBounds = getRawBounds();
         } else if (adjusted && mAdjustedForIme) {
             if (mImeGoingAway) {
-                insetBounds = mBounds;
+                insetBounds = getRawBounds();
             } else {
                 insetBounds = mFullyAdjustedImeBounds;
             }
         }
-        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
+        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds);
         mDisplayContent.setLayoutNeeded();
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
-        if (mFillsParent) {
+        if (matchParentBounds()) {
             return;
         }
 
@@ -253,13 +239,15 @@
         }
     }
 
-    private void setAnimationBackgroundBounds(Rect bounds) {
+    private void updateAnimationBackgroundBounds() {
         if (mAnimationBackgroundSurface == null) {
             return;
         }
+        getRawBounds(mTmpRect);
         // TODO: Should be in relative coordinates.
-        getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
-                .setPosition(mAnimationBackgroundSurface, bounds.left, bounds.top);
+        getPendingTransaction().setSize(mAnimationBackgroundSurface, mTmpRect.width(),
+                mTmpRect.height()).setPosition(mAnimationBackgroundSurface, mTmpRect.left,
+                mTmpRect.top);
         scheduleAnimation();
     }
 
@@ -283,50 +271,53 @@
         scheduleAnimation();
     }
 
-    private boolean setBounds(Rect bounds) {
-        boolean oldFullscreen = mFillsParent;
+    @Override
+    public int setBounds(Rect bounds) {
+        return setBounds(getOverrideBounds(), bounds);
+    }
+
+    private int setBounds(Rect existing, Rect bounds) {
         int rotation = Surface.ROTATION_0;
         int density = DENSITY_DPI_UNDEFINED;
         if (mDisplayContent != null) {
-            mDisplayContent.getLogicalDisplayRect(mTmpRect);
+            mDisplayContent.getBounds(mTmpRect);
             rotation = mDisplayContent.getDisplayInfo().rotation;
             density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
-            mFillsParent = bounds == null;
-            if (mFillsParent) {
-                bounds = mTmpRect;
-            }
         }
 
-        if (bounds == null) {
-            // Can't set to fullscreen if we don't have a display to get bounds from...
-            return false;
-        }
-        if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) {
-            return false;
+        if (equivalentBounds(existing, bounds) && mRotation == rotation) {
+            return BOUNDS_CHANGE_NONE;
         }
 
-        setAnimationBackgroundBounds(bounds);
+        final int result = super.setBounds(bounds);
 
-        mBounds.set(bounds);
+        if (mDisplayContent != null) {
+            updateAnimationBackgroundBounds();
+        }
+
         mRotation = rotation;
         mDensity = density;
 
         updateAdjustedBounds();
 
-        return true;
+        return result;
     }
 
     /** Bounds of the stack without adjusting for other factors in the system like visibility
      * of docked stack.
-     * Most callers should be using {@link #getBounds} as it take into consideration other system
-     * factors. */
+     * Most callers should be using {@link ConfigurationContainer#getOverrideBounds} as it take into
+     * consideration other system factors. */
     void getRawBounds(Rect out) {
-        out.set(mBounds);
+        out.set(getRawBounds());
+    }
+
+    Rect getRawBounds() {
+        return super.getBounds();
     }
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
     private boolean useCurrentBounds() {
-        if (mFillsParent
+        if (matchParentBounds()
                 || !inSplitScreenSecondaryWindowingMode()
                 || mDisplayContent == null
                 || mDisplayContent.getSplitScreenPrimaryStack() != null) {
@@ -335,24 +326,29 @@
         return false;
     }
 
-    public void getBounds(Rect out) {
+    @Override
+    public void getBounds(Rect bounds) {
+        bounds.set(getBounds());
+    }
+
+    @Override
+    public Rect getBounds() {
         if (useCurrentBounds()) {
             // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
             // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
             // stack is visible since it is already what we want to represent to the rest of the
             // system.
             if (!mAdjustedBounds.isEmpty()) {
-                out.set(mAdjustedBounds);
+                return mAdjustedBounds;
             } else {
-                out.set(mBounds);
+                return super.getBounds();
             }
-            return;
         }
 
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
         // system.
-        mDisplayContent.getLogicalDisplayRect(out);
+        return mDisplayContent.getBounds();
     }
 
     /**
@@ -373,7 +369,7 @@
             mBoundsAnimationSourceHintBounds.setEmpty();
         }
 
-        mPreAnimationBounds.set(mBounds);
+        mPreAnimationBounds.set(getRawBounds());
     }
 
     /**
@@ -418,12 +414,12 @@
         if (bounds != null) {
             setBounds(bounds);
             return;
-        } else if (mFillsParent) {
+        } else if (matchParentBounds()) {
             setBounds(null);
             return;
         }
 
-        mTmpRect2.set(mBounds);
+        mTmpRect2.set(getRawBounds());
         final int newRotation = mDisplayContent.getDisplayInfo().rotation;
         final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
         if (mRotation == newRotation && mDensity == newDensity) {
@@ -466,14 +462,14 @@
             return false;
         }
 
-        if (mFillsParent) {
+        if (matchParentBounds()) {
             // Update stack bounds again since rotation changed since updateDisplayInfo().
             setBounds(null);
             // Return false since we don't need the client to resize.
             return false;
         }
 
-        mTmpRect2.set(mBounds);
+        mTmpRect2.set(getRawBounds());
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (inSplitScreenPrimaryWindowingMode()) {
             repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
@@ -510,7 +506,7 @@
         if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) {
             return;
         }
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+        mDisplayContent.getBounds(mTmpRect);
         dockSide = DockedDividerUtils.invertDockSide(dockSide);
         switch (dockSide) {
             case DOCKED_LEFT:
@@ -755,7 +751,7 @@
             // not fullscreen. If it's fullscreen, it means that we are in the transition of
             // dismissing it, so we must not resize this stack.
             bounds = new Rect();
-            mDisplayContent.getLogicalDisplayRect(mTmpRect);
+            mDisplayContent.getBounds(mTmpRect);
             mTmpRect2.setEmpty();
             if (splitScreenStack != null) {
                 splitScreenStack.getRawBounds(mTmpRect2);
@@ -818,7 +814,7 @@
         }
 
         if (!inSplitScreenWindowingMode() || mDisplayContent == null) {
-            outStackBounds.set(mBounds);
+            outStackBounds.set(getRawBounds());
             return;
         }
 
@@ -833,7 +829,7 @@
             // The docked stack is being dismissed, but we caught before it finished being
             // dismissed. In that case we want to treat it as if it is not occupying any space and
             // let others occupy the whole display.
-            mDisplayContent.getLogicalDisplayRect(outStackBounds);
+            mDisplayContent.getBounds(outStackBounds);
             return;
         }
 
@@ -841,11 +837,11 @@
         if (dockedSide == DOCKED_INVALID) {
             // Not sure how you got here...Only thing we can do is return current bounds.
             Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
-            outStackBounds.set(mBounds);
+            outStackBounds.set(getRawBounds());
             return;
         }
 
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+        mDisplayContent.getBounds(mTmpRect);
         dockedStack.getRawBounds(mTmpRect2);
         final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
         getStackDockedModeBounds(mTmpRect, outStackBounds, mTmpRect2,
@@ -1146,14 +1142,14 @@
             // occluded by IME. We shift its bottom up by the height of the IME, but
             // leaves at least 30% of the top stack visible.
             final int minTopStackBottom =
-                    getMinTopStackBottom(displayContentRect, mBounds.bottom);
+                    getMinTopStackBottom(displayContentRect, getRawBounds().bottom);
             final int bottom = Math.max(
-                    mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
+                    getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
                     minTopStackBottom);
-            mTmpAdjustedBounds.set(mBounds);
-            mTmpAdjustedBounds.bottom =
-                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
-            mFullyAdjustedImeBounds.set(mBounds);
+            mTmpAdjustedBounds.set(getRawBounds());
+            mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
+                    * getRawBounds().bottom);
+            mFullyAdjustedImeBounds.set(getRawBounds());
         } else {
             // When the stack is on bottom and has no focus, it's only adjusted for divider width.
             final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
@@ -1163,22 +1159,24 @@
             // We try to move it up by the height of the IME window, but only to the extent
             // that leaves at least 30% of the top stack visible.
             // 'top' is where the top of bottom stack will move to in this case.
-            final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive;
+            final int topBeforeImeAdjust =
+                    getRawBounds().top - dividerWidth + dividerWidthInactive;
             final int minTopStackBottom =
-                    getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
+                    getMinTopStackBottom(displayContentRect,
+                            getRawBounds().top - dividerWidth);
             final int top = Math.max(
-                    mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
+                    getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
 
-            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.set(getRawBounds());
             // Account for the adjustment for IME and divider width separately.
             // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
             // and dividerWidthDelta is due to divider width change only.
-            mTmpAdjustedBounds.top = mBounds.top +
+            mTmpAdjustedBounds.top = getRawBounds().top +
                     (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) +
                             mAdjustDividerAmount * dividerWidthDelta);
-            mFullyAdjustedImeBounds.set(mBounds);
+            mFullyAdjustedImeBounds.set(getRawBounds());
             mFullyAdjustedImeBounds.top = top;
-            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
+            mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
         }
         return true;
     }
@@ -1192,21 +1190,21 @@
         if (dockSide == DOCKED_TOP) {
             mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
             int topInset = mTmpRect.top;
-            mTmpAdjustedBounds.set(mBounds);
-            mTmpAdjustedBounds.bottom =
-                    (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
+            mTmpAdjustedBounds.set(getRawBounds());
+            mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
+                    * getRawBounds().bottom);
         } else if (dockSide == DOCKED_LEFT) {
-            mTmpAdjustedBounds.set(mBounds);
-            final int width = mBounds.width();
+            mTmpAdjustedBounds.set(getRawBounds());
+            final int width = getRawBounds().width();
             mTmpAdjustedBounds.right =
                     (int) (minimizeAmount * mDockedStackMinimizeThickness
-                            + (1 - minimizeAmount) * mBounds.right);
+                            + (1 - minimizeAmount) * getRawBounds().right);
             mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
         } else if (dockSide == DOCKED_RIGHT) {
-            mTmpAdjustedBounds.set(mBounds);
-            mTmpAdjustedBounds.left =
-                    (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness)
-                            + (1 - minimizeAmount) * mBounds.left);
+            mTmpAdjustedBounds.set(getRawBounds());
+            mTmpAdjustedBounds.left = (int) (minimizeAmount *
+                    (getRawBounds().right - mDockedStackMinimizeThickness)
+                            + (1 - minimizeAmount) * getRawBounds().left);
         }
         return true;
     }
@@ -1228,9 +1226,9 @@
         if (dockSide == DOCKED_TOP) {
             mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
             int topInset = mTmpRect.top;
-            return mBounds.bottom - topInset;
+            return getRawBounds().bottom - topInset;
         } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
-            return mBounds.width() - mDockedStackMinimizeThickness;
+            return getRawBounds().width() - mDockedStackMinimizeThickness;
         } else {
             return 0;
         }
@@ -1264,11 +1262,12 @@
             return;
         }
 
-        final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds;
+        final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
         task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
         mDisplayContent.setLayoutNeeded();
     }
 
+
     boolean isAdjustedForMinimizedDockedStack() {
         return mMinimizeAmount != 0f;
     }
@@ -1282,8 +1281,8 @@
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
             mChildren.get(taskNdx).writeToProto(proto, TASKS, trim);
         }
-        proto.write(FILLS_PARENT, mFillsParent);
-        mBounds.writeToProto(proto, BOUNDS);
+        proto.write(FILLS_PARENT, matchParentBounds());
+        getRawBounds().writeToProto(proto, BOUNDS);
         proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
         proto.end(token);
     }
@@ -1291,8 +1290,7 @@
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "mStackId=" + mStackId);
         pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
-        pw.println(prefix + "mFillsParent=" + mFillsParent);
-        pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
         if (mMinimizeAmount != 0f) {
             pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
         }
@@ -1323,18 +1321,10 @@
         }
     }
 
-    /** Fullscreen status of the stack without adjusting for other factors in the system like
-     * visibility of docked stack.
-     * Most callers should be using {@link #fillsParent} as it take into consideration other
-     * system factors. */
-    boolean getRawFullscreen() {
-        return mFillsParent;
-    }
-
     @Override
     boolean fillsParent() {
         if (useCurrentBounds()) {
-            return mFillsParent;
+            return matchParentBounds();
         }
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
@@ -1360,7 +1350,7 @@
      * information which side of the screen was the dock anchored.
      */
     int getDockSide() {
-        return getDockSide(mBounds);
+        return getDockSide(getRawBounds());
     }
 
     private int getDockSide(Rect bounds) {
@@ -1370,7 +1360,7 @@
         if (mDisplayContent == null) {
             return DOCKED_INVALID;
         }
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+        mDisplayContent.getBounds(mTmpRect);
         final int orientation = mDisplayContent.getConfiguration().orientation;
         return getDockSideUnchecked(bounds, mTmpRect, orientation);
     }
@@ -1490,7 +1480,7 @@
              */
 
             if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
-                mDisplayContent.getLogicalDisplayRect(mTmpRect);
+                mDisplayContent.getBounds(mTmpRect);
             } else {
                 task.getDimBounds(mTmpRect);
             }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index b3bb0b7..5caae32 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
@@ -30,6 +29,7 @@
 import java.lang.ref.WeakReference;
 
 import static com.android.server.EventLogTags.WM_TASK_CREATED;
+import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -75,7 +75,7 @@
                         + stackController);
             }
             EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
-            final Task task = createTask(taskId, stack, userId, bounds, resizeMode,
+            final Task task = createTask(taskId, stack, userId, resizeMode,
                     supportsPictureInPicture, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
             // We only want to move the parents to the parents if we are creating this task at the
@@ -85,10 +85,10 @@
     }
 
     @VisibleForTesting
-    Task createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode,
+    Task createTask(int taskId, TaskStack stack, int userId, int resizeMode,
             boolean supportsPictureInPicture, TaskDescription taskDescription) {
-        return new Task(taskId, stack, userId, mService, bounds, resizeMode,
-                supportsPictureInPicture, taskDescription, this);
+        return new Task(taskId, stack, userId, mService, resizeMode, supportsPictureInPicture,
+                taskDescription, this);
     }
 
     @Override
@@ -151,14 +151,14 @@
         }
     }
 
-    public void resize(Rect bounds, Configuration overrideConfig, boolean relayout,
-            boolean forced) {
+    public void resize(boolean relayout, boolean forced) {
         synchronized (mWindowMap) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
             }
 
-            if (mContainer.resizeLocked(bounds, overrideConfig, forced) && relayout) {
+            if (mContainer.setBounds(mContainer.getOverrideBounds(), forced) != BOUNDS_CHANGE_NONE
+                    && relayout) {
                 mContainer.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 a5e6288..610453e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -318,11 +318,24 @@
      * @see #mFullConfiguration
      */
     @Override
-    final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+    public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        // We must diff before the configuration is applied so that we can capture the change
+        // against the existing bounds.
+        final int diff = diffOverrideBounds(overrideConfiguration.windowConfiguration.getBounds());
         super.onOverrideConfigurationChanged(overrideConfiguration);
         if (mParent != null) {
             mParent.onDescendantOverrideConfigurationChanged();
         }
+
+        if (diff == BOUNDS_CHANGE_NONE) {
+            return;
+        }
+
+        if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
+            onResize();
+        } else {
+            onMovedByResize();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d88cf4d..1cd4080 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -126,7 +126,6 @@
 import android.app.IActivityManager;
 import android.app.IAssistDataReceiver;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -5517,7 +5516,7 @@
     }
 
     void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) {
-        if (!mDisplayReady) {
+        if (!displayContent.isReady()) {
             return;
         }
         displayContent.configureDisplayPolicy();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e53fccf..90e9c23 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -814,7 +814,7 @@
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
-            getContainerBounds(mContainingFrame);
+            getBounds(mContainingFrame);
             if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
 
                 // If the bounds are frozen, we still want to translate the window freely and only
@@ -972,7 +972,7 @@
             mContentInsets.setEmpty();
             mVisibleInsets.setEmpty();
         } else {
-            getDisplayContent().getLogicalDisplayRect(mTmpRect);
+            getDisplayContent().getBounds(mTmpRect);
             // Override right and/or bottom insets in case if the frame doesn't fit the screen in
             // non-fullscreen mode.
             boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
@@ -1044,6 +1044,18 @@
                 + " of=" + mOutsets.toShortString());
     }
 
+    // TODO: Look into whether this override is still necessary.
+    @Override
+    public Rect getBounds() {
+        if (isInMultiWindowMode()) {
+            return getTask().getBounds();
+        } else if (mAppToken != null){
+            return mAppToken.getBounds();
+        } else {
+            return super.getBounds();
+        }
+    }
+
     @Override
     public Rect getFrameLw() {
         return mFrame;
@@ -2951,33 +2963,12 @@
 
     /** Is this window in a container that takes up the entire screen space? */
     private boolean inFullscreenContainer() {
-        if (mAppToken == null) {
-            return true;
-        }
-        if (mAppToken.hasBounds()) {
-            return false;
-        }
-        return !isInMultiWindowMode();
+        return mAppToken == null || (mAppToken.matchParentBounds() && !isInMultiWindowMode());
     }
 
     /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */
     boolean isLetterboxedAppWindow() {
-        final Task task = getTask();
-        final boolean taskIsFullscreen = task != null && task.isFullscreen();
-        final boolean appWindowIsFullscreen = mAppToken != null && !mAppToken.hasBounds();
-
-        return taskIsFullscreen && !appWindowIsFullscreen;
-    }
-
-    /** Returns the appropriate bounds to use for computing frames. */
-    private void getContainerBounds(Rect outBounds) {
-        if (isInMultiWindowMode()) {
-            getTask().getBounds(outBounds);
-        } else if (mAppToken != null){
-            mAppToken.getBounds(outBounds);
-        } else {
-            outBounds.setEmpty();
-        }
+        return !isInMultiWindowMode() && mAppToken != null && !mAppToken.matchParentBounds();
     }
 
     boolean isDragResizeChanged() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d11b0db..d2b3748 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1971,7 +1971,7 @@
         final float width = w.mFrame.width();
         final float height = w.mFrame.height();
 
-        mService.getDefaultDisplayContentLocked().getLogicalDisplayRect(displayRect);
+        mService.getDefaultDisplayContentLocked().getBounds(displayRect);
         final float displayWidth = displayRect.width();
         final float displayHeight = displayRect.height();
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index d90e284..ee45595 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -37,7 +37,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.ComponentName;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.MediumTest;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index bc503c4..b4919b6 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -28,7 +28,6 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -108,8 +107,8 @@
         final Rect bounds = new Rect(10, 10, 100, 100);
 
         mStarter.updateBounds(task, bounds);
-        assertEquals(task.mBounds, bounds);
-        assertEquals(task.getStack().mBounds, null);
+        assertEquals(task.getOverrideBounds(), bounds);
+        assertEquals(new Rect(), task.getStack().getOverrideBounds());
 
         // When in a resizeable stack, the stack bounds should be updated as well.
         final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
@@ -124,10 +123,10 @@
 
         // In the case of no animation, the stack and task bounds should be set immediately.
         if (!ANIMATE) {
-            assertEquals(task2.getStack().mBounds, bounds);
-            assertEquals(task2.mBounds, bounds);
+            assertEquals(task2.getStack().getOverrideBounds(), bounds);
+            assertEquals(task2.getOverrideBounds(), bounds);
         } else {
-            assertEquals(task2.mBounds, null);
+            assertEquals(task2.getOverrideBounds(), new Rect());
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 9683e22..2fffb89 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.doNothing;
@@ -415,6 +416,12 @@
         @Override
         protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
             mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
+
+            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
+            // will be moved to the full screen stack.
+            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                outBounds.set(0, 0, 100, 100);
+            }
             return mContainerController;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
index 01e2da6..13daf3e 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.am;
 
-import android.content.ComponentName;
 import android.content.pm.ActivityInfo.WindowLayout;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -93,8 +92,8 @@
      */
     @Test
     public void testInitialBounds() throws Exception {
-        assertEquals(mStack.mBounds, STACK_BOUNDS);
-        assertEquals(mTask.mBounds, null);
+        assertEquals(mStack.getOverrideBounds(), STACK_BOUNDS);
+        assertEquals(mTask.getOverrideBounds(), new Rect());
     }
 
     /**
@@ -182,7 +181,7 @@
         mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(mTask, layout);
 
         // Second task will be laid out on top of the first so starting bounds is the same.
-        final Rect expectedBounds = new Rect(mTask.mBounds);
+        final Rect expectedBounds = new Rect(mTask.getOverrideBounds());
 
         ActivityRecord activity = null;
         TaskRecord secondTask = null;
@@ -203,14 +202,16 @@
             if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
                     || (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
                     == (Gravity.BOTTOM | Gravity.RIGHT)) {
-                expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
-                        0);
+                expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(
+                        mStack.getOverrideBounds()), 0);
             } else if ((gravity & Gravity.TOP) == Gravity.TOP
                     || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
-                expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), 0);
+                expectedBounds.offset(
+                        LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()), 0);
             } else {
-                expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
-                        LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
+                expectedBounds.offset(
+                        LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()),
+                        LaunchingTaskPositioner.getVerticalStep(mStack.getOverrideBounds()));
             }
 
             assertEquals(mResult, expectedBounds);
@@ -228,10 +229,12 @@
 
     private Rect getDefaultBounds(int gravity) {
         final Rect bounds = new Rect();
-        bounds.set(mStack.mBounds);
+        bounds.set(mStack.getOverrideBounds());
 
-        final int verticalInset = LaunchingTaskPositioner.getFreeformStartTop(mStack.mBounds);
-        final int horizontalInset = LaunchingTaskPositioner.getFreeformStartLeft(mStack.mBounds);
+        final int verticalInset =
+                LaunchingTaskPositioner.getFreeformStartTop(mStack.getOverrideBounds());
+        final int horizontalInset =
+                LaunchingTaskPositioner.getFreeformStartLeft(mStack.getOverrideBounds());
 
         bounds.inset(horizontalInset, verticalInset);
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 5d2bb4d..503e1ac 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -21,7 +21,7 @@
 import org.junit.runner.RunWith;
 
 import android.app.ActivityManager.TaskDescription;
-import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
@@ -75,11 +75,23 @@
         final Rect mInsetBounds = new Rect();
         boolean mFullscreenForTest = true;
         TaskWithBounds(Rect bounds) {
-            super(0, mStubStack, 0, sWm, null, 0, false, new TaskDescription(), null);
+            super(0, mStubStack, 0, sWm, 0, false, new TaskDescription(), null);
             mBounds = bounds;
+            setBounds(bounds);
         }
+
         @Override
-        void getBounds(Rect outBounds) {
+        public Rect getBounds() {
+            return mBounds;
+        }
+
+        @Override
+        public void getBounds(Rect out) {
+            out.set(mBounds);
+        }
+
+        @Override
+        public void getOverrideBounds(Rect outBounds) {
             outBounds.set(mBounds);
         }
         @Override
@@ -395,7 +407,9 @@
         final int xInset = logicalWidth / 10;
         final int yInset = logicalWidth / 10;
         final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset);
-        w.mAppToken.onOverrideConfigurationChanged(w.mAppToken.getOverrideConfiguration(), cf);
+        Configuration config = new Configuration(w.mAppToken.getOverrideConfiguration());
+        config.windowConfiguration.setBounds(cf);
+        w.mAppToken.onOverrideConfigurationChanged(config);
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.mFullscreenForTest = true;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 1aafac6..b2334e8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -29,11 +29,17 @@
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
+
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import org.mockito.invocation.InvocationOnMock;
+
 /**
  * A collection of static functions that can be referenced by other test packages to provide access
  * to WindowManager related test functionality.
@@ -64,13 +70,24 @@
     public static StackWindowController createMockStackWindowContainerController() {
         StackWindowController controller = mock(StackWindowController.class);
         controller.mContainer = mock(TestTaskStack.class);
+
+        // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
+        // to properly set bounds values in the configuration. We must mimick those actions here.
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Configuration config = invocationOnMock.<Configuration>getArgument(7);
+            final Rect bounds = invocationOnMock.<Rect>getArgument(0);
+            config.windowConfiguration.setBounds(bounds);
+            return null;
+        }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+                anyBoolean(), anyBoolean(), anyFloat(), any(), any());
+
         return controller;
     }
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
             int userId) {
-        final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, 0, false,
+        final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
                 new ActivityManager.TaskDescription(), null);
         stack.addTask(newTask, POSITION_TOP);
         return newTask;
@@ -98,17 +115,17 @@
         TestAppWindowToken(DisplayContent dc) {
             super(dc.mService, new IApplicationToken.Stub() {
                 public String getName() {return null;}
-                }, false, dc, true /* fillsParent */, null /* bounds */);
+                }, false, dc, true /* fillsParent */);
         }
 
         TestAppWindowToken(WindowManagerService service, IApplicationToken token,
                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
+                boolean alwaysFocusable, AppWindowContainerController controller) {
             super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen,
                     showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges,
-                    launchTaskBehind, alwaysFocusable, controller, bounds);
+                    launchTaskBehind, alwaysFocusable, controller);
         }
 
         int getWindowsCount() {
@@ -175,10 +192,10 @@
         private boolean mUseLocalIsAnimating = false;
         private boolean mIsAnimating = false;
 
-        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
+        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
                 int resizeMode, boolean supportsPictureInPicture,
                 TaskWindowContainerController controller) {
-            super(taskId, stack, userId, service, bounds, resizeMode, supportsPictureInPicture,
+            super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
                     new ActivityManager.TaskDescription(), controller);
         }
 
@@ -247,9 +264,9 @@
         }
 
         @Override
-        TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode,
+        TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
                 boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
-            return new TestTask(taskId, stack, userId, mService, bounds, resizeMode,
+            return new TestTask(taskId, stack, userId, mService, resizeMode,
                     supportsPictureInPicture, this);
         }
     }
@@ -269,8 +286,7 @@
                     true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */,
                     false /* launchTaskBehind */, false /* alwaysFocusable */,
                     0 /* targetSdkVersion */, 0 /* rotationAnimationHint */,
-                    0 /* inputDispatchingTimeoutNanos */, taskController.mService,
-                    null /* bounds */);
+                    0 /* inputDispatchingTimeoutNanos */, taskController.mService);
             mToken = token;
         }
 
@@ -279,12 +295,12 @@
                 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
                 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
                 int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) {
+                boolean alwaysFocusable, AppWindowContainerController controller) {
             return new TestAppWindowToken(service, token, voiceInteraction, dc,
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
                     orientation,
                     rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                    controller, bounds);
+                    controller);
         }
 
         AppWindowToken getAppWindowToken(DisplayContent dc) {
diff --git a/telephony/java/android/telephony/data/DataProfile.aidl b/telephony/java/android/telephony/data/DataProfile.aidl
new file mode 100644
index 0000000..65fdf91
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataProfile.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 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.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable DataProfile;
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
new file mode 100644
index 0000000..41c1430
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.RILConstants;
+
+/**
+ * Description of a mobile data profile used for establishing
+ * data connections.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DataProfile implements Parcelable {
+
+    // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network.
+    public static final int TYPE_COMMON = 0;
+    public static final int TYPE_3GPP = 1;
+    public static final int TYPE_3GPP2 = 2;
+
+    private final int mProfileId;
+
+    private final String mApn;
+
+    private final String mProtocol;
+
+    private final int mAuthType;
+
+    private final String mUserName;
+
+    private final String mPassword;
+
+    private final int mType;
+
+    private final int mMaxConnsTime;
+
+    private final int mMaxConns;
+
+    private final int mWaitTime;
+
+    private final boolean mEnabled;
+
+    private final int mSupportedApnTypesBitmap;
+
+    private final String mRoamingProtocol;
+
+    private final int mBearerBitmap;
+
+    private final int mMtu;
+
+    private final String mMvnoType;
+
+    private final String mMvnoMatchData;
+
+    private final boolean mModemCognitive;
+
+    public DataProfile(int profileId, String apn, String protocol, int authType,
+                String userName, String password, int type, int maxConnsTime, int maxConns,
+                int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
+                int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData,
+                boolean modemCognitive) {
+
+        this.mProfileId = profileId;
+        this.mApn = apn;
+        this.mProtocol = protocol;
+        if (authType == -1) {
+            authType = TextUtils.isEmpty(userName) ? RILConstants.SETUP_DATA_AUTH_NONE
+                    : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
+        }
+        this.mAuthType = authType;
+        this.mUserName = userName;
+        this.mPassword = password;
+        this.mType = type;
+        this.mMaxConnsTime = maxConnsTime;
+        this.mMaxConns = maxConns;
+        this.mWaitTime = waitTime;
+        this.mEnabled = enabled;
+
+        this.mSupportedApnTypesBitmap = supportedApnTypesBitmap;
+        this.mRoamingProtocol = roamingProtocol;
+        this.mBearerBitmap = bearerBitmap;
+        this.mMtu = mtu;
+        this.mMvnoType = mvnoType;
+        this.mMvnoMatchData = mvnoMatchData;
+        this.mModemCognitive = modemCognitive;
+    }
+
+    public DataProfile(Parcel source) {
+        mProfileId = source.readInt();
+        mApn = source.readString();
+        mProtocol = source.readString();
+        mAuthType = source.readInt();
+        mUserName = source.readString();
+        mPassword = source.readString();
+        mType = source.readInt();
+        mMaxConnsTime = source.readInt();
+        mMaxConns = source.readInt();
+        mWaitTime = source.readInt();
+        mEnabled = source.readBoolean();
+        mSupportedApnTypesBitmap = source.readInt();
+        mRoamingProtocol = source.readString();
+        mBearerBitmap = source.readInt();
+        mMtu = source.readInt();
+        mMvnoType = source.readString();
+        mMvnoMatchData = source.readString();
+        mModemCognitive = source.readBoolean();
+    }
+
+    /**
+     * @return Id of the data profile.
+     */
+    public int getProfileId() { return mProfileId; }
+
+    /**
+     * @return The APN to establish data connection.
+     */
+    public String getApn() { return mApn; }
+
+    /**
+     * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section
+     * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     */
+    public String getProtocol() { return mProtocol; }
+
+    /**
+     * @return The authentication protocol used for this PDP context
+     * (None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3)
+     */
+    public int getAuthType() { return mAuthType; }
+
+    /**
+     * @return The username for APN. Can be null.
+     */
+    public String getUserName() { return mUserName; }
+
+    /**
+     * @return The password for APN. Can be null.
+     */
+    public String getPassword() { return mPassword; }
+
+    /**
+     * @return The profile type. Could be one of TYPE_COMMON, TYPE_3GPP, or TYPE_3GPP2.
+     */
+    public int getType() { return mType; }
+
+    /**
+     * @return The period in seconds to limit the maximum connections.
+     */
+    public int getMaxConnsTime() { return mMaxConnsTime; }
+
+    /**
+     * @return The maximum connections allowed.
+     */
+    public int getMaxConns() { return mMaxConns; }
+
+    /**
+     * @return The required wait time in seconds after a successful UE initiated disconnect of a
+     * given PDN connection before the device can send a new PDN connection request for that given
+     * PDN.
+     */
+    public int getWaitTime() { return mWaitTime; }
+
+    /**
+     * @return True if the profile is enabled.
+     */
+    public boolean isEnabled() { return mEnabled; }
+
+    /**
+     * @return The supported APN types bitmap. See RIL_ApnTypes for the value of each bit.
+     */
+    public int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; }
+
+    /**
+     * @return  The connection protocol on roaming network, should be one of the PDP_type values in
+     * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     */
+    public String getRoamingProtocol() { return mRoamingProtocol; }
+
+    /**
+     * @return The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit.
+     */
+    public int getBearerBitmap() { return mBearerBitmap; }
+
+    /**
+     * @return The maximum transmission unit (MTU) size in bytes.
+     */
+    public int getMtu() { return mMtu; }
+
+    /**
+     * @return The MVNO type: possible values are "imsi", "gid", "spn".
+     */
+    public String getMvnoType() { return mMvnoType; }
+
+    /**
+     * @return The MVNO match data. For example,
+     * SPN: A MOBILE, BEN NL, ...
+     * IMSI: 302720x94, 2060188, ...
+     * GID: 4E, 33, ...
+     */
+    public String getMvnoMatchData() { return mMvnoMatchData; }
+
+    /**
+     * @return True if the data profile was sent to the modem through setDataProfile earlier.
+     */
+    public boolean isModemCognitive() { return mModemCognitive; }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "DataProfile=" + mProfileId + "/" + mApn + "/" + mProtocol + "/" + mAuthType
+                + "/" + mUserName + "/" + mPassword + "/" + mType + "/" + mMaxConnsTime
+                + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
+                + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
+                + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof DataProfile == false) return false;
+        return (o == this || toString().equals(o.toString()));
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mProfileId);
+        dest.writeString(mApn);
+        dest.writeString(mProtocol);
+        dest.writeInt(mAuthType);
+        dest.writeString(mUserName);
+        dest.writeString(mPassword);
+        dest.writeInt(mType);
+        dest.writeInt(mMaxConnsTime);
+        dest.writeInt(mMaxConns);
+        dest.writeInt(mWaitTime);
+        dest.writeBoolean(mEnabled);
+        dest.writeInt(mSupportedApnTypesBitmap);
+        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mBearerBitmap);
+        dest.writeInt(mMtu);
+        dest.writeString(mMvnoType);
+        dest.writeString(mMvnoMatchData);
+        dest.writeBoolean(mModemCognitive);
+    }
+
+    public static final Parcelable.Creator<DataProfile> CREATOR =
+            new Parcelable.Creator<DataProfile>() {
+        @Override
+        public DataProfile createFromParcel(Parcel source) {
+            return new DataProfile(source);
+        }
+
+        @Override
+        public DataProfile[] newArray(int size) {
+            return new DataProfile[size];
+        }
+    };
+}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index cca1294..423d028 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -18,6 +18,10 @@
 namespace android {
 namespace stats_log_api_gen {
 
+const int PULL_ATOM_START_ID = 1000;
+
+int maxPushedAtomId = 2;
+
 using android::os::statsd::Atom;
 
 // TODO: Support WorkSources
@@ -195,12 +199,18 @@
         fprintf(out, "     */\n");
         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
         fprintf(out, "    %s = %d%s\n", constant.c_str(), atom->code, comma);
+        if (atom->code < PULL_ATOM_START_ID && atom->code > maxPushedAtomId) {
+            maxPushedAtomId = atom->code;
+        }
         i++;
     }
     fprintf(out, "\n");
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
+    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
+            maxPushedAtomId);
+
     // Print write methods
     fprintf(out, "//\n");
     fprintf(out, "// Write methods\n");