Merge "Avoid unnecessary copies in updateInputFlinger" into main
diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h
new file mode 100644
index 0000000..f36472d
--- /dev/null
+++ b/include/input/CoordinateFilter.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <input/Input.h>
+#include <input/OneEuroFilter.h>
+
+namespace android {
+
+/**
+ * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same
+ * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the
+ * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor
+ * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff
+ * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered|
+ */
+class CoordinateFilter {
+public:
+    explicit CoordinateFilter(float minCutoffFreq, float beta);
+
+    /**
+     * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must
+     * provide a timestamp strictly greater than the timestamp of the previous call. The first time
+     * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with
+     * filtered data.
+     *
+     * @param timestamp The timestamps at which to filter. It must be greater than the one passed in
+     * the previous call.
+     * @param coords Coordinates to be overwritten by the corresponding filtered coordinates.
+     */
+    void filter(std::chrono::duration<float> timestamp, PointerCoords& coords);
+
+private:
+    OneEuroFilter mXFilter;
+    OneEuroFilter mYFilter;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h
new file mode 100644
index 0000000..a0168e4
--- /dev/null
+++ b/include/input/OneEuroFilter.h
@@ -0,0 +1,101 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+
+#include <input/Input.h>
+
+namespace android {
+
+/**
+ * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff
+ * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to
+ * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to
+ * tune it.
+ */
+class OneEuroFilter {
+public:
+    /**
+     * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's
+     * paper.
+     */
+    static constexpr float kDefaultSpeedCutoffFreq = 1.0;
+
+    OneEuroFilter() = delete;
+
+    explicit OneEuroFilter(float minCutoffFreq, float beta,
+                           float speedCutoffFreq = kDefaultSpeedCutoffFreq);
+
+    OneEuroFilter(const OneEuroFilter&) = delete;
+    OneEuroFilter& operator=(const OneEuroFilter&) = delete;
+    OneEuroFilter(OneEuroFilter&&) = delete;
+    OneEuroFilter& operator=(OneEuroFilter&&) = delete;
+
+    /**
+     * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp
+     * strictly greater than the timestamp of the previous call. The first time the method is
+     * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value.
+     *
+     * @param timestamp The timestamp at which to filter. It must be strictly greater than the one
+     * provided in the previous call.
+     * @param rawPosition Position to be filtered.
+     */
+    float filter(std::chrono::duration<float> timestamp, float rawPosition);
+
+private:
+    /**
+     * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency
+     * criterion. Units are Hertz.
+     */
+    const float mMinCutoffFreq;
+
+    /**
+     * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the
+     * filtered signal's speed. The data member is dimensionless, that is, it does not have units.
+     */
+    const float mBeta;
+
+    /**
+     * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering
+     * of the signal's speed. Units are Hertz.
+     */
+    const float mSpeedCutoffFreq;
+
+    /**
+     * The timestamp from the previous call. Units are seconds.
+     */
+    std::optional<std::chrono::duration<float>> mPrevTimestamp;
+
+    /**
+     * The raw position from the previous call.
+     */
+    std::optional<float> mPrevRawPosition;
+
+    /**
+     * The filtered velocity from the previous call. Units are position per second.
+     */
+    std::optional<float> mPrevFilteredVelocity;
+
+    /**
+     * The filtered position from the previous call.
+     */
+    std::optional<float> mPrevFilteredPosition;
+};
+
+} // namespace android
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 6d95ca7..1550977 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -19,11 +19,13 @@
 #include <array>
 #include <chrono>
 #include <iterator>
+#include <map>
 #include <optional>
 #include <vector>
 
 #include <android-base/logging.h>
 #include <ftl/mixins.h>
+#include <input/CoordinateFilter.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 #include <input/RingBuffer.h>
@@ -293,4 +295,43 @@
     inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
 };
 
+/**
+ * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters
+ * the X and Y coordinates with a pair of One Euro filters.
+ */
+class FilteredLegacyResampler final : public Resampler {
+public:
+    /**
+     * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro
+     * filters.
+     */
+    explicit FilteredLegacyResampler(float minCutoffFreq, float beta);
+
+    void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent,
+                             const InputMessage* futureMessage) override;
+
+    std::chrono::nanoseconds getResampleLatency() const override;
+
+private:
+    LegacyResampler mResampler;
+
+    /**
+     * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a
+     * more detailed explanation.
+     */
+    const float mMinCutoffFreq;
+
+    /**
+     * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a
+     * more detailed explanation.
+     */
+    const float mBeta;
+
+    /*
+     * Note: an associative array with constant insertion and lookup times would be more efficient.
+     * When this was implemented, there was no container with these properties.
+     */
+    std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers;
+};
+
 } // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 35c40ab..334d84b 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -46,9 +46,12 @@
     }
     if (mSize != 0) {
         if (t.data() != nullptr) {
-            mData = new uint64_t[mSize];
+            if (mData == nullptr) {
+                mData = new uint64_t[mSize];
+            }
             memcpy(mData, t.data(), mSize * sizeof(uint64_t));
         } else {
+            delete[] mData;
             mData = nullptr;
         }
     }
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 6301c74..a62ad96 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -56,7 +56,7 @@
         });
         auto elapsedMs = millisSince(start);
         EXPECT_GE(elapsedMs, 1000);
-        EXPECT_LT(elapsedMs, 2000);
+        EXPECT_LT(elapsedMs, 3000); // b/377571547: higher to reduce flake
 
         ASSERT_TRUE(result.has_value());
         EXPECT_EQ(std::nullopt, result->exitCode);
@@ -65,7 +65,7 @@
 
     // ~CommandResult() called, child process is killed.
     // Assert that the second sleep does not finish.
-    EXPECT_LT(millisSince(start), 2000);
+    EXPECT_LT(millisSince(start), 3000);
 }
 
 TEST(UtilsHost, ExecuteLongRunning2) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1243b21..80e148b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -341,6 +341,10 @@
         "libgui_aidl_headers",
     ],
 
+    static_libs: [
+        "libsurfaceflingerflags",
+    ],
+
     afdo: true,
 
     lto: {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 74097b8..3260c53 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -92,6 +92,7 @@
 }
 
 constexpr int64_t INVALID_VSYNC = -1;
+const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry";
 
 } // namespace
 
@@ -873,6 +874,7 @@
     const bool earlyWakeupEnd = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
     const bool isAutoTimestamp = parcel->readBool();
+    const bool logCallPoints = parcel->readBool();
     FrameTimelineInfo frameTimelineInfo;
     frameTimelineInfo.readFromParcel(parcel);
 
@@ -1000,6 +1002,7 @@
     parcel->writeBool(mEarlyWakeupEnd);
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeBool(mIsAutoTimestamp);
+    parcel->writeBool(mLogCallPoints);
     mFrameTimelineInfo.writeToParcel(parcel);
     parcel->writeStrongBinder(mApplyToken);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -1135,6 +1138,12 @@
 
     mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
 
+    mLogCallPoints |= other.mLogCallPoints;
+    if (mLogCallPoints) {
+        ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
+             "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
+    }
+
     other.clear();
     return *this;
 }
@@ -1154,6 +1163,7 @@
     mFrameTimelineInfo = {};
     mApplyToken = nullptr;
     mMergedTransactionIds.clear();
+    mLogCallPoints = false;
 }
 
 uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1362,6 +1372,10 @@
         syncCallback->wait();
     }
 
+    if (mLogCallPoints) {
+        ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+    }
+
     mStatus = NO_ERROR;
     return binderStatus;
 }
@@ -1392,6 +1406,11 @@
     t.registerSurfaceControlForCallback(sc);
     return t.apply(/*sync=*/false, /* oneWay=*/true);
 }
+
+void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
+    mLogCallPoints = true;
+}
+
 // ---------------------------------------------------------------------------
 
 sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5ea0c16..e9262b3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -437,6 +437,8 @@
         static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
         // Tracks registered callbacks
         sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
+        // Prints debug logs when enabled.
+        bool mLogCallPoints = false;
 
     protected:
         std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -809,6 +811,7 @@
         static void setDefaultApplyToken(sp<IBinder> applyToken);
 
         static status_t sendSurfaceFlushJankDataTransaction(const sp<SurfaceControl>& sc);
+        void enableDebugLogCallPoints();
     };
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 1c7e0e4..22d32e9 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -2,6 +2,14 @@
 container: "system"
 
 flag {
+  name: "apply_picture_profiles"
+  namespace: "tv_os_media"
+  description: "This flag controls sending picture profiles from BBQ to Composer HAL"
+  bug: "337330263"
+  is_fixed_read_only: true
+} # apply_picture_profiles
+
+flag {
   name: "bq_setframerate"
   namespace: "core_graphics"
   description: "This flag controls plumbing setFrameRate thru BufferQueue"
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index e4e81ad..a4ae54b 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -217,6 +217,7 @@
     ],
     srcs: [
         "AccelerationCurve.cpp",
+        "CoordinateFilter.cpp",
         "Input.cpp",
         "InputConsumer.cpp",
         "InputConsumerNoResampling.cpp",
@@ -230,6 +231,7 @@
         "KeyLayoutMap.cpp",
         "MotionPredictor.cpp",
         "MotionPredictorMetricsManager.cpp",
+        "OneEuroFilter.cpp",
         "PrintTools.cpp",
         "PropertyMap.cpp",
         "Resampler.cpp",
diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp
new file mode 100644
index 0000000..d231474
--- /dev/null
+++ b/libs/input/CoordinateFilter.cpp
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CoordinateFilter"
+
+#include <input/CoordinateFilter.h>
+
+namespace android {
+
+CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
+      : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}
+
+void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) {
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX()));
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY()));
+}
+
+} // namespace android
diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp
new file mode 100644
index 0000000..400d7c9
--- /dev/null
+++ b/libs/input/OneEuroFilter.cpp
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OneEuroFilter"
+
+#include <chrono>
+#include <cmath>
+
+#include <android-base/logging.h>
+#include <input/CoordinateFilter.h>
+
+namespace android {
+namespace {
+
+inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
+    return minCutoffFreq + beta * std::abs(filteredSpeed);
+}
+
+inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) {
+    return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq)));
+}
+
+inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) {
+    return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition;
+}
+
+} // namespace
+
+OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq)
+      : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {}
+
+float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) {
+    LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp))
+            << "Timestamp must be greater than mPrevTimestamp";
+
+    const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value())
+            ? (timestamp - *mPrevTimestamp)
+            : std::chrono::duration<float>{1.0};
+
+    const float rawVelocity = (mPrevFilteredPosition.has_value())
+            ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count())
+            : 0.0;
+
+    const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);
+
+    const float filteredVelocity = (mPrevFilteredVelocity.has_value())
+            ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor)
+            : rawVelocity;
+
+    const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity);
+
+    const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq);
+
+    const float filteredPosition = (mPrevFilteredPosition.has_value())
+            ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor)
+            : rawPosition;
+
+    mPrevTimestamp = timestamp;
+    mPrevRawPosition = rawPosition;
+    mPrevFilteredVelocity = filteredVelocity;
+    mPrevFilteredPosition = filteredPosition;
+
+    return filteredPosition;
+}
+
+} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 056db09..3ab132d 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -389,4 +389,34 @@
     mLastRealSample = *(mLatestSamples.end() - 1);
 }
 
+// --- FilteredLegacyResampler ---
+
+FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta)
+      : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {}
+
+void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime,
+                                                  MotionEvent& motionEvent,
+                                                  const InputMessage* futureSample) {
+    mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample);
+    const size_t numSamples = motionEvent.getHistorySize() + 1;
+    for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+             ++pointerIndex) {
+            const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id;
+            const nanoseconds eventTime =
+                    nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)};
+            // Refer to the static function `setMotionEventPointerCoords` for a justification of
+            // casting away const.
+            PointerCoords& pointerCoords = const_cast<PointerCoords&>(
+                    *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+            const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta);
+            iter->second.filter(eventTime, pointerCoords);
+        }
+    }
+}
+
+std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const {
+    return mResampler.getResampleLatency();
+}
+
 } // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 661c9f7..46e8190 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -25,6 +25,7 @@
         "InputVerifier_test.cpp",
         "MotionPredictor_test.cpp",
         "MotionPredictorMetricsManager_test.cpp",
+        "OneEuroFilter_test.cpp",
         "Resampler_test.cpp",
         "RingBuffer_test.cpp",
         "TestInputChannel.cpp",
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
new file mode 100644
index 0000000..270e789
--- /dev/null
+++ b/libs/input/tests/OneEuroFilter_test.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/OneEuroFilter.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <input/Input.h>
+
+namespace android {
+namespace {
+
+using namespace std::literals::chrono_literals;
+using std::chrono::duration;
+
+struct Sample {
+    duration<double> timestamp{};
+    double value{};
+
+    friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; }
+};
+
+/**
+ * Generates a sinusoidal signal with the passed frequency and amplitude.
+ */
+std::vector<Sample> generateSinusoidalSignal(duration<double> signalDuration,
+                                             double samplingFrequency, double signalFrequency,
+                                             double amplitude) {
+    std::vector<Sample> signal;
+    const duration<double> samplingPeriod{1.0 / samplingFrequency};
+    for (duration<double> timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) {
+        signal.push_back(
+                Sample{timestamp,
+                       amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())});
+    }
+    return signal;
+}
+
+double meanAbsoluteError(const std::vector<Sample>& filteredSignal,
+                         const std::vector<Sample>& signal) {
+    if (filteredSignal.size() != signal.size()) {
+        ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples";
+        return std::numeric_limits<double>::max();
+    }
+    std::vector<double> absoluteError;
+    for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) {
+        absoluteError.push_back(
+                std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value));
+    }
+    if (absoluteError.empty()) {
+        ADD_FAILURE() << "Zero division. absoluteError is empty";
+        return std::numeric_limits<double>::max();
+    }
+    return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size();
+}
+
+double maxAbsoluteAmplitude(const std::vector<Sample>& signal) {
+    if (signal.empty()) {
+        ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty";
+        return std::numeric_limits<double>::max();
+    }
+    std::vector<Sample> absoluteSignal;
+    for (const Sample& sample : signal) {
+        absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)});
+    }
+    return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value;
+}
+
+} // namespace
+
+class OneEuroFilterTest : public ::testing::Test {
+protected:
+    // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60
+    // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information
+    // about these parameters.
+    OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {}
+
+    std::vector<Sample> filterSignal(const std::vector<Sample>& signal) {
+        std::vector<Sample> filteredSignal;
+        for (const Sample& sample : signal) {
+            filteredSignal.push_back(
+                    Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)});
+        }
+        return filteredSignal;
+    }
+
+    OneEuroFilter mFilter;
+};
+
+TEST_F(OneEuroFilterTest, PassLowFrequencySignal) {
+    const std::vector<Sample> signal =
+            generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1,
+                                     /*amplitude=*/1);
+
+    const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+    // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency
+    // filtered signal is expected to be almost identical to the raw one. Therefore, the error
+    // between them should be minimal. The constant is heuristically chosen.
+    EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25);
+}
+
+TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) {
+    const std::vector<Sample> signal =
+            generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5,
+                                     /*amplitude=*/1);
+
+    const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+    // The filtered signal should consist of values that are much closer to zero. The comparison
+    // constant is heuristically chosen.
+    EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25);
+}
+
+} // namespace android
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index a3a43e2..cc73f40 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -21,12 +21,15 @@
 
 #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
 
+#include <android-base/stringprintf.h>
 #include <common/trace.h>
 #include <log/log_main.h>
 #include <sync/sync.h>
 
 namespace android::renderengine::skia {
 
+using base::StringAppendF;
+
 std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create(
         const RenderEngineCreationArgs& args) {
     std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args));
@@ -111,4 +114,9 @@
     return res;
 }
 
+void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+    StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n");
+    SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
 } // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
index e6123c2..ba17f71 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -28,6 +28,7 @@
     std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
     void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
     base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+    void appendBackendSpecificInfoToDump(std::string& result) override;
 
 private:
     GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index 390ad6e..a9332fa 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -25,6 +25,7 @@
 #include <include/gpu/graphite/Recording.h>
 #include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
 
+#include <android-base/stringprintf.h>
 #include <log/log_main.h>
 #include <sync/sync.h>
 
@@ -33,6 +34,8 @@
 
 namespace android::renderengine::skia {
 
+using base::StringAppendF;
+
 std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create(
         const RenderEngineCreationArgs& args) {
     std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args));
@@ -139,4 +142,9 @@
     return drawFenceFd;
 }
 
+void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+    StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n");
+    SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
 } // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h
index cf24a3b..33a47f1 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.h
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h
@@ -30,6 +30,7 @@
     std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
     void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
     base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+    void appendBackendSpecificInfoToDump(std::string& result) override;
 
 private:
     GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 4ef7d5b..ddae9fc 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -541,7 +541,7 @@
 
 void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
     const GLExtensions& extensions = GLExtensions::getInstance();
-    StringAppendF(&result, "\n ------------RE GLES------------\n");
+    StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n");
     StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
     StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
     StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 677a2b6..177abe6 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -169,24 +169,26 @@
 }
 
 void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
-    StringAppendF(&result, "\n ------------RE Vulkan----------\n");
-    StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
-    StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
+    // Subclasses will prepend a backend-specific name / section header
+    StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
+    StringAppendF(&result, "Vulkan protected device initialized: %d\n",
                   sProtectedContentVulkanInterface.isInitialized());
 
     if (!sVulkanInterface.isInitialized()) {
         return;
     }
 
-    StringAppendF(&result, "\n Instance extensions:\n");
+    StringAppendF(&result, "Instance extensions: [\n");
     for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) {
-        StringAppendF(&result, "\n %s\n", name.c_str());
+        StringAppendF(&result, "  %s\n", name.c_str());
     }
+    StringAppendF(&result, "]\n");
 
-    StringAppendF(&result, "\n Device extensions:\n");
+    StringAppendF(&result, "Device extensions: [\n");
     for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) {
-        StringAppendF(&result, "\n %s\n", name.c_str());
+        StringAppendF(&result, "  %s\n", name.c_str());
     }
+    StringAppendF(&result, "]\n");
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index d2bb3d5..88b04df 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -81,7 +81,7 @@
     SkiaRenderEngine::Contexts createContexts() override;
     bool supportsProtectedContentImpl() const override;
     bool useProtectedContextImpl(GrProtected isProtected) override;
-    void appendBackendSpecificInfoToDump(std::string& result) override;
+    virtual void appendBackendSpecificInfoToDump(std::string& result) override;
 
     // TODO: b/300533018 - refactor this to be non-static
     static VulkanInterface& getVulkanInterface(bool protectedContext);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 12230f9..87e213e 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -136,6 +136,7 @@
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
+        "PictureProfileHandle.cpp",
         "PixelFormat.cpp",
         "PublicFormat.cpp",
         "StaticAsserts.cpp",
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/libs/ui/PictureProfileHandle.cpp
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
copy to libs/ui/PictureProfileHandle.cpp
index 1ba38a8..0701e90 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/libs/ui/PictureProfileHandle.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2009 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.
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-#include "MockPowerAdvisor.h"
+#include <ui/PictureProfileHandle.h>
 
-namespace android::Hwc2::mock {
+#include <format>
 
-// Explicit default instantiation is recommended.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android {
 
-} // namespace android::Hwc2::mock
+const PictureProfileHandle PictureProfileHandle::NONE(0);
+
+::std::string toString(const PictureProfileHandle& handle) {
+    return std::format("{:#010x}", handle.getId());
+}
+
+} // namespace android
diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h
new file mode 100644
index 0000000..9b709b6
--- /dev/null
+++ b/libs/ui/include/ui/PictureProfileHandle.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <array>
+#include <string>
+
+namespace android {
+
+/**
+ * An opaque value that uniquely identifies a picture profile, or a set of parameters, which
+ * describes the configuration of a picture processing pipeline that is applied to a graphic buffer
+ * to enhance its quality prior to rendering on the display.
+ */
+typedef int64_t PictureProfileId;
+
+/**
+ * A picture profile handle wraps the picture profile ID for type-safety, and represents an opaque
+ * handle that doesn't have the performance drawbacks of Binders.
+ */
+class PictureProfileHandle {
+public:
+    // A profile that represents no picture processing.
+    static const PictureProfileHandle NONE;
+
+    PictureProfileHandle() { *this = NONE; }
+    PictureProfileHandle(PictureProfileId id) : mId(id) {}
+
+    PictureProfileId const& getId() const { return mId; }
+
+    inline bool operator==(const PictureProfileHandle& rhs) { return mId == rhs.mId; }
+    inline bool operator!=(const PictureProfileHandle& rhs) { return !(*this == rhs); }
+
+    // Is the picture profile effectively null, or not-specified?
+    inline bool operator!() const { return mId == NONE.mId; }
+
+    operator bool() const { return !!*this; }
+
+    friend ::std::string toString(const PictureProfileHandle& handle);
+
+private:
+    PictureProfileId mId;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index f1c79c1..8a667ae 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -177,13 +177,13 @@
 filegroup {
     name: "libsurfaceflinger_backend_sources",
     srcs: [
+        "PowerAdvisor/*.cpp",
         "DisplayHardware/AidlComposerHal.cpp",
         "DisplayHardware/ComposerHal.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/HidlComposerHal.cpp",
-        "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
     ],
 }
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 7095b9d..8067ace 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -49,9 +49,7 @@
         "libaidlcommonsupport",
         "libprocessgroup",
         "libprocessgroup_util",
-        "libcgrouprc",
         "libjsoncpp",
-        "libcgrouprc_format",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6e60839..98bccd8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -24,7 +24,7 @@
 #include <ui/Size.h>
 #include <ui/StaticDisplayInfo.h>
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -48,7 +48,7 @@
 
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
-    Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+    adpf::PowerAdvisor* powerAdvisor = nullptr;
 
     // Debugging. Human readable name for the display.
     std::string name;
@@ -82,7 +82,7 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
+    DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
     }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d8466ff..104a61b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -30,7 +30,7 @@
 #include <ui/DisplayIdentification.h>
 
 #include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -102,7 +102,7 @@
     void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
     DisplayId mId;
     bool mIsDisconnected = false;
-    Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::PowerAdvisor* mPowerAdvisor = nullptr;
     const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
             override;
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1825065..6a97c49 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -36,7 +36,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 416001e..c1e59d0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -39,7 +39,7 @@
 #include "ftl/future.h"
 #include "mock/DisplayHardware/MockHWC2.h"
 #include "mock/DisplayHardware/MockHWComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
@@ -192,7 +192,7 @@
     }
 
     StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    StrictMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make();
@@ -1035,7 +1035,7 @@
     }
 
     NiceMock<android::mock::HWComposer> mHwComposer;
-    NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    NiceMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
     NiceMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make();
     sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make();
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d09a6b3..af2b48f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -42,7 +42,6 @@
 
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "FrontEnd/DisplayInfo.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
@@ -286,6 +285,8 @@
     bool isProtected = false;
     // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
     Fps requestedRefreshRate;
+    int32_t maxLayerPictureProfiles = 0;
+    bool hasPictureProcessing = false;
 
 private:
     static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 5814aa4..4c8ff58 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,12 +44,11 @@
 using aidl::android::hardware::graphics::composer3::BnComposerCallback;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
 using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::PowerMode;
 using aidl::android::hardware::graphics::composer3::VirtualDisplay;
 
-using aidl::android::hardware::graphics::composer3::CommandResultPayload;
-
 using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
 using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
 using AidlDisplayIdentification =
@@ -1639,6 +1638,41 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) {
+    const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate<int64_t>(display),
+                                                                        outMaxProfiles);
+    if (!status.isOk()) {
+        ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setDisplayPictureProfileId(translate<int64_t>(display), id);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
+Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerPictureProfileId(translate<int64_t>(display),
+                                               translate<int64_t>(layer), id);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
 ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
         REQUIRES_SHARED(mMutex) {
     return mWriters.get(display);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d724b21..933e8d1 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -250,6 +250,9 @@
             std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
                     outLuts) override;
     Error setLayerLuts(Display display, Layer layer, Luts& luts) override;
+    Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+    Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
+    Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 42ddcd1..c1333c2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -29,11 +29,13 @@
 #include <math/mat4.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
 #include <utils/StrongPointer.h>
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
+#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -44,7 +46,6 @@
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
-#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <optional>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -307,6 +308,9 @@
     virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers,
                                    std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
     virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0;
+    virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
+    virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
+    virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 5355a12..a274995 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -31,6 +31,7 @@
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
 
 #include <algorithm>
 #include <cinttypes>
@@ -53,6 +54,7 @@
 using android::GraphicBuffer;
 using android::HdrCapabilities;
 using android::HdrMetadata;
+using android::PictureProfileHandle;
 using android::Rect;
 using android::Region;
 using android::sp;
@@ -655,6 +657,16 @@
     return static_cast<Error>(error);
 }
 
+Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) {
+    const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles);
+    return static_cast<Error>(error);
+}
+
+Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) {
+    const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId());
+    return static_cast<Error>(error);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
@@ -1086,6 +1098,15 @@
     return static_cast<Error>(intError);
 }
 
+Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+    const auto intError =
+            mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId());
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 799fd02..6740d8a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -24,6 +24,7 @@
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/HdrCapabilities.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Region.h>
 #include <ui/StaticDisplayInfo.h>
 #include <utils/Log.h>
@@ -199,6 +200,9 @@
     [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0;
     [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation(
             Hwc2::AidlTransform* outTransform) const = 0;
+    [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
+    [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+            const PictureProfileHandle& handle) = 0;
 };
 
 namespace impl {
@@ -282,6 +286,8 @@
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) override;
     hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
+    hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
+    hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
 
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
@@ -377,6 +383,8 @@
     [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
     [[nodiscard]] virtual hal::Error setLuts(
             aidl::android::hardware::graphics::composer3::Luts& luts) = 0;
+    [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+            const PictureProfileHandle& handle) = 0;
 };
 
 namespace impl {
@@ -428,6 +436,7 @@
     hal::Error setBrightness(float brightness) override;
     hal::Error setBlockingRegion(const android::Region& region) override;
     hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override;
+    hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override;
 
 private:
     // These are references to data owned by HWComposer, which will outlive
@@ -449,6 +458,7 @@
     android::HdrMetadata mHdrMetadata;
     android::mat4 mColorMatrix;
     uint32_t mBufferSlot;
+    android::PictureProfileHandle profile;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7d77634..61d4541 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -1022,6 +1022,24 @@
     return NO_ERROR;
 }
 
+int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) {
+    int32_t maxProfiles = 0;
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles);
+    RETURN_IF_HWC_ERROR(error, displayId, 0);
+    return maxProfiles;
+}
+
+status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId,
+                                                    const PictureProfileHandle& handle) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle);
+    if (error != hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    return NO_ERROR;
+}
+
 const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
     return mSupportedLayerGenericMetadata;
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7b04d67..e21ce1d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -29,6 +29,7 @@
 #include <ftl/future.h>
 #include <ui/DisplayIdentification.h>
 #include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -65,6 +66,7 @@
 class TestableSurfaceFlinger;
 struct HWComposerTest;
 struct CompositionInfo;
+class PictureProfileHandle;
 
 namespace Hwc2 {
 class Composer;
@@ -296,7 +298,7 @@
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
 
-    // Composer 3.0
+    // AIDL Composer
     virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
     virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
     virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
@@ -315,8 +317,10 @@
     virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
     virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
                                            Fps frameInterval) = 0;
-    // mapper
     virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0;
+    virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
+    virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+                                                    const PictureProfileHandle& handle) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -480,9 +484,10 @@
     status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
     status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
                                    Fps frameInterval) override;
-
-    // get a mapper
     HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override;
+    int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
+    status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+                                            const android::PictureProfileHandle& profile) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 6a7a09b..e359a26 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1446,6 +1446,18 @@
                      "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL");
 }
 
+Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) {
+    return Error::UNSUPPORTED;
+}
+
 void HidlComposer::registerCallback(ComposerCallback& callback) {
     const bool vsyncSwitchingSupported =
             isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index a3d1f7f..9a89dba 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -357,6 +357,9 @@
             override;
     Error setLayerLuts(Display, Layer,
                        aidl::android::hardware::graphics::composer3::Luts&) override;
+    Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+    Error setDisplayPictureProfileId(Display, PictureProfileId) override;
+    Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 47b811b..c13e444 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -378,6 +378,11 @@
     }
 }
 
+void SurfaceFrame::setDesiredPresentTime(nsecs_t desiredPresentTime) {
+    std::scoped_lock lock(mMutex);
+    mActuals.desiredPresentTime = desiredPresentTime;
+}
+
 void SurfaceFrame::setDropTime(nsecs_t dropTime) {
     std::scoped_lock lock(mMutex);
     mDropTime = dropTime;
@@ -1456,6 +1461,30 @@
             static_cast<float>(totalPresentToPresentWalls);
 }
 
+void FrameTimeline::generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const {
+    std::scoped_lock lock(mMutex);
+
+    // TODO: Include FPS calculation here
+    for (auto displayFrame : mDisplayFrames) {
+        if (!count--) {
+            break;
+        }
+
+        if (displayFrame->getActuals().presentTime <= 0) {
+            continue;
+        }
+
+        for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+            if (surfaceFrame->getLayerId() == layer) {
+                outStats->actualPresentTimesNano.push_back(surfaceFrame->getActuals().presentTime);
+                outStats->desiredPresentTimesNano.push_back(
+                        surfaceFrame->getActuals().desiredPresentTime);
+                outStats->frameReadyTimesNano.push_back(surfaceFrame->getActuals().endTime);
+            }
+        }
+    }
+}
+
 std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& [fence, _] = mPendingPresentFences[i];
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index cffb61e..6cda309 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -85,16 +85,20 @@
  */
 struct TimelineItem {
     TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
-                 const nsecs_t presentTime = 0)
-          : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+                 const nsecs_t presentTime = 0, const nsecs_t desiredPresentTime = 0)
+          : startTime(startTime),
+            endTime(endTime),
+            presentTime(presentTime),
+            desiredPresentTime(desiredPresentTime) {}
 
     nsecs_t startTime;
     nsecs_t endTime;
     nsecs_t presentTime;
+    nsecs_t desiredPresentTime;
 
     bool operator==(const TimelineItem& other) const {
         return startTime == other.startTime && endTime == other.endTime &&
-                presentTime == other.presentTime;
+                presentTime == other.presentTime && desiredPresentTime != other.desiredPresentTime;
     }
 
     bool operator!=(const TimelineItem& other) const { return !(*this == other); }
@@ -183,6 +187,7 @@
     void setActualStartTime(nsecs_t actualStartTime);
     void setActualQueueTime(nsecs_t actualQueueTime);
     void setAcquireFenceTime(nsecs_t acquireFenceTime);
+    void setDesiredPresentTime(nsecs_t desiredPresentTime);
     void setDropTime(nsecs_t dropTime);
     void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
     void setRenderRate(Fps renderRate);
@@ -341,6 +346,9 @@
     // containing at least one layer ID.
     virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0;
 
+    // Supports the legacy FrameStats interface
+    virtual void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const = 0;
+
     // Restores the max number of display frames to default. Called by SF backdoor.
     virtual void reset() = 0;
 };
@@ -501,6 +509,7 @@
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
     float computeFps(const std::unordered_set<int32_t>& layerIds) override;
+    void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const override;
     void reset() override;
 
     // Sets up the perfetto tracing backend and data source.
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index d709530..da536b6 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -166,7 +166,8 @@
             }
             out << "(Mirroring) ";
         }
-        out << *mLayer;
+
+        out << *mLayer << " pid=" << mLayer->ownerPid.val() << " uid=" << mLayer->ownerUid.val();
     }
 
     for (size_t i = 0; i < mChildren.size(); i++) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 11b674b..a8be50a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -250,6 +250,7 @@
     if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
     if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
     if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+    if (contentDirty) reason << " contentDirty";
     return reason.str();
 }
 
@@ -359,8 +360,9 @@
                           uint32_t displayRotationFlags) {
     clientChanges = requested.what;
     changes = requested.changes;
-    contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
-    hasReadyFrame = requested.autoRefresh;
+    autoRefresh = requested.autoRefresh;
+    contentDirty = requested.what & layer_state_t::CONTENT_DIRTY || autoRefresh;
+    hasReadyFrame = autoRefresh;
     sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
     updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
 
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b7d4cc5..b8df3ed 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -77,6 +77,7 @@
     gui::LayerMetadata layerMetadata;
     gui::LayerMetadata relativeLayerMetadata;
     bool hasReadyFrame; // used in post composition to check if there is another frame ready
+    bool autoRefresh;
     ui::Transform localTransformInverse;
     gui::WindowInfo inputInfo;
     ui::Transform localTransform;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 10e212e..7569c1b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -314,8 +314,8 @@
 void clearChanges(LayerSnapshot& snapshot) {
     snapshot.changes.clear();
     snapshot.clientChanges = 0;
-    snapshot.contentDirty = false;
-    snapshot.hasReadyFrame = false;
+    snapshot.contentDirty = snapshot.autoRefresh;
+    snapshot.hasReadyFrame = snapshot.autoRefresh;
     snapshot.sidebandStreamHasFrame = false;
     snapshot.surfaceDamage.clear();
 }
@@ -724,10 +724,12 @@
     if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
     snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
     snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
+    // mark the content as dirty if the parent state changes can dirty the child's content (for
+    // example alpha)
+    snapshot.contentDirty |= (snapshot.clientChanges & layer_state_t::CONTENT_DIRTY) != 0;
     snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
             parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
             (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
-
     const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
             snapshot.clientChanges & layer_state_t::eReparent ||
             snapshot.changes.any(RequestedLayerState::Changes::Visibility |
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 20ba45f..195461f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -154,7 +154,7 @@
     mDrawingState.metadata = args.metadata;
     mDrawingState.frameTimelineInfo = {};
     mDrawingState.postTime = -1;
-    mFrameTracker.setDisplayRefreshPeriod(
+    mDeprecatedFrameTracker.setDisplayRefreshPeriod(
             args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
 
     mOwnerUid = args.ownerUid;
@@ -472,6 +472,9 @@
                                                                  getSequence(), mName,
                                                                  mTransactionName,
                                                                  /*isBuffer*/ false, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
+
     surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
@@ -490,6 +493,8 @@
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
                                                                  /*isBuffer*/ true, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
     surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
@@ -514,6 +519,8 @@
                                                                  mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
                                                                  /*isBuffer*/ false, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
     surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
@@ -605,15 +612,42 @@
 }
 
 void Layer::dumpFrameStats(std::string& result) const {
-    mFrameTracker.dumpStats(result);
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        FrameStats fs = FrameStats();
+        getFrameStats(&fs);
+        for (auto desired = fs.desiredPresentTimesNano.begin(),
+                  actual = fs.actualPresentTimesNano.begin(),
+                  ready = fs.frameReadyTimesNano.begin();
+             desired != fs.desiredPresentTimesNano.end() &&
+             actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end();
+             ++desired, ++actual, ++ready) {
+            result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready));
+        }
+
+        result.push_back('\n');
+    } else {
+        mDeprecatedFrameTracker.dumpStats(result);
+    }
 }
 
 void Layer::clearFrameStats() {
-    mFrameTracker.clearStats();
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        mFrameStatsHistorySize = 0;
+    } else {
+        mDeprecatedFrameTracker.clearStats();
+    }
 }
 
 void Layer::getFrameStats(FrameStats* outStats) const {
-    mFrameTracker.getStats(outStats);
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        if (auto ftl = getTimeline()) {
+            float fps = ftl->get().computeFps({getSequence()});
+            ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats);
+            outStats->refreshPeriodNano = Fps::fromValue(fps).getPeriodNsecs();
+        }
+    } else {
+        mDeprecatedFrameTracker.getStats(outStats);
+    }
 }
 
 void Layer::onDisconnect() {
@@ -1348,9 +1382,9 @@
         handle->compositorTiming = compositorTiming;
     }
 
-    // Update mFrameTracker.
+    // Update mDeprecatedFrameTracker.
     nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
-    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+    mDeprecatedFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
@@ -1370,15 +1404,15 @@
         }
     }
 
+    // The SurfaceFrame's AcquireFence is the same as this.
     std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
     if (frameReadyFence->isValid()) {
-        mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+        mDeprecatedFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
         // There was no fence for this frame, so assume that it was ready
         // to be presented at the desired present time.
-        mFrameTracker.setFrameReadyTime(desiredPresentTime);
+        mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
-
     if (display) {
         const auto activeMode = display->refreshRateSelector().getActiveMode();
         const Fps refreshRate = activeMode.fps;
@@ -1393,7 +1427,7 @@
             mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+            mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
         } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                    displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
             // The HWC doesn't support present fences, so use the present timestamp instead.
@@ -1414,11 +1448,12 @@
             mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
                                                    mCurrentFrameNumber, actualPresentTime,
                                                    FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentTime(actualPresentTime);
+            mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime);
         }
     }
 
-    mFrameTracker.advanceFrame();
+    mFrameStatsHistorySize++;
+    mDeprecatedFrameTracker.advanceFrame();
     mBufferInfo.mFrameLatencyNeeded = false;
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index a2716c6..c234a75 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,6 +18,7 @@
 
 #include <android/gui/DropInputMode.h>
 #include <android/gui/ISurfaceComposerClient.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
 #include <gui/LayerState.h>
@@ -44,6 +45,7 @@
 #include <scheduler/Seamlessness.h>
 
 #include <cstdint>
+#include <functional>
 #include <optional>
 #include <vector>
 
@@ -433,8 +435,12 @@
 
     uint32_t mTransactionFlags{0};
 
+    // Leverages FrameTimeline to generate FrameStats. Since FrameTimeline already has the data,
+    // statistical history needs to only be tracked by count of frames.
+    // TODO: Deprecate the '--latency-clear' and get rid of this.
+    std::atomic<uint16_t> mFrameStatsHistorySize;
     // Timestamp history for UIAutomation. Thread safe.
-    FrameTracker mFrameTracker;
+    FrameTracker mDeprecatedFrameTracker;
 
     // main thread
     sp<NativeHandle> mSidebandStream;
@@ -556,6 +562,9 @@
 
     std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
     bool mHandleAlive = false;
+    std::optional<std::reference_wrapper<frametimeline::FrameTimeline>> getTimeline() const {
+        return *mFlinger->mFrameTimeline;
+    }
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
similarity index 92%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index c914ec3..c7d0b2c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <cinttypes>
 #include <cstdint>
+#include <functional>
 #include <optional>
 
 #include <android-base/properties.h>
@@ -33,45 +34,29 @@
 
 #include <binder/IServiceManager.h>
 
-#include "../SurfaceFlingerProperties.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+#pragma clang diagnostic pop
 
+#include <common/FlagManager.h>
 #include "PowerAdvisor.h"
-#include "SurfaceFlinger.h"
 
-namespace android {
-namespace Hwc2 {
+namespace hal = aidl::android::hardware::power;
 
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android::adpf::impl {
 
-namespace impl {
-
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::Mode;
-using aidl::android::hardware::power::SessionHint;
-using aidl::android::hardware::power::SessionTag;
-using aidl::android::hardware::power::WorkDuration;
-using aidl::android::hardware::power::WorkDurationFixedV1;
-
-using aidl::android::hardware::common::fmq::MQDescriptor;
 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::power::ChannelMessage;
 using android::hardware::EventFlag;
 
-using ChannelMessageContents = ChannelMessage::ChannelMessageContents;
-using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>;
+using ChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
+using MsgQueue = android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>;
 using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
 
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
-std::chrono::milliseconds getUpdateTimeout() {
-    // Default to a timeout of 80ms if nothing else is specified
-    static std::chrono::milliseconds timeout =
-            std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80));
-    return timeout;
-}
-
 void traceExpensiveRendering(bool enabled) {
     if (enabled) {
         SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
@@ -82,28 +67,30 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
-      : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) {
-    if (getUpdateTimeout() > 0ms) {
-        mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(),
+PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn,
+                           std::chrono::milliseconds timeout)
+      : mPowerHal(std::make_unique<power::PowerHalController>()) {
+    if (timeout > 0ms) {
+        mScreenUpdateTimer.emplace("UpdateImminentTimer", timeout,
                                    /* resetCallback */ nullptr,
                                    /* timeoutCallback */
-                                   [this] {
+                                   [this, disableExpensiveFn = std::move(sfDisableExpensiveFn),
+                                    timeout] {
                                        while (true) {
                                            auto timeSinceLastUpdate = std::chrono::nanoseconds(
                                                    systemTime() - mLastScreenUpdatedTime.load());
-                                           if (timeSinceLastUpdate >= getUpdateTimeout()) {
+                                           if (timeSinceLastUpdate >= timeout) {
                                                break;
                                            }
                                            // We may try to disable expensive rendering and allow
                                            // for sending DISPLAY_UPDATE_IMMINENT hints too early if
                                            // we idled very shortly after updating the screen, so
                                            // make sure we wait enough time.
-                                           std::this_thread::sleep_for(getUpdateTimeout() -
+                                           std::this_thread::sleep_for(timeout -
                                                                        timeSinceLastUpdate);
                                        }
                                        mSendUpdateImminent.store(true);
-                                       mFlinger.disableExpensiveRendering();
+                                       disableExpensiveFn();
                                    });
     }
 }
@@ -132,7 +119,7 @@
 
     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
-        auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
+        auto ret = getPowerHal().setMode(hal::Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
         if (!ret.isOk()) {
             if (ret.isUnsupported()) {
                 mHasExpensiveRendering = false;
@@ -151,7 +138,7 @@
     if (!mBootFinished.load()) {
         return;
     }
-    sendHintSessionHint(SessionHint::CPU_LOAD_UP);
+    sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP);
 }
 
 void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -163,12 +150,12 @@
 
     if (mSendUpdateImminent.exchange(false)) {
         ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
-        sendHintSessionHint(SessionHint::CPU_LOAD_RESET);
+        sendHintSessionHint(hal::SessionHint::CPU_LOAD_RESET);
 
         if (!mHasDisplayUpdateImminent) {
             ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
         } else {
-            auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+            auto ret = getPowerHal().setBoost(hal::Boost::DISPLAY_UPDATE_IMMINENT, 0);
             if (ret.isUnsupported()) {
                 mHasDisplayUpdateImminent = false;
             }
@@ -205,7 +192,7 @@
             FlagManager::getInstance().adpf_use_fmq_channel();
 }
 
-void PowerAdvisor::sendHintSessionHint(SessionHint hint) {
+void PowerAdvisor::sendHintSessionHint(hal::SessionHint hint) {
     if (!mBootFinished || !usePowerHintSession()) {
         ALOGV("Power hint session is not enabled, skip sending session hint");
         return;
@@ -236,7 +223,7 @@
                                                                  static_cast<int32_t>(getuid()),
                                                                  mHintSessionThreadIds,
                                                                  mTargetDuration.ns(),
-                                                                 SessionTag::SURFACEFLINGER,
+                                                                 hal::SessionTag::SURFACEFLINGER,
                                                                  &mSessionConfig);
             if (ret.isOk()) {
                 mHintSession = ret.value();
@@ -326,7 +313,7 @@
         return;
     }
     SFTRACE_CALL();
-    std::optional<WorkDuration> actualDuration = estimateWorkDuration();
+    std::optional<hal::WorkDuration> actualDuration = estimateWorkDuration();
     if (!actualDuration.has_value() || actualDuration->durationNanos < 0) {
         ALOGV("Failed to send actual work duration, skipping");
         return;
@@ -377,7 +364,7 @@
     mHintSessionQueue.clear();
 }
 
-template <ChannelMessage::ChannelMessageContents::Tag T, class In>
+template <hal::ChannelMessage::ChannelMessageContents::Tag T, class In>
 bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) {
     if (!mMsgQueue) {
         ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T);
@@ -395,13 +382,13 @@
     }
     for (size_t i = 0; i < count; ++i) {
         if constexpr (T == ChannelMessageContents::Tag::workDuration) {
-            const WorkDuration& duration = contents[i];
-            new (tx.getSlot(i)) ChannelMessage{
+            const hal::WorkDuration& duration = contents[i];
+            new (tx.getSlot(i)) hal::ChannelMessage{
                     .sessionID = static_cast<int32_t>(mSessionConfig.id),
                     .timeStampNanos =
                             (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos,
                     .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration,
-                                                         WorkDurationFixedV1>({
+                                                         hal::WorkDurationFixedV1>({
                             .durationNanos = duration.durationNanos,
                             .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
                             .cpuDurationNanos = duration.cpuDurationNanos,
@@ -409,7 +396,7 @@
                     }),
             };
         } else {
-            new (tx.getSlot(i)) ChannelMessage{
+            new (tx.getSlot(i)) hal::ChannelMessage{
                     .sessionID = static_cast<int32_t>(mSessionConfig.id),
                     .timeStampNanos = ::android::uptimeNanos(),
                     .data = ChannelMessageContents::make<T, In>(std::move(contents[i])),
@@ -572,7 +559,7 @@
     return sortedDisplays;
 }
 
-std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() {
+std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() {
     if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
         return std::nullopt;
     }
@@ -657,7 +644,7 @@
     Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
     Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration);
 
-    WorkDuration duration{
+    hal::WorkDuration duration{
             .timeStampNanos = TimePoint::now().ns(),
             .durationNanos = combinedDuration.ns(),
             .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
@@ -760,6 +747,4 @@
     return *mPowerHal;
 }
 
-} // namespace impl
-} // namespace Hwc2
-} // namespace android
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
similarity index 97%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.h
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 1076b2b..458b46d 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <atomic>
-#include <chrono>
+#include <future>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -30,10 +30,8 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #include <aidl/android/hardware/power/IPower.h>
 #include <fmq/AidlMessageQueue.h>
-#include <powermanager/PowerHalController.h>
 #pragma clang diagnostic pop
 
-#include <compositionengine/impl/OutputCompositionState.h>
 #include <scheduler/Time.h>
 #include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
@@ -42,13 +40,16 @@
 
 namespace android {
 
-class SurfaceFlinger;
+namespace power {
+class PowerHalController;
+class PowerHintSessionWrapper;
+} // namespace power
 
-namespace Hwc2 {
+namespace adpf {
 
 class PowerAdvisor {
 public:
-    virtual ~PowerAdvisor();
+    virtual ~PowerAdvisor() = default;
 
     // Initializes resources that cannot be initialized on construction
     virtual void init() = 0;
@@ -113,9 +114,9 @@
 
 // PowerAdvisor is a wrapper around IPower HAL which takes into account the
 // full state of the system when sending out power hints to things like the GPU.
-class PowerAdvisor final : public Hwc2::PowerAdvisor {
+class PowerAdvisor final : public adpf::PowerAdvisor {
 public:
-    PowerAdvisor(SurfaceFlinger& flinger);
+    PowerAdvisor(std::function<void()>&& function, std::chrono::milliseconds timeout);
     ~PowerAdvisor() override;
 
     void init() override;
@@ -159,7 +160,6 @@
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
 
-    SurfaceFlinger& mFlinger;
     std::atomic_bool mSendUpdateImminent = true;
     std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
     std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
@@ -326,5 +326,5 @@
 };
 
 } // namespace impl
-} // namespace Hwc2
+} // namespace adpf
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 64b85c0..e45bdfc 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -308,6 +308,12 @@
                 const auto setFrameRateVoteType =
                         info->isVisible() ? voteType : LayerVoteType::NoVote;
 
+                const bool hasSetFrameRateOpinion = frameRate.isValid() && !frameRate.isNoVote();
+                const bool hasCategoryOpinion =
+                        frameRate.category != FrameRateCategory::NoPreference &&
+                        frameRate.category != FrameRateCategory::Default;
+                const bool hasFrameRateOpinion = hasSetFrameRateOpinion || hasCategoryOpinion;
+
                 if (gameModeFrameRateOverride.isValid()) {
                     info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
                     SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
@@ -315,7 +321,7 @@
                         trace(*info, gameFrameRateOverrideVoteType,
                               gameModeFrameRateOverride.getIntValue());
                     }
-                } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
+                } else if (hasFrameRateOpinion && frameRate.isVoteValidForMrr(isVrrDevice)) {
                     info->setLayerVote({setFrameRateVoteType,
                                         isValuelessVote ? 0_Hz : frameRate.vote.rate,
                                         frameRate.vote.seamlessness, frameRate.category});
@@ -332,7 +338,7 @@
                               gameDefaultFrameRateOverride.getIntValue());
                     }
                 } else {
-                    if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) {
+                    if (hasFrameRateOpinion && !frameRate.isVoteValidForMrr(isVrrDevice)) {
                         SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
                                                "%s %s",
                                                info->getName().c_str(),
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index ad067be..84fa139 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -489,6 +489,20 @@
     return mGetRankedFrameRatesCache->result;
 }
 
+using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
+using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
+
+PerUidLayerRequirements groupLayersByUid(
+        const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
+    PerUidLayerRequirements layersByUid;
+    for (const auto& layer : layers) {
+        const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
+        auto& layersWithSameUid = it->second;
+        layersWithSameUid.push_back(&layer);
+    }
+    return layersByUid;
+}
+
 auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
                                                     GlobalSignals signals, Fps pacesetterFps) const
         -> RankedFrameRates {
@@ -525,6 +539,43 @@
         return {ranking, GlobalSignals{.powerOnImminent = true}};
     }
 
+    // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
+    // which will touch boost when there are no ExplicitDefault layer votes on the app.
+    // At most one app can have the "HighHint" touch boost vote at a time.
+    // This accounts for cases such as games that use `setFrameRate`
+    // with Default compatibility to limit the frame rate and disabling touch boost.
+    bool isAppTouchBoost = false;
+    const auto layersByUid = groupLayersByUid(layers);
+    for (const auto& [uid, layersWithSameUid] : layersByUid) {
+        bool hasHighHint = false;
+        bool hasExplicitDefault = false;
+        for (const auto& layer : layersWithSameUid) {
+            switch (layer->vote) {
+                case LayerVoteType::ExplicitDefault:
+                    hasExplicitDefault = true;
+                    break;
+                case LayerVoteType::ExplicitCategory:
+                    if (layer->frameRateCategory == FrameRateCategory::HighHint) {
+                        hasHighHint = true;
+                    }
+                    break;
+                default:
+                    // No action
+                    break;
+            }
+            if (hasHighHint && hasExplicitDefault) {
+                break;
+            }
+        }
+
+        if (hasHighHint && !hasExplicitDefault) {
+            // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes
+            // (which prevents touch boost due to games use case).
+            isAppTouchBoost = true;
+            break;
+        }
+    }
+
     int noVoteLayers = 0;
     // Layers that prefer the same mode ("no-op").
     int noPreferenceLayers = 0;
@@ -535,7 +586,6 @@
     int explicitExact = 0;
     int explicitGteLayers = 0;
     int explicitCategoryVoteLayers = 0;
-    int interactiveLayers = 0;
     int seamedFocusedLayers = 0;
     int categorySmoothSwitchOnlyLayers = 0;
 
@@ -563,11 +613,9 @@
                 explicitGteLayers++;
                 break;
             case LayerVoteType::ExplicitCategory:
-                if (layer.frameRateCategory == FrameRateCategory::HighHint) {
-                    // HighHint does not count as an explicit signal from an app. It may be
-                    // be a touch signal.
-                    interactiveLayers++;
-                } else {
+                // HighHint does not count as an explicit signal from an app. It is a touch signal
+                // sent from UI Toolkit.
+                if (layer.frameRateCategory != FrameRateCategory::HighHint) {
                     explicitCategoryVoteLayers++;
                 }
                 if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
@@ -882,14 +930,11 @@
         return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
     };
 
-    // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
-    // which will touch boost when there are no ExplicitDefault layer votes. This is an
-    // incomplete solution but accounts for cases such as games that use `setFrameRate` with default
+    // This accounts for cases such as games that use `setFrameRate` with Default
     // compatibility to limit the frame rate, which should not have touch boost.
-    const bool hasInteraction = signals.touch || interactiveLayers > 0;
-
-    if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() &&
-        isTouchBoostForCategory()) {
+    const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0;
+    const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost;
+    if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) {
         const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
         using fps_approx_ops::operator<;
 
@@ -917,42 +962,6 @@
     return {ranking, kNoSignals};
 }
 
-using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
-using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
-
-PerUidLayerRequirements groupLayersByUid(
-        const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
-    PerUidLayerRequirements layersByUid;
-    for (const auto& layer : layers) {
-        const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
-        auto& layersWithSameUid = it->second;
-        layersWithSameUid.push_back(&layer);
-    }
-
-    // Remove uids that can't have a frame rate override
-    for (auto it = layersByUid.begin(); it != layersByUid.end();) {
-        const auto& layersWithSameUid = it->second;
-        bool skipUid = false;
-        for (const auto& layer : layersWithSameUid) {
-            using LayerVoteType = RefreshRateSelector::LayerVoteType;
-
-            if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
-                ALOGV("%s: %s skips uid=%d due to the vote", __func__,
-                      formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid);
-                skipUid = true;
-                break;
-            }
-        }
-        if (skipUid) {
-            it = layersByUid.erase(it);
-        } else {
-            ++it;
-        }
-    }
-
-    return layersByUid;
-}
-
 auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
                                                 Fps displayRefreshRate,
                                                 GlobalSignals globalSignals) const
@@ -997,6 +1006,7 @@
         bool hasExplicitExactOrMultiple = false;
         bool hasExplicitDefault = false;
         bool hasHighHint = false;
+        bool hasSkipOverrideLayer = false;
         for (const auto& layer : layersWithSameUid) {
             switch (layer->vote) {
                 case LayerVoteType::ExplicitExactOrMultiple:
@@ -1010,15 +1020,25 @@
                         hasHighHint = true;
                     }
                     break;
+                case LayerVoteType::Max:
+                case LayerVoteType::Heuristic:
+                    hasSkipOverrideLayer = true;
+                    break;
                 default:
                     // No action
                     break;
             }
-            if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) {
+            if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint &&
+                hasSkipOverrideLayer) {
                 break;
             }
         }
 
+        if (hasSkipOverrideLayer) {
+            ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid);
+            continue;
+        }
+
         // Layers with ExplicitExactOrMultiple expect touch boost
         if (globalSignals.touch && hasExplicitExactOrMultiple) {
             ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b8b1f59..274e121 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -436,7 +436,8 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunused-value" // b/369277774
-bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode,
+                                     bool clearContentRequirements) {
     const bool isPacesetter =
             FTL_FAKE_GUARD(kMainThreadContext,
                            (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
@@ -445,9 +446,11 @@
         std::lock_guard<std::mutex> lock(mPolicyLock);
         mPolicy.emittedModeOpt = mode;
 
-        // Invalidate content based refresh rate selection so it could be calculated
-        // again for the new refresh rate.
-        mPolicy.contentRequirements.clear();
+        if (clearContentRequirements) {
+            // Invalidate content based refresh rate selection so it could be calculated
+            // again for the new refresh rate.
+            mPolicy.contentRequirements.clear();
+        }
     }
 
     if (hasEventThreads()) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index d029488..e77af60 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -151,7 +151,8 @@
     void dispatchHotplugError(int32_t errorCode);
 
     // Returns true if the PhysicalDisplayId is the pacesetter.
-    bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
+    bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
+                              bool clearContentRequirements) EXCLUDES(mPolicyLock);
 
     void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
     void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1768204..a7ab117 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -133,7 +133,6 @@
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
 #include "Effects/Daltonizer.h"
@@ -153,6 +152,7 @@
 #include "LayerVector.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 #include "RegionSamplingThread.h"
 #include "RenderAreaBuilder.h"
 #include "Scheduler/EventThread.h"
@@ -426,7 +426,11 @@
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
         mInternalDisplayDensity(
                 getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
-        mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
+        mPowerAdvisor(std::make_unique<
+                      adpf::impl::PowerAdvisor>([this] { disableExpensiveRendering(); },
+                                                std::chrono::milliseconds(
+                                                        sysprop::display_update_imminent_timeout_ms(
+                                                                80)))),
         mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()),
         mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -1354,7 +1358,8 @@
             mScheduler->updatePhaseConfiguration(displayId, mode.fps);
 
             if (emitEvent) {
-                mScheduler->onDisplayModeChanged(displayId, mode);
+                mScheduler->onDisplayModeChanged(displayId, mode,
+                                                 /*clearContentRequirements*/ false);
             }
             break;
         case DesiredModeAction::None:
@@ -1449,7 +1454,7 @@
     mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
 
     if (pendingModeOpt->emitEvent) {
-        mScheduler->onDisplayModeChanged(displayId, activeMode);
+        mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
     }
 }
 
@@ -2532,17 +2537,13 @@
         frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence);
         gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported;
         mLayersWithQueuedFrames.emplace(it->second, gameMode);
-        mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
     }
 
     updateLayerHistory(latchTime);
     mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) {
-        // update output dirty region if we have a queued buffer that is visible or a snapshot
-        // recently became invisible
-        // TODO(b/360050020) investigate if we need to update dirty region when layer color changes
-        if ((snapshot.isVisible &&
-             (mLayersIdsWithQueuedFrames.find(snapshot.path.id) !=
-              mLayersIdsWithQueuedFrames.end())) ||
+        // update output's dirty region if a snapshot is visible and its
+        // content is dirty or if a snapshot recently became invisible
+        if ((snapshot.isVisible && snapshot.contentDirty) ||
             (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) {
             Region visibleReg;
             visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
@@ -2932,7 +2933,6 @@
     mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
 
     mLayersWithQueuedFrames.clear();
-    mLayersIdsWithQueuedFrames.clear();
     doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(),
                                   vsyncId);
 
@@ -7655,7 +7655,8 @@
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     if (const bool isPacesetter =
-                mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
+                mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
+                                                 /*clearContentRequirements*/ true)) {
         mDisplayModeController.updateKernelIdleTimer(displayId);
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c96dc6f..d3479b7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -57,6 +57,7 @@
 #include <utils/threads.h>
 
 #include <compositionengine/OutputColorSetting.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <scheduler/Fps.h>
 #include <scheduler/PresentLatencyTracker.h>
 #include <scheduler/Time.h>
@@ -70,7 +71,6 @@
 #include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FrontEnd/DisplayInfo.h"
@@ -81,6 +81,7 @@
 #include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
 #include "MutexUtils.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 #include "Scheduler/ISchedulerCallback.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/Scheduler.h"
@@ -1241,7 +1242,6 @@
     // latched.
     std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames;
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
-    std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
 
     // Sorted list of layers that were composed during previous frame. This is used to
     // avoid an expensive traversal of the layer hierarchy when there are no
@@ -1361,7 +1361,7 @@
     sp<os::IInputFlinger> mInputFlinger;
     InputWindowCommands mInputWindowCommands;
 
-    std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
+    std::unique_ptr<adpf::PowerAdvisor> mPowerAdvisor;
 
     void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 57ef4c7..12616e3 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -158,6 +158,7 @@
     DUMP_READ_ONLY_FLAG(true_hdr_screenshots);
     DUMP_READ_ONLY_FLAG(display_config_error_hal);
     DUMP_READ_ONLY_FLAG(connected_display_hdr);
+    DUMP_READ_ONLY_FLAG(deprecate_frame_tracker);
 
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
@@ -264,6 +265,7 @@
 FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
 FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, "");
 FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, "");
+FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, "");
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 7716762..f5bea72 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -96,6 +96,7 @@
     bool true_hdr_screenshots() const;
     bool display_config_error_hal() const;
     bool connected_display_hdr() const;
+    bool deprecate_frame_tracker() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index ce334e4..014c736 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -76,6 +76,17 @@
 } # correct_dpi_with_display_size
 
 flag {
+  name: "deprecate_frame_tracker"
+  namespace: "core_graphics"
+  description: "Deprecate using FrameTracker to accumulate and provide FrameStats"
+  bug: "241394120"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+} # deprecate_frame_tracker
+
+flag {
   name: "deprecate_vsync_sf"
   namespace: "core_graphics"
   description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index b472047..9794620 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -218,6 +218,17 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setAutoRefresh(uint32_t id, bool autoRefresh) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eAutoRefreshChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.autoRefresh = autoRefresh;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void hideLayer(uint32_t id) {
         setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
     }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index cb8820a..6af5143 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -29,13 +29,10 @@
 filegroup {
     name: "libsurfaceflinger_backend_mock_sources",
     srcs: [
+        ":poweradvisor_mock_sources",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
         "mock/DisplayHardware/MockHWComposer.cpp",
-        "mock/DisplayHardware/MockIPower.cpp",
-        "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
-        "mock/DisplayHardware/MockPowerAdvisor.cpp",
-        "mock/DisplayHardware/MockPowerHalController.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
 }
@@ -54,6 +51,13 @@
 }
 
 filegroup {
+    name: "poweradvisor_mock_sources",
+    srcs: [
+        "mock/PowerAdvisor/*.cpp",
+    ],
+}
+
+filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
         "mock/MockEventThread.cpp",
@@ -86,79 +90,7 @@
         ":libsurfaceflinger_backend_mock_sources",
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
-        "libsurfaceflinger_unittest_main.cpp",
-        "ActiveDisplayRotationFlagsTest.cpp",
-        "BackgroundExecutorTest.cpp",
-        "CommitTest.cpp",
-        "CompositionTest.cpp",
-        "DaltonizerTest.cpp",
-        "DisplayIdGeneratorTest.cpp",
-        "DisplayTransactionTest.cpp",
-        "DisplayDevice_GetBestColorModeTest.cpp",
-        "DisplayDevice_SetDisplayBrightnessTest.cpp",
-        "DisplayDevice_SetProjectionTest.cpp",
-        "DisplayModeControllerTest.cpp",
-        "EventThreadTest.cpp",
-        "FlagManagerTest.cpp",
-        "FpsReporterTest.cpp",
-        "FpsTest.cpp",
-        "FramebufferSurfaceTest.cpp",
-        "FrameRateOverrideMappingsTest.cpp",
-        "FrameTimelineTest.cpp",
-        "HWComposerTest.cpp",
-        "JankTrackerTest.cpp",
-        "OneShotTimerTest.cpp",
-        "LayerHistoryIntegrationTest.cpp",
-        "LayerInfoTest.cpp",
-        "LayerMetadataTest.cpp",
-        "LayerHierarchyTest.cpp",
-        "LayerLifecycleManagerTest.cpp",
-        "LayerSnapshotTest.cpp",
-        "LayerTestUtils.cpp",
-        "MessageQueueTest.cpp",
-        "PowerAdvisorTest.cpp",
-        "SmallAreaDetectionAllowMappingsTest.cpp",
-        "SurfaceFlinger_ColorMatrixTest.cpp",
-        "SurfaceFlinger_CreateDisplayTest.cpp",
-        "SurfaceFlinger_DestroyDisplayTest.cpp",
-        "SurfaceFlinger_DisplayModeSwitching.cpp",
-        "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
-        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
-        "SurfaceFlinger_FoldableTest.cpp",
-        "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
-        "SurfaceFlinger_GetDisplayStatsTest.cpp",
-        "SurfaceFlinger_HdrOutputControlTest.cpp",
-        "SurfaceFlinger_HotplugTest.cpp",
-        "SurfaceFlinger_InitializeDisplaysTest.cpp",
-        "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
-        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
-        "SurfaceFlinger_PowerHintTest.cpp",
-        "SurfaceFlinger_SetDisplayStateTest.cpp",
-        "SurfaceFlinger_SetPowerModeInternalTest.cpp",
-        "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
-        "SchedulerTest.cpp",
-        "RefreshRateSelectorTest.cpp",
-        "RefreshRateStatsTest.cpp",
-        "RegionSamplingTest.cpp",
-        "TestableScheduler.cpp",
-        "TimeStatsTest.cpp",
-        "FrameTracerTest.cpp",
-        "TransactionApplicationTest.cpp",
-        "TransactionFrameTracerTest.cpp",
-        "TransactionProtoParserTest.cpp",
-        "TransactionSurfaceFrameTest.cpp",
-        "TransactionTraceWriterTest.cpp",
-        "TransactionTracingTest.cpp",
-        "TunnelModeEnabledReporterTest.cpp",
-        "VSyncCallbackRegistrationTest.cpp",
-        "VSyncDispatchTimerQueueTest.cpp",
-        "VSyncDispatchRealtimeTest.cpp",
-        "VsyncModulatorTest.cpp",
-        "VSyncPredictorTest.cpp",
-        "VSyncReactorTest.cpp",
-        "VsyncConfigurationTest.cpp",
-        "VsyncScheduleTest.cpp",
-        "WindowInfosListenerInvokerTest.cpp",
+        "*.cpp",
     ],
 }
 
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index d4c801f..b517ff0 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -22,8 +22,8 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockTimeStats.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -33,11 +33,11 @@
     void SetUp() override {
         mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
         mComposer = new Hwc2::mock::Composer();
-        mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+        mPowerAdvisor = new adpf::mock::PowerAdvisor();
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
         mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-        mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+        mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
 
         constexpr bool kIsPrimary = true;
         FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -79,7 +79,7 @@
             sp<compositionengine::mock::DisplaySurface>::make();
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
-    Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
     Hwc2::mock::Composer* mComposer = nullptr;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4f72424..860ad2e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -40,10 +40,10 @@
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockTimeStats.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -110,9 +110,9 @@
         mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
 
         mComposer = new Hwc2::mock::Composer();
-        mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+        mPowerAdvisor = new adpf::mock::PowerAdvisor();
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-        mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+        mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
         mFlinger.mutableMaxRenderTargetSize() = 16384;
     }
 
@@ -158,7 +158,7 @@
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
-    Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index db3c0a1..fa976c8 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,10 +47,10 @@
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -118,7 +118,7 @@
     sp<GraphicBuffer> mBuffer =
             sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
                                     GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
-    Hwc2::mock::PowerAdvisor mPowerAdvisor;
+    adpf::mock::PowerAdvisor mPowerAdvisor;
 
     FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
 
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 6e4bf2b..744c536 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -19,14 +19,14 @@
 #include <gmock/gmock.h>
 
 #include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::adpf::mock::PowerAdvisor;
 using android::hardware::graphics::composer::hal::HWDisplayId;
-using android::Hwc2::mock::PowerAdvisor;
 
 struct FakeDisplayInjectorArgs {
     PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
@@ -36,7 +36,7 @@
 
 class FakeDisplayInjector {
 public:
-    FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+    FakeDisplayInjector(TestableSurfaceFlinger& flinger, PowerAdvisor& powerAdvisor,
                         sp<mock::NativeWindow> nativeWindow)
           : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
 
@@ -89,7 +89,7 @@
     }
 
     TestableSurfaceFlinger& mFlinger;
-    Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+    PowerAdvisor& mPowerAdvisor;
     sp<mock::NativeWindow> mNativeWindow;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index a35ae15..e6b8a26 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1935,4 +1935,54 @@
     EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
 }
 
+// content dirty test
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentAlphaChanges) {
+    setAlpha(1, 0.5);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+    EXPECT_TRUE(getSnapshot(11)->contentDirty);
+    EXPECT_TRUE(getSnapshot(111)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+    EXPECT_FALSE(getSnapshot(11)->contentDirty);
+    EXPECT_FALSE(getSnapshot(111)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenAutoRefresh) {
+    setAutoRefresh(1, true);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates don't clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // second update after removing auto refresh will clear content dirty
+    setAutoRefresh(1, false);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenColorChanges) {
+    setColor(1, {1, 2, 3});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) {
+    setPosition(1, 2, 3);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 71f9f88..908637a 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -159,9 +159,9 @@
     constexpr VsyncId vsyncId{42};
 
     EXPECT_CALL(mTokenManager,
-                generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(),
-                                                                        kEndTime.ns(),
-                                                                        kPresentTime.ns())))
+                generateTokenForPredictions(
+                        frametimeline::TimelineItem(kStartTime.ns(), kEndTime.ns(),
+                                                    kPresentTime.ns(), kPresentTime.ns())))
             .WillOnce(Return(ftl::to_underlying(vsyncId)));
     EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1);
     EXPECT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 8375bb9..5c25f34 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -17,7 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisorTest"
 
-#include <DisplayHardware/PowerAdvisor.h>
+#include "PowerAdvisor/PowerAdvisor.h"
+
 #include <android_os.h>
 #include <binder/Status.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
@@ -29,18 +30,17 @@
 #include <ui/DisplayId.h>
 #include <chrono>
 #include <future>
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerHalController.h"
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "mock/PowerAdvisor/MockPowerHalController.h"
+#include "mock/PowerAdvisor/MockPowerHintSessionWrapper.h"
 
 using namespace android;
-using namespace android::Hwc2::mock;
+using namespace android::adpf::mock;
 using namespace android::hardware::power;
 using namespace std::chrono_literals;
 using namespace testing;
 using namespace android::power;
 
-namespace android::Hwc2::impl {
+namespace android::adpf::impl {
 
 class PowerAdvisorTest : public testing::Test {
 public:
@@ -73,7 +73,6 @@
     void testGpuScenario(GpuTestConfig& config, WorkDuration& ret);
 
 protected:
-    TestableSurfaceFlinger mFlinger;
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
     MockPowerHalController* mMockPowerHalController;
     std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
@@ -98,7 +97,7 @@
 }
 
 void PowerAdvisorTest::SetUp() {
-    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
+    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>([]() {}, 80ms);
     mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
     mMockPowerHalController =
             reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
@@ -844,4 +843,4 @@
 }
 
 } // namespace
-} // namespace android::Hwc2::impl
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 29e1c21..b5be8db 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2240,6 +2240,46 @@
     EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
 }
 
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    // Device with VRR config mode
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::Normal;
+    lr1.name = "ExplicitCategory Normal";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+    // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+    // However see 60 due to Normal vote.
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz,
+                           actualRankedFrameRates.ranking.front().frameRateMode);
+    EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::HighHint;
+    lr1.name = "ExplicitCategory HighHint";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+    actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz,
+                           actualRankedFrameRates.ranking.front().frameRateMode);
+    EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+}
+
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) {
     SET_FLAG_FOR_TEST(flags::vrr_config, false);
@@ -3825,6 +3865,51 @@
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    // Device with VRR config mode
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::Normal;
+    lr1.name = "ExplicitCategory Normal";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+    // The `displayFrameRate` is 60.
+    // However 30 Default app still gets frame rate override.
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {});
+    EXPECT_EQ(2u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+    ASSERT_EQ(1u, frameRateOverrides.count(5678));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::HighHint;
+    lr1.name = "ExplicitCategory HighHint";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+    // The `displayFrameRate` is 120 (late touch boost).
+    // However 30 Default app still gets frame rate override.
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(5678));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+}
+
 TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) {
     if (GetParam() == Config::FrameRateOverride::Disabled) {
         return;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ac09cbc..1fc874d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -222,7 +222,7 @@
     const auto selectorPtr =
             std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
     mScheduler->registerDisplay(kDisplayId1, selectorPtr);
-    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
 
     mScheduler->setContentRequirements({kLayer});
 
@@ -250,7 +250,7 @@
     EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
 
     mScheduler->touchTimerCallback(TimerState::Reset);
-    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
 }
 
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6778af3..7f0b7a6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -44,22 +44,22 @@
 #include "NativeWindowSurface.h"
 #include "RenderArea.h"
 #include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
 #include "TestableScheduler.h"
 #include "android/gui/ISurfaceComposerClient.h"
+
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
-#include "mock/system/window/MockNativeWindow.h"
-
-#include "Scheduler/VSyncTracker.h"
-#include "Scheduler/VsyncController.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 
@@ -190,7 +190,7 @@
                 &mFlinger->mCompositionEngine->getHwComposer());
     }
 
-    void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
+    void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) {
         mFlinger->mPowerAdvisor = std::move(powerAdvisor);
     }
 
@@ -1160,7 +1160,7 @@
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
-    Hwc2::mock::PowerAdvisor mPowerAdvisor;
+    adpf::mock::PowerAdvisor mPowerAdvisor;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 3e6a768..88052db 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -186,6 +186,9 @@
              std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
     MOCK_METHOD(Error, setLayerLuts,
                 (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&));
+    MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
+    MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
+    MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 121104d..fa74492 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -52,7 +52,7 @@
                 (override));
     MOCK_METHOD(hal::Error, getName, (std::string *), (const, override));
     MOCK_METHOD(hal::Error, getRequests,
-                (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)),
+                (hal::DisplayRequest*, (std::unordered_map<Layer*, hal::LayerRequest>*)),
                 (override));
     MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (),
                 (const, override));
@@ -111,7 +111,9 @@
                 (aidl::android::hardware::graphics::composer3::OverlayProperties *),
                 (const override));
     MOCK_METHOD(hal::Error, getRequestedLuts,
-                ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)),
+                (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override));
+    MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
+    MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
                 (override));
 };
 
@@ -151,6 +153,8 @@
     MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
     MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&),
                 (override));
+    MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
+                (override));
 };
 
 } // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index fa7128c..88f83d2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -148,6 +148,9 @@
     MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
     MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (),
                 (override));
+    MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
+    MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
+                (PhysicalDisplayId, const PictureProfileHandle&));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
deleted file mode 100644
index 2323ebb..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/DisplayHardware/MockIPower.h"
-
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockIPower::MockIPower() = default;
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
deleted file mode 100644
index 4c034d7..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "binder/Status.h"
-
-// FMQ library in IPower does questionable conversions
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
-#pragma clang diagnostic pop
-
-#include <gmock/gmock.h>
-
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::IPower;
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionTag;
-using aidl::android::hardware::power::SupportInfo;
-
-using aidl::android::hardware::power::Mode;
-using android::binder::Status;
-
-namespace android::Hwc2::mock {
-
-class MockIPower : public IPower {
-public:
-    MockIPower();
-
-    MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
-                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
-                (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
-                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, SessionTag tag, SessionConfig* config,
-                 std::shared_ptr<IPowerHintSession>* _aidl_return),
-                (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
-                (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
-    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
-    MOCK_METHOD(bool, isRemote, (), (override));
-};
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
index 1ba38a8..f4c1e52 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
@@ -16,10 +16,10 @@
 
 #include "MockPowerAdvisor.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 // Explicit default instantiation is recommended.
 PowerAdvisor::PowerAdvisor() = default;
 PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index 4efdfe8..5c4512a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
@@ -18,11 +18,11 @@
 
 #include <gmock/gmock.h>
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
+class PowerAdvisor : public android::adpf::PowerAdvisor {
 public:
     PowerAdvisor();
     ~PowerAdvisor() override;
@@ -65,4 +65,4 @@
     MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
index 3ec5c2d..3b8de55 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
@@ -16,9 +16,9 @@
 
 #include "MockPowerHalController.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 MockPowerHalController::MockPowerHalController() = default;
 MockPowerHalController::~MockPowerHalController() = default;
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
similarity index 92%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
index af1862d..fba4cd8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
@@ -19,10 +19,7 @@
 #include <gmock/gmock.h>
 #include <scheduler/Time.h>
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
 #include <powermanager/PowerHalController.h>
-#pragma clang diagnostic pop
 
 namespace android {
 namespace hardware {
@@ -32,7 +29,7 @@
 } // namespace hardware
 } // namespace android
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 using android::power::HalResult;
 
@@ -59,4 +56,4 @@
     MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override));
 };
 
-} // namespace android::Hwc2::mock
\ No newline at end of file
+} // namespace android::adpf::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
similarity index 85%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
index d383283..cb39783 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "MockPowerHintSessionWrapper.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 // Explicit default instantiation is recommended.
 MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
       : power::PowerHintSessionWrapper(nullptr) {}
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
similarity index 88%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
index bc6950c..4518de8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
@@ -16,13 +16,7 @@
 
 #pragma once
 
-#include "binder/Status.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
 #include <powermanager/PowerHintSessionWrapper.h>
-#pragma clang diagnostic pop
 
 #include <gmock/gmock.h>
 
@@ -34,7 +28,7 @@
 
 using namespace aidl::android::hardware::power;
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
 public:
@@ -52,4 +46,4 @@
     MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override));
 };
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index ba35d15..494f88f 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -150,6 +150,23 @@
     return apply(cancelSyncedFn, "cancelSynced");
 }
 
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession(
+        const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+        const std::function<void()>& completionCallback) {
+    hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn =
+            [&](std::shared_ptr<ManagerHalWrapper> hal) {
+                return hal->startSession(ids, config, completionCallback);
+            };
+    return apply(startSessionFn, "startSession");
+}
+
+HalResult<void> ManagerHalController::clearSessions() {
+    hal_fn<void> clearSessionsFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+        return hal->clearSessions();
+    };
+    return apply(clearSessionsFn, "clearSessions");
+}
+
 }; // namespace vibrator
 
 }; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 93ec781..3db8ff8 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -29,6 +29,30 @@
 constexpr int32_t SINGLE_VIBRATOR_ID = 0;
 const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
 
+HalResult<void> ManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+    return HalResult<void>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+    return HalResult<void>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::cancelSynced() {
+    return HalResult<void>::unsupported();
+}
+
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession(
+        const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&,
+        const std::function<void()>&) {
+    return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::clearSessions() {
+    return HalResult<void>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
 HalResult<void> LegacyManagerHalWrapper::ping() {
     auto pingFn = [](HalWrapper* hal) { return hal->ping(); };
     return mController->doWithRetry<void>(pingFn, "ping");
@@ -59,18 +83,6 @@
             (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
 }
 
-HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
-    return HalResult<void>::unsupported();
-}
-
-HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
-    return HalResult<void>::unsupported();
-}
-
-HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
-    return HalResult<void>::unsupported();
-}
-
 // -------------------------------------------------------------------------------------------------
 
 std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
@@ -186,6 +198,17 @@
     return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
 }
 
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession(
+        const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+        const std::function<void()>& completionCallback) {
+    auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
+    std::shared_ptr<Aidl::IVibrationSession> session;
+    auto status = getHal()->startSession(ids, config, cb, &session);
+    return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status),
+                                                                                  std::move(
+                                                                                          session));
+}
+
 HalResult<void> AidlManagerHalWrapper::cancelSynced() {
     auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced());
     if (ret.isOk()) {
@@ -200,6 +223,10 @@
     return ret;
 }
 
+HalResult<void> AidlManagerHalWrapper::clearSessions() {
+    return HalResultFactory::fromStatus(getHal()->clearSessions());
+}
+
 std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
index 70c846b..72d4752 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -62,6 +62,10 @@
     HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
     HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
     HalResult<void> cancelSynced() override final;
+    HalResult<std::shared_ptr<IVibrationSession>> startSession(
+            const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+            const std::function<void()>& completionCallback) override final;
+    HalResult<void> clearSessions() override final;
 
 private:
     Connector mConnector;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
index 9e3f221..8d4ca0e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -38,7 +38,8 @@
             aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM,
     MIXED_TRIGGER_COMPOSE =
             aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE,
-    TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK
+    TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK,
+    START_SESSIONS = aidl::android::hardware::vibrator::IVibratorManager::CAP_START_SESSIONS
 };
 
 inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) {
@@ -64,6 +65,9 @@
 // Wrapper for VibratorManager HAL handlers.
 class ManagerHalWrapper {
 public:
+    using IVibrationSession = aidl::android::hardware::vibrator::IVibrationSession;
+    using VibrationSessionConfig = aidl::android::hardware::vibrator::VibrationSessionConfig;
+
     ManagerHalWrapper() = default;
     virtual ~ManagerHalWrapper() = default;
 
@@ -78,9 +82,13 @@
     virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
     virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
 
-    virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
-    virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
-    virtual HalResult<void> cancelSynced() = 0;
+    virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids);
+    virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback);
+    virtual HalResult<void> cancelSynced();
+    virtual HalResult<std::shared_ptr<IVibrationSession>> startSession(
+            const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+            const std::function<void()>& completionCallback);
+    virtual HalResult<void> clearSessions();
 };
 
 // Wrapper for the VibratorManager over single Vibrator HAL.
@@ -98,10 +106,6 @@
     HalResult<std::vector<int32_t>> getVibratorIds() override final;
     HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
 
-    HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
-    HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
-    HalResult<void> cancelSynced() override final;
-
 private:
     const std::shared_ptr<HalController> mController;
 };
@@ -126,6 +130,10 @@
     HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
     HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
     HalResult<void> cancelSynced() override final;
+    HalResult<std::shared_ptr<IVibrationSession>> startSession(
+            const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+            const std::function<void()>& completionCallback) override final;
+    HalResult<void> clearSessions() override final;
 
 private:
     std::mutex mHandleMutex;
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
index c7214e0..b201670 100644
--- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -27,6 +27,8 @@
 #include "test_mocks.h"
 #include "test_utils.h"
 
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
 using android::vibrator::HalController;
 
 using namespace android;
@@ -34,6 +36,7 @@
 
 static constexpr int MAX_ATTEMPTS = 2;
 static const std::vector<int32_t> VIBRATOR_IDS = {1, 2};
+static const VibrationSessionConfig SESSION_CONFIG;
 static constexpr int VIBRATOR_ID = 1;
 
 // -------------------------------------------------------------------------------------------------
@@ -52,6 +55,11 @@
     MOCK_METHOD(vibrator::HalResult<void>, triggerSynced,
                 (const std::function<void()>& completionCallback), (override));
     MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override));
+    MOCK_METHOD(vibrator::HalResult<std::shared_ptr<IVibrationSession>>, startSession,
+                (const std::vector<int32_t>& ids, const VibrationSessionConfig& s,
+                 const std::function<void()>& completionCallback),
+                (override));
+    MOCK_METHOD(vibrator::HalResult<void>, clearSessions, (), (override));
 };
 
 // -------------------------------------------------------------------------------------------------
@@ -79,7 +87,8 @@
     void setHalExpectations(int32_t cardinality, vibrator::HalResult<void> voidResult,
                             vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult,
                             vibrator::HalResult<std::vector<int32_t>> idsResult,
-                            vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) {
+                            vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult,
+                            vibrator::HalResult<std::shared_ptr<IVibrationSession>> sessionResult) {
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(voidResult));
@@ -101,10 +110,16 @@
         EXPECT_CALL(*mMockHal.get(), cancelSynced())
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(voidResult));
+        EXPECT_CALL(*mMockHal.get(), startSession(_, _, _))
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(sessionResult));
+        EXPECT_CALL(*mMockHal.get(), clearSessions())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(voidResult));
 
         if (cardinality > 1) {
             // One reconnection for each retry.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1)));
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(9 * (cardinality - 1)));
         } else {
             EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
         }
@@ -127,7 +142,8 @@
                        vibrator::HalResult<vibrator::ManagerCapabilities>::ok(
                                vibrator::ManagerCapabilities::SYNC),
                        vibrator::HalResult<std::vector<int32_t>>::ok(VIBRATOR_IDS),
-                       vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr));
+                       vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr),
+                       vibrator::HalResult<std::shared_ptr<IVibrationSession>>::ok(nullptr));
 
     ASSERT_TRUE(mController->ping().isOk());
 
@@ -146,6 +162,8 @@
     ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isOk());
     ASSERT_TRUE(mController->triggerSynced([]() {}).isOk());
     ASSERT_TRUE(mController->cancelSynced().isOk());
+    ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isOk());
+    ASSERT_TRUE(mController->clearSessions().isOk());
 
     ASSERT_EQ(1, mConnectCounter);
 }
@@ -154,7 +172,8 @@
     setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(),
                        vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(),
                        vibrator::HalResult<std::vector<int32_t>>::unsupported(),
-                       vibrator::HalResult<std::shared_ptr<HalController>>::unsupported());
+                       vibrator::HalResult<std::shared_ptr<HalController>>::unsupported(),
+                       vibrator::HalResult<std::shared_ptr<IVibrationSession>>::unsupported());
 
     ASSERT_TRUE(mController->ping().isUnsupported());
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
@@ -163,6 +182,8 @@
     ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isUnsupported());
     ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported());
     ASSERT_TRUE(mController->cancelSynced().isUnsupported());
+    ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isUnsupported());
+    ASSERT_TRUE(mController->clearSessions().isUnsupported());
 
     ASSERT_EQ(1, mConnectCounter);
 }
@@ -171,7 +192,8 @@
     setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"),
                        vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"),
                        vibrator::HalResult<std::vector<int32_t>>::failed("msg"),
-                       vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"));
+                       vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"),
+                       vibrator::HalResult<std::shared_ptr<IVibrationSession>>::failed("msg"));
 
     ASSERT_TRUE(mController->ping().isFailed());
     ASSERT_TRUE(mController->getCapabilities().isFailed());
@@ -180,6 +202,8 @@
     ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
     ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
     ASSERT_TRUE(mController->cancelSynced().isFailed());
+    ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed());
+    ASSERT_TRUE(mController->clearSessions().isFailed());
 
     ASSERT_EQ(1, mConnectCounter);
 }
@@ -188,7 +212,9 @@
     setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"),
                        vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"),
                        vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"),
-                       vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"));
+                       vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"),
+                       vibrator::HalResult<std::shared_ptr<IVibrationSession>>::transactionFailed(
+                               "m"));
 
     ASSERT_TRUE(mController->ping().isFailed());
     ASSERT_TRUE(mController->getCapabilities().isFailed());
@@ -197,6 +223,8 @@
     ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
     ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
     ASSERT_TRUE(mController->cancelSynced().isFailed());
+    ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed());
+    ASSERT_TRUE(mController->clearSessions().isFailed());
 
     ASSERT_EQ(1, mConnectCounter);
 }
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index ca13c0b..a2f002d 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -69,12 +69,25 @@
     MOCK_METHOD(bool, isRemote, (), (override));
 };
 
+class MockIVibrationSession : public IVibrationSession {
+public:
+    MockIVibrationSession() = default;
+
+    MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, abort, (), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));
+    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+    MOCK_METHOD(bool, isRemote, (), (override));
+};
+
 // -------------------------------------------------------------------------------------------------
 
 class VibratorManagerHalWrapperAidlTest : public Test {
 public:
     void SetUp() override {
         mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>();
+        mMockSession = ndk::SharedRefBase::make<StrictMock<MockIVibrationSession>>();
         mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>();
         mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
         mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal);
@@ -86,11 +99,13 @@
     std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
     std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr;
     std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr;
+    std::shared_ptr<StrictMock<MockIVibrationSession>> mMockSession = nullptr;
 };
 
 // -------------------------------------------------------------------------------------------------
 
 static const std::vector<int32_t> kVibratorIds = {1, 2};
+static const VibrationSessionConfig kSessionConfig;
 static constexpr int kVibratorId = 1;
 
 TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
@@ -319,3 +334,35 @@
     ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
     ASSERT_TRUE(mWrapper->cancelSynced().isOk());
 }
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestStartSession) {
+    EXPECT_CALL(*mMockHal.get(), startSession(_, _, _, _))
+            .Times(Exactly(3))
+            .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+            .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+            .WillOnce(
+                    DoAll(DoAll(SetArgPointee<3>(mMockSession), Return(ndk::ScopedAStatus::ok()))));
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isUnsupported());
+    ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isFailed());
+
+    auto result = mWrapper->startSession(kVibratorIds, kSessionConfig, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_NE(nullptr, result.value().get());
+    ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestClearSessions) {
+    EXPECT_CALL(*mMockHal.get(), clearSessions())
+            .Times(Exactly(3))
+            .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+            .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+            .WillOnce(Return(ndk::ScopedAStatus::ok()));
+
+    ASSERT_TRUE(mWrapper->clearSessions().isUnsupported());
+    ASSERT_TRUE(mWrapper->clearSessions().isFailed());
+    ASSERT_TRUE(mWrapper->clearSessions().isOk());
+}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 7877236..52c865e 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -29,6 +29,8 @@
 using aidl::android::hardware::vibrator::CompositePrimitive;
 using aidl::android::hardware::vibrator::Effect;
 using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
 
 using std::chrono::milliseconds;
 
@@ -112,3 +114,12 @@
     ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
     ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
 }
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSessionOperationsUnsupported) {
+    std::vector<int32_t> vibratorIds;
+    vibratorIds.push_back(0);
+    VibrationSessionConfig config;
+
+    ASSERT_TRUE(mWrapper->startSession(vibratorIds, config, []() {}).isUnsupported());
+    ASSERT_TRUE(mWrapper->clearSessions().isUnsupported());
+}