Merge "Revert "IDLCLI: Update android.hardware.vibrator to V3 for idlcli"" into main
diff --git a/include/android/OWNERS b/include/android/OWNERS
index 64d7e54..5b014d0 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -5,4 +5,8 @@
 per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
 
 # CoGS
-per-file *luts* = file:platform/frameworks/base:/graphics/java/android/graphics/OWNERS
\ No newline at end of file
+per-file *luts* = file:platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+
+# ADPF
+per-file performance_hint.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file thermal.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 22eab94..37d320a 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -111,9 +111,24 @@
 typedef struct APerformanceHintManager APerformanceHintManager;
 
 /**
+ * An opaque type representing a handle to a performance hint session creation configuration.
+ * It is consumed by {@link APerformanceHint_createSessionUsingConfig}.
+ *
+ * A session creation config encapsulates the required information for a session.
+ * Additionally, the caller can set various settings for the session,
+ * to be passed during creation, streamlining the session setup process.
+ *
+ * The caller may reuse this object and modify the settings in it
+ * to create additional sessions.
+ *
+ */
+typedef struct ASessionCreationConfig ASessionCreationConfig;
+
+/**
  * An opaque type representing a handle to a performance hint session.
  * A session can only be acquired from a {@link APerformanceHintManager}
- * with {@link APerformanceHint_createSession}. It must be
+ * with {@link APerformanceHint_createSession}
+ * or {@link APerformanceHint_createSessionUsingConfig}. It must be
  * freed with {@link APerformanceHint_closeSession} after use.
  *
  * A Session represents a group of threads with an inter-related workload such that hints for
@@ -153,7 +168,7 @@
  * @param size The size of the list of threadIds.
  * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session.
  *     This must be positive if using the work duration API, or 0 otherwise.
- * @return APerformanceHintManager instance on success, nullptr on failure.
+ * @return APerformanceHintSession pointer on success, nullptr on failure.
  */
 APerformanceHintSession* _Nullable APerformanceHint_createSession(
         APerformanceHintManager* _Nonnull manager,
@@ -161,6 +176,20 @@
         int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
 
 /**
+ * Creates a session for the given set of threads that are graphics pipeline threads
+ * and set their initial target work duration.
+ *
+ * @param manager The performance hint manager instance.
+ * @param config The configuration struct containing required information
+ *        to create a session.
+ * @return APerformanceHintSession pointer on success, nullptr on failure.
+ */
+APerformanceHintSession* _Nullable APerformanceHint_createSessionUsingConfig(
+        APerformanceHintManager* _Nonnull manager,
+        ASessionCreationConfig* _Nonnull config)
+        __INTRODUCED_IN(36);
+
+/**
  * Get preferred update rate information for this device.
  *
  * @param manager The performance hint manager instance.
@@ -170,6 +199,15 @@
         APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__);
 
 /**
+ * Get maximum number of graphics pipieline threads per-app for this device.
+ *
+ * @param manager The performance hint manager instance.
+ * @return the maximum number of graphics pipeline threads supported by device.
+ */
+ int APerformanceHint_getMaxGraphicsPipelineThreadsCount(
+        APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(36);
+
+/**
  * Updates this session's target duration for each cycle of work.
  *
  * @param session The performance hint session instance to update.
@@ -320,12 +358,12 @@
  * call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources
  * associated with it.
  *
- * @return AWorkDuration on success and nullptr otherwise.
+ * @return AWorkDuration pointer.
  */
 AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
- * Destroys {@link AWorkDuration} and free all resources associated to it.
+ * Destroys a {@link AWorkDuration} and frees all resources associated with it.
  *
  * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
  */
@@ -388,6 +426,99 @@
 APerformanceHintSession* _Nonnull APerformanceHint_borrowSessionFromJava(
         JNIEnv* _Nonnull env, jobject _Nonnull sessionObj) __INTRODUCED_IN(36);
 
+/*
+ * Creates a new ASessionCreationConfig.
+ *
+ * When the client finishes using {@link ASessionCreationConfig}, it should
+ * call {@link ASessionCreationConfig_release()} to destroy
+ * {@link ASessionCreationConfig} and release all resources
+ * associated with it.
+ *
+ * @return ASessionCreationConfig pointer.
+ */
+ASessionCreationConfig* _Nonnull ASessionCreationConfig_create()
+                __INTRODUCED_IN(36);
+
+
+/**
+ * Destroys a {@link ASessionCreationConfig} and frees all
+ * resources associated with it.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ *        created by calling {@link ASessionCreationConfig_create()}.
+ */
+void ASessionCreationConfig_release(
+                ASessionCreationConfig* _Nonnull config) __INTRODUCED_IN(36);
+
+/**
+ * Sets the tids to be associated with the session to be created.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ *        created by calling {@link ASessionCreationConfig_create()}
+ * @param tids The list of tids to be associated with this session. They must be part of
+ *        this process' thread group.
+ * @param size The size of the list of tids.
+ *
+ * @return 0 on success.
+ *         EINVAL if invalid array pointer or the value of size
+ */
+int ASessionCreationConfig_setTids(
+        ASessionCreationConfig* _Nonnull config,
+        const pid_t* _Nonnull tids, size_t size)  __INTRODUCED_IN(36);
+
+/**
+ * Sets the initial target work duration in nanoseconds for the session to be created.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ *        created by calling {@link ASessionCreationConfig_create()}.
+ * @param targetWorkDurationNanos The parameter to specify a target duration
+ *        in nanoseconds for the new session; this value must be positive to use
+ *        the work duration API.
+ *
+ * @return 0 on success.
+ *         ENOTSUP if unsupported
+ *         EINVAL if invalid value
+ */
+int ASessionCreationConfig_setTargetWorkDurationNanos(
+        ASessionCreationConfig* _Nonnull config,
+        int64_t targetWorkDurationNanos)  __INTRODUCED_IN(36);
+
+/**
+ * Sets whether power efficiency mode will be enabled for the session.
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ *        created by calling {@link ASessionCreationConfig_create()}.
+ * @param enabled Whether power efficiency mode will be enabled.
+ *
+ * @return 0 on success.
+ *         ENOTSUP if unsupported
+ *         EINVAL if invalid pointer to creation config
+ */
+int ASessionCreationConfig_setPreferPowerEfficiency(
+        ASessionCreationConfig* _Nonnull config, bool enabled)  __INTRODUCED_IN(36);
+
+/**
+ * Sessions setting this hint are expected to time the critical path of
+ * graphics pipeline from end to end, with the total work duration
+ * representing the time from the start of frame production until the
+ * buffer is fully finished drawing.
+ *
+ * It should include any threads on the critical path of that pipeline,
+ * up to a limit accessible from {@link getMaxGraphicsPipelineThreadsCount()}.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ *        created by calling {@link ASessionCreationConfig_create()}.
+ * @param enabled Whether this session manages a graphics pipeline's critical path.
+ *
+ * @return 0 on success.
+ *         ENOTSUP if unsupported
+ *         EINVAL if invalid pointer to creation config or maximum threads for graphics
+                  pipeline is reached.
+ */
+int ASessionCreationConfig_setGraphicsPipeline(
+        ASessionCreationConfig* _Nonnull confi, bool enabled)  __INTRODUCED_IN(36);
 
 __END_DECLS
 
diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h
index f36472d..8f2e605 100644
--- a/include/input/CoordinateFilter.h
+++ b/include/input/CoordinateFilter.h
@@ -44,7 +44,7 @@
      * the previous call.
      * @param coords Coordinates to be overwritten by the corresponding filtered coordinates.
      */
-    void filter(std::chrono::duration<float> timestamp, PointerCoords& coords);
+    void filter(std::chrono::nanoseconds timestamp, PointerCoords& coords);
 
 private:
     OneEuroFilter mXFilter;
diff --git a/include/input/Input.h b/include/input/Input.h
index 127046d..2cabd56 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -996,6 +996,15 @@
     std::vector<PointerProperties> mPointerProperties;
     std::vector<nsecs_t> mSampleEventTimes;
     std::vector<PointerCoords> mSamplePointerCoords;
+
+private:
+    /**
+     * Create a human-readable string representation of the event's data for debugging purposes.
+     *
+     * Unlike operator<<, this method does not assume that the event data is valid or consistent, or
+     * call any accessor methods that might themselves call safeDump in the case of invalid data.
+     */
+    std::string safeDump() const;
 };
 
 std::ostream& operator<<(std::ostream& out, const MotionEvent& event);
diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h
index a0168e4..bdd82b2 100644
--- a/include/input/OneEuroFilter.h
+++ b/include/input/OneEuroFilter.h
@@ -56,7 +56,7 @@
      * provided in the previous call.
      * @param rawPosition Position to be filtered.
      */
-    float filter(std::chrono::duration<float> timestamp, float rawPosition);
+    float filter(std::chrono::nanoseconds timestamp, float rawPosition);
 
 private:
     /**
@@ -67,7 +67,7 @@
 
     /**
      * 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.
+     * filtered signal's speed. Units are 1 / position.
      */
     const float mBeta;
 
@@ -78,9 +78,9 @@
     const float mSpeedCutoffFreq;
 
     /**
-     * The timestamp from the previous call. Units are seconds.
+     * The timestamp from the previous call.
      */
-    std::optional<std::chrono::duration<float>> mPrevTimestamp;
+    std::optional<std::chrono::nanoseconds> mPrevTimestamp;
 
     /**
      * The raw position from the previous call.
@@ -88,7 +88,7 @@
     std::optional<float> mPrevRawPosition;
 
     /**
-     * The filtered velocity from the previous call. Units are position per second.
+     * The filtered velocity from the previous call. Units are position per nanosecond.
      */
     std::optional<float> mPrevFilteredVelocity;
 
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 7e0bd5b..f4f4d5e 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -72,6 +72,7 @@
     virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
             int tgid, int uid) override;
     virtual HalResult<void> closeSessionChannel(int tgid, int uid) override;
+    virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
 
 private:
     std::mutex mConnectedHalMutex;
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 6e347a9..4290182 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -63,6 +63,7 @@
     virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
                                                                                        int uid) = 0;
     virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0;
+    virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() = 0;
 };
 
 // Empty Power HAL wrapper that ignores all api calls.
@@ -85,6 +86,7 @@
     HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
                                                                                int uid) override;
     HalResult<void> closeSessionChannel(int tgid, int uid) override;
+    HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
 
 protected:
     virtual const char* getUnsupportedMessage();
@@ -170,6 +172,7 @@
     HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
                                                                                int uid) override;
     HalResult<void> closeSessionChannel(int tgid, int uid) override;
+    HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
 
 protected:
     const char* getUnsupportedMessage() override;
diff --git a/include/private/OWNERS b/include/private/OWNERS
new file mode 100644
index 0000000..37da96d
--- /dev/null
+++ b/include/private/OWNERS
@@ -0,0 +1,3 @@
+# ADPF
+per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index fb31351..f150fb1 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -108,6 +108,12 @@
 APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHintManager* manager,
                                         const int32_t* threadIds, size_t size,
                                         int64_t initialTargetWorkDurationNanos, SessionTag tag);
+/**
+ * Creates a session using ASessionCreationConfig
+ */
+APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal(
+        APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig,
+        SessionTag tag);
 
 /**
  * Creates a session from the Java SDK implementation
@@ -137,6 +143,10 @@
  */
 void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior);
 
+/*
+ * Forces the graphics pipeline flag to be enabled or disabled, for testing only.
+ */
+void APerformanceHint_setUseGraphicsPipelineForTesting(bool enabled);
 
 __END_DECLS
 
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index ab44957..99a9c91 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -67,15 +67,7 @@
       "name": "fuzz_service_test"
     },
     {
-      "name": "CtsOsTestCases",
-      "options": [
-        {
-          "include-filter": "android.os.cts.BinderTest"
-        },
-        {
-          "include-filter": "android.os.cts.ParcelTest"
-        }
-      ]
+      "name": "CtsOsTestCases_ParcelAndBinderTests"
     },
     {
       "name": "libbinder_rs-internal_test"
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 53ab68e..bb5989d 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -107,16 +107,20 @@
         ABinderRpc_AccessorProvider_getAccessorCallback provider,
         const char* const* const instances, size_t numInstances, void* data,
         ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) {
-    if (provider == nullptr) {
-        ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
-        return nullptr;
-    }
     if (data && onDelete == nullptr) {
         ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a "
               "ABinderRpc_AccessorProviderUserData_deleteCallback must also be passed to delete "
               "the data object once the ABinderRpc_AccessorProvider is removed.");
         return nullptr;
     }
+    // call the onDelete when the last reference of this goes away (when the
+    // last reference to the generate std::function goes away).
+    std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder =
+            std::make_shared<OnDeleteProviderHolder>(data, onDelete);
+    if (provider == nullptr) {
+        ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
+        return nullptr;
+    }
     if (numInstances == 0 || instances == nullptr) {
         ALOGE("No instances passed to ABinderRpc_registerAccessorProvider. numInstances: %zu",
               numInstances);
@@ -126,10 +130,6 @@
     for (size_t i = 0; i < numInstances; i++) {
         instanceStrings.emplace(instances[i]);
     }
-    // call the onDelete when the last reference of this goes away (when the
-    // last reference to the generate std::function goes away).
-    std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder =
-            std::make_shared<OnDeleteProviderHolder>(data, onDelete);
     android::RpcAccessorProvider generate = [provider,
                                              onDeleteHolder](const String16& name) -> sp<IBinder> {
         ABinderRpc_Accessor* accessor = provider(String8(name).c_str(), onDeleteHolder->mData);
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 7d54e2d..1318889 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -139,6 +139,8 @@
  *         registered. In the error case of duplicate instances, if data was
  *         provided with a ABinderRpc_AccessorProviderUserData_deleteCallback,
  *         the callback will be called to delete the data.
+ *         If nullptr is returned, ABinderRpc_AccessorProviderUserData_deleteCallback
+ *         will be called on data immediately.
  *         Otherwise returns a pointer to the ABinderRpc_AccessorProvider that
  *         can be used to remove with ABinderRpc_unregisterAccessorProvider.
  */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 4d691f8..a637165 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -164,96 +164,53 @@
 LIBBINDER_NDK35 { # introduced=VanillaIceCream
   global:
     APersistableBundle_readFromParcel;
-    APersistableBundle_readFromParcel; # llndk=202404
     APersistableBundle_writeToParcel;
-    APersistableBundle_writeToParcel; # llndk=202404
     APersistableBundle_new;
-    APersistableBundle_new; # llndk=202404
     APersistableBundle_dup;
-    APersistableBundle_dup; # llndk=202404
     APersistableBundle_delete;
-    APersistableBundle_delete; # llndk=202404
     APersistableBundle_isEqual;
-    APersistableBundle_isEqual; # llndk=202404
     APersistableBundle_size;
-    APersistableBundle_size; # llndk=202404
     APersistableBundle_erase;
-    APersistableBundle_erase; # llndk=202404
     APersistableBundle_putBoolean;
-    APersistableBundle_putBoolean; # llndk=202404
     APersistableBundle_putInt;
-    APersistableBundle_putInt; # llndk=202404
     APersistableBundle_putLong;
-    APersistableBundle_putLong; # llndk=202404
     APersistableBundle_putDouble;
-    APersistableBundle_putDouble; # llndk=202404
     APersistableBundle_putString;
-    APersistableBundle_putString; # llndk=202404
     APersistableBundle_putBooleanVector;
-    APersistableBundle_putBooleanVector; # llndk=202404
     APersistableBundle_putIntVector;
-    APersistableBundle_putIntVector; # llndk=202404
     APersistableBundle_putLongVector;
-    APersistableBundle_putLongVector; # llndk=202404
     APersistableBundle_putDoubleVector;
-    APersistableBundle_putDoubleVector; # llndk=202404
     APersistableBundle_putStringVector;
-    APersistableBundle_putStringVector; # llndk=202404
     APersistableBundle_putPersistableBundle;
-    APersistableBundle_putPersistableBundle; # llndk=202404
     APersistableBundle_getBoolean;
-    APersistableBundle_getBoolean; # llndk=202404
     APersistableBundle_getInt;
-    APersistableBundle_getInt; # llndk=202404
     APersistableBundle_getLong;
-    APersistableBundle_getLong; # llndk=202404
     APersistableBundle_getDouble;
-    APersistableBundle_getDouble; # llndk=202404
     APersistableBundle_getString;
-    APersistableBundle_getString; # llndk=202404
     APersistableBundle_getBooleanVector;
-    APersistableBundle_getBooleanVector; # llndk=202404
     APersistableBundle_getIntVector;
-    APersistableBundle_getIntVector; # llndk=202404
     APersistableBundle_getLongVector;
-    APersistableBundle_getLongVector; # llndk=202404
     APersistableBundle_getDoubleVector;
-    APersistableBundle_getDoubleVector; # llndk=202404
     APersistableBundle_getStringVector;
-    APersistableBundle_getStringVector; # llndk=202404
     APersistableBundle_getPersistableBundle;
-    APersistableBundle_getPersistableBundle; # llndk=202404
     APersistableBundle_getBooleanKeys;
-    APersistableBundle_getBooleanKeys; # llndk=202404
     APersistableBundle_getIntKeys;
-    APersistableBundle_getIntKeys; # llndk=202404
     APersistableBundle_getLongKeys;
-    APersistableBundle_getLongKeys; # llndk=202404
     APersistableBundle_getDoubleKeys;
-    APersistableBundle_getDoubleKeys; # llndk=202404
     APersistableBundle_getStringKeys;
-    APersistableBundle_getStringKeys; # llndk=202404
     APersistableBundle_getBooleanVectorKeys;
-    APersistableBundle_getBooleanVectorKeys; # llndk=202404
     APersistableBundle_getIntVectorKeys;
-    APersistableBundle_getIntVectorKeys; # llndk=202404
     APersistableBundle_getLongVectorKeys;
-    APersistableBundle_getLongVectorKeys; # llndk=202404
     APersistableBundle_getDoubleVectorKeys;
-    APersistableBundle_getDoubleVectorKeys; # llndk=202404
     APersistableBundle_getStringVectorKeys;
-    APersistableBundle_getStringVectorKeys; # llndk=202404
     APersistableBundle_getPersistableBundleKeys;
-    APersistableBundle_getPersistableBundleKeys; # llndk=202404
-    AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404
+    AServiceManager_openDeclaredPassthroughHal; # systemapi llndk
 };
 
 LIBBINDER_NDK36 { # introduced=36
   global:
     AIBinder_Class_setTransactionCodeToFunctionNameMap;
-    AIBinder_Class_setTransactionCodeToFunctionNameMap; # llndk=202504
     AIBinder_Class_getFunctionName;
-    AIBinder_Class_getFunctionName; # llndk=202504
     ABinderRpc_registerAccessorProvider; # systemapi
     ABinderRpc_unregisterAccessorProvider; # systemapi
     ABinderRpc_Accessor_new; # systemapi
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 506fc71..da5a8e3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1400,6 +1400,26 @@
     EXPECT_TRUE(isDeleted);
 }
 
+TEST_F(BinderARpcNdk, ARpcProviderDeleteOnError) {
+    bool isDeleted = false;
+    AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted};
+
+    ABinderRpc_AccessorProvider* provider =
+            ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, 0, data,
+                                                accessorProviderDataOnDelete);
+
+    ASSERT_EQ(provider, nullptr);
+    EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcProvideOnErrorNoDeleteCbNoCrash) {
+    ABinderRpc_AccessorProvider* provider =
+            ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, 0, nullptr,
+                                                nullptr);
+
+    ASSERT_EQ(provider, nullptr);
+}
+
 TEST_F(BinderARpcNdk, ARpcProviderDuplicateInstance) {
     const char* instance = "some.instance.name.IFoo/default";
     const uint32_t numInstances = 2;
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
index 690c39a..4008c56 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -59,6 +59,11 @@
         darwin: {
             enabled: false,
         },
+        host: {
+            data_libs: [
+                "libc++",
+            ],
+        },
     },
     test_suites: ["general-tests"],
 }
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index 5d68fe1..b623c5f 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -39,6 +39,7 @@
     else
         echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash"
         echo "${color_reset}"
+        cat "$FUZZER_OUT"
         exit 1
     fi
 done
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 17919c2..0580046 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -151,8 +151,10 @@
 
 int RpcServerTrusty::handleMessageInternal(void* ctx) {
     auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
-    LOG_ALWAYS_FATAL_IF(channelContext == nullptr,
-                        "bad state: message received on uninitialized channel");
+    if (channelContext == nullptr) {
+        LOG_RPC_DETAIL("bad state: message received on uninitialized channel");
+        return ERR_BAD_STATE;
+    }
 
     auto& session = channelContext->session;
     auto& connection = channelContext->connection;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index d0607bf..9855b5b 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -28,6 +28,10 @@
 #define VALIDATE_CONSISTENCY()
 #endif
 
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueConsumer.h>
 #include <gui/BufferQueueCore.h>
@@ -486,6 +490,27 @@
         return BAD_VALUE;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    if (eglFence != EGL_NO_SYNC_KHR) {
+        // Most platforms will be using native fences, so it's unlikely that we'll ever have to
+        // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our
+        // best to wait for it so the buffer stays valid, otherwise return an error to the caller.
+        //
+        // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't
+        // shown up on the GPU yet.
+        EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+                                             1000000000);
+        if (result == EGL_FALSE) {
+            BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError());
+            return UNKNOWN_ERROR;
+        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            BQ_LOGE("releaseBuffer: timeout waiting for fence");
+            return UNKNOWN_ERROR;
+        }
+        eglDestroySyncKHR(eglDisplay, eglFence);
+    }
+#endif
+
     sp<IProducerListener> listener;
     { // Autolock scope
         std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -507,8 +532,10 @@
             return BAD_VALUE;
         }
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         mSlots[slot].mEglDisplay = eglDisplay;
         mSlots[slot].mEglFence = eglFence;
+#endif
         mSlots[slot].mFence = releaseFence;
         mSlots[slot].mBufferState.release();
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d52cf70..5a09399 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -262,14 +262,16 @@
     mSlots[slot].mFrameNumber = 0;
     mSlots[slot].mAcquireCalled = false;
     mSlots[slot].mNeedsReallocation = true;
+    mSlots[slot].mFence = Fence::NO_FENCE;
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     // Destroy fence as BufferQueue now takes ownership
     if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
         eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
         mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
     }
-    mSlots[slot].mFence = Fence::NO_FENCE;
     mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
+#endif
 
     if (mLastQueuedSlot == slot) {
         mLastQueuedSlot = INVALID_BUFFER_SLOT;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 39209f9..2e7cef0 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -451,8 +451,10 @@
     }
 
     status_t returnFlags = NO_ERROR;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     EGLDisplay eglDisplay = EGL_NO_DISPLAY;
     EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
+#endif
     bool attachedByConsumer = false;
 
     sp<IConsumerListener> listener;
@@ -569,8 +571,10 @@
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = nullptr;
             mSlots[found].mRequestBufferCalled = false;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
             mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
             mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+#endif
             mSlots[found].mFence = Fence::NO_FENCE;
             mCore->mBufferAge = 0;
             mCore->mIsAllocating = true;
@@ -595,14 +599,18 @@
                     found, buffer->width, buffer->height, buffer->format);
         }
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         eglDisplay = mSlots[found].mEglDisplay;
         eglFence = mSlots[found].mEglFence;
+#endif
         // Don't return a fence in shared buffer mode, except for the first
         // frame.
         *outFence = (mCore->mSharedBufferMode &&
                 mCore->mSharedBufferSlot == found) ?
                 Fence::NO_FENCE : mSlots[found].mFence;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+#endif
         mSlots[found].mFence = Fence::NO_FENCE;
 
         // If shared buffer mode has just been enabled, cache the slot of the
@@ -691,6 +699,7 @@
         returnFlags |= BUFFER_NEEDS_REALLOCATION;
     }
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     if (eglFence != EGL_NO_SYNC_KHR) {
         EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
                 1000000000);
@@ -705,6 +714,7 @@
         }
         eglDestroySyncKHR(eglDisplay, eglFence);
     }
+#endif
 
     BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
             *outSlot,
@@ -908,7 +918,9 @@
 
     mSlots[*outSlot].mGraphicBuffer = buffer;
     mSlots[*outSlot].mBufferState.attachProducer();
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
+#endif
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
     mSlots[*outSlot].mAcquireCalled = false;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 61aabaa..be88b11 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2873,6 +2873,11 @@
     outInfo->hasArrSupport = ginfo.hasArrSupport;
     outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal,
                                                                ginfo.frameRateCategoryRate.high);
+    outInfo->supportedRefreshRates.clear();
+    outInfo->supportedRefreshRates.reserve(ginfo.supportedRefreshRates.size());
+    for (const auto rate : ginfo.supportedRefreshRates) {
+        outInfo->supportedRefreshRates.push_back(static_cast<float>(rate));
+    }
 }
 
 status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
@@ -3056,6 +3061,14 @@
     ComposerServiceAIDL::getComposerService()->setPowerMode(token, mode);
 }
 
+status_t SurfaceComposerClient::getMaxLayerPictureProfiles(const sp<IBinder>& token,
+                                                           int32_t* outMaxProfiles) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->getMaxLayerPictureProfiles(token,
+                                                                                  outMaxProfiles);
+    return statusTFromBinderStatus(status);
+}
+
 status_t SurfaceComposerClient::getCompositionPreference(
         ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat,
         ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) {
@@ -3280,6 +3293,13 @@
     return statusTFromBinderStatus(status);
 }
 
+status_t SurfaceComposerClient::setActivePictureListener(
+        const sp<gui::IActivePictureListener>& listener) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener);
+    return statusTFromBinderStatus(status);
+}
+
 status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
     binder::Status status = ComposerServiceAIDL::getComposerService()->notifyPowerBoost(boostId);
     return statusTFromBinderStatus(status);
diff --git a/libs/gui/aidl/android/gui/ActivePicture.aidl b/libs/gui/aidl/android/gui/ActivePicture.aidl
new file mode 100644
index 0000000..9b83be1
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ActivePicture.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+/**
+ * Visible content that is using picture processing.
+ * @hide
+ */
+parcelable ActivePicture {
+    /** The layer ID that is using picture processing. */
+    int layerId;
+
+    /** UID that owns layer using picture processing. */
+    int ownerUid;
+
+    /** ID of the picture profile that was used to configure the picture processing. */
+    long pictureProfileId;
+}
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 67cc273..26c12c5 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -50,4 +50,7 @@
 
     // Represents frame rate for FrameRateCategory Normal and High.
     FrameRateCategoryRate frameRateCategoryRate;
+
+    // All the refresh rates supported for the default display mode.
+    float[] supportedRefreshRates;
 }
diff --git a/libs/gui/aidl/android/gui/IActivePictureListener.aidl b/libs/gui/aidl/android/gui/IActivePictureListener.aidl
new file mode 100644
index 0000000..ad310bb
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IActivePictureListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+import android.gui.ActivePicture;
+
+/**
+ * Receive callbacks whenever the visible content using picture profiles changes.
+ * @hide
+ */
+interface IActivePictureListener {
+    /**
+     * Callback reporting the visible content on the screen using picture profiles.
+     */
+    oneway void onActivePicturesChanged(in ActivePicture[] activePictures);
+}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index ac14138..8c19bbb 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -33,6 +33,7 @@
 import android.gui.FrameStats;
 import android.gui.HdrConversionCapability;
 import android.gui.HdrConversionStrategy;
+import android.gui.IActivePictureListener;
 import android.gui.IDisplayEventConnection;
 import android.gui.IFpsListener;
 import android.gui.IHdrLayerInfoListener;
@@ -228,6 +229,11 @@
     void setGameContentType(IBinder display, boolean on);
 
     /**
+     * Gets the maximum number of picture profiles supported by the display.
+     */
+    int getMaxLayerPictureProfiles(IBinder display);
+
+    /**
      * Capture the specified screen. This requires READ_FRAME_BUFFER
      * permission.  This function will fail if there is a secure window on
      * screen and DisplayCaptureArgs.captureSecureLayers is false.
@@ -599,4 +605,10 @@
      * past the provided VSync.
      */
     oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
+
+    /**
+     * Sets the listener used to monitor visible content that is being processed with picture
+     * profiles.
+     */
+    oneway void setActivePictureListener(IActivePictureListener listener);
 }
diff --git a/libs/gui/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h
index 5b32710..e83d2e3 100644
--- a/libs/gui/include/gui/BufferSlot.h
+++ b/libs/gui/include/gui/BufferSlot.h
@@ -174,26 +174,30 @@
 };
 
 struct BufferSlot {
-
     BufferSlot()
-    : mGraphicBuffer(nullptr),
-      mEglDisplay(EGL_NO_DISPLAY),
-      mBufferState(),
-      mRequestBufferCalled(false),
-      mFrameNumber(0),
-      mEglFence(EGL_NO_SYNC_KHR),
-      mFence(Fence::NO_FENCE),
-      mAcquireCalled(false),
-      mNeedsReallocation(false) {
+          : mGraphicBuffer(nullptr),
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+            mEglDisplay(EGL_NO_DISPLAY),
+#endif
+            mBufferState(),
+            mRequestBufferCalled(false),
+            mFrameNumber(0),
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+            mEglFence(EGL_NO_SYNC_KHR),
+#endif
+            mFence(Fence::NO_FENCE),
+            mAcquireCalled(false),
+            mNeedsReallocation(false) {
     }
 
     // mGraphicBuffer points to the buffer allocated for this slot or is NULL
     // if no buffer has been allocated.
     sp<GraphicBuffer> mGraphicBuffer;
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
     EGLDisplay mEglDisplay;
-
+#endif
     // mBufferState is the current state of this buffer slot.
     BufferState mBufferState;
 
@@ -207,12 +211,14 @@
     // may be released before their release fence is signaled).
     uint64_t mFrameNumber;
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     // mEglFence is the EGL sync object that must signal before the buffer
     // associated with this buffer slot may be dequeued. It is initialized
     // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
     // new sync object in releaseBuffer.  (This is deprecated in favor of
     // mFence, below.)
     EGLSyncKHR mEglFence;
+#endif
 
     // mFence is a fence which will signal when work initiated by the
     // previous owner of the buffer is finished. When the buffer is FREE,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0d7f8c2..0ce0c0a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -298,6 +298,8 @@
     static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
                                                const sp<gui::IHdrLayerInfoListener>& listener);
 
+    static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
     /*
      * Sends a power boost to the composer. This function is asynchronous.
      *
@@ -343,6 +345,15 @@
 
     static bool flagEdgeExtensionEffectUseShader();
 
+    /**
+     * Returns how many picture profiles are supported by the display.
+     *
+     * displayToken
+     *      The token of the display.
+     */
+    static status_t getMaxLayerPictureProfiles(const sp<IBinder>& displayToken,
+                                               int32_t* outMaxProfiles);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c741866..76362ff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -21,6 +21,7 @@
 #include <gtest/gtest.h>
 
 #include <SurfaceFlingerProperties.h>
+#include <android/gui/IActivePictureListener.h>
 #include <android/gui/IDisplayEventConnection.h>
 #include <android/gui/ISurfaceComposer.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
@@ -1015,6 +1016,15 @@
         return binder::Status::ok();
     }
 
+    binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) {
+        return binder::Status::ok();
+    }
+
+    binder::Status getMaxLayerPictureProfiles(const sp<IBinder>& /*display*/,
+                                              int32_t* /*outMaxProfiles*/) {
+        return binder::Status::ok();
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp
index d231474..a32685b 100644
--- a/libs/input/CoordinateFilter.cpp
+++ b/libs/input/CoordinateFilter.cpp
@@ -23,7 +23,7 @@
 CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
       : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}
 
-void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) {
+void CoordinateFilter::filter(std::chrono::nanoseconds 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()));
 }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index a2bb345..65a088e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -685,11 +685,12 @@
 
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
     if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
-        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for "
+                   << safeDump();
     }
     const size_t position = getHistorySize() * getPointerCount() + pointerIndex;
     if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
-        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump();
     }
     return &mSamplePointerCoords[position];
 }
@@ -705,15 +706,16 @@
 const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
         size_t pointerIndex, size_t historicalIndex) const {
     if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
-        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for "
+                   << safeDump();
     }
     if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) {
         LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for "
-                   << *this;
+                   << safeDump();
     }
     const size_t position = historicalIndex * getPointerCount() + pointerIndex;
     if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
-        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump();
     }
     return &mSamplePointerCoords[position];
 }
@@ -1144,6 +1146,53 @@
     // clang-format on
 }
 
+std::string MotionEvent::safeDump() const {
+    std::stringstream out;
+    // Field names have the m prefix here to make it easy to distinguish safeDump output from
+    // operator<< output in logs.
+    out << "MotionEvent { mAction=" << MotionEvent::actionToString(mAction);
+    if (mActionButton != 0) {
+        out << ", mActionButton=" << mActionButton;
+    }
+    if (mButtonState != 0) {
+        out << ", mButtonState=" << mButtonState;
+    }
+    if (mClassification != MotionClassification::NONE) {
+        out << ", mClassification=" << motionClassificationToString(mClassification);
+    }
+    if (mMetaState != 0) {
+        out << ", mMetaState=" << mMetaState;
+    }
+    if (mFlags != 0) {
+        out << ", mFlags=0x" << std::hex << mFlags << std::dec;
+    }
+    if (mEdgeFlags != 0) {
+        out << ", mEdgeFlags=" << mEdgeFlags;
+    }
+    out << ", mDownTime=" << mDownTime;
+    out << ", mDeviceId=" << mDeviceId;
+    out << ", mSource=" << inputEventSourceToString(mSource);
+    out << ", mDisplayId=" << mDisplayId;
+    out << ", mEventId=0x" << std::hex << mId << std::dec;
+    // Since we're not assuming the data is at all valid, we also limit the number of items that
+    // might be printed from vectors, in case the vector's size field is corrupted.
+    out << ", mPointerProperties=(" << mPointerProperties.size() << ")[";
+    for (size_t i = 0; i < mPointerProperties.size() && i < MAX_POINTERS; i++) {
+        out << (i > 0 ? ", " : "") << mPointerProperties.at(i);
+    }
+    out << "], mSampleEventTimes=(" << mSampleEventTimes.size() << ")[";
+    for (size_t i = 0; i < mSampleEventTimes.size() && i < 256; i++) {
+        out << (i > 0 ? ", " : "") << mSampleEventTimes.at(i);
+    }
+    out << "], mSamplePointerCoords=(" << mSamplePointerCoords.size() << ")[";
+    for (size_t i = 0; i < mSamplePointerCoords.size() && i < MAX_POINTERS; i++) {
+        const PointerCoords& coords = mSamplePointerCoords.at(i);
+        out << (i > 0 ? ", " : "") << "(" << coords.getX() << ", " << coords.getY() << ")";
+    }
+    out << "] }";
+    return out.str();
+}
+
 std::ostream& operator<<(std::ostream& out, const MotionEvent& event) {
     out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction());
     if (event.getActionButton() != 0) {
diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp
index 400d7c9..7b0d104 100644
--- a/libs/input/OneEuroFilter.cpp
+++ b/libs/input/OneEuroFilter.cpp
@@ -25,16 +25,24 @@
 namespace android {
 namespace {
 
+using namespace std::literals::chrono_literals;
+
+const float kHertzPerGigahertz = 1E9f;
+const float kGigahertzPerHertz = 1E-9f;
+
+// filteredSpeed's units are position per nanosecond. beta's units are 1 / position.
 inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
-    return minCutoffFreq + beta * std::abs(filteredSpeed);
+    return kHertzPerGigahertz *
+            ((minCutoffFreq * kGigahertzPerHertz) + 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 smoothingFactor(std::chrono::nanoseconds samplingPeriod, float cutoffFreq) {
+    const float constant = 2.0f * M_PI * samplingPeriod.count() * (cutoffFreq * kGigahertzPerHertz);
+    return constant / (constant + 1);
 }
 
-inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) {
-    return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition;
+inline float lowPassFilter(float rawValue, float prevFilteredValue, float smoothingFactor) {
+    return smoothingFactor * rawValue + (1 - smoothingFactor) * prevFilteredValue;
 }
 
 } // namespace
@@ -42,17 +50,17 @@
 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";
+float OneEuroFilter::filter(std::chrono::nanoseconds timestamp, float rawPosition) {
+    LOG_IF(FATAL, mPrevTimestamp.has_value() && (*mPrevTimestamp >= timestamp))
+            << "Timestamp must be greater than mPrevTimestamp. Timestamp: " << timestamp.count()
+            << "ns. mPrevTimestamp: " << mPrevTimestamp->count() << "ns";
 
-    const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value())
-            ? (timestamp - *mPrevTimestamp)
-            : std::chrono::duration<float>{1.0};
+    const std::chrono::nanoseconds samplingPeriod =
+            (mPrevTimestamp.has_value()) ? (timestamp - *mPrevTimestamp) : 1s;
 
     const float rawVelocity = (mPrevFilteredPosition.has_value())
-            ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count())
-            : 0.0;
+            ? ((rawPosition - *mPrevFilteredPosition) / (samplingPeriod.count()))
+            : 0.0f;
 
     const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);
 
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 46e8190..d1c564d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -17,6 +17,7 @@
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
         "InputConsumer_test.cpp",
+        "InputConsumerFilteredResampling_test.cpp",
         "InputConsumerResampling_test.cpp",
         "InputDevice_test.cpp",
         "InputEvent_test.cpp",
diff --git a/libs/input/tests/InputConsumerFilteredResampling_test.cpp b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
new file mode 100644
index 0000000..757cd18
--- /dev/null
+++ b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
@@ -0,0 +1,218 @@
+/**
+ * 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/InputConsumerNoResampling.h>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <queue>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+
+using ::testing::AllOf;
+using ::testing::Matcher;
+
+const int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+const int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
+struct Pointer {
+    int32_t id{0};
+    ToolType toolType{ToolType::FINGER};
+    float x{0.0f};
+    float y{0.0f};
+    bool isResampled{false};
+
+    PointerBuilder asPointerBuilder() const {
+        return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+    }
+};
+
+} // namespace
+
+class InputConsumerFilteredResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
+protected:
+    InputConsumerFilteredResamplingTest()
+          : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+            mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+        Looper::setForThread(mLooper);
+        mConsumer = std::make_unique<
+                InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, []() {
+            return std::make_unique<FilteredLegacyResampler>(/*minCutoffFreq=*/4.7, /*beta=*/0.01);
+        });
+    }
+
+    void invokeLooperCallback() const {
+        sp<LooperCallback> callback;
+        ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+                                             /*events=*/nullptr, &callback, /*data=*/nullptr));
+        ASSERT_NE(callback, nullptr);
+        callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+    }
+
+    void assertOnBatchedInputEventPendingWasCalled() {
+        ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+                << "onBatchedInputEventPending was not called";
+        --mOnBatchedInputEventPendingInvocationCount;
+    }
+
+    void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+        ASSERT_TRUE(!mMotionEvents.empty()) << "No motion events were received";
+        std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+        mMotionEvents.pop();
+        ASSERT_NE(motionEvent, nullptr) << "The consumed motion event must not be nullptr";
+        EXPECT_THAT(*motionEvent, matcher);
+    }
+
+    InputMessage nextPointerMessage(nanoseconds eventTime, int32_t action, const Pointer& pointer);
+
+    std::shared_ptr<TestInputChannel> mClientTestChannel;
+    sp<Looper> mLooper;
+    std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+    // Batched input events
+    std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+    std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+    std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+    std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+    std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+    // InputConsumer callbacks
+    void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+        mKeyEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+        mMotionEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+        if (!mConsumer->probablyHasInput()) {
+            ADD_FAILURE() << "Should deterministically have input because there is a batch";
+        }
+        ++mOnBatchedInputEventPendingInvocationCount;
+    }
+
+    void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+        mFocusEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+        mCaptureEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+        mDragEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+        mTouchModeEvents.push(std::move(event));
+        mConsumer->finishInputEvent(seq, /*handled=*/true);
+    }
+
+    uint32_t mLastSeq{0};
+    size_t mOnBatchedInputEventPendingInvocationCount{0};
+};
+
+InputMessage InputConsumerFilteredResamplingTest::nextPointerMessage(nanoseconds eventTime,
+                                                                     int32_t action,
+                                                                     const Pointer& pointer) {
+    ++mLastSeq;
+    return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+            .eventTime(eventTime.count())
+            .source(AINPUT_SOURCE_TOUCHSCREEN)
+            .action(action)
+            .pointer(pointer.asPointerBuilder())
+            .build();
+}
+
+TEST_F(InputConsumerFilteredResamplingTest, NeighboringTimestampsDoNotResultInZeroDivision) {
+    mClientTestChannel->enqueueMessage(
+            nextPointerMessage(0ms, ACTION_DOWN, Pointer{.x = 0.0f, .y = 0.0f}));
+
+    invokeLooperCallback();
+
+    assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+
+    const std::chrono::nanoseconds initialTime{56'821'700'000'000};
+
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 4'929'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 1.0f, .y = 1.0f}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 9'352'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 2.0f, .y = 2.0f}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 14'531'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 3.0f, .y = 3.0f}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(initialTime.count() + 18'849'395 /*ns*/);
+
+    assertOnBatchedInputEventPendingWasCalled();
+    // Three samples are expected. The first two of the batch, and the resampled one. The
+    // coordinates of the resampled sample are hardcoded because the matcher requires them. However,
+    // the primary intention here is to check that the last sample is resampled.
+    assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+                                    WithSample(/*sampleIndex=*/2,
+                                               Sample{initialTime + 13'849'395ns,
+                                                      {PointerArgs{.x = 1.3286f,
+                                                                   .y = 1.3286f,
+                                                                   .isResampled = true}}})));
+
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 20'363'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 4.0f, .y = 4.0f}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 25'745'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 5.0f, .y = 5.0f}));
+    // This sample is part of the stream of messages, but should not be consumed because its
+    // timestamp is greater than the ajusted frame time.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 31'337'000ns, ACTION_MOVE,
+                                                          Pointer{.x = 6.0f, .y = 6.0f}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(initialTime.count() + 35'516'062 /*ns*/);
+
+    assertOnBatchedInputEventPendingWasCalled();
+    // Four samples are expected because the last sample of the previous batch was not consumed.
+    assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(4)));
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true);
+}
+
+} // namespace android
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
index 270e789..8645508 100644
--- a/libs/input/tests/OneEuroFilter_test.cpp
+++ b/libs/input/tests/OneEuroFilter_test.cpp
@@ -98,7 +98,10 @@
         std::vector<Sample> filteredSignal;
         for (const Sample& sample : signal) {
             filteredSignal.push_back(
-                    Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)});
+                    Sample{sample.timestamp,
+                           mFilter.filter(std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                                  sample.timestamp),
+                                          sample.value)});
         }
         return filteredSignal;
     }
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
index 290a97d..56eaefd 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <chrono>
+#include <cmath>
 #include <ostream>
 #include <vector>
 
@@ -156,14 +157,18 @@
              ++pointerIndex) {
             const PointerCoords& pointerCoords =
                     *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
-            if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) ||
-                (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) {
+
+            if ((std::abs(pointerCoords.getX() - mSample.pointers[pointerIndex].x) >
+                 MotionEvent::ROUNDING_PRECISION) ||
+                (std::abs(pointerCoords.getY() - mSample.pointers[pointerIndex].y) >
+                 MotionEvent::ROUNDING_PRECISION)) {
                 *os << "sample coordinates mismatch at pointer index " << pointerIndex
                     << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
                     << ") expected: (" << mSample.pointers[pointerIndex].x << ", "
                     << mSample.pointers[pointerIndex].y << ")";
                 return false;
             }
+
             if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
                 mSample.pointers[pointerIndex].isResampled) {
                 *os << "resampling flag mismatch. sample: "
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index a93f6c3..8f23249 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -572,8 +572,10 @@
         }
 
         // disable tonemapping if we already locally tonemapped
-        auto inputDataspace =
-                usingLocalTonemap ? parameters.outputDataSpace : parameters.layer.sourceDataspace;
+        // skip tonemapping if the luts is in use
+        auto inputDataspace = usingLocalTonemap || (graphicBuffer && parameters.layer.luts)
+                ? parameters.outputDataSpace
+                : parameters.layer.sourceDataspace;
         auto effect =
                 shaders::LinearEffect{.inputDataspace = inputDataspace,
                                       .outputDataspace = parameters.outputDataSpace,
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index af494dc..9d97151 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -59,6 +59,9 @@
 
     // Represents frame rate for FrameRateCategory Normal and High.
     ui::FrameRateCategoryRate frameRateCategoryRate;
+
+    // All the refresh rates supported for the default display mode.
+    std::vector<float> supportedRefreshRates;
 };
 
 } // namespace android::ui
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
index b0af878..70c50f0 100644
--- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -61,4 +61,24 @@
     return HdrRenderType::SDR;
 }
 
-} // namespace android
\ No newline at end of file
+/**
+ * Returns the maximum headroom allowed for this content under "idealized"
+ * display conditions (low surround luminance, high-enough display brightness).
+ *
+ * TODO: take into account hdr metadata, but square it with the fact that some
+ * HLG content has CTA.861-3 metadata
+ */
+inline float getIdealizedMaxHeadroom(ui::Dataspace dataspace) {
+    const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+    switch (transfer) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            return 10000.0f / 203.0f;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            return 1000.0f / 203.0f;
+        default:
+            return 1.0f;
+    }
+}
+
+} // namespace android
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 72bd292..b63e919 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -23,6 +23,11 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+vintf_fragment {
+    name: "manifest_android.frameworks.automotive.display@1.0.xml",
+    src: "manifest_android.frameworks.automotive.display@1.0.xml",
+}
+
 cc_binary {
     name: "android.frameworks.automotive.display@1.0-service",
     defaults: ["hidl_defaults"],
@@ -50,7 +55,7 @@
         "-DLOG_TAG=\"AutomotiveDisplayService\""
     ],
 
-    vintf_fragments: [
+    vintf_fragment_modules: [
         "manifest_android.frameworks.automotive.display@1.0.xml",
     ],
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 755995c..cd4ed5c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -6805,14 +6805,15 @@
         // The fallback keycode cannot change at any other point in the lifecycle.
         if (initialDown) {
             if (fallback) {
-                *fallbackKeyCode = event.getKeyCode();
+                fallbackKeyCode = event.getKeyCode();
             } else {
-                *fallbackKeyCode = AKEYCODE_UNKNOWN;
+                fallbackKeyCode = AKEYCODE_UNKNOWN;
             }
             connection->inputState.setFallbackKey(originalKeyCode, *fallbackKeyCode);
         }
 
-        ALOG_ASSERT(fallbackKeyCode);
+        LOG_IF(FATAL, !fallbackKeyCode)
+                << "fallbackKeyCode is not initialized. initialDown = " << initialDown;
 
         // Cancel the fallback key if the policy decides not to send it anymore.
         // We will continue to dispatch the key to the policy but we will no
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 40fd097..0ba1909 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -168,6 +168,11 @@
                                           "closeSessionChannel"));
 }
 
+HalResult<aidl::android::hardware::power::SupportInfo> PowerHalController::getSupportInfo() {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo"));
+}
+
 } // namespace power
 
 } // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index bd6685c..068c23f 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -18,6 +18,7 @@
 #include <aidl/android/hardware/power/Boost.h>
 #include <aidl/android/hardware/power/IPowerHintSession.h>
 #include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/SupportInfo.h>
 #include <powermanager/HalResult.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
@@ -73,6 +74,11 @@
     return HalResult<void>::unsupported();
 }
 
+HalResult<Aidl::SupportInfo> EmptyHalWrapper::getSupportInfo() {
+    ALOGV("Skipped getSupportInfo because %s", getUnsupportedMessage());
+    return HalResult<Aidl::SupportInfo>::unsupported();
+}
+
 const char* EmptyHalWrapper::getUnsupportedMessage() {
     return "Power HAL is not supported";
 }
@@ -280,6 +286,12 @@
     return HalResult<void>::fromStatus(mHandle->closeSessionChannel(tgid, uid));
 }
 
+HalResult<Aidl::SupportInfo> AidlHalWrapper::getSupportInfo() {
+    Aidl::SupportInfo support;
+    auto result = mHandle->getSupportInfo(&support);
+    return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support));
+}
+
 const char* AidlHalWrapper::getUnsupportedMessage() {
     return "Power HAL doesn't support it";
 }
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
new file mode 100644
index 0000000..210e948
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "ActivePictureUpdater.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+                                           const CompositionResult& result) {
+    if (result.wasPictureProfileCommitted) {
+        gui::ActivePicture picture;
+        picture.layerId = int32_t(layer.sequence);
+        picture.ownerUid = int32_t(layer.getOwnerUid());
+        // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+        if (layerFE.getCompositionState()) {
+            picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+        } else {
+            picture.pictureProfileId = result.pictureProfileHandle.getId();
+        }
+        mNewActivePictures.push_back(picture);
+    }
+}
+
+bool ActivePictureUpdater::updateAndHasChanged() {
+    bool hasChanged = true;
+    if (mNewActivePictures.size() == mOldActivePictures.size()) {
+        auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
+            if (lhs.layerId == rhs.layerId) {
+                return lhs.pictureProfileId < rhs.pictureProfileId;
+            }
+            return lhs.layerId < rhs.layerId;
+        };
+        std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+        if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+                       mOldActivePictures.begin())) {
+            hasChanged = false;
+        }
+    }
+    std::swap(mOldActivePictures, mNewActivePictures);
+    mNewActivePictures.resize(0);
+    return hasChanged;
+}
+
+const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
+    return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureUpdater.h
new file mode 100644
index 0000000..20779bb
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <vector>
+
+#include <android/gui/ActivePicture.h>
+
+namespace android {
+
+class Layer;
+class LayerFE;
+struct CompositionResult;
+
+// Keeps track of active pictures - layers that are undergoing picture processing.
+class ActivePictureUpdater {
+public:
+    // Called for each visible layer when SurfaceFlinger finishes composing.
+    void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+                         const CompositionResult& result);
+
+    // Update internals and return whether the set of active pictures have changed.
+    bool updateAndHasChanged();
+
+    // The current set of active pictures.
+    const std::vector<gui::ActivePicture>& getActivePictures() const;
+
+private:
+    std::vector<gui::ActivePicture> mOldActivePictures;
+    std::vector<gui::ActivePicture> mNewActivePictures;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 8a667ae..3f3d2c6 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -199,6 +199,7 @@
     name: "libsurfaceflinger_sources",
     srcs: [
         ":libsurfaceflinger_backend_sources",
+        "ActivePictureUpdater.cpp",
         "BackgroundExecutor.cpp",
         "Client.cpp",
         "ClientCache.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a5e9dde..cda4edc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -161,6 +161,9 @@
     // Checks if the buffer's release fence has been set
     virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
 
+    // Indicates that the picture profile request was applied to this layer.
+    virtual void onPictureProfileCommitted() = 0;
+
     // Gets some kind of identifier for the layer for debug purposes.
     virtual const char* getDebugName() const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index c7ff704..272fa3e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -58,6 +58,7 @@
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
     MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
     MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
+    MOCK_METHOD0(onPictureProfileCommitted, void());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ee813bf..f9ed92d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -830,11 +830,13 @@
         for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty();
              layersWithProfiles.pop(), ++i) {
             layersWithProfiles.top()->commitPictureProfileToCompositionState();
+            layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
         }
         // No layer-specific picture processing, so apply the highest priority picture profile to
         // the entire display.
     } else if (!layersWithProfiles.empty()) {
         editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle();
+        layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index f6d9a1a..a040c88 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -26,6 +26,7 @@
 #include <ui/FloatRect.h>
 #include <ui/HdrRenderTypeUtils.h>
 #include <cstdint>
+#include <limits>
 #include "system/graphics-base-v1.0.h"
 
 #include <com_android_graphics_libgui_flags.h>
@@ -398,11 +399,22 @@
     // For hdr content, treat the white point as the display brightness - HDR content should not be
     // boosted or dimmed.
     // If the layer explicitly requests to disable dimming, then don't dim either.
-    if (hdrRenderType == HdrRenderType::GENERIC_HDR ||
-        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
-        getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
+    if (getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+        getOutput().getState().displayBrightnessNits <= 0.f || !layerFEState->dimmingEnabled) {
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
+    } else if (hdrRenderType == HdrRenderType::GENERIC_HDR) {
+        float deviceHeadroom = getOutput().getState().displayBrightnessNits /
+                getOutput().getState().sdrWhitePointNits;
+        float idealizedMaxHeadroom = deviceHeadroom;
+
+        if (FlagManager::getInstance().begone_bright_hlg()) {
+            idealizedMaxHeadroom =
+                    std::min(idealizedMaxHeadroom, getIdealizedMaxHeadroom(state.dataspace));
+        }
+
+        state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
+        state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
     } else {
         float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
         // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
@@ -432,7 +444,7 @@
     }
     const auto* layerState = getLayerFE().getCompositionState();
     if (layerState) {
-        editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle;
+        editState().pictureProfileHandle = layerState->pictureProfileHandle;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 99e68eb..442b603 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -5117,6 +5117,11 @@
     // Sets display picture profile to the highest priority layer's profile
     EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
 
+    // Marks only the highest priority layer as committed
+    EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+    EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+    EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted).Times(0);
+
     mOutput->editState().isEnabled = true;
     CompositionRefreshArgs args;
     args.updatingGeometryThisFrame = false;
@@ -5172,6 +5177,11 @@
     EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
     EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
 
+    // Marks only the highest priority layers as committed
+    EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+    EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+    EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted);
+
     // No display picture profile is sent
     EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
 
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 231b40b..fea7671 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -342,8 +342,15 @@
     caster.shadow = state;
 }
 
-CompositionResult&& LayerFE::stealCompositionResult() {
-    return std::move(mCompositionResult);
+void LayerFE::onPictureProfileCommitted() {
+    mCompositionResult.wasPictureProfileCommitted = true;
+    mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle;
+}
+
+CompositionResult LayerFE::stealCompositionResult() {
+    CompositionResult result;
+    std::swap(mCompositionResult, result);
+    return result;
 }
 
 const char* LayerFE::getDebugName() const {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 5081e10..9483aeb 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,6 +18,9 @@
 
 #include <android/gui/CachingHint.h>
 #include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
+
 #include "FrontEnd/LayerSnapshot.h"
 #include "compositionengine/LayerFE.h"
 #include "compositionengine/LayerFECompositionState.h"
@@ -29,6 +32,10 @@
 
 struct CompositionResult {
     sp<Fence> lastClientCompositionFence = nullptr;
+    bool wasPictureProfileCommitted = false;
+    // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+    // It would be better not to duplicate this information
+    PictureProfileHandle pictureProfileHandle = PictureProfileHandle::NONE;
 };
 
 class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
@@ -47,10 +54,11 @@
     const gui::LayerMetadata* getRelativeMetadata() const override;
     std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
-    CompositionResult&& stealCompositionResult();
+    CompositionResult stealCompositionResult();
     ftl::Future<FenceResult> createReleaseFenceFuture() override;
     void setReleaseFence(const FenceResult& releaseFence) override;
     LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+    void onPictureProfileCommitted() override;
 
     std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index e4069dd..668fa54 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1557,6 +1557,19 @@
     return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
 }
 
+std::vector<float> RefreshRateSelector::getSupportedFrameRates() const {
+    std::scoped_lock lock(mLock);
+    // TODO(b/356986687) Remove the limit once we have the anchor list implementation.
+    const size_t frameRatesSize = std::min<size_t>(11, mPrimaryFrameRates.size());
+    std::vector<float> supportedFrameRates;
+    supportedFrameRates.reserve(frameRatesSize);
+    std::transform(mPrimaryFrameRates.rbegin(),
+                   mPrimaryFrameRates.rbegin() + static_cast<int>(frameRatesSize),
+                   std::back_inserter(supportedFrameRates),
+                   [](FrameRateMode mode) { return mode.fps.getValue(); });
+    return supportedFrameRates;
+}
+
 auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
     std::lock_guard lock(mLock);
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index ee3a4f7..508f9d7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -441,6 +441,8 @@
 
     std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; }
 
+    std::vector<float> getSupportedFrameRates() const EXCLUDES(mLock);
+
 private:
     friend struct TestableRefreshRateSelector;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 6e36f02..ff360b7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -458,7 +458,8 @@
 
 Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
                                                       TimePoint lastConfirmedPresentTime) {
-    SFTRACE_CALL();
+    SFTRACE_FORMAT("%s mNumVsyncsForFrame=%d mPastExpectedPresentTimes.size()=%zu", __func__,
+                   mNumVsyncsForFrame, mPastExpectedPresentTimes.size());
 
     if (mNumVsyncsForFrame <= 1) {
         return 0ns;
@@ -470,12 +471,8 @@
 
     auto prev = lastConfirmedPresentTime.ns();
     for (auto& current : mPastExpectedPresentTimes) {
-        if (CC_UNLIKELY(mTraceOn)) {
-            SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
-                                   static_cast<float>(current.ns() -
-                                                      lastConfirmedPresentTime.ns()) /
-                                           1e6f);
-        }
+        SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
+                               static_cast<float>(current.ns() - prev) / 1e6f);
 
         const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
         if (minPeriodViolation) {
@@ -522,11 +519,9 @@
         const auto front = mPastExpectedPresentTimes.front().ns();
         const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
         if (frontIsBeforeConfirmed) {
-            if (CC_UNLIKELY(mTraceOn)) {
-                SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
-                                       static_cast<float>(lastConfirmedPresentTime.ns() - front) /
-                                               1e6f);
-            }
+            SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
+                                   static_cast<float>(lastConfirmedPresentTime.ns() - front) /
+                                           1e6f);
             mPastExpectedPresentTimes.pop_front();
         } else {
             break;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 4f16130..5019949 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -31,6 +31,7 @@
 
 std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
 FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+    SFTRACE_CALL();
     if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
         const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
         return {true, mPresentFencesLegacy[i]};
@@ -40,17 +41,28 @@
     auto expectedPresentTime = mExpectedPresentTime;
     for (size_t i = mPresentFences.size(); i != 0; --i) {
         const auto& fence = mPresentFences[i - 1];
+        SFTRACE_FORMAT_INSTANT("fence at idx: %zu expectedPresentTime in %.2f", i - 1,
+                               ticks<std::milli, float>(fence.expectedPresentTime -
+                                                        TimePoint::now()));
 
         if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+            SFTRACE_FORMAT_INSTANT("would not backpressure");
             wouldBackpressure = false;
         }
 
         if (fence.expectedPresentTime <= mFrameBeginTime) {
+            SFTRACE_FORMAT_INSTANT("fence at idx: %zu is %.2f before frame begin "
+                                   "(wouldBackpressure=%s)",
+                                   i - 1,
+                                   ticks<std::milli, float>(mFrameBeginTime -
+                                                            fence.expectedPresentTime),
+                                   wouldBackpressure ? "true" : "false");
             return {wouldBackpressure, fence};
         }
 
         expectedPresentTime = fence.expectedPresentTime;
     }
+    SFTRACE_FORMAT_INSTANT("No fence found");
     return {wouldBackpressure, PresentFence{}};
 }
 
@@ -154,6 +166,12 @@
         if (pastPresentTime < 0) return false;
         mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
                                   .expectedPresentTime = fence.expectedPresentTime};
+        SFTRACE_FORMAT_INSTANT("LastSignaledFrameTime expectedPresentTime %.2f ago, signalTime "
+                               "%.2f ago",
+                               ticks<std::milli, float>(mLastSignaledFrameTime.expectedPresentTime -
+                                                        TimePoint::now()),
+                               ticks<std::milli, float>(mLastSignaledFrameTime.signalTime -
+                                                        TimePoint::now()));
         const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
         return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
     }();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 37b35f2..0ea26d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -126,6 +126,7 @@
 #include <gui/SchedulingPolicy.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayIdentification.h>
+#include "ActivePictureUpdater.h"
 #include "BackgroundExecutor.h"
 #include "Client.h"
 #include "ClientCache.h"
@@ -372,6 +373,7 @@
 const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
+const String16 sObservePictureProfiles("android.permission.OBSERVE_PICTURE_PROFILES");
 const String16 sDump("android.permission.DUMP");
 const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
 const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
@@ -1229,6 +1231,8 @@
     const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates();
     ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
     info->frameRateCategoryRate = frameRateCategoryRate;
+
+    info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
@@ -1838,6 +1842,24 @@
     }));
 }
 
+status_t SurfaceFlinger::getMaxLayerPictureProfiles(const sp<IBinder>& displayToken,
+                                                    int32_t* outMaxProfiles) {
+    const char* const whence = __func__;
+    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+        const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+        if (index < 0) {
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+            return 0;
+        }
+        const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+        return state.maxLayerPictureProfiles > 0 ? state.maxLayerPictureProfiles
+                : state.hasPictureProcessing     ? 1
+                                                 : 0;
+    });
+    *outMaxProfiles = future.get();
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
                                           const std::vector<ui::Hdr>& hdrTypes) {
     Mutex::Autolock lock(mStateLock);
@@ -2248,6 +2270,19 @@
         return;
     }
 
+    if (event < DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+        // This needs to be kept in sync with DisplayHotplugEvent to prevent passing new errors.
+        const auto errorCode = static_cast<int32_t>(event);
+        ALOGW("%s: Unknown hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode,
+              hwcDisplayId);
+        return;
+    }
+
+    if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
+        !FlagManager::getInstance().display_config_error_hal()) {
+        return;
+    }
+
     // TODO(b/311403559): use enum type instead of int
     const auto errorCode = static_cast<int32_t>(event);
     ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
@@ -2552,7 +2587,7 @@
     }
 
     {
-        SFTRACE_NAME("LLM:commitChanges");
+        SFTRACE_NAME("LayerLifecycleManager:commitChanges");
         mLayerLifecycleManager.commitChanges();
     }
 
@@ -2851,6 +2886,9 @@
         if (compositionResult.lastClientCompositionFence) {
             layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
         }
+        if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+            mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
+        }
     }
 
     SFTRACE_NAME("postComposition");
@@ -3146,30 +3184,6 @@
         layer->releasePendingBuffer(presentTime.ns());
     }
 
-    std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
-            hdrInfoListeners;
-    bool haveNewListeners = false;
-    {
-        Mutex::Autolock lock(mStateLock);
-        if (mFpsReporter) {
-            mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
-        }
-
-        if (mTunnelModeEnabledReporter) {
-            mTunnelModeEnabledReporter->updateTunnelModeStatus();
-        }
-        hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
-        for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
-            if (reporter && reporter->hasListeners()) {
-                if (const auto display = getDisplayDeviceLocked(displayId)) {
-                    hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
-                }
-            }
-        }
-        haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock
-        mAddingHDRLayerInfoListener = false;
-    }
-
     for (const auto& layerEvent : mLayerEvents) {
         auto result =
                 stats::stats_write(stats::SURFACE_CONTROL_EVENT,
@@ -3180,10 +3194,40 @@
             ALOGW("Failed to report layer event with error: %d", result);
         }
     }
-
     mLayerEvents.clear();
 
-    if (haveNewListeners || mHdrLayerInfoChanged) {
+    std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+            hdrInfoListeners;
+    bool haveNewHdrInfoListeners = false;
+    sp<gui::IActivePictureListener> activePictureListener;
+    bool haveNewActivePictureListener = false;
+    {
+        Mutex::Autolock lock(mStateLock);
+        if (mFpsReporter) {
+            mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
+        }
+
+        if (mTunnelModeEnabledReporter) {
+            mTunnelModeEnabledReporter->updateTunnelModeStatus();
+        }
+
+        hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+        for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
+            if (reporter && reporter->hasListeners()) {
+                if (const auto display = getDisplayDeviceLocked(displayId)) {
+                    hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
+                }
+            }
+        }
+        haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
+        mAddingHDRLayerInfoListener = false;
+
+        activePictureListener = mActivePictureListener;
+        haveNewActivePictureListener = mHaveNewActivePictureListener;
+        mHaveNewActivePictureListener = false;
+    }
+
+    if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
             int32_t maxArea = 0;
@@ -3201,7 +3245,15 @@
                                             snapshot.desiredHdrSdrRatio < 1.f
                                             ? std::numeric_limits<float>::infinity()
                                             : snapshot.desiredHdrSdrRatio;
-                                    info.mergeDesiredRatio(desiredHdrSdrRatio);
+
+                                    float desiredRatio = desiredHdrSdrRatio;
+                                    if (FlagManager::getInstance().begone_bright_hlg() &&
+                                        desiredHdrSdrRatio ==
+                                                std::numeric_limits<float>::infinity()) {
+                                        desiredRatio = getIdealizedMaxHeadroom(snapshot.dataspace);
+                                    }
+
+                                    info.mergeDesiredRatio(desiredRatio);
                                     info.numberOfHdrLayers++;
                                     const auto displayFrame = outputLayer->getState().displayFrame;
                                     const int32_t area =
@@ -3233,9 +3285,19 @@
             listener->dispatchHdrLayerInfo(info);
         }
     }
-
     mHdrLayerInfoChanged = false;
 
+    if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        // Track, update and notify changes to active pictures - layers that are undergoing picture
+        // processing
+        if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
+            if (activePictureListener) {
+                activePictureListener->onActivePicturesChanged(
+                        mActivePictureUpdater.getActivePictures());
+            }
+        }
+    }
+
     mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
     mTransactionCallbackInvoker.clearCompletedTransactions();
 
@@ -8073,6 +8135,14 @@
     }));
 }
 
+void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+    if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        Mutex::Autolock lock(mStateLock);
+        mActivePictureListener = listener;
+        mHaveNewActivePictureListener = listener != nullptr;
+    }
+}
+
 std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
         BufferData& bufferData, const char* layerName, uint64_t transactionId) {
     if (bufferData.buffer &&
@@ -8575,6 +8645,11 @@
     gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate;
     frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal();
     frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh();
+    outInfo->supportedRefreshRates.clear();
+    outInfo->supportedRefreshRates.reserve(info.supportedRefreshRates.size());
+    for (float supportedRefreshRate : info.supportedRefreshRates) {
+        outInfo->supportedRefreshRates.push_back(supportedRefreshRate);
+    }
 
     outInfo->supportedColorModes.clear();
     outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
@@ -8730,6 +8805,16 @@
     return binder::Status::ok();
 }
 
+binder::Status SurfaceComposerAIDL::getMaxLayerPictureProfiles(const sp<IBinder>& display,
+                                                               int32_t* outMaxProfiles) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+    mFlinger->getMaxLayerPictureProfiles(display, outMaxProfiles);
+    return binder::Status::ok();
+}
+
 binder::Status SurfaceComposerAIDL::captureDisplay(
         const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
     mFlinger->captureDisplay(args, captureListener);
@@ -9008,6 +9093,15 @@
     return binderStatusFromStatusT(status);
 }
 
+binder::Status SurfaceComposerAIDL::setActivePictureListener(
+        const sp<gui::IActivePictureListener>& listener) {
+    status_t status = checkObservePictureProfilesPermission();
+    if (status == OK) {
+        mFlinger->setActivePictureListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
 binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) {
     status_t status = checkAccessPermission();
     if (status == OK) {
@@ -9263,6 +9357,17 @@
     return OK;
 }
 
+status_t SurfaceComposerAIDL::checkObservePictureProfilesPermission() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(sObservePictureProfiles, pid, uid)) {
+        ALOGE("Permission Denial: can't manage picture profiles pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+    return OK;
+}
+
 void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
     static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7e9d5b8..211f374 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -24,9 +24,11 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/thread_annotations.h>
+#include <android/gui/ActivePicture.h>
 #include <android/gui/BnSurfaceComposer.h>
 #include <android/gui/DisplayStatInfo.h>
 #include <android/gui/DisplayState.h>
+#include <android/gui/IActivePictureListener.h>
 #include <android/gui/IJankListener.h>
 #include <android/gui/ISurfaceComposerClient.h>
 #include <common/trace.h>
@@ -67,6 +69,8 @@
 #include <ui/FenceResult.h>
 
 #include <common/FlagManager.h>
+#include "ActivePictureUpdater.h"
+#include "BackgroundExecutor.h"
 #include "Display/DisplayModeController.h"
 #include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
@@ -93,6 +97,7 @@
 #include "TransactionState.h"
 #include "Utils/OnceFuture.h"
 
+#include <algorithm>
 #include <atomic>
 #include <cstdint>
 #include <functional>
@@ -591,6 +596,7 @@
     status_t getHdrOutputConversionSupport(bool* outSupport) const;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
+    status_t getMaxLayerPictureProfiles(const sp<IBinder>& displayToken, int32_t* outMaxProfiles);
     void setPowerMode(const sp<IBinder>& displayToken, int mode);
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
                               const std::vector<ui::Hdr>& hdrTypes);
@@ -660,6 +666,8 @@
 
     void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
 
+    void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
     // IBinder::DeathRecipient overrides:
     void binderDied(const wp<IBinder>& who) override;
 
@@ -1377,6 +1385,10 @@
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
 
+    sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
+    bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
+    ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
     // Must only be accessed on the main thread.
@@ -1522,6 +1534,8 @@
     binder::Status getHdrOutputConversionSupport(bool* outSupport) override;
     binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override;
     binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
+    binder::Status getMaxLayerPictureProfiles(const sp<IBinder>& display,
+                                              int32_t* outMaxProfiles) override;
     binder::Status captureDisplay(const DisplayCaptureArgs&,
                                   const sp<IScreenCaptureListener>&) override;
     binder::Status captureDisplayById(int64_t, const CaptureArgs&,
@@ -1610,12 +1624,15 @@
     binder::Status flushJankData(int32_t layerId) override;
     binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
                                       int64_t afterVsync) override;
+    binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+    binder::Status clearActivePictureListener();
 
 private:
     static const constexpr bool kUsePermissionCache = true;
     status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
     status_t checkControlDisplayBrightnessPermission();
     status_t checkReadFrameBufferPermission();
+    status_t checkObservePictureProfilesPermission();
     static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
                                               gui::DynamicDisplayInfo*& outInfo);
 
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b56ee01..659a919 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -113,6 +113,7 @@
     /// Trunk stable server flags ///
     DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
     DUMP_SERVER_FLAG(adpf_gpu_sf);
+    DUMP_SERVER_FLAG(adpf_native_session_manager);
     DUMP_SERVER_FLAG(adpf_use_fmq_channel);
 
     /// Trunk stable readonly flags ///
@@ -160,6 +161,7 @@
     DUMP_READ_ONLY_FLAG(connected_display_hdr);
     DUMP_READ_ONLY_FLAG(deprecate_frame_tracker);
     DUMP_READ_ONLY_FLAG(skip_invisible_windows_in_input);
+    DUMP_READ_ONLY_FLAG(begone_bright_hlg);
 
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
@@ -268,10 +270,12 @@
 FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, "");
 FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, "");
 FLAG_MANAGER_READ_ONLY_FLAG(skip_invisible_windows_in_input, "");
+FLAG_MANAGER_READ_ONLY_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
 FLAG_MANAGER_SERVER_FLAG(adpf_gpu_sf, "")
+FLAG_MANAGER_SERVER_FLAG(adpf_native_session_manager, "");
 
 /// Trunk stable server flags from outside SurfaceFlinger ///
 FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 9086537..d46947f 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -51,6 +51,7 @@
     bool refresh_rate_overlay_on_external_display() const;
     bool adpf_gpu_sf() const;
     bool adpf_use_fmq_channel() const;
+    bool adpf_native_session_manager() const;
     bool adpf_use_fmq_channel_fixed() const;
 
     /// Trunk stable readonly flags ///
@@ -98,6 +99,7 @@
     bool connected_display_hdr() const;
     bool deprecate_frame_tracker() const;
     bool skip_invisible_windows_in_input() const;
+    bool begone_bright_hlg() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d4250bc..34a935a 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -19,6 +19,13 @@
 } # adpf_gpu_sf
 
 flag {
+  name: "adpf_native_session_manager"
+  namespace: "game"
+  description: "Controls ADPF SessionManager being enabled in SF"
+  bug: "367803904"
+} # adpf_sessionmanager
+
+flag {
   name: "arr_setframerate_api"
   namespace: "core_graphics"
   description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16"
@@ -35,6 +42,14 @@
 } # arr_surfacecontrol_setframerate_api
 
 flag {
+  name: "begone_bright_hlg"
+  namespace: "core_graphics"
+  description: "Caps HLG brightness relative to SDR"
+  bug: "362510107"
+  is_fixed_read_only: true
+} # begone_bright_hlg
+
+flag {
   name: "ce_fence_promise"
   namespace: "window_surfaces"
   description: "Moves logic for buffer release fences into LayerFE"
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
new file mode 100644
index 0000000..b926d2f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureUpdater.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+class TestableLayerFE : public LayerFE {
+public:
+    TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+        mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+    }
+
+    LayerSnapshot& snapshot;
+};
+
+class ActivePictureUpdaterTest : public testing::Test {
+protected:
+    SurfaceFlinger* flinger() {
+        if (!mFlingerSetup) {
+            mFlinger.setupMockScheduler();
+            mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+            mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+            mFlingerSetup = true;
+        }
+        return mFlinger.flinger();
+    }
+
+private:
+    TestableSurfaceFlinger mFlinger;
+    bool mFlingerSetup = false;
+};
+
+// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
+// Parcelable, which has a virtual destructor.
+auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
+    std::vector<ActivePicture> activePictures;
+    for (auto tuple : tuples) {
+        ActivePicture ap;
+        ap.layerId = std::get<0>(tuple);
+        ap.ownerUid = std::get<1>(tuple);
+        ap.pictureProfileId = std::get<2>(tuple);
+        activePictures.push_back(ap);
+    }
+    return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+    *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
index 5413bae..72d1351 100644
--- a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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 <gtest/gtest.h>
 #include <condition_variable>
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 45f86fa..59f0966 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -19,6 +19,8 @@
 #include <gmock/gmock.h>
 #include <optional>
 
+#include "Layer.h"
+
 namespace android::mock {
 
 class MockLayer : public Layer {
@@ -26,8 +28,11 @@
     MockLayer(SurfaceFlinger* flinger, std::string name)
           : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
 
-    MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
+    MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> id)
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, id)) {}
+
+    MockLayer(SurfaceFlinger* flinger, std::optional<uint32_t> id)
+          : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 0, {}, id)) {}
 
     explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
 
diff --git a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
index fba4cd8..891f507 100644
--- a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
@@ -54,6 +54,8 @@
     MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel,
                 (int tgid, int uid), (override));
     MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override));
+    MOCK_METHOD(HalResult<aidl::android::hardware::power::SupportInfo>, getSupportInfo, (),
+                (override));
 };
 
 } // namespace android::adpf::mock
\ No newline at end of file