Merge "Fix duplicate reg. KeyguardSliceViewController in DumpManager."
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index 050fecd..d3938f4 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -17,7 +17,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.view.Display;
@@ -136,4 +135,22 @@
             }
         }
     }
+
+    @Test
+    public void getDisplayMetrics() {
+        ResourcesManager resourcesManager = ResourcesManager.getInstance();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            // Invalidate cache.
+            resourcesManager.applyConfigurationToResourcesLocked(
+                    resourcesManager.getConfiguration(), null);
+            state.resumeTiming();
+
+            // Invoke twice for testing cache.
+            resourcesManager.getDisplayMetrics();
+            resourcesManager.getDisplayMetrics();
+        }
+    }
 }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9b684f1..693402a 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -652,7 +652,7 @@
             (unsigned long)mCurrentSlicedBucket.size());
     if (verbose) {
         for (const auto& it : mCurrentSlicedBucket) {
-          for (const auto& interval : it.second) {
+          for (const auto& interval : it.second.intervals) {
               fprintf(out, "\t(what)%s\t(states)%s  (value)%s\n",
                       it.first.getDimensionKeyInWhat().toString().c_str(),
                       it.first.getStateValuesKey().toString().c_str(),
@@ -804,7 +804,8 @@
     // We need to get the intervals stored with the previous state key so we can
     // close these value intervals.
     const auto oldStateKey = baseInfos[0].currentState;
-    vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+    vector<Interval>& intervals =
+            mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
     if (intervals.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         intervals.resize(mFieldMatchers.size());
@@ -990,7 +991,7 @@
         bool bucketHasData = false;
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
-            ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
+            PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
             bucket.mConditionTrueNs = conditionTrueDuration;
             // it will auto create new vector of ValuebucketInfo if the key is not found.
             if (bucket.valueIndex.size() > 0) {
@@ -1030,9 +1031,9 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
-ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
-                                                    const std::vector<Interval>& intervals) {
-    ValueBucket bucket;
+PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+                                                        const std::vector<Interval>& intervals) {
+    PastValueBucket bucket;
     bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
     bucket.mBucketEndNs = bucketEndTime;
     for (const auto& interval : intervals) {
@@ -1059,7 +1060,7 @@
     // Cleanup data structure to aggregate values.
     for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
         bool obsolete = true;
-        for (auto& interval : it->second) {
+        for (auto& interval : it->second.intervals) {
             interval.hasValue = false;
             interval.sampleSize = 0;
             if (interval.seenNewData) {
@@ -1107,7 +1108,7 @@
                     continue;
                 }
                 // TODO: fix this when anomaly can accept double values
-                auto& interval = slice.second[0];
+                auto& interval = slice.second.intervals[0];
                 if (interval.hasValue) {
                     mCurrentFullBucket[slice.first] += interval.value.long_value;
                 }
@@ -1126,7 +1127,7 @@
                 for (auto& tracker : mAnomalyTrackers) {
                     if (tracker != nullptr) {
                         // TODO: fix this when anomaly can accept double values
-                        auto& interval = slice.second[0];
+                        auto& interval = slice.second.intervals[0];
                         if (interval.hasValue) {
                             tracker->addPastBucket(slice.first, interval.value.long_value,
                                                    mCurrentBucketNum);
@@ -1139,7 +1140,7 @@
         // Accumulate partial bucket.
         for (const auto& slice : mCurrentSlicedBucket) {
             // TODO: fix this when anomaly can accept double values
-            auto& interval = slice.second[0];
+            auto& interval = slice.second.intervals[0];
             if (interval.hasValue) {
                 mCurrentFullBucket[slice.first] += interval.value.long_value;
             }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index e72002e..fffc866 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -31,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-struct ValueBucket {
+struct PastValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     std::vector<int> valueIndex;
@@ -41,7 +41,6 @@
     int64_t mConditionTrueNs;
 };
 
-
 // Aggregates values within buckets.
 //
 // There are different events that might complete a bucket
@@ -173,7 +172,7 @@
     // if this is pulled metric
     const bool mIsPulled;
 
-    // internal state of an ongoing aggregation bucket.
+    // Tracks the value information of one value field.
     typedef struct {
         // Index in multi value aggregation.
         int valueIndex;
@@ -188,6 +187,12 @@
         bool seenNewData = false;
     } Interval;
 
+    // Internal state of an ongoing aggregation bucket.
+    typedef struct CurrentValueBucket {
+        // Value information for each value field of the metric.
+        std::vector<Interval> intervals;
+    } CurrentValueBucket;
+
     typedef struct {
         // Holds current base value of the dimension. Take diff and update if necessary.
         Value base;
@@ -199,14 +204,16 @@
         bool hasCurrentState;
     } BaseInfo;
 
-    std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+    // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
+    // key and StateValuesKey pair.
+    std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
 
     std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
 
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
 
     const int64_t mMinBucketSizeNs;
 
@@ -224,8 +231,8 @@
     void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
 
-    ValueBucket buildPartialBucket(int64_t bucketEndTime,
-                                   const std::vector<Interval>& intervals);
+    PastValueBucket buildPartialBucket(int64_t bucketEndTime,
+                                       const std::vector<Interval>& intervals);
 
     void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
 
@@ -234,7 +241,7 @@
     // Reset diff base and mHasGlobalBase
     void resetBase();
 
-    static const size_t kBucketSize = sizeof(ValueBucket{});
+    static const size_t kBucketSize = sizeof(PastValueBucket{});
 
     const size_t mDimensionSoftLimit;
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1000aea..ec8adfd 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -57,7 +57,7 @@
 double epsilon = 0.001;
 
 static void assertPastBucketValuesSingleKey(
-        const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+        const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
         const std::initializer_list<int>& expectedValuesList,
         const std::initializer_list<int64_t>& expectedDurationNsList,
         const std::initializer_list<int64_t>& expectedStartTimeNsList,
@@ -79,7 +79,7 @@
     ASSERT_EQ(1, mPastBuckets.size());
     ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
 
-    const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
+    const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
     for (int i = 0; i < expectedValues.size(); i++) {
         EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
                 << "Values differ at index " << i;
@@ -288,7 +288,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -304,7 +304,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -322,7 +322,7 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -426,7 +426,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -455,7 +455,7 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     // the base was reset
@@ -489,7 +489,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -502,7 +502,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
@@ -516,7 +516,7 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -549,7 +549,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -562,7 +562,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
@@ -573,7 +573,7 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -624,7 +624,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -641,7 +641,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(110, curBaseInfo.base.long_value);
@@ -654,7 +654,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
@@ -879,7 +879,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -888,7 +888,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -925,8 +925,8 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
@@ -935,7 +935,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -946,7 +946,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1089,7 +1089,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     // startUpdated:true sum:0 start:11
@@ -1104,7 +1104,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // tartUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1121,7 +1121,7 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1180,7 +1180,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1189,7 +1189,7 @@
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
@@ -1203,7 +1203,7 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1252,7 +1252,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1265,7 +1265,7 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1274,7 +1274,7 @@
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(130, curBaseInfo.base.long_value);
@@ -1286,7 +1286,7 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
@@ -1327,7 +1327,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1335,7 +1335,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1364,7 +1364,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1374,7 +1374,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1405,7 +1405,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval;
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(1, curInterval.sampleSize);
@@ -1414,7 +1414,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
     EXPECT_EQ(2, curInterval.sampleSize);
 
@@ -1449,7 +1449,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1457,7 +1457,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1487,7 +1487,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
@@ -1499,7 +1499,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
 
@@ -1509,7 +1509,7 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
 
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
@@ -1520,7 +1520,7 @@
     CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
@@ -1558,7 +1558,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
@@ -1572,11 +1572,11 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
@@ -1587,13 +1587,13 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(25, curBaseInfo.base.long_value);
@@ -1604,12 +1604,12 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
     curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(29, curBaseInfo.base.long_value);
@@ -1656,7 +1656,7 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
     auto& baseInfo1 = iterBase->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
@@ -1692,7 +1692,7 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it->second[0];
+    auto& interval2 = it->second.intervals[0];
     auto& baseInfo2 = itBase->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
@@ -1732,7 +1732,7 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     const auto& it = valueProducer->mCurrentSlicedBucket.begin();
-    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo1 =
             valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
@@ -1761,7 +1761,7 @@
         }
     }
     EXPECT_TRUE(it2 != it);
-    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo2 =
             valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
@@ -1792,10 +1792,10 @@
     // Get new references now that entries have been deleted from the map
     const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
     const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    ASSERT_EQ(it3->second.size(), 1);
-    ASSERT_EQ(it4->second.size(), 1);
-    ValueMetricProducer::Interval& interval3 = it3->second[0];
-    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ASSERT_EQ(it3->second.intervals.size(), 1);
+    ASSERT_EQ(it4->second.intervals.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
+    ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo3 =
             valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
     ValueMetricProducer::BaseInfo& baseInfo4 =
@@ -1837,7 +1837,7 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
     auto& baseInfo1 = iterBase->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
@@ -1875,7 +1875,7 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto interval2 = it->second[0];
+    auto interval2 = it->second.intervals[0];
     auto baseInfo2 = itBase->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
@@ -1889,7 +1889,7 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     // Only one interval left. One was trimmed.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
@@ -1903,7 +1903,7 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(14, baseInfo2.base.long_value);
@@ -1943,7 +1943,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1980,7 +1980,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -2030,7 +2030,7 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2103,7 +2103,7 @@
     valueProducer->mHasGlobalBase = true;
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -2156,7 +2156,7 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
@@ -2294,7 +2294,7 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
@@ -2373,7 +2373,7 @@
     // Last pull failed so base has been reset.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2460,7 +2460,7 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2469,7 +2469,7 @@
     // Empty pull.
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2513,7 +2513,7 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -2524,7 +2524,7 @@
     allData.clear();
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // Data is empty, base should be reset.
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -2572,12 +2572,12 @@
     auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
     EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
     EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
     iterator++;
     baseInfoIter++;
     EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
     EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
 
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
@@ -2676,7 +2676,7 @@
 
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
@@ -2811,7 +2811,7 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
@@ -3045,7 +3045,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -3058,7 +3058,7 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -3091,7 +3091,7 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
     ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -3995,7 +3995,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4016,8 +4016,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
@@ -4037,16 +4037,16 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change OFF->ON.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4067,24 +4067,24 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(12, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(12, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, ON}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4206,7 +4206,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4227,8 +4227,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->VR.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4250,8 +4250,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->ON.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4273,8 +4273,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4295,16 +4295,16 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
               it->first.getStateValuesKey().getValues()[0].mValue.long_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(16, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(16, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4459,7 +4459,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4475,7 +4475,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
     auto uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4497,8 +4497,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
@@ -4514,7 +4514,7 @@
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 2 process state change kStateUnknown -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4535,8 +4535,8 @@
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
@@ -4552,8 +4552,8 @@
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4582,7 +4582,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
@@ -4599,7 +4599,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4608,7 +4608,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4617,7 +4617,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change from Foreground -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4642,7 +4642,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4658,7 +4658,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Value for key {uid 1, FOREGROUND}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4666,8 +4666,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
     // Value for key {uid 2, kStateUnknown}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4675,7 +4675,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change Background->Foreground.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4699,7 +4699,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
@@ -4716,7 +4716,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, BACKGROUND}
     it++;
@@ -4725,8 +4725,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4735,8 +4735,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4867,13 +4867,13 @@
               itBase->second[0].currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, -1}
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator
-            it = valueProducer->mCurrentSlicedBucket.begin();
+    std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
+            valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after battery saver mode OFF event.
     unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4895,8 +4895,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4933,8 +4933,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 7cd3fca..9e4ab33 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -54,6 +54,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -245,7 +246,7 @@
     /**
      * A cache of DisplayId, DisplayAdjustments to Display.
      */
-    private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>>
+    private final ArrayMap<Pair<Integer, DisplayAdjustments>, SoftReference<Display>>
             mAdjustedDisplays = new ArrayMap<>();
 
     /**
@@ -373,25 +374,28 @@
                 ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
         final Pair<Integer, DisplayAdjustments> key =
                 Pair.create(displayId, displayAdjustmentsCopy);
+        SoftReference<Display> sd;
         synchronized (this) {
-            WeakReference<Display> wd = mAdjustedDisplays.get(key);
-            if (wd != null) {
-                final Display display = wd.get();
-                if (display != null) {
-                    return display;
-                }
-            }
-            final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            if (dm == null) {
-                // may be null early in system startup
-                return null;
-            }
-            final Display display = dm.getCompatibleDisplay(displayId, key.second);
-            if (display != null) {
-                mAdjustedDisplays.put(key, new WeakReference<>(display));
-            }
-            return display;
+            sd = mAdjustedDisplays.get(key);
         }
+        if (sd != null) {
+            final Display display = sd.get();
+            if (display != null) {
+                return display;
+            }
+        }
+        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+        if (dm == null) {
+            // may be null early in system startup
+            return null;
+        }
+        final Display display = dm.getCompatibleDisplay(displayId, key.second);
+        if (display != null) {
+            synchronized (this) {
+                mAdjustedDisplays.put(key, new SoftReference<>(display));
+            }
+        }
+        return display;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
similarity index 100%
rename from libs/WindowManager/Shell/res/layout/pip_menu_activity.xml
rename to libs/WindowManager/Shell/res/layout/pip_menu.xml
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index fd6685f..6d31a8d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -22,8 +22,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.wm.DisplaySystemBarsController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
@@ -43,12 +41,4 @@
         return new DisplaySystemBarsController(context, wmService, displayController,
                 mainHandler, transactionPool);
     }
-
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    @SysUISingleton
-    @PipMenuActivityClass
-    @Provides
-    Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
-    }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index af008b9..7f4f580 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -530,20 +530,6 @@
             androidprv:alwaysFocusable="true"
             android:excludeFromRecents="true" />
 
-        <activity
-            android:name=".pip.phone.PipMenuActivity"
-            android:permission="com.android.systemui.permission.SELF"
-            android:theme="@style/PipPhoneOverlayControlTheme"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:stateNotNeeded="true"
-            android:taskAffinity=""
-            android:launchMode="singleTop"
-            androidprv:alwaysFocusable="true" />
-
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 7421ec1..c956702 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -49,6 +49,9 @@
 import android.util.Log;
 import android.util.Size;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
@@ -57,6 +60,7 @@
 
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.pip.phone.PipMenuActivityController;
 import com.android.systemui.pip.phone.PipUpdateThread;
 import com.android.systemui.stackdivider.SplitScreen;
 import com.android.wm.shell.R;
@@ -95,6 +99,7 @@
     private static final int MSG_FINISH_RESIZE = 4;
     private static final int MSG_RESIZE_USER = 5;
 
+    private final Context mContext;
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
     private final PipBoundsHandler mPipBoundsHandler;
@@ -107,6 +112,8 @@
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
     private final Optional<SplitScreen> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
+    private SurfaceControlViewHost mPipViewHost;
+    private SurfaceControl mPipMenuSurface;
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -212,6 +219,7 @@
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsHandler = boundsHandler;
@@ -504,6 +512,45 @@
     }
 
     /**
+     * Setup the ViewHost and attach the provided menu view to the ViewHost.
+     */
+    public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
+        if (mPipMenuSurface != null) {
+            Log.e(TAG, "PIP Menu View already created and attached.");
+            return;
+        }
+
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
+        }
+
+        mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+                (android.os.Binder) null);
+        mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl();
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        transaction.reparent(mPipMenuSurface, mLeash);
+        transaction.show(mPipMenuSurface);
+        transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1);
+        transaction.apply();
+        mPipViewHost.setView(menuView, lp);
+    }
+
+
+    /**
+     * Releases the PIP Menu's View host, remove it from PIP task surface.
+     */
+    public void detachPipMenuViewHost() {
+        if (mPipMenuSurface != null) {
+            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+            transaction.remove(mPipMenuSurface);
+            transaction.apply();
+            mPipMenuSurface = null;
+            mPipViewHost = null;
+        }
+    }
+
+
+    /**
      * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
      *   - as a result of removeStacksInWindowingModes from WM
@@ -838,6 +885,12 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
         applyFinishBoundsResize(wct, direction);
+        runOnMainHandler(() -> {
+            if (mPipViewHost != null) {
+                mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams(
+                        destinationBounds.width(), destinationBounds.height()));
+            }
+        });
     }
 
     private void prepareFinishResizeTransaction(Rect destinationBounds,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index d8864ec..5ef5b90 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -49,7 +49,6 @@
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
@@ -267,7 +266,6 @@
 
     @Inject
     public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
-            @PipMenuActivityClass Class<?> pipMenuActivityClass,
             ConfigurationController configController,
             DeviceConfigProxy deviceConfig,
             DisplayController displayController,
@@ -296,8 +294,8 @@
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
-        mMenuController = new PipMenuActivityController(context, pipMenuActivityClass,
-                mMediaController, mInputConsumerController);
+        mMenuController = new PipMenuActivityController(context,
+                mMediaController, mInputConsumerController, mPipTaskOrganizer);
         mTouchHandler = new PipTouchHandler(context, mActivityManager,
                 mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
                 floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
deleted file mode 100644
index 1b1b2de..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (C) 2016 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.pip.phone;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_RESIZE_HANDLE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ParceledListSlice;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.wm.shell.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
- * TODO(b/150319024): PipMenuActivity will move to a Window
- */
-public class PipMenuActivity extends Activity {
-
-    private static final String TAG = "PipMenuActivity";
-
-    private static final int MESSAGE_INVALID_TYPE = -1;
-
-    public static final int MESSAGE_SHOW_MENU = 1;
-    public static final int MESSAGE_POKE_MENU = 2;
-    public static final int MESSAGE_HIDE_MENU = 3;
-    public static final int MESSAGE_UPDATE_ACTIONS = 4;
-    public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
-    public static final int MESSAGE_ANIMATION_ENDED = 6;
-    public static final int MESSAGE_POINTER_EVENT = 7;
-    public static final int MESSAGE_MENU_EXPANDED = 8;
-    public static final int MESSAGE_FADE_OUT_MENU = 9;
-    public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10;
-
-    private static final int INITIAL_DISMISS_DELAY = 3500;
-    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
-    private static final long MENU_FADE_DURATION = 125;
-    private static final long MENU_SLOW_FADE_DURATION = 175;
-    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
-
-    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
-    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
-
-    private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
-    private static final boolean ENABLE_RESIZE_HANDLE = false;
-
-    private int mMenuState;
-    private boolean mResize = true;
-    private boolean mAllowMenuTimeout = true;
-    private boolean mAllowTouches = true;
-
-    private final List<RemoteAction> mActions = new ArrayList<>();
-
-    private AccessibilityManager mAccessibilityManager;
-    private Drawable mBackgroundDrawable;
-    private View mMenuContainer;
-    private LinearLayout mActionsGroup;
-    private int mBetweenActionPaddingLand;
-
-    private AnimatorSet mMenuContainerAnimator;
-
-    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float alpha = (float) animation.getAnimatedValue();
-                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
-                }
-            };
-
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_SHOW_MENU: {
-                    final Bundle data = (Bundle) msg.obj;
-                    showMenu(data.getInt(EXTRA_MENU_STATE),
-                            data.getParcelable(EXTRA_STACK_BOUNDS),
-                            data.getBoolean(EXTRA_ALLOW_TIMEOUT),
-                            data.getBoolean(EXTRA_WILL_RESIZE_MENU),
-                            data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY),
-                            data.getBoolean(EXTRA_SHOW_RESIZE_HANDLE));
-                    break;
-                }
-                case MESSAGE_POKE_MENU:
-                    cancelDelayedFinish();
-                    break;
-                case MESSAGE_HIDE_MENU:
-                    hideMenu((Runnable) msg.obj);
-                    break;
-                case MESSAGE_UPDATE_ACTIONS: {
-                    final Bundle data = (Bundle) msg.obj;
-                    final ParceledListSlice<RemoteAction> actions = data.getParcelable(
-                            EXTRA_ACTIONS);
-                    setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
-                            ? actions.getList() : Collections.emptyList());
-                    break;
-                }
-                case MESSAGE_UPDATE_DISMISS_FRACTION: {
-                    final Bundle data = (Bundle) msg.obj;
-                    updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
-                    break;
-                }
-                case MESSAGE_ANIMATION_ENDED: {
-                    mAllowTouches = true;
-                    break;
-                }
-                case MESSAGE_POINTER_EVENT: {
-                    final MotionEvent ev = (MotionEvent) msg.obj;
-                    dispatchPointerEvent(ev);
-                    break;
-                }
-                case MESSAGE_MENU_EXPANDED : {
-                    if (mMenuContainerAnimator == null) {
-                        return;
-                    }
-                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
-                    mMenuContainerAnimator.start();
-                    break;
-                }
-                case MESSAGE_FADE_OUT_MENU: {
-                    fadeOutMenu();
-                    break;
-                }
-                case MESSAGE_UPDATE_MENU_LAYOUT: {
-                    if (mPipMenuIconsAlgorithm == null) {
-                        return;
-                    }
-                    final Rect bounds = (Rect) msg.obj;
-                    mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mToControllerMessenger;
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private final Runnable mFinishRunnable = this::hideMenu;
-
-    protected View mViewRoot;
-    protected View mSettingsButton;
-    protected View mDismissButton;
-    protected View mResizeHandle;
-    protected View mTopEndContainer;
-    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        // Set the flags to allow us to watch for outside touches and also hide the menu and start
-        // manipulating the PIP in the same touch gesture
-        getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.pip_menu_activity);
-
-        mAccessibilityManager = getSystemService(AccessibilityManager.class);
-        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
-        mBackgroundDrawable.setAlpha(0);
-        mViewRoot = findViewById(R.id.background);
-        mViewRoot.setBackground(mBackgroundDrawable);
-        mMenuContainer = findViewById(R.id.menu_container);
-        mMenuContainer.setAlpha(0);
-        mTopEndContainer = findViewById(R.id.top_end_container);
-        mSettingsButton = findViewById(R.id.settings);
-        mSettingsButton.setAlpha(0);
-        mSettingsButton.setOnClickListener((v) -> {
-            if (v.getAlpha() != 0) {
-                showSettings();
-            }
-        });
-        mDismissButton = findViewById(R.id.dismiss);
-        mDismissButton.setAlpha(0);
-        mDismissButton.setOnClickListener(v -> dismissPip());
-        findViewById(R.id.expand_button).setOnClickListener(v -> {
-            if (mMenuContainer.getAlpha() != 0) {
-                expandPip();
-            }
-        });
-        mResizeHandle = findViewById(R.id.resize_handle);
-        mResizeHandle.setAlpha(0);
-        mActionsGroup = findViewById(R.id.actions_group);
-        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
-                R.dimen.pip_between_action_padding_land);
-        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(this.getApplicationContext());
-        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
-                mResizeHandle, mSettingsButton, mDismissButton);
-        updateFromIntent(getIntent());
-        setTitle(R.string.pip_menu_title);
-        setDisablePreviewScreenshots(true);
-
-        // Hide without an animation.
-        getWindow().setExitTransition(null);
-
-        initAccessibility();
-    }
-
-    private void initAccessibility() {
-        getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                String label = getResources().getString(R.string.pip_menu_title);
-                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
-            }
-
-            @Override
-            public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
-                    Message m = Message.obtain();
-                    m.what = PipMenuActivityController.MESSAGE_SHOW_MENU;
-                    sendMessage(m, "Could not notify controller to show PIP menu");
-                }
-                return super.performAccessibilityAction(host, action, args);
-            }
-        });
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
-            hideMenu();
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        updateFromIntent(intent);
-    }
-
-    @Override
-    public void onUserInteraction() {
-        if (mAllowMenuTimeout) {
-            repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-        }
-    }
-
-    @Override
-    protected void onUserLeaveHint() {
-        super.onUserLeaveHint();
-
-        // If another task is starting on top of the menu, then hide and finish it so that it can be
-        // recreated on the top next time it starts
-        hideMenu();
-    }
-
-    @Override
-    public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
-        super.onTopResumedActivityChanged(isTopResumedActivity);
-        if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) {
-            hideMenu();
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // In cases such as device lock, hide and finish it so that it can be recreated on the top
-        // next time it starts, see also {@link #onUserLeaveHint}
-        hideMenu();
-        cancelDelayedFinish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        // Fallback, if we are destroyed for any other reason (like when the task is being reset),
-        // also reset the callback.
-        notifyActivityCallback(null);
-    }
-
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (!isInPictureInPictureMode) {
-            finish();
-        }
-    }
-
-    /**
-     * Dispatch a pointer event from {@link PipTouchHandler}.
-     */
-    private void dispatchPointerEvent(MotionEvent event) {
-        if (event.isTouchEvent()) {
-            dispatchTouchEvent(event);
-        } else {
-            dispatchGenericMotionEvent(event);
-        }
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (!mAllowTouches) {
-            return false;
-        }
-
-        // On the first action outside the window, hide the menu
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_OUTSIDE:
-                hideMenu();
-                return true;
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    public void finish() {
-        notifyActivityCallback(null);
-        super.finish();
-    }
-
-    @Override
-    public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
-        // Do nothing
-    }
-
-    private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
-        mAllowMenuTimeout = allowMenuTimeout;
-        if (mMenuState != menuState) {
-            // Disallow touches if the menu needs to resize while showing, and we are transitioning
-            // to/from a full menu state.
-            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
-                    (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
-            mAllowTouches = !disallowTouchesUntilAnimationEnd;
-            cancelDelayedFinish();
-            updateActionViews(stackBounds);
-            if (mMenuContainerAnimator != null) {
-                mMenuContainerAnimator.cancel();
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 1f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 1f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 1f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(),
-                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
-                            ? 1f : 0f);
-            if (menuState == MENU_STATE_FULL) {
-                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
-                        resizeAnim);
-            } else {
-                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
-            }
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
-            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
-                    ? MENU_FADE_DURATION
-                    : MENU_SLOW_FADE_DURATION);
-            if (allowMenuTimeout) {
-                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        repostDelayedFinish(INITIAL_DISMISS_DELAY);
-                    }
-                });
-            }
-            if (withDelay) {
-                // starts the menu container animation after window expansion is completed
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_MENU_EXPANDED);
-            } else {
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_INVALID_TYPE);
-                mMenuContainerAnimator.start();
-            }
-        } else {
-            // If we are already visible, then just start the delayed dismiss and unregister any
-            // existing input consumers from the previous drag
-            if (allowMenuTimeout) {
-                repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-            }
-        }
-    }
-
-    /**
-     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
-     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
-     * visibility callbacks invoked.
-     */
-    private void fadeOutMenu() {
-        mMenuContainer.setAlpha(0f);
-        mSettingsButton.setAlpha(0f);
-        mDismissButton.setAlpha(0f);
-        mResizeHandle.setAlpha(0f);
-    }
-
-    private void hideMenu() {
-        hideMenu(null);
-    }
-
-    private void hideMenu(Runnable animationEndCallback) {
-        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */,
-                true /* animate */);
-    }
-
-    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
-            boolean isDismissing, boolean animate) {
-        if (mMenuState != MENU_STATE_NONE) {
-            cancelDelayedFinish();
-            if (notifyMenuVisibility) {
-                notifyMenuStateChange(MENU_STATE_NONE, mResize, MESSAGE_INVALID_TYPE);
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 0f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 0f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 0f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(), 0f);
-            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
-            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
-            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (animationFinishedRunnable != null) {
-                        animationFinishedRunnable.run();
-                    }
-
-                    if (!isDismissing) {
-                        // If we are dismissing the PiP, then don't try to pre-emptively finish the
-                        // menu activity
-                        finish();
-                    }
-                }
-            });
-            mMenuContainerAnimator.start();
-        } else {
-            // If the menu is not visible, just finish now
-            finish();
-        }
-    }
-
-    private void updateFromIntent(Intent intent) {
-        mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
-        if (mToControllerMessenger == null) {
-            Log.w(TAG, "Controller messenger is null. Stopping.");
-            finish();
-            return;
-        }
-        notifyActivityCallback(mMessenger);
-
-        ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS);
-        if (actions != null) {
-            mActions.clear();
-            mActions.addAll(actions.getList());
-        }
-
-        final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
-        if (menuState != MENU_STATE_NONE) {
-            Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
-            boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
-            boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
-            boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false);
-            boolean showResizeHandle = intent.getBooleanExtra(EXTRA_SHOW_RESIZE_HANDLE, false);
-            showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
-        }
-    }
-
-    private void setActions(Rect stackBounds, List<RemoteAction> actions) {
-        mActions.clear();
-        mActions.addAll(actions);
-        updateActionViews(stackBounds);
-    }
-
-    private void updateActionViews(Rect stackBounds) {
-        ViewGroup expandContainer = findViewById(R.id.expand_container);
-        ViewGroup actionsContainer = findViewById(R.id.actions_container);
-        actionsContainer.setOnTouchListener((v, ev) -> {
-            // Do nothing, prevent click through to parent
-            return true;
-        });
-
-        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
-            actionsContainer.setVisibility(View.INVISIBLE);
-        } else {
-            actionsContainer.setVisibility(View.VISIBLE);
-            if (mActionsGroup != null) {
-                // Ensure we have as many buttons as actions
-                final LayoutInflater inflater = LayoutInflater.from(this);
-                while (mActionsGroup.getChildCount() < mActions.size()) {
-                    final ImageButton actionView = (ImageButton) inflater.inflate(
-                            R.layout.pip_menu_action, mActionsGroup, false);
-                    mActionsGroup.addView(actionView);
-                }
-
-                // Update the visibility of all views
-                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
-                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
-                            ? View.VISIBLE
-                            : View.GONE);
-                }
-
-                // Recreate the layout
-                final boolean isLandscapePip = stackBounds != null &&
-                        (stackBounds.width() > stackBounds.height());
-                for (int i = 0; i < mActions.size(); i++) {
-                    final RemoteAction action = mActions.get(i);
-                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
-
-                    // TODO: Check if the action drawable has changed before we reload it
-                    action.getIcon().loadDrawableAsync(this, d -> {
-                        d.setTint(Color.WHITE);
-                        actionView.setImageDrawable(d);
-                    }, mHandler);
-                    actionView.setContentDescription(action.getContentDescription());
-                    if (action.isEnabled()) {
-                        actionView.setOnClickListener(v -> {
-                            mHandler.post(() -> {
-                                try {
-                                    action.getActionIntent().send();
-                                } catch (CanceledException e) {
-                                    Log.w(TAG, "Failed to send action", e);
-                                }
-                            });
-                        });
-                    }
-                    actionView.setEnabled(action.isEnabled());
-                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
-
-                    // Update the margin between actions
-                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                            actionView.getLayoutParams();
-                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
-                }
-            }
-
-            // Update the expand container margin to adjust the center of the expand button to
-            // account for the existence of the action container
-            FrameLayout.LayoutParams expandedLp =
-                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
-            expandedLp.topMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_action_padding);
-            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_expand_container_edge_margin);
-            expandContainer.requestLayout();
-        }
-    }
-
-    private void updateDismissFraction(float fraction) {
-        int alpha;
-        final float menuAlpha = 1 - fraction;
-        if (mMenuState == MENU_STATE_FULL) {
-            mMenuContainer.setAlpha(menuAlpha);
-            mSettingsButton.setAlpha(menuAlpha);
-            mDismissButton.setAlpha(menuAlpha);
-            final float interpolatedAlpha =
-                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
-            alpha = (int) (interpolatedAlpha * 255);
-        } else {
-            if (mMenuState == MENU_STATE_CLOSE) {
-                mDismissButton.setAlpha(menuAlpha);
-            }
-            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
-        }
-        mBackgroundDrawable.setAlpha(alpha);
-    }
-
-    private void notifyMenuStateChange(int menuState, boolean resize, int callbackWhat) {
-        mMenuState = menuState;
-        mResize = resize;
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
-        m.arg1 = menuState;
-        m.arg2 = resize ? 1 : 0;
-        if (callbackWhat != MESSAGE_INVALID_TYPE) {
-            // This message could be sent across processes when in secondary user.
-            // Make the receiver end sending back via our own Messenger
-            m.replyTo = mMessenger;
-            final Bundle data = new Bundle(1);
-            data.putInt(PipMenuActivityController.EXTRA_MESSAGE_CALLBACK_WHAT, callbackWhat);
-            m.obj = data;
-        }
-        sendMessage(m, "Could not notify controller of PIP menu visibility");
-    }
-
-    private void expandPip() {
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
-                    "Could not notify controller to expand PIP");
-        }, false /* notifyMenuVisibility */, false /* isDismissing */, true /* animate */);
-    }
-
-    private void dismissPip() {
-        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
-        // we want to disable animating the fadeout animation of the buttons in order to call on
-        // PipTouchHandler#onPipDismiss fast enough.
-        final boolean animate = mMenuState != MENU_STATE_CLOSE;
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
-                    "Could not notify controller to dismiss PIP");
-        }, false /* notifyMenuVisibility */, true /* isDismissing */, animate);
-    }
-
-    private void showSettings() {
-        final Pair<ComponentName, Integer> topPipActivityInfo =
-                PipUtils.getTopPipActivity(this, ActivityManager.getService());
-        if (topPipActivityInfo.first != null) {
-            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
-            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
-                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
-            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
-            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-            startActivity(settingsIntent);
-        }
-    }
-
-    private void notifyActivityCallback(Messenger callback) {
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
-        m.replyTo = callback;
-        m.arg1 = mResize ?  1 : 0;
-        sendMessage(m, "Could not notify controller of activity finished");
-    }
-
-    private void sendEmptyMessage(int what, String errorMsg) {
-        Message m = Message.obtain();
-        m.what = what;
-        sendMessage(m, errorMsg);
-    }
-
-    private void sendMessage(Message m, String errorMsg) {
-        if (mToControllerMessenger == null) {
-            return;
-        }
-        try {
-            mToControllerMessenger.send(m);
-        } catch (RemoteException e) {
-            Log.e(TAG, errorMsg, e);
-        }
-    }
-
-    private void cancelDelayedFinish() {
-        mHandler.removeCallbacks(mFinishRunnable);
-    }
-
-    private void repostDelayedFinish(int delay) {
-        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
-                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
-        mHandler.removeCallbacks(mFinishRunnable);
-        mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 383f6b3..873ba26 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,27 +19,20 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.RemoteAction;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.WindowManager;
 
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.phone.PipMediaController.ActionListener;
 import com.android.systemui.shared.system.InputConsumerController;
 
@@ -58,30 +51,10 @@
     private static final String TAG = "PipMenuActController";
     private static final boolean DEBUG = false;
 
-    public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
-    public static final String EXTRA_ACTIONS = "actions";
-    public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
-    public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout";
-    public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show";
-    public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction";
-    public static final String EXTRA_MENU_STATE = "menu_state";
-    public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay";
-    public static final String EXTRA_SHOW_RESIZE_HANDLE = "show_resize_handle";
-    public static final String EXTRA_MESSAGE_CALLBACK_WHAT = "message_callback_what";
-
-    public static final int MESSAGE_MENU_STATE_CHANGED = 100;
-    public static final int MESSAGE_EXPAND_PIP = 101;
-    public static final int MESSAGE_DISMISS_PIP = 103;
-    public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
-    public static final int MESSAGE_SHOW_MENU = 107;
-
     public static final int MENU_STATE_NONE = 0;
     public static final int MENU_STATE_CLOSE = 1;
     public static final int MENU_STATE_FULL = 2;
 
-    // The duration to wait before we consider the start activity as having timed out
-    private static final long START_ACTIVITY_REQUEST_TIMEOUT_MS = 300;
-
     /**
      * A listener interface to receive notification on changes in PIP.
      */
@@ -110,9 +83,8 @@
         void onPipShowMenu();
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    private Class<?> mPipMenuActivityClass;
     private Context mContext;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private PipMediaController mMediaController;
     private InputConsumerController mInputConsumerController;
 
@@ -121,63 +93,7 @@
     private ParceledListSlice<RemoteAction> mMediaActions;
     private int mMenuState;
 
-    // The dismiss fraction update is sent frequently, so use a temporary bundle for the message
-    private Bundle mTmpDismissFractionData = new Bundle();
-
-    private Runnable mOnAnimationEndRunnable;
-    private boolean mStartActivityRequested;
-    private long mStartActivityRequestedTime;
-    private Messenger mToActivityMessenger;
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_MENU_STATE_CHANGED: {
-                    final int menuState = msg.arg1;
-                    final boolean resize = msg.arg2 != 0;
-                    onMenuStateChanged(menuState, resize,
-                            getMenuStateChangeFinishedCallback(msg.replyTo, (Bundle) msg.obj));
-                    break;
-                }
-                case MESSAGE_EXPAND_PIP: {
-                    mListeners.forEach(Listener::onPipExpand);
-                    break;
-                }
-                case MESSAGE_DISMISS_PIP: {
-                    mListeners.forEach(Listener::onPipDismiss);
-                    break;
-                }
-                case MESSAGE_SHOW_MENU: {
-                    mListeners.forEach(Listener::onPipShowMenu);
-                    break;
-                }
-                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
-                    mToActivityMessenger = msg.replyTo;
-                    setStartActivityRequested(false);
-                    if (mOnAnimationEndRunnable != null) {
-                        mOnAnimationEndRunnable.run();
-                        mOnAnimationEndRunnable = null;
-                    }
-                    // Mark the menu as invisible once the activity finishes as well
-                    if (mToActivityMessenger == null) {
-                        final boolean resize = msg.arg1 != 0;
-                        onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */);
-                    }
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private Runnable mStartActivityRequestedTimeoutRunnable = () -> {
-        setStartActivityRequested(false);
-        if (mOnAnimationEndRunnable != null) {
-            mOnAnimationEndRunnable.run();
-            mOnAnimationEndRunnable = null;
-        }
-        Log.e(TAG, "Expected start menu activity request timed out");
-    };
+    private PipMenuView mPipMenuView;
 
     private ActionListener mMediaActionListener = new ActionListener() {
         @Override
@@ -187,39 +103,40 @@
         }
     };
 
-    public PipMenuActivityController(Context context, Class<?> pipMenuActivityClass,
-            PipMediaController mediaController, InputConsumerController inputConsumerController
+    public PipMenuActivityController(Context context,
+            PipMediaController mediaController, InputConsumerController inputConsumerController,
+            PipTaskOrganizer pipTaskOrganizer
     ) {
         mContext = context;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
-        mPipMenuActivityClass = pipMenuActivityClass;
+        mPipTaskOrganizer = pipTaskOrganizer;
     }
 
-    public boolean isMenuActivityVisible() {
-        return mToActivityMessenger != null;
+    public boolean isMenuVisible() {
+        return mPipMenuView != null && mMenuState != MENU_STATE_NONE;
     }
 
     public void onActivityPinned() {
+        if (mPipMenuView == null) {
+            WindowManager.LayoutParams lp =
+                    getPipMenuLayoutParams(0, 0);
+            mPipMenuView = new PipMenuView(mContext, this);
+            mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp);
+        }
         mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
     }
 
     public void onActivityUnpinned() {
         hideMenu();
         mInputConsumerController.unregisterInputConsumer();
-        setStartActivityRequested(false);
+        mPipTaskOrganizer.detachPipMenuViewHost();
+        mPipMenuView = null;
     }
 
     public void onPinnedStackAnimationEnded() {
-        // Note: Only active menu activities care about this event
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_ANIMATION_ENDED;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu pinned animation ended", e);
-            }
+        if (isMenuVisible()) {
+            mPipMenuView.onPipAnimationEnded();
         }
     }
 
@@ -236,27 +153,13 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     public void setDismissFraction(float fraction) {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null)
+            Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
                     + " fraction=" + fraction);
         }
-        if (mToActivityMessenger != null) {
-            mTmpDismissFractionData.clear();
-            mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
-            m.obj = mTmpDismissFractionData;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to update dismiss fraction", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(MENU_STATE_NONE, null /* stackBounds */,
-                    false /* allowMenuTimeout */, false /* resizeMenuOnShow */,
-                    false /* withDelay */, false /* showResizeHandle */);
+        if (isMenuVisible) {
+            mPipMenuView.updateDismissFraction(fraction);
         }
     }
 
@@ -282,27 +185,11 @@
                 false /* withDelay */, showResizeHandle);
     }
 
-    private Runnable getMenuStateChangeFinishedCallback(@Nullable Messenger replyTo,
-            @Nullable Bundle data) {
-        if (replyTo == null || data == null) {
-            return null;
-        }
-        return () -> {
-            try {
-                final Message m = Message.obtain();
-                m.what = data.getInt(EXTRA_MESSAGE_CALLBACK_WHAT);
-                replyTo.send(m);
-            } catch (RemoteException e) {
-                // ignored
-            }
-        };
-    }
-
     private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout,
             boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
         if (DEBUG) {
             Log.d(TAG, "showMenu() state=" + menuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible()
                     + " allowMenuTimeout=" + allowMenuTimeout
                     + " willResizeMenu=" + willResizeMenu
                     + " withDelay=" + withDelay
@@ -310,64 +197,34 @@
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
 
-        if (mToActivityMessenger != null) {
-            Bundle data = new Bundle();
-            data.putInt(EXTRA_MENU_STATE, menuState);
-            if (stackBounds != null) {
-                data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            }
-            data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-            data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-            data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-            data.putBoolean(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to show", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
+        if (mPipMenuView == null) {
+            Log.e(TAG, "PipMenu has not been attached yet.");
+            return;
         }
+        mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
+                showResizeHandle);
     }
 
     /**
      * Pokes the menu, indicating that the user is interacting with it.
      */
     public void pokeMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null));
+            Log.d(TAG, "pokeMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POKE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify poke menu", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.pokeMenu();
         }
     }
 
     private void fadeOutMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "fadeOutMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
-                    + " callers=\n" + Debug.getCallers(5, "    "));
+            Log.d(TAG, "fadeOutMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_FADE_OUT_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to fade out", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.fadeOutMenu();
         }
     }
 
@@ -375,19 +232,14 @@
      * Hides the menu activity.
      */
     public void hideMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
             Log.d(TAG, "hideMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to hide", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.hideMenu();
         }
     }
 
@@ -395,29 +247,11 @@
      * Hides the menu activity.
      */
     public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) {
-        if (mStartActivityRequested) {
-            // If the menu has been start-requested, but not actually started, then we defer the
-            // trigger callback until the menu has started and called back to the controller.
-            mOnAnimationEndRunnable = onEndCallback;
-            onStartCallback.run();
-
-            // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any
-            // callbacks. Don't continue to wait for the menu to show past some timeout.
-            mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-            mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable,
-                    START_ACTIVITY_REQUEST_TIMEOUT_MS);
-        } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // If the menu is visible in either the closed or full state, then hide the menu and
             // trigger the animation trigger afterwards
             onStartCallback.run();
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            m.obj = onEndCallback;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify hide menu", e);
-            }
+            mPipMenuView.hideMenu(onEndCallback);
         }
     }
 
@@ -438,6 +272,18 @@
         updateMenuActions();
     }
 
+    void onPipExpand() {
+        mListeners.forEach(Listener::onPipExpand);
+    }
+
+    void onPipDismiss() {
+        mListeners.forEach(Listener::onPipDismiss);
+    }
+
+    void onPipShowMenu() {
+        mListeners.forEach(Listener::onPipShowMenu);
+    }
+
     /**
      * @return the best set of actions to show in the PiP menu.
      */
@@ -449,47 +295,20 @@
     }
 
     /**
-     * Starts the menu activity on the top task of the pinned stack.
+     * Returns a default LayoutParams for the PIP Menu.
+     * @param width the PIP stack width.
+     * @param height the PIP stack height.
      */
-    private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
-        try {
-            StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
-                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                    pinnedStackInfo.taskIds.length > 0) {
-                Intent intent = new Intent(mContext, mPipMenuActivityClass);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
-                if (stackBounds != null) {
-                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds);
-                }
-                intent.putExtra(EXTRA_MENU_STATE, menuState);
-                intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-                intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-                intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-                intent.putExtra(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-                options.setLaunchTaskId(
-                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
-                options.setTaskOverlay(true, true /* canResume */);
-                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-                setStartActivityRequested(true);
-            } else {
-                Log.e(TAG, "No PIP tasks found");
-            }
-        } catch (RemoteException e) {
-            setStartActivityRequested(false);
-            Log.e(TAG, "Error showing PIP menu activity", e);
-        }
+    public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) {
+        return new WindowManager.LayoutParams(width, height,
+                WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT);
     }
 
     /**
-     * Updates the PiP menu activity with the best set of actions provided.
+     * Updates the PiP menu with the best set of actions provided.
      */
     private void updateMenuActions() {
-        if (mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // Fetch the pinned stack bounds
             Rect stackBounds = null;
             try {
@@ -499,20 +318,10 @@
                     stackBounds = pinnedStackInfo.bounds;
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu activity", e);
+                Log.e(TAG, "Error showing PIP menu", e);
             }
 
-            Bundle data = new Bundle();
-            data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            data.putParcelable(EXTRA_ACTIONS, resolveMenuActions());
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu activity to update actions", e);
-            }
+            mPipMenuView.setActions(stackBounds, resolveMenuActions().getList());
         }
     }
 
@@ -524,17 +333,9 @@
     }
 
     /**
-     * @return whether the time of the activity request has exceeded the timeout.
-     */
-    private boolean isStartActivityRequestedElapsed() {
-        return (SystemClock.uptimeMillis() - mStartActivityRequestedTime)
-                >= START_ACTIVITY_REQUEST_TIMEOUT_MS;
-    }
-
-    /**
      * Handles changes in menu visibility.
      */
-    private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+    void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
         if (DEBUG) {
             Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
                     + " menuState=" + menuState + " resize=" + resize
@@ -556,25 +357,14 @@
         mMenuState = menuState;
     }
 
-    private void setStartActivityRequested(boolean requested) {
-        mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-        mStartActivityRequested = requested;
-        mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0;
-    }
-
     /**
      * Handles a pointer event sent from pip input consumer.
      */
     void handlePointerEvent(MotionEvent ev) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POINTER_EVENT;
-            m.obj = ev;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        if (ev.isTouchEvent()) {
+            mPipMenuView.dispatchTouchEvent(ev);
+        } else {
+            mPipMenuView.dispatchGenericMotionEvent(ev);
         }
     }
 
@@ -582,15 +372,14 @@
      * Tell the PIP Menu to recalculate its layout given its current position on the display.
      */
     public void updateMenuLayout(Rect bounds) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT;
-            m.obj = bounds;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        final boolean isMenuVisible = isMenuVisible();
+        if (DEBUG) {
+            Log.d(TAG, "updateMenuLayout() state=" + mMenuState
+                    + " isMenuVisible=" + isMenuVisible
+                    + " callers=\n" + Debug.getCallers(5, "    "));
+        }
+        if (isMenuVisible) {
+            mPipMenuView.updateMenuLayout(bounds);
         }
     }
 
@@ -598,9 +387,7 @@
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mMenuState=" + mMenuState);
-        pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger);
+        pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView);
         pw.println(innerPrefix + "mListeners=" + mListeners.size());
-        pw.println(innerPrefix + "mStartActivityRequested=" + mStartActivityRequested);
-        pw.println(innerPrefix + "mStartActivityRequestedTime=" + mStartActivityRequestedTime);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
new file mode 100644
index 0000000..993bfe0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2020 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.pip.phone;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translucent window that gets started on top of a task in PIP to allow the user to control it.
+ */
+public class PipMenuView extends FrameLayout {
+
+    private static final String TAG = "PipMenuView";
+
+    private static final int MESSAGE_INVALID_TYPE = -1;
+    public static final int MESSAGE_MENU_EXPANDED = 8;
+
+    private static final int INITIAL_DISMISS_DELAY = 3500;
+    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
+    private static final long MENU_FADE_DURATION = 125;
+    private static final long MENU_SLOW_FADE_DURATION = 175;
+    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
+
+    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
+
+    private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
+    private static final boolean ENABLE_RESIZE_HANDLE = false;
+
+    private int mMenuState;
+    private boolean mResize = true;
+    private boolean mAllowMenuTimeout = true;
+    private boolean mAllowTouches = true;
+
+    private final List<RemoteAction> mActions = new ArrayList<>();
+
+    private AccessibilityManager mAccessibilityManager;
+    private Drawable mBackgroundDrawable;
+    private View mMenuContainer;
+    private LinearLayout mActionsGroup;
+    private int mBetweenActionPaddingLand;
+
+    private AnimatorSet mMenuContainerAnimator;
+    private PipMenuActivityController mController;
+
+    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    final float alpha = (float) animation.getAnimatedValue();
+                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255));
+                }
+            };
+
+    private Handler mHandler = new Handler();
+
+    private final Runnable mHideMenuRunnable = this::hideMenu;
+
+    protected View mViewRoot;
+    protected View mSettingsButton;
+    protected View mDismissButton;
+    protected View mResizeHandle;
+    protected View mTopEndContainer;
+    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
+
+    public PipMenuView(Context context, PipMenuActivityController controller) {
+        super(context, null, 0);
+        mContext = context;
+        mController = controller;
+
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        inflate(context, R.layout.pip_menu, this);
+
+        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
+        mBackgroundDrawable.setAlpha(0);
+        mViewRoot = findViewById(R.id.background);
+        mViewRoot.setBackground(mBackgroundDrawable);
+        mMenuContainer = findViewById(R.id.menu_container);
+        mMenuContainer.setAlpha(0);
+        mTopEndContainer = findViewById(R.id.top_end_container);
+        mSettingsButton = findViewById(R.id.settings);
+        mSettingsButton.setAlpha(0);
+        mSettingsButton.setOnClickListener((v) -> {
+            if (v.getAlpha() != 0) {
+                showSettings();
+            }
+        });
+        mDismissButton = findViewById(R.id.dismiss);
+        mDismissButton.setAlpha(0);
+        mDismissButton.setOnClickListener(v -> dismissPip());
+        findViewById(R.id.expand_button).setOnClickListener(v -> {
+            if (mMenuContainer.getAlpha() != 0) {
+                expandPip();
+            }
+        });
+        // TODO (b/161710689): Remove this once focusability for Windowless window is working
+        findViewById(R.id.expand_button).setFocusable(false);
+        mDismissButton.setFocusable(false);
+        mSettingsButton.setFocusable(false);
+
+        mResizeHandle = findViewById(R.id.resize_handle);
+        mResizeHandle.setAlpha(0);
+        mActionsGroup = findViewById(R.id.actions_group);
+        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
+                R.dimen.pip_between_action_padding_land);
+        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
+        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
+                mResizeHandle, mSettingsButton, mDismissButton);
+
+        initAccessibility();
+    }
+
+    private void initAccessibility() {
+        this.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                String label = getResources().getString(R.string.pip_menu_title);
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
+                    mController.onPipShowMenu();
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            hideMenu();
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mAllowTouches) {
+            return false;
+        }
+
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchGenericMotionEvent(event);
+    }
+
+    void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
+        mAllowMenuTimeout = allowMenuTimeout;
+        if (mMenuState != menuState) {
+            // Disallow touches if the menu needs to resize while showing, and we are transitioning
+            // to/from a full menu state.
+            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow
+                    && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
+            mAllowTouches = !disallowTouchesUntilAnimationEnd;
+            cancelDelayedHide();
+            updateActionViews(stackBounds);
+            if (mMenuContainerAnimator != null) {
+                mMenuContainerAnimator.cancel();
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 1f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 1f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 1f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(),
+                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
+                            ? 1f : 0f);
+            if (menuState == MENU_STATE_FULL) {
+                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+                        resizeAnim);
+            } else {
+                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
+            }
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
+                    ? MENU_FADE_DURATION
+                    : MENU_SLOW_FADE_DURATION);
+            if (allowMenuTimeout) {
+                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        repostDelayedHide(INITIAL_DISMISS_DELAY);
+                    }
+                });
+            }
+            if (withDelay) {
+                // starts the menu container animation after window expansion is completed
+                notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
+                    if (mMenuContainerAnimator == null) {
+                        return;
+                    }
+                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
+                    mMenuContainerAnimator.start();
+                });
+            } else {
+                notifyMenuStateChange(menuState, resizeMenuOnShow, null);
+                mMenuContainerAnimator.start();
+            }
+        } else {
+            // If we are already visible, then just start the delayed dismiss and unregister any
+            // existing input consumers from the previous drag
+            if (allowMenuTimeout) {
+                repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+            }
+        }
+    }
+
+    /**
+     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
+     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
+     * visibility callbacks invoked.
+     */
+    void fadeOutMenu() {
+        mMenuContainer.setAlpha(0f);
+        mSettingsButton.setAlpha(0f);
+        mDismissButton.setAlpha(0f);
+        mResizeHandle.setAlpha(0f);
+    }
+
+    void pokeMenu() {
+        cancelDelayedHide();
+    }
+
+    void onPipAnimationEnded() {
+        mAllowTouches = true;
+    }
+
+    void updateMenuLayout(Rect bounds) {
+        mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
+    }
+
+    void hideMenu() {
+        hideMenu(null);
+    }
+
+    void hideMenu(Runnable animationEndCallback) {
+        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false);
+    }
+
+    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
+            boolean animate) {
+        if (mMenuState != MENU_STATE_NONE) {
+            cancelDelayedHide();
+            if (notifyMenuVisibility) {
+                notifyMenuStateChange(MENU_STATE_NONE, mResize, null);
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 0f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 0f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 0f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(), 0f);
+            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
+            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (animationFinishedRunnable != null) {
+                        animationFinishedRunnable.run();
+                    }
+                }
+            });
+            mMenuContainerAnimator.start();
+        }
+    }
+
+    void setActions(Rect stackBounds, List<RemoteAction> actions) {
+        mActions.clear();
+        mActions.addAll(actions);
+        updateActionViews(stackBounds);
+    }
+
+    private void updateActionViews(Rect stackBounds) {
+        ViewGroup expandContainer = findViewById(R.id.expand_container);
+        ViewGroup actionsContainer = findViewById(R.id.actions_container);
+        actionsContainer.setOnTouchListener((v, ev) -> {
+            // Do nothing, prevent click through to parent
+            return true;
+        });
+
+        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+            actionsContainer.setVisibility(View.INVISIBLE);
+        } else {
+            actionsContainer.setVisibility(View.VISIBLE);
+            if (mActionsGroup != null) {
+                // Ensure we have as many buttons as actions
+                final LayoutInflater inflater = LayoutInflater.from(mContext);
+                while (mActionsGroup.getChildCount() < mActions.size()) {
+                    final ImageButton actionView = (ImageButton) inflater.inflate(
+                            R.layout.pip_menu_action, mActionsGroup, false);
+                    mActionsGroup.addView(actionView);
+                }
+
+                // Update the visibility of all views
+                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
+                            ? View.VISIBLE
+                            : View.GONE);
+                }
+
+                // Recreate the layout
+                final boolean isLandscapePip = stackBounds != null
+                        && (stackBounds.width() > stackBounds.height());
+                for (int i = 0; i < mActions.size(); i++) {
+                    final RemoteAction action = mActions.get(i);
+                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
+
+                    // TODO: Check if the action drawable has changed before we reload it
+                    action.getIcon().loadDrawableAsync(mContext, d -> {
+                        d.setTint(Color.WHITE);
+                        actionView.setImageDrawable(d);
+                    }, mHandler);
+                    actionView.setContentDescription(action.getContentDescription());
+                    if (action.isEnabled()) {
+                        actionView.setOnClickListener(v -> {
+                            mHandler.post(() -> {
+                                try {
+                                    action.getActionIntent().send();
+                                } catch (CanceledException e) {
+                                    Log.w(TAG, "Failed to send action", e);
+                                }
+                            });
+                        });
+                    }
+                    actionView.setEnabled(action.isEnabled());
+                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+
+                    // Update the margin between actions
+                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+                            actionView.getLayoutParams();
+                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
+                }
+            }
+
+            // Update the expand container margin to adjust the center of the expand button to
+            // account for the existence of the action container
+            FrameLayout.LayoutParams expandedLp =
+                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
+            expandedLp.topMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_action_padding);
+            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_expand_container_edge_margin);
+            expandContainer.requestLayout();
+        }
+    }
+
+    void updateDismissFraction(float fraction) {
+        int alpha;
+        final float menuAlpha = 1 - fraction;
+        if (mMenuState == MENU_STATE_FULL) {
+            mMenuContainer.setAlpha(menuAlpha);
+            mSettingsButton.setAlpha(menuAlpha);
+            mDismissButton.setAlpha(menuAlpha);
+            final float interpolatedAlpha =
+                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
+            alpha = (int) (interpolatedAlpha * 255);
+        } else {
+            if (mMenuState == MENU_STATE_CLOSE) {
+                mDismissButton.setAlpha(menuAlpha);
+            }
+            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
+        }
+        mBackgroundDrawable.setAlpha(alpha);
+    }
+
+    private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
+        mMenuState = menuState;
+        mController.onMenuStateChanged(menuState, resize, callback);
+    }
+
+    private void expandPip() {
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */);
+    }
+
+    private void dismissPip() {
+        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
+        // we want to disable animating the fadeout animation of the buttons in order to call on
+        // PipTouchHandler#onPipDismiss fast enough.
+        final boolean animate = mMenuState != MENU_STATE_CLOSE;
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate);
+    }
+
+    private void showSettings() {
+        final Pair<ComponentName, Integer> topPipActivityInfo =
+                PipUtils.getTopPipActivity(mContext, ActivityManager.getService());
+        if (topPipActivityInfo.first != null) {
+            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
+            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
+                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
+            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
+            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+            mContext.startActivity(settingsIntent);
+        }
+    }
+
+    private void cancelDelayedHide() {
+        mHandler.removeCallbacks(mHideMenuRunnable);
+    }
+
+    private void repostDelayedHide(int delay) {
+        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
+        mHandler.removeCallbacks(mHideMenuRunnable);
+        mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 19138fdb..dcee2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,6 +23,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.Choreographer;
 
@@ -66,6 +68,8 @@
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
 
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
     /** PIP's current bounds on the screen. */
     private final Rect mBounds = new Rect();
 
@@ -128,8 +132,10 @@
                         SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
 
     private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
-        mMenuController.updateMenuLayout(newBounds);
-        mBounds.set(newBounds);
+        mMainHandler.post(() -> {
+            mMenuController.updateMenuLayout(newBounds);
+            mBounds.set(newBounds);
+        });
     };
 
     /**
@@ -253,7 +259,9 @@
                 mTemporaryBounds.set(toBounds);
                 mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds,
                         (Rect newBounds) -> {
-                            mMenuController.updateMenuLayout(newBounds);
+                            mMainHandler.post(() -> {
+                                mMenuController.updateMenuLayout(newBounds);
+                            });
                     });
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 2800bb9..f6853ec 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -111,6 +111,7 @@
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
     private PipTaskOrganizer mPipTaskOrganizer;
+    private PipMenuActivityController mPipMenuActivityController;
     private PipUiEventLogger mPipUiEventLogger;
 
     private int mCtrlType;
@@ -119,7 +120,7 @@
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
             PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
             Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
-            PipUiEventLogger pipUiEventLogger) {
+            PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
@@ -129,6 +130,7 @@
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mSysUiState = sysUiState;
+        mPipMenuActivityController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
 
         context.getDisplay().getRealSize(mMaxSize);
@@ -298,6 +300,7 @@
         float x = ev.getX();
         float y = ev.getY();
         if (action == MotionEvent.ACTION_DOWN) {
+            final Rect currentPipBounds = mMotionHelper.getBounds();
             mLastResizeBounds.setEmpty();
             mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
             if (mAllowGesture) {
@@ -305,6 +308,10 @@
                 mDownPoint.set(x, y);
                 mLastDownBounds.set(mMotionHelper.getBounds());
             }
+            if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
+                    && mPipMenuActivityController.isMenuVisible()) {
+                mPipMenuActivityController.hideMenu();
+            }
 
         } else if (mAllowGesture) {
             switch (action) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 1b84c14..9693f23 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -230,7 +230,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer, this::getMovementBounds,
-                        this::updateMovementBounds, sysUiState, pipUiEventLogger);
+                        this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
@@ -773,10 +773,7 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     private void updateDismissFraction() {
-        // Skip updating the dismiss fraction when the IME is showing. This is to work around an
-        // issue where starting the menu activity for the dismiss overlay will steal the window
-        // focus, which closes the IME.
-        if (mMenuController != null && !mIsImeShowing) {
+        if (mMenuController != null) {
             Rect bounds = mMotionHelper.getBounds();
             final float target = mInsetBounds.bottom;
             float fraction = 0f;
@@ -784,7 +781,7 @@
                 final float distance = bounds.bottom - target;
                 fraction = Math.min(distance / bounds.height(), 1f);
             }
-            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
+            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
                 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
                 mMenuController.setDismissFraction(fraction);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
deleted file mode 100644
index 114c30e..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 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.pip.phone.dagger;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
-}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 7b45476..6a2ca44 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -22,8 +22,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.stackdivider.SplitScreenController;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -50,14 +48,6 @@
         return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    @SysUISingleton
-    @PipMenuActivityClass
-    @Provides
-    static Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
-    }
-
     @SysUISingleton
     @Provides
     static SplitScreen provideSplitScreen(Context context,