Merge "jpegrecoverymap: Return ICCProfile data from JPEG/R file"
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 62c3ae1..da97c3e 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -51,33 +51,24 @@
         static const size_t MAX_DEGREE = 4;
 
         // Estimator time base.
-        nsecs_t time;
+        nsecs_t time = 0;
 
         // Polynomial coefficients describing motion.
-        float coeff[MAX_DEGREE + 1];
+        std::array<float, MAX_DEGREE + 1> coeff{};
 
         // Polynomial degree (number of coefficients), or zero if no information is
         // available.
-        uint32_t degree;
+        uint32_t degree = 0;
 
         // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
-        float confidence;
-
-        inline void clear() {
-            time = 0;
-            degree = 0;
-            confidence = 0;
-            for (size_t i = 0; i <= MAX_DEGREE; i++) {
-                coeff[i] = 0;
-            }
-        }
+        float confidence = 0;
     };
 
     /*
      * Contains all available velocity data from a VelocityTracker.
      */
     struct ComputedVelocity {
-        inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const {
+        inline std::optional<float> getVelocity(int32_t axis, int32_t id) const {
             const auto& axisVelocities = mVelocities.find(axis);
             if (axisVelocities == mVelocities.end()) {
                 return {};
@@ -91,7 +82,7 @@
             return axisIdVelocity->second;
         }
 
-        inline void addVelocity(int32_t axis, uint32_t id, float velocity) {
+        inline void addVelocity(int32_t axis, int32_t id, float velocity) {
             mVelocities[axis][id] = velocity;
         }
 
@@ -112,19 +103,13 @@
     // Resets the velocity tracker state.
     void clear();
 
-    // Resets the velocity tracker state for specific pointers.
+    // Resets the velocity tracker state for a specific pointer.
     // Call this method when some pointers have changed and may be reusing
     // an id that was assigned to a different pointer earlier.
-    void clearPointers(BitSet32 idBits);
+    void clearPointer(int32_t pointerId);
 
-    // Adds movement information for a set of pointers.
-    // The idBits bitfield specifies the pointer ids of the pointers whose data points
-    // are included in the movement.
-    // The positions map contains a mapping of an axis to positions array.
-    // The positions arrays contain information for each pointer in order by increasing id.
-    // Each array's size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::map<int32_t, std::vector<float>>& positions);
+    // Adds movement information for a pointer for a specific axis
+    void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
@@ -132,7 +117,7 @@
     // Returns the velocity of the specified pointer id and axis in position units per second.
     // Returns empty optional if there is insufficient movement information for the pointer, or if
     // the given axis is not supported for velocity tracking.
-    std::optional<float> getVelocity(int32_t axis, uint32_t id) const;
+    std::optional<float> getVelocity(int32_t axis, int32_t pointerId) const;
 
     // Returns a ComputedVelocity instance with all available velocity data, using the given units
     // (reference: units == 1 means "per millisecond"), and clamping each velocity between
@@ -142,15 +127,15 @@
     // Gets an estimator for the recent movements of the specified pointer id for the given axis.
     // Returns false and clears the estimator if there is no information available
     // about the pointer.
-    bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const;
+    std::optional<Estimator> getEstimator(int32_t axis, int32_t pointerId) const;
 
     // Gets the active pointer id, or -1 if none.
-    inline int32_t getActivePointerId() const { return mActivePointerId; }
+    inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); }
 
 private:
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
-    int32_t mActivePointerId;
+    std::optional<int32_t> mActivePointerId;
 
     // An override strategy passed in the constructor to be used for all axes.
     // This strategy will apply to all axes, unless the default strategy is specified here.
@@ -182,10 +167,9 @@
 public:
     virtual ~VelocityTrackerStrategy() { }
 
-    virtual void clearPointers(BitSet32 idBits) = 0;
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                             const std::vector<float>& positions) = 0;
-    virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
+    virtual void clearPointer(int32_t pointerId) = 0;
+    virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0;
+    virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0;
 };
 
 
@@ -194,29 +178,28 @@
  */
 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
 public:
-    enum Weighting {
+    enum class Weighting {
         // No weights applied.  All data points are equally reliable.
-        WEIGHTING_NONE,
+        NONE,
 
         // Weight by time delta.  Data points clustered together are weighted less.
-        WEIGHTING_DELTA,
+        DELTA,
 
         // Weight such that points within a certain horizon are weighed more than those
         // outside of that horizon.
-        WEIGHTING_CENTRAL,
+        CENTRAL,
 
         // Weight such that points older than a certain amount are weighed less.
-        WEIGHTING_RECENT,
+        RECENT,
     };
 
     // Degree must be no greater than Estimator::MAX_DEGREE.
-    LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE);
-    virtual ~LeastSquaresVelocityTrackerStrategy();
+    LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE);
+    ~LeastSquaresVelocityTrackerStrategy() override;
 
-    virtual void clearPointers(BitSet32 idBits);
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
-    virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
+    void clearPointer(int32_t pointerId) override;
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -229,18 +212,15 @@
 
     struct Movement {
         nsecs_t eventTime;
-        BitSet32 idBits;
-        float positions[MAX_POINTERS];
-
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        float position;
     };
 
-    float chooseWeight(uint32_t index) const;
+    float chooseWeight(int32_t pointerId, uint32_t index) const;
 
     const uint32_t mDegree;
     const Weighting mWeighting;
-    uint32_t mIndex;
-    Movement mMovements[HISTORY_SIZE];
+    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
+    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
 
@@ -251,12 +231,11 @@
 public:
     // Degree must be 1 or 2.
     IntegratingVelocityTrackerStrategy(uint32_t degree);
-    ~IntegratingVelocityTrackerStrategy();
+    ~IntegratingVelocityTrackerStrategy() override;
 
-    virtual void clearPointers(BitSet32 idBits);
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
-    virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
+    void clearPointer(int32_t pointerId) override;
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override;
+    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
 
 private:
     // Current state estimate for a particular pointer.
@@ -283,12 +262,11 @@
 class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy {
 public:
     LegacyVelocityTrackerStrategy();
-    virtual ~LegacyVelocityTrackerStrategy();
+    ~LegacyVelocityTrackerStrategy() override;
 
-    virtual void clearPointers(BitSet32 idBits);
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
-    virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
+    void clearPointer(int32_t pointerId) override;
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
 
 private:
     // Oldest sample to consider when calculating the velocity.
@@ -302,25 +280,21 @@
 
     struct Movement {
         nsecs_t eventTime;
-        BitSet32 idBits;
-        float positions[MAX_POINTERS];
-
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        float position;
     };
 
-    uint32_t mIndex;
-    Movement mMovements[HISTORY_SIZE];
+    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
+    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
 class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
 public:
     ImpulseVelocityTrackerStrategy(bool deltaValues);
-    virtual ~ImpulseVelocityTrackerStrategy();
+    ~ImpulseVelocityTrackerStrategy() override;
 
-    virtual void clearPointers(BitSet32 idBits);
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
-    virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
+    void clearPointer(int32_t pointerId) override;
+    void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override;
+    std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override;
 
 private:
     // Sample horizon.
@@ -333,10 +307,7 @@
 
     struct Movement {
         nsecs_t eventTime;
-        BitSet32 idBits;
-        float positions[MAX_POINTERS];
-
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        float position;
     };
 
     // Whether or not the input movement values for the strategy come in the form of delta values.
@@ -344,8 +315,8 @@
     // velocity calculation.
     const bool mDeltaValues;
 
-    size_t mIndex;
-    Movement mMovements[HISTORY_SIZE];
+    std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex;
+    std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 8cc8105..dc572ac 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,6 +230,7 @@
         "android.graphicsenv.IGpuService",
         "android.gui.IConsumerListener",
         "android.gui.IGraphicBufferConsumer",
+        "android.gui.ITransactionComposerListener",
         "android.gui.SensorEventConnection",
         "android.gui.SensorServer",
         "android.hardware.ICamera",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a0e75ff..a77ca04 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,7 +42,6 @@
 
 namespace android {
 
-using gui::CallbackId;
 using gui::DisplayCaptureArgs;
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 23d7d50..2b25b61 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -21,11 +21,22 @@
 #include <optional>
 
 #include <gui/ISurfaceComposer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
-#include <gui/ListenerStats.h>
 #include <private/gui/ParcelUtils.h>
 
-namespace android::gui {
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
+    ON_RELEASE_BUFFER,
+    ON_TRANSACTION_QUEUE_STALLED,
+    LAST = ON_TRANSACTION_QUEUE_STALLED,
+};
+
+} // Anonymous namespace
 
 status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
     status_t err = output->writeUint64(frameNumber);
@@ -263,6 +274,60 @@
     return listenerStats;
 }
 
+class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
+public:
+    explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
+          : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
+    }
+
+    ~BpTransactionCompletedListener() override;
+
+    void onTransactionCompleted(ListenerStats stats) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+                                                                  stats);
+    }
+
+    void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
+                         uint32_t currentMaxAcquiredBufferCount) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
+                                                           releaseFence,
+                                                           currentMaxAcquiredBufferCount);
+    }
+
+    void onTransactionQueueStalled(const String8& reason) override {
+        callRemoteAsync<
+                decltype(&ITransactionCompletedListener::
+                                 onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
+                                                             reason);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
+
+IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
+
+status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
+                                                    Parcel* reply, uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_TRANSACTION_COMPLETED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionCompleted);
+        case Tag::ON_RELEASE_BUFFER:
+            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
+        case Tag::ON_TRANSACTION_QUEUE_STALLED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionQueueStalled);
+    }
+}
+
 ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
     std::vector<CallbackId> filteredCallbackIds;
     for (const auto& callbackId : callbackIds) {
@@ -301,4 +366,4 @@
 
 const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
 
-}; // namespace android::gui
+}; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0d1a69b..59b62fe 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -51,7 +51,6 @@
 
 namespace android {
 
-using gui::CallbackId;
 using gui::FocusRequest;
 using gui::WindowInfoHandle;
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a2ed8aa..92125ea 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -314,8 +314,7 @@
     }
 }
 
-binder::Status TransactionCompletedListener::onTransactionCompleted(
-        const ListenerStats& listenerStats) {
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
     std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
     {
@@ -455,10 +454,9 @@
             }
         }
     }
-    return binder::Status::ok();
 }
 
-binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
+void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
     std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
     {
         std::scoped_lock<std::mutex> lock(mMutex);
@@ -467,7 +465,6 @@
     for (auto const& it : callbackCopy) {
         it.second(reason.c_str());
     }
-    return binder::Status::ok();
 }
 
 void TransactionCompletedListener::addQueueStallListener(
@@ -481,12 +478,9 @@
     mQueueStallListeners.erase(id);
 }
 
-binder::Status TransactionCompletedListener::onReleaseBuffer(
-        const ReleaseCallbackId& callbackId,
-        const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
-        int32_t currentMaxAcquiredBufferCount) {
-    sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
-                                          : Fence::NO_FENCE);
+void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
+                                                   sp<Fence> releaseFence,
+                                                   uint32_t currentMaxAcquiredBufferCount) {
     ReleaseBufferCallback callback;
     {
         std::scoped_lock<std::mutex> lock(mMutex);
@@ -495,14 +489,13 @@
     if (!callback) {
         ALOGE("Could not call release buffer callback, buffer not found %s",
               callbackId.to_string().c_str());
-        return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
+        return;
     }
     std::optional<uint32_t> optionalMaxAcquiredBufferCount =
-            static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
+            currentMaxAcquiredBufferCount == UINT_MAX
             ? std::nullopt
             : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
     callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
-    return binder::Status::ok();
 }
 
 ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -832,11 +825,7 @@
                 ->mReleaseCallbackThread
                 .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
     } else {
-        std::optional<os::ParcelFileDescriptor> fenceFd;
-        if (fence != Fence::NO_FENCE) {
-            fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
-        }
-        listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
+        listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
     }
 }
 
@@ -2496,6 +2485,20 @@
     return statusTFromBinderStatus(status);
 }
 
+status_t SurfaceComposerClient::getHdrConversionCapabilities(
+        std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) {
+    binder::Status status = ComposerServiceAIDL::getComposerService()->getHdrConversionCapabilities(
+            hdrConversionCapabilities);
+    return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::setHdrConversionStrategy(
+        gui::HdrConversionStrategy hdrConversionStrategy) {
+    binder::Status status = ComposerServiceAIDL::getComposerService()->setHdrConversionStrategy(
+            hdrConversionStrategy);
+    return statusTFromBinderStatus(status);
+}
+
 status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
     binder::Status status =
             ComposerServiceAIDL::getComposerService()->setOverrideFrameRate(uid, frameRate);
@@ -2866,11 +2869,7 @@
 
         while (!callbackInfos.empty()) {
             auto [callbackId, releaseFence] = callbackInfos.front();
-            std::optional<os::ParcelFileDescriptor> fenceFd;
-            if (releaseFence != Fence::NO_FENCE) {
-                fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
-            }
-            listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
+            listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
             callbackInfos.pop();
         }
 
diff --git a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl b/libs/gui/aidl/android/gui/HdrConversionCapability.aidl
similarity index 75%
rename from libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
rename to libs/gui/aidl/android/gui/HdrConversionCapability.aidl
index c86de34..1bcfd38 100644
--- a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
+++ b/libs/gui/aidl/android/gui/HdrConversionCapability.aidl
@@ -16,4 +16,10 @@
 
 package android.gui;
 
-parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
+// TODO(b/265277221): use android.hardware.graphics.common.HdrConversionCapability.aidl
+/** @hide */
+parcelable HdrConversionCapability {
+    int sourceType;
+    int outputType;
+    boolean addsLatency;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl b/libs/gui/aidl/android/gui/HdrConversionStrategy.aidl
similarity index 74%
copy from libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
copy to libs/gui/aidl/android/gui/HdrConversionStrategy.aidl
index c86de34..1be74b4 100644
--- a/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
+++ b/libs/gui/aidl/android/gui/HdrConversionStrategy.aidl
@@ -16,4 +16,10 @@
 
 package android.gui;
 
-parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
+// TODO(b/265277221): use android.hardware.graphics.common.HdrConversionStrategy.aidl
+/** @hide */
+union HdrConversionStrategy {
+    boolean passthrough = true;
+    int[] autoAllowedHdrTypes;
+    int forceHdrConversion;
+}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index a0b613c..c08a7c6 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -30,6 +30,8 @@
 import android.gui.DynamicDisplayInfo;
 import android.gui.FrameEvent;
 import android.gui.FrameStats;
+import android.gui.HdrConversionCapability;
+import android.gui.HdrConversionStrategy;
 import android.gui.IDisplayEventConnection;
 import android.gui.IFpsListener;
 import android.gui.IHdrLayerInfoListener;
@@ -162,6 +164,26 @@
     boolean getBootDisplayModeSupport();
 
     /**
+     * Gets the HDR conversion capabilities of the device. The conversion capability defines whether
+     * conversion from sourceType to outputType is possible (with or without latency).
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+     List<HdrConversionCapability> getHdrConversionCapabilities();
+
+     /**
+      * Sets the HDR conversion strategy of the device.
+      *
+      * Requires the ACCESS_SURFACE_FLINGER permission.
+      */
+     void setHdrConversionStrategy(in HdrConversionStrategy hdrConversionStrategy);
+
+     /**
+      * Gets whether HDR output conversion operations are supported on the device.
+      */
+     boolean getHdrOutputConversionSupport();
+
+    /**
      * Switches Auto Low Latency Mode on/off on the connected display, if it is
      * available. This should only be called if the display supports Auto Low
      * Latency Mode as reported in #getDynamicDisplayInfo.
diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
deleted file mode 100644
index dde4d38..0000000
--- a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.gui;
-
-import android.gui.ListenerStats;
-import android.gui.ReleaseCallbackId;
-
-/** @hide */
-oneway interface ITransactionCompletedListener {
-   void onTransactionCompleted(in ListenerStats stats);
-
-   void onReleaseBuffer(in ReleaseCallbackId callbackId,
-                        in @nullable ParcelFileDescriptor releaseFenceFd,
-                        int currentMaxAcquiredBufferCount);
-
-   void onTransactionQueueStalled(@utf8InCpp String name);
-}
diff --git a/libs/gui/aidl/android/gui/ListenerStats.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
deleted file mode 100644
index 63248b2..0000000
--- a/libs/gui/aidl/android/gui/ListenerStats.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.gui;
-
-parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 8810e4e..685bd92 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -91,6 +91,11 @@
     MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override));
     MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override));
     MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override));
+    MOCK_METHOD(binder::Status, getHdrConversionCapabilities,
+                (std::vector<gui::HdrConversionCapability>*), (override));
+    MOCK_METHOD(binder::Status, setHdrConversionStrategy, (const gui::HdrConversionStrategy&),
+                (override));
+    MOCK_METHOD(binder::Status, getHdrOutputConversionSupport, (bool*), (override));
     MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override));
     MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override));
     MOCK_METHOD(binder::Status, captureDisplay,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 06a246e..045cc2a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,11 +23,11 @@
 #include <android/gui/IHdrLayerInfoListener.h>
 #include <android/gui/IRegionSamplingListener.h>
 #include <android/gui/IScreenCaptureListener.h>
-#include <android/gui/ITransactionCompletedListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
 #include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/SpHash.h>
 #include <math/vec4.h>
 #include <stdint.h>
@@ -66,7 +66,6 @@
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
 using gui::IScreenCaptureListener;
-using gui::ListenerCallbacks;
 using gui::SpHash;
 
 namespace gui {
diff --git a/libs/gui/include/gui/ListenerStats.h b/libs/gui/include/gui/ITransactionCompletedListener.h
similarity index 81%
rename from libs/gui/include/gui/ListenerStats.h
rename to libs/gui/include/gui/ITransactionCompletedListener.h
index 3a12802..453e8f3 100644
--- a/libs/gui/include/gui/ListenerStats.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -24,8 +24,6 @@
 #include <binder/SafeInterface.h>
 
 #include <gui/FrameTimestamps.h>
-#include <gui/ReleaseCallbackId.h>
-
 #include <ui/Fence.h>
 #include <utils/Timers.h>
 
@@ -34,7 +32,10 @@
 #include <unordered_set>
 #include <variant>
 
-namespace android::gui {
+namespace android {
+
+class ITransactionCompletedListener;
+class ListenerCallbacks;
 
 class CallbackId : public Parcelable {
 public:
@@ -53,6 +54,30 @@
     std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
 };
 
+class ReleaseCallbackId : public Parcelable {
+public:
+    static const ReleaseCallbackId INVALID_ID;
+
+    uint64_t bufferId;
+    uint64_t framenumber;
+    ReleaseCallbackId() {}
+    ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+          : bufferId(bufferId), framenumber(framenumber) {}
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    bool operator==(const ReleaseCallbackId& rhs) const {
+        return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+    }
+    bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+    std::string to_string() const {
+        if (*this == INVALID_ID) return "INVALID_ID";
+
+        return "bufferId:" + std::to_string(bufferId) +
+                " framenumber:" + std::to_string(framenumber);
+    }
+};
+
 struct ReleaseBufferCallbackIdHash {
     std::size_t operator()(const ReleaseCallbackId& key) const {
         return std::hash<uint64_t>()(key.bufferId);
@@ -161,6 +186,27 @@
     std::vector<TransactionStats> transactionStats;
 };
 
+class ITransactionCompletedListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(TransactionCompletedListener)
+
+    virtual void onTransactionCompleted(ListenerStats stats) = 0;
+
+    virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
+                                 uint32_t currentMaxAcquiredBufferCount) = 0;
+
+    virtual void onTransactionQueueStalled(const String8& name) = 0;
+};
+
+class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
+public:
+    BnTransactionCompletedListener()
+          : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
 class ListenerCallbacks {
 public:
     ListenerCallbacks(const sp<IBinder>& listener,
@@ -222,4 +268,4 @@
     }
 };
 
-} // namespace android::gui
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index c5fdf82..45a84f6 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <android/gui/ITransactionCompletedListener.h>
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <android/native_window.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
 #include <android/gui/DropInputMode.h>
@@ -35,7 +35,6 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerCaptureArgs.h>
 #include <gui/LayerMetadata.h>
-#include <gui/ReleaseCallbackId.h>
 #include <gui/SpHash.h>
 #include <gui/SurfaceControl.h>
 #include <gui/WindowInfo.h>
@@ -57,9 +56,6 @@
 using gui::ISurfaceComposerClient;
 using gui::LayerMetadata;
 
-using gui::ITransactionCompletedListener;
-using gui::ReleaseCallbackId;
-
 struct client_cache_t {
     wp<IBinder> token = nullptr;
     uint64_t id;
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
deleted file mode 100644
index 142ee5a..0000000
--- a/libs/gui/include/gui/ReleaseCallbackId.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-
-#include <cstdint>
-
-namespace android::gui {
-
-class ReleaseCallbackId : public Parcelable {
-public:
-    static const ReleaseCallbackId INVALID_ID;
-
-    uint64_t bufferId;
-    uint64_t framenumber;
-    ReleaseCallbackId() {}
-    ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
-          : bufferId(bufferId), framenumber(framenumber) {}
-    status_t writeToParcel(Parcel* output) const override;
-    status_t readFromParcel(const Parcel* input) override;
-
-    bool operator==(const ReleaseCallbackId& rhs) const {
-        return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
-    }
-    bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
-    std::string to_string() const {
-        if (*this == INVALID_ID) return "INVALID_ID";
-
-        return "bufferId:" + std::to_string(bufferId) +
-                " framenumber:" + std::to_string(framenumber);
-    }
-};
-
-} // namespace android::gui
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index cc459c5..2458a40 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,13 +42,10 @@
 
 #include <android/gui/ISurfaceComposerClient.h>
 
-#include <android/gui/BnTransactionCompletedListener.h>
-
 #include <gui/CpuConsumer.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
-#include <gui/ListenerStats.h>
-#include <gui/ReleaseCallbackId.h>
 #include <gui/SurfaceControl.h>
 #include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
@@ -62,21 +59,11 @@
 class ITunnelModeEnabledListener;
 class Region;
 
-using gui::BnTransactionCompletedListener;
-using gui::CallbackId;
-using gui::CallbackIdHash;
 using gui::DisplayCaptureArgs;
-using gui::FrameEventHistoryStats;
 using gui::IRegionSamplingListener;
 using gui::ISurfaceComposerClient;
-using gui::ITransactionCompletedListener;
-using gui::JankData;
 using gui::LayerCaptureArgs;
 using gui::LayerMetadata;
-using gui::ListenerStats;
-using gui::ReleaseBufferCallbackIdHash;
-using gui::ReleaseCallbackId;
-using gui::SurfaceStats;
 
 struct SurfaceControlStats {
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -200,6 +187,11 @@
     // Clears the user-preferred display mode
     static status_t clearBootDisplayMode(const sp<IBinder>& display);
 
+    // Gets the HDR conversion capabilities of the device
+    static status_t getHdrConversionCapabilities(std::vector<gui::HdrConversionCapability>*);
+    // Sets the HDR conversion strategy for the device
+    static status_t setHdrConversionStrategy(gui::HdrConversionStrategy hdrConversionStrategy);
+
     // Sets the frame rate of a particular app (uid). This is currently called
     // by GameManager.
     static status_t setOverrideFrameRate(uid_t uid, float frameRate);
@@ -841,17 +833,17 @@
     void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
 
     // BnTransactionCompletedListener overrides
-    binder::Status onTransactionCompleted(const ListenerStats& stats) override;
-    binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
-                                   const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
-                                   int32_t currentMaxAcquiredBufferCount) override;
-    binder::Status onTransactionQueueStalled(const std::string& reason) override;
+    void onTransactionCompleted(ListenerStats stats) override;
+    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
+                         uint32_t currentMaxAcquiredBufferCount) override;
 
     void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
 
     // For Testing Only
     static void setInstance(const sp<TransactionCompletedListener>&);
 
+    void onTransactionQueueStalled(const String8& reason) override;
+
 private:
     ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
     static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 55242df..3014804 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -819,6 +819,20 @@
         return binder::Status::ok();
     }
 
+    binder::Status getHdrConversionCapabilities(
+            std::vector<gui::HdrConversionCapability>*) override {
+        return binder::Status::ok();
+    }
+
+    binder::Status setHdrConversionStrategy(
+            const gui::HdrConversionStrategy& /*hdrConversionStrategy*/) override {
+        return binder::Status::ok();
+    }
+
+    binder::Status getHdrOutputConversionSupport(bool* /*outSupport*/) override {
+        return binder::Status::ok();
+    }
+
     binder::Status setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {
         return binder::Status::ok();
     }
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 5c008b1..5720099 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -70,9 +70,10 @@
         if (deltaY) {
             mRawPositionY += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)),
-                                     {{AMOTION_EVENT_AXIS_X, {mRawPositionX}},
-                                      {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}});
+        mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X,
+                                     mRawPositionX);
+        mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y,
+                                     mRawPositionY);
 
         std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
         std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 19b4684..1cd782d 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -23,6 +23,7 @@
 #include <optional>
 
 #include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
 #include <input/VelocityTracker.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
@@ -142,10 +143,7 @@
 // --- VelocityTracker ---
 
 VelocityTracker::VelocityTracker(const Strategy strategy)
-      : mLastEventTime(0),
-        mCurrentPointerIdBits(0),
-        mActivePointerId(-1),
-        mOverrideStrategy(strategy) {}
+      : mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {}
 
 VelocityTracker::~VelocityTracker() {
 }
@@ -191,17 +189,17 @@
             return std::make_unique<
                     LeastSquaresVelocityTrackerStrategy>(2,
                                                          LeastSquaresVelocityTrackerStrategy::
-                                                                 WEIGHTING_DELTA);
+                                                                 Weighting::DELTA);
         case VelocityTracker::Strategy::WLSQ2_CENTRAL:
             return std::make_unique<
                     LeastSquaresVelocityTrackerStrategy>(2,
                                                          LeastSquaresVelocityTrackerStrategy::
-                                                                 WEIGHTING_CENTRAL);
+                                                                 Weighting::CENTRAL);
         case VelocityTracker::Strategy::WLSQ2_RECENT:
             return std::make_unique<
                     LeastSquaresVelocityTrackerStrategy>(2,
                                                          LeastSquaresVelocityTrackerStrategy::
-                                                                 WEIGHTING_RECENT);
+                                                                 Weighting::RECENT);
 
         case VelocityTracker::Strategy::INT1:
             return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
@@ -220,30 +218,30 @@
 
 void VelocityTracker::clear() {
     mCurrentPointerIdBits.clear();
-    mActivePointerId = -1;
+    mActivePointerId = std::nullopt;
     mConfiguredStrategies.clear();
 }
 
-void VelocityTracker::clearPointers(BitSet32 idBits) {
-    BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value);
-    mCurrentPointerIdBits = remainingIdBits;
+void VelocityTracker::clearPointer(int32_t pointerId) {
+    mCurrentPointerIdBits.clearBit(pointerId);
 
-    if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
-        mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
+    if (mActivePointerId && *mActivePointerId == pointerId) {
+        // The active pointer id is being removed. Mark it invalid and try to find a new one
+        // from the remaining pointers.
+        mActivePointerId = std::nullopt;
+        if (!mCurrentPointerIdBits.isEmpty()) {
+            mActivePointerId = mCurrentPointerIdBits.firstMarkedBit();
+        }
     }
 
     for (const auto& [_, strategy] : mConfiguredStrategies) {
-        strategy->clearPointers(idBits);
+        strategy->clearPointer(pointerId);
     }
 }
 
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                  const std::map<int32_t /*axis*/, std::vector<float>>& positions) {
-    while (idBits.count() > MAX_POINTERS) {
-        idBits.clearLastMarkedBit();
-    }
-
-    if ((mCurrentPointerIdBits.value & idBits.value) &&
+void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis,
+                                  float position) {
+    if (mCurrentPointerIdBits.hasBit(pointerId) &&
         std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) {
         ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.",
                  toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str());
@@ -254,39 +252,28 @@
     }
     mLastEventTime = eventTime;
 
-    mCurrentPointerIdBits = idBits;
-    if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
-        mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
+    mCurrentPointerIdBits.markBit(pointerId);
+    if (!mActivePointerId) {
+        // Let this be the new active pointer if no active pointer is currently set
+        mActivePointerId = pointerId;
     }
 
-    for (const auto& [axis, positionValues] : positions) {
-        LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(),
-                            "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
-                            idBits.count(), positionValues.size());
-        if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) {
-            configureStrategy(axis);
-        }
-        mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues);
+    if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) {
+        configureStrategy(axis);
     }
+    mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position);
 
     if (DEBUG_VELOCITY) {
-        ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
-              ", idBits=0x%08x, activePointerId=%d",
-              eventTime, idBits.value, mActivePointerId);
-        for (const auto& positionsEntry : positions) {
-            for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
-                uint32_t id = iterBits.firstMarkedBit();
-                uint32_t index = idBits.getIndexOfBit(id);
-                iterBits.clearBit(id);
-                Estimator estimator;
-                getEstimator(positionsEntry.first, id, &estimator);
-                ALOGD("  %d: axis=%d, position=%0.3f, "
-                      "estimator (degree=%d, coeff=%s, confidence=%f)",
-                      id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree),
-                      vectorToString(estimator.coeff, estimator.degree + 1).c_str(),
-                      estimator.confidence);
-            }
-        }
+        ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32
+              ", activePointerId=%s",
+              eventTime, pointerId, toString(mActivePointerId).c_str());
+
+        std::optional<Estimator> estimator = getEstimator(axis, pointerId);
+        ALOGD("  %d: axis=%d, position=%0.3f, "
+              "estimator (degree=%d, coeff=%s, confidence=%f)",
+              pointerId, axis, position, int((*estimator).degree),
+              vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(),
+              (*estimator).confidence);
     }
 }
 
@@ -296,94 +283,73 @@
     int32_t actionMasked = event->getActionMasked();
 
     switch (actionMasked) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_HOVER_ENTER:
-        // Clear all pointers on down before adding the new movement.
-        clear();
-        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
-        break;
-    case AMOTION_EVENT_ACTION_POINTER_DOWN: {
-        // Start a new movement trace for a pointer that just went down.
-        // We do this on down instead of on up because the client may want to query the
-        // final velocity for a pointer that just went up.
-        BitSet32 downIdBits;
-        downIdBits.markBit(event->getPointerId(event->getActionIndex()));
-        clearPointers(downIdBits);
-        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
-        break;
-    }
-    case AMOTION_EVENT_ACTION_MOVE:
-    case AMOTION_EVENT_ACTION_HOVER_MOVE:
-        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
-        break;
-    case AMOTION_EVENT_ACTION_POINTER_UP:
-    case AMOTION_EVENT_ACTION_UP: {
-        std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime);
-        if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) {
-            ALOGD_IF(DEBUG_VELOCITY,
-                     "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.",
-                     toString(delaySinceLastEvent).c_str());
-            // We have not received any movements for too long.  Assume that all pointers
-            // have stopped.
-            for (int32_t axis : PLANAR_AXES) {
-                mConfiguredStrategies.erase(axis);
-            }
+        case AMOTION_EVENT_ACTION_DOWN:
+        case AMOTION_EVENT_ACTION_HOVER_ENTER:
+            // Clear all pointers on down before adding the new movement.
+            clear();
+            axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
+            break;
+        case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+            // Start a new movement trace for a pointer that just went down.
+            // We do this on down instead of on up because the client may want to query the
+            // final velocity for a pointer that just went up.
+            clearPointer(event->getPointerId(event->getActionIndex()));
+            axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
+            break;
         }
-        // These actions because they do not convey any new information about
-        // pointer movement.  We also want to preserve the last known velocity of the pointers.
-        // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
-        // of the pointers that went up.  ACTION_POINTER_UP does include the new position of
-        // pointers that remained down but we will also receive an ACTION_MOVE with this
-        // information if any of them actually moved.  Since we don't know how many pointers
-        // will be going up at once it makes sense to just wait for the following ACTION_MOVE
-        // before adding the movement.
-        return;
-    }
-    case AMOTION_EVENT_ACTION_SCROLL:
-        axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
-        break;
-    default:
-        // Ignore all other actions.
-        return;
-    }
-
-    size_t pointerCount = event->getPointerCount();
-    if (pointerCount > MAX_POINTERS) {
-        pointerCount = MAX_POINTERS;
-    }
-
-    BitSet32 idBits;
-    for (size_t i = 0; i < pointerCount; i++) {
-        idBits.markBit(event->getPointerId(i));
-    }
-
-    uint32_t pointerIndex[MAX_POINTERS];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
-    }
-
-    std::map<int32_t, std::vector<float>> positions;
-    for (int32_t axis : axesToProcess) {
-        positions[axis].resize(pointerCount);
+        case AMOTION_EVENT_ACTION_MOVE:
+        case AMOTION_EVENT_ACTION_HOVER_MOVE:
+            axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
+            break;
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+        case AMOTION_EVENT_ACTION_UP: {
+            std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime);
+            if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) {
+                ALOGD_IF(DEBUG_VELOCITY,
+                         "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.",
+                         toString(delaySinceLastEvent).c_str());
+                // We have not received any movements for too long.  Assume that all pointers
+                // have stopped.
+                for (int32_t axis : PLANAR_AXES) {
+                    mConfiguredStrategies.erase(axis);
+                }
+            }
+            // These actions because they do not convey any new information about
+            // pointer movement.  We also want to preserve the last known velocity of the pointers.
+            // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
+            // of the pointers that went up.  ACTION_POINTER_UP does include the new position of
+            // pointers that remained down but we will also receive an ACTION_MOVE with this
+            // information if any of them actually moved.  Since we don't know how many pointers
+            // will be going up at once it makes sense to just wait for the following ACTION_MOVE
+            // before adding the movement.
+            return;
+        }
+        case AMOTION_EVENT_ACTION_SCROLL:
+            axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
+            break;
+        default:
+            // Ignore all other actions.
+            return;
     }
 
     size_t historySize = event->getHistorySize();
     for (size_t h = 0; h <= historySize; h++) {
         nsecs_t eventTime = event->getHistoricalEventTime(h);
-        for (int32_t axis : axesToProcess) {
-            for (size_t i = 0; i < pointerCount; i++) {
-                positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h);
+        for (size_t i = 0; i < event->getPointerCount(); i++) {
+            // TODO(b/167946721): skip resampled samples
+            const int32_t pointerId = event->getPointerId(i);
+            for (int32_t axis : axesToProcess) {
+                const float position = event->getHistoricalAxisValue(axis, i, h);
+                addMovement(eventTime, pointerId, axis, position);
             }
         }
-        addMovement(eventTime, idBits, positions);
     }
 }
 
-std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const {
-    Estimator estimator;
-    bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1;
-    if (validVelocity) {
-        return estimator.coeff[1];
+std::optional<float> VelocityTracker::getVelocity(int32_t axis, int32_t pointerId) const {
+    std::optional<Estimator> estimator = getEstimator(axis, pointerId);
+    if (estimator && (*estimator).degree >= 1) {
+        return (*estimator).coeff[1];
     }
     return {};
 }
@@ -406,50 +372,55 @@
     return computedVelocity;
 }
 
-bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const {
+std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t axis,
+                                                                        int32_t pointerId) const {
     const auto& it = mConfiguredStrategies.find(axis);
     if (it == mConfiguredStrategies.end()) {
-        return false;
+        return std::nullopt;
     }
-    return it->second->getEstimator(id, outEstimator);
+    return it->second->getEstimator(pointerId);
 }
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree,
                                                                          Weighting weighting)
-      : mDegree(degree), mWeighting(weighting), mIndex(0) {}
+      : mDegree(degree), mWeighting(weighting) {}
 
 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
 }
 
-void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
-    BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
-    mMovements[mIndex].idBits = remainingIdBits;
+void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mIndex.erase(pointerId);
+    mMovements.erase(pointerId);
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                      const std::vector<float>& positions) {
-    if (mMovements[mIndex].eventTime != eventTime) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                      float position) {
+    // If data for this pointer already exists, we have a valid entry at the position of
+    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
+    // to the next position in the circular buffer and write the new Movement there. Otherwise,
+    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
+    // for this pointer and write to the first position.
+    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
+    auto [indexIt, _] = mIndex.insert({pointerId, 0});
+    size_t& index = indexIt->second;
+    if (!inserted && movementIt->second[index].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
         // the new pointer. If the eventtimes for both events are identical, just update the data
         // for this time.
         // We only compare against the last value, as it is likely that addMovement is called
         // in chronological order as events occur.
-        mIndex++;
+        index++;
     }
-    if (mIndex == HISTORY_SIZE) {
-        mIndex = 0;
+    if (index == HISTORY_SIZE) {
+        index = 0;
     }
 
-    Movement& movement = mMovements[mIndex];
+    Movement& movement = movementIt->second[index];
     movement.eventTime = eventTime;
-    movement.idBits = idBits;
-    uint32_t count = idBits.count();
-    for (uint32_t i = 0; i < count; i++) {
-        movement.positions[i] = positions[i];
-    }
+    movement.position = position;
 }
 
 /**
@@ -502,7 +473,9 @@
  * http://en.wikipedia.org/wiki/Gram-Schmidt
  */
 static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
-                              const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
+                              const std::vector<float>& w, uint32_t n,
+                              std::array<float, VelocityTracker::Estimator::MAX_DEGREE + 1>& outB,
+                              float* outDet) {
     const size_t m = x.size();
 
     ALOGD_IF(DEBUG_STRATEGY, "solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
@@ -583,7 +556,7 @@
         outB[i] /= r[i][i];
     }
 
-    ALOGD_IF(DEBUG_STRATEGY, "  - b=%s", vectorToString(outB, n).c_str());
+    ALOGD_IF(DEBUG_STRATEGY, "  - b=%s", vectorToString(outB.data(), n).c_str());
 
     // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
     // SSerr is the residual sum of squares (variance of the error),
@@ -671,37 +644,47 @@
     return std::make_optional(std::array<float, 3>({c, b, a}));
 }
 
-bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->clear();
-
+std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator(
+        int32_t pointerId) const {
+    const auto movementIt = mMovements.find(pointerId);
+    if (movementIt == mMovements.end()) {
+        return std::nullopt; // no data
+    }
     // Iterate over movement samples in reverse time order and collect samples.
     std::vector<float> positions;
     std::vector<float> w;
     std::vector<float> time;
 
-    uint32_t index = mIndex;
-    const Movement& newestMovement = mMovements[mIndex];
+    uint32_t index = mIndex.at(pointerId);
+    const Movement& newestMovement = movementIt->second[index];
     do {
-        const Movement& movement = mMovements[index];
-        if (!movement.idBits.hasBit(id)) {
-            break;
-        }
+        const Movement& movement = movementIt->second[index];
 
         nsecs_t age = newestMovement.eventTime - movement.eventTime;
         if (age > HORIZON) {
             break;
         }
-
-        positions.push_back(movement.getPosition(id));
-        w.push_back(chooseWeight(index));
+        if (movement.eventTime == 0 && index != 0) {
+            // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's
+            // possible that not all entries are valid. We use a time=0 as a signal for those
+            // uninitialized values. If we encounter a time of 0 in a position
+            // that's > 0, it means that we hit the block where the data wasn't initialized.
+            // We still don't know whether the value at index=0, with eventTime=0 is valid.
+            // However, that's only possible when the value is by itself. So there's no hard in
+            // processing it anyways, since the velocity for a single point is zero, and this
+            // situation will only be encountered in artificial circumstances (in tests).
+            // In practice, time will never be 0.
+            break;
+        }
+        positions.push_back(movement.position);
+        w.push_back(chooseWeight(pointerId, index));
         time.push_back(-age * 0.000000001f);
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
     } while (positions.size() < HISTORY_SIZE);
 
     const size_t m = positions.size();
     if (m == 0) {
-        return false; // no data
+        return std::nullopt; // no data
     }
 
     // Calculate a least squares polynomial fit.
@@ -710,112 +693,116 @@
         degree = m - 1;
     }
 
-    if (degree == 2 && mWeighting == WEIGHTING_NONE) {
+    if (degree == 2 && mWeighting == Weighting::NONE) {
         // Optimize unweighted, quadratic polynomial fit
         std::optional<std::array<float, 3>> coeff =
                 solveUnweightedLeastSquaresDeg2(time, positions);
         if (coeff) {
-            outEstimator->time = newestMovement.eventTime;
-            outEstimator->degree = 2;
-            outEstimator->confidence = 1;
-            for (size_t i = 0; i <= outEstimator->degree; i++) {
-                outEstimator->coeff[i] = (*coeff)[i];
+            VelocityTracker::Estimator estimator;
+            estimator.time = newestMovement.eventTime;
+            estimator.degree = 2;
+            estimator.confidence = 1;
+            for (size_t i = 0; i <= estimator.degree; i++) {
+                estimator.coeff[i] = (*coeff)[i];
             }
-            return true;
+            return estimator;
         }
     } else if (degree >= 1) {
         // General case for an Nth degree polynomial fit
         float det;
         uint32_t n = degree + 1;
-        if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) {
-            outEstimator->time = newestMovement.eventTime;
-            outEstimator->degree = degree;
-            outEstimator->confidence = det;
+        VelocityTracker::Estimator estimator;
+        if (solveLeastSquares(time, positions, w, n, estimator.coeff, &det)) {
+            estimator.time = newestMovement.eventTime;
+            estimator.degree = degree;
+            estimator.confidence = det;
 
             ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
-                     int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(),
-                     outEstimator->confidence);
+                     int(estimator.degree), vectorToString(estimator.coeff.data(), n).c_str(),
+                     estimator.confidence);
 
-            return true;
+            return estimator;
         }
     }
 
     // No velocity data available for this pointer, but we do have its current position.
-    outEstimator->coeff[0] = positions[0];
-    outEstimator->time = newestMovement.eventTime;
-    outEstimator->degree = 0;
-    outEstimator->confidence = 1;
-    return true;
+    VelocityTracker::Estimator estimator;
+    estimator.coeff[0] = positions[0];
+    estimator.time = newestMovement.eventTime;
+    estimator.degree = 0;
+    estimator.confidence = 1;
+    return estimator;
 }
 
-float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const {
+float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const {
+    const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId);
     switch (mWeighting) {
-    case WEIGHTING_DELTA: {
-        // Weight points based on how much time elapsed between them and the next
-        // point so that points that "cover" a shorter time span are weighed less.
-        //   delta  0ms: 0.5
-        //   delta 10ms: 1.0
-        if (index == mIndex) {
+        case Weighting::DELTA: {
+            // Weight points based on how much time elapsed between them and the next
+            // point so that points that "cover" a shorter time span are weighed less.
+            //   delta  0ms: 0.5
+            //   delta 10ms: 1.0
+            if (index == mIndex.at(pointerId)) {
+                return 1.0f;
+            }
+            uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
+            float deltaMillis =
+                    (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f;
+            if (deltaMillis < 0) {
+                return 0.5f;
+            }
+            if (deltaMillis < 10) {
+                return 0.5f + deltaMillis * 0.05;
+            }
             return 1.0f;
         }
-        uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
-        float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime)
-                * 0.000001f;
-        if (deltaMillis < 0) {
+
+        case Weighting::CENTRAL: {
+            // Weight points based on their age, weighing very recent and very old points less.
+            //   age  0ms: 0.5
+            //   age 10ms: 1.0
+            //   age 50ms: 1.0
+            //   age 60ms: 0.5
+            float ageMillis =
+                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
+                    0.000001f;
+            if (ageMillis < 0) {
+                return 0.5f;
+            }
+            if (ageMillis < 10) {
+                return 0.5f + ageMillis * 0.05;
+            }
+            if (ageMillis < 50) {
+                return 1.0f;
+            }
+            if (ageMillis < 60) {
+                return 0.5f + (60 - ageMillis) * 0.05;
+            }
             return 0.5f;
         }
-        if (deltaMillis < 10) {
-            return 0.5f + deltaMillis * 0.05;
-        }
-        return 1.0f;
-    }
 
-    case WEIGHTING_CENTRAL: {
-        // Weight points based on their age, weighing very recent and very old points less.
-        //   age  0ms: 0.5
-        //   age 10ms: 1.0
-        //   age 50ms: 1.0
-        //   age 60ms: 0.5
-        float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
-                * 0.000001f;
-        if (ageMillis < 0) {
+        case Weighting::RECENT: {
+            // Weight points based on their age, weighing older points less.
+            //   age   0ms: 1.0
+            //   age  50ms: 1.0
+            //   age 100ms: 0.5
+            float ageMillis =
+                    (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) *
+                    0.000001f;
+            if (ageMillis < 50) {
+                return 1.0f;
+            }
+            if (ageMillis < 100) {
+                return 0.5f + (100 - ageMillis) * 0.01f;
+            }
             return 0.5f;
         }
-        if (ageMillis < 10) {
-            return 0.5f + ageMillis * 0.05;
-        }
-        if (ageMillis < 50) {
-            return 1.0f;
-        }
-        if (ageMillis < 60) {
-            return 0.5f + (60 - ageMillis) * 0.05;
-        }
-        return 0.5f;
-    }
 
-    case WEIGHTING_RECENT: {
-        // Weight points based on their age, weighing older points less.
-        //   age   0ms: 1.0
-        //   age  50ms: 1.0
-        //   age 100ms: 0.5
-        float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime)
-                * 0.000001f;
-        if (ageMillis < 50) {
+        case Weighting::NONE:
             return 1.0f;
-        }
-        if (ageMillis < 100) {
-            return 0.5f + (100 - ageMillis) * 0.01f;
-        }
-        return 0.5f;
-    }
-
-    case WEIGHTING_NONE:
-    default:
-        return 1.0f;
     }
 }
 
-
 // --- IntegratingVelocityTrackerStrategy ---
 
 IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) :
@@ -825,38 +812,32 @@
 IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
 }
 
-void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
-    mPointerIdBits.value &= ~idBits.value;
+void IntegratingVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mPointerIdBits.clearBit(pointerId);
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                     const std::vector<float>& positions) {
-    uint32_t index = 0;
-    for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
-        uint32_t id = iterIdBits.clearFirstMarkedBit();
-        State& state = mPointerState[id];
-        const float position = positions[index++];
-        if (mPointerIdBits.hasBit(id)) {
-            updateState(state, eventTime, position);
-        } else {
-            initState(state, eventTime, position);
-        }
+void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                     float position) {
+    State& state = mPointerState[pointerId];
+    if (mPointerIdBits.hasBit(pointerId)) {
+        updateState(state, eventTime, position);
+    } else {
+        initState(state, eventTime, position);
     }
 
-    mPointerIdBits = idBits;
+    mPointerIdBits.markBit(pointerId);
 }
 
-bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->clear();
-
-    if (mPointerIdBits.hasBit(id)) {
-        const State& state = mPointerState[id];
-        populateEstimator(state, outEstimator);
-        return true;
+std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator(
+        int32_t pointerId) const {
+    if (mPointerIdBits.hasBit(pointerId)) {
+        const State& state = mPointerState[pointerId];
+        VelocityTracker::Estimator estimator;
+        populateEstimator(state, &estimator);
+        return estimator;
     }
 
-    return false;
+    return std::nullopt;
 }
 
 void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime,
@@ -916,49 +897,60 @@
 
 // --- LegacyVelocityTrackerStrategy ---
 
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {}
+LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {}
 
 LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
 }
 
-void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
-    BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
-    mMovements[mIndex].idBits = remainingIdBits;
+void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mIndex.erase(pointerId);
+    mMovements.erase(pointerId);
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                const std::vector<float>& positions) {
-    if (++mIndex == HISTORY_SIZE) {
-        mIndex = 0;
+void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                float position) {
+    // If data for this pointer already exists, we have a valid entry at the position of
+    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
+    // to the next position in the circular buffer and write the new Movement there. Otherwise,
+    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
+    // for this pointer and write to the first position.
+    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
+    auto [indexIt, _] = mIndex.insert({pointerId, 0});
+    size_t& index = indexIt->second;
+    if (!inserted && movementIt->second[index].eventTime != eventTime) {
+        // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
+        // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
+        // the new pointer. If the eventtimes for both events are identical, just update the data
+        // for this time.
+        // We only compare against the last value, as it is likely that addMovement is called
+        // in chronological order as events occur.
+        index++;
+    }
+    if (index == HISTORY_SIZE) {
+        index = 0;
     }
 
-    Movement& movement = mMovements[mIndex];
+    Movement& movement = movementIt->second[index];
     movement.eventTime = eventTime;
-    movement.idBits = idBits;
-    uint32_t count = idBits.count();
-    for (uint32_t i = 0; i < count; i++) {
-        movement.positions[i] = positions[i];
-    }
+    movement.position = position;
 }
 
-bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->clear();
-
-    const Movement& newestMovement = mMovements[mIndex];
-    if (!newestMovement.idBits.hasBit(id)) {
-        return false; // no data
+std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator(
+        int32_t pointerId) const {
+    const auto movementIt = mMovements.find(pointerId);
+    if (movementIt == mMovements.end()) {
+        return std::nullopt; // no data
     }
+    const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)];
 
     // Find the oldest sample that contains the pointer and that is not older than HORIZON.
     nsecs_t minTime = newestMovement.eventTime - HORIZON;
-    uint32_t oldestIndex = mIndex;
+    uint32_t oldestIndex = mIndex.at(pointerId);
     uint32_t numTouches = 1;
     do {
         uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
-        const Movement& nextOldestMovement = mMovements[nextOldestIndex];
-        if (!nextOldestMovement.idBits.hasBit(id)
-                || nextOldestMovement.eventTime < minTime) {
+        const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex];
+        if (nextOldestMovement.eventTime < minTime) {
             break;
         }
         oldestIndex = nextOldestIndex;
@@ -978,22 +970,22 @@
     float accumV = 0;
     uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
-    const Movement& oldestMovement = mMovements[oldestIndex];
-    float oldestPosition = oldestMovement.getPosition(id);
+    const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex];
+    float oldestPosition = oldestMovement.position;
     nsecs_t lastDuration = 0;
 
     while (numTouches-- > 1) {
         if (++index == HISTORY_SIZE) {
             index = 0;
         }
-        const Movement& movement = mMovements[index];
+        const Movement& movement = mMovements.at(pointerId)[index];
         nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
 
         // If the duration between samples is small, we may significantly overestimate
         // the velocity.  Consequently, we impose a minimum duration constraint on the
         // samples that we include in the calculation.
         if (duration >= MIN_DURATION) {
-            float position = movement.getPosition(id);
+            float position = movement.position;
             float scale = 1000000000.0f / duration; // one over time delta in seconds
             float v = (position - oldestPosition) * scale;
             accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration);
@@ -1003,54 +995,59 @@
     }
 
     // Report velocity.
-    float newestPosition = newestMovement.getPosition(id);
-    outEstimator->time = newestMovement.eventTime;
-    outEstimator->confidence = 1;
-    outEstimator->coeff[0] = newestPosition;
+    float newestPosition = newestMovement.position;
+    VelocityTracker::Estimator estimator;
+    estimator.time = newestMovement.eventTime;
+    estimator.confidence = 1;
+    estimator.coeff[0] = newestPosition;
     if (samplesUsed) {
-        outEstimator->coeff[1] = accumV;
-        outEstimator->degree = 1;
+        estimator.coeff[1] = accumV;
+        estimator.degree = 1;
     } else {
-        outEstimator->degree = 0;
+        estimator.degree = 0;
     }
-    return true;
+    return estimator;
 }
 
 // --- ImpulseVelocityTrackerStrategy ---
 
 ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
-      : mDeltaValues(deltaValues), mIndex(0) {}
+      : mDeltaValues(deltaValues) {}
 
 ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
 }
 
-void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
-    BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
-    mMovements[mIndex].idBits = remainingIdBits;
+void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) {
+    mIndex.erase(pointerId);
+    mMovements.erase(pointerId);
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                 const std::vector<float>& positions) {
-    if (mMovements[mIndex].eventTime != eventTime) {
+void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId,
+                                                 float position) {
+    // If data for this pointer already exists, we have a valid entry at the position of
+    // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index
+    // to the next position in the circular buffer and write the new Movement there. Otherwise,
+    // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements
+    // for this pointer and write to the first position.
+    auto [movementIt, inserted] = mMovements.insert({pointerId, {}});
+    auto [indexIt, _] = mIndex.insert({pointerId, 0});
+    size_t& index = indexIt->second;
+    if (!inserted && movementIt->second[index].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
         // the new pointer. If the eventtimes for both events are identical, just update the data
         // for this time.
         // We only compare against the last value, as it is likely that addMovement is called
         // in chronological order as events occur.
-        mIndex++;
+        index++;
     }
-    if (mIndex == HISTORY_SIZE) {
-        mIndex = 0;
+    if (index == HISTORY_SIZE) {
+        index = 0;
     }
 
-    Movement& movement = mMovements[mIndex];
+    Movement& movement = movementIt->second[index];
     movement.eventTime = eventTime;
-    movement.idBits = idBits;
-    uint32_t count = idBits.count();
-    for (uint32_t i = 0; i < count; i++) {
-        movement.positions[i] = positions[i];
-    }
+    movement.position = position;
 }
 
 /**
@@ -1178,55 +1175,61 @@
     return kineticEnergyToVelocity(work);
 }
 
-bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id,
-        VelocityTracker::Estimator* outEstimator) const {
-    outEstimator->clear();
+std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator(
+        int32_t pointerId) const {
+    const auto movementIt = mMovements.find(pointerId);
+    if (movementIt == mMovements.end()) {
+        return std::nullopt; // no data
+    }
 
     // Iterate over movement samples in reverse time order and collect samples.
     float positions[HISTORY_SIZE];
     nsecs_t time[HISTORY_SIZE];
     size_t m = 0; // number of points that will be used for fitting
-    size_t index = mIndex;
-    const Movement& newestMovement = mMovements[mIndex];
+    size_t index = mIndex.at(pointerId);
+    const Movement& newestMovement = movementIt->second[index];
     do {
-        const Movement& movement = mMovements[index];
-        if (!movement.idBits.hasBit(id)) {
-            break;
-        }
+        const Movement& movement = movementIt->second[index];
 
         nsecs_t age = newestMovement.eventTime - movement.eventTime;
         if (age > HORIZON) {
             break;
         }
+        if (movement.eventTime == 0 && index != 0) {
+            // All eventTime's are initialized to 0. If we encounter a time of 0 in a position
+            // that's >0, it means that we hit the block where the data wasn't initialized.
+            // It's also possible that the sample at 0 would be invalid, but there's no harm in
+            // processing it, since it would be just a single point, and will only be encountered
+            // in artificial circumstances (in tests).
+            break;
+        }
 
-        positions[m] = movement.getPosition(id);
+        positions[m] = movement.position;
         time[m] = movement.eventTime;
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
     } while (++m < HISTORY_SIZE);
 
     if (m == 0) {
-        return false; // no data
+        return std::nullopt; // no data
     }
-    outEstimator->coeff[0] = 0;
-    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
-    outEstimator->coeff[2] = 0;
+    VelocityTracker::Estimator estimator;
+    estimator.coeff[0] = 0;
+    estimator.coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
+    estimator.coeff[2] = 0;
 
-    outEstimator->time = newestMovement.eventTime;
-    outEstimator->degree = 2; // similar results to 2nd degree fit
-    outEstimator->confidence = 1;
+    estimator.time = newestMovement.eventTime;
+    estimator.degree = 2; // similar results to 2nd degree fit
+    estimator.confidence = 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", estimator.coeff[1]);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
         // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
         // X axis chosen arbitrarily for velocity comparisons.
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
-        BitSet32 idBits;
-        const uint32_t pointerId = 0;
-        idBits.markBit(pointerId);
         for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}});
+            lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]);
         }
         std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
         if (v) {
@@ -1235,7 +1238,7 @@
             ALOGD("lsq2 velocity: could not compute velocity");
         }
     }
-    return true;
+    return estimator;
 }
 
 } // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 54feea2..2678f2f 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -288,13 +288,13 @@
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    VelocityTracker::Estimator estimatorX;
-    VelocityTracker::Estimator estimatorY;
-    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX));
-    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY));
+    std::optional<VelocityTracker::Estimator> estimatorX = vt.getEstimator(AMOTION_EVENT_AXIS_X, 0);
+    std::optional<VelocityTracker::Estimator> estimatorY = vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0);
+    EXPECT_TRUE(estimatorX);
+    EXPECT_TRUE(estimatorY);
     for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient(estimatorX.coeff[i], coefficients[i]);
-        checkCoefficient(estimatorY.coeff[i], coefficients[i]);
+        checkCoefficient((*estimatorX).coeff[i], coefficients[i]);
+        checkCoefficient((*estimatorY).coeff[i], coefficients[i]);
     }
 }
 
@@ -420,8 +420,7 @@
 
     EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
 
-    VelocityTracker::Estimator estimator;
-    EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID, &estimator));
+    EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
 
     VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000);
     for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
@@ -432,7 +431,7 @@
     EXPECT_EQ(-1, vt.getActivePointerId());
 
     // Make sure that the clearing functions execute without an issue.
-    vt.clearPointers(BitSet32(7U));
+    vt.clearPointer(7U);
     vt.clear();
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 4d51aee..8cd2cf0 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2703,18 +2703,15 @@
 
     // Update the velocity tracker.
     {
-        std::vector<float> positionsX;
-        std::vector<float> positionsY;
         for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            positionsX.push_back(pointer.x * mPointerXMovementScale);
-            positionsY.push_back(pointer.y * mPointerYMovementScale);
+            const float x = pointer.x * mPointerXMovementScale;
+            const float y = pointer.y * mPointerYMovementScale;
+            mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_X, x);
+            mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_Y, y);
         }
-        mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
-                                                    {{AMOTION_EVENT_AXIS_X, positionsX},
-                                                     {AMOTION_EVENT_AXIS_Y, positionsY}});
     }
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 33caa7a..6199a5a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -112,6 +112,11 @@
     MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
     MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
     MOCK_METHOD0(getBootDisplayModeSupport, bool());
+    MOCK_CONST_METHOD0(
+            getHdrConversionCapabilities,
+            std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>());
+    MOCK_METHOD1(setHdrConversionStrategy,
+                 status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy));
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
     MOCK_METHOD(status_t, getSupportedContentTypes,
                 (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index bd5d21a..0982077 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -171,7 +171,7 @@
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
-        if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+        if (mStagedBrightness && mBrightness != mStagedBrightness) {
             getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
             mBrightness = *mStagedBrightness;
         }
@@ -298,7 +298,7 @@
 }
 
 void DisplayDevice::persistBrightness(bool needsComposite) {
-    if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+    if (mStagedBrightness && mBrightness != mStagedBrightness) {
         if (needsComposite) {
             getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
         }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 08d64f1..370bd66 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -272,7 +272,7 @@
     std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
 
     std::optional<float> mStagedBrightness;
-    float mBrightness = -1.f;
+    std::optional<float> mBrightness;
 
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index e372b72..9470552 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -56,6 +56,9 @@
 using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
 using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
 using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlHdrConversionCapability =
+        aidl::android::hardware::graphics::common::HdrConversionCapability;
+using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
 using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
 using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
@@ -1396,6 +1399,27 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getHdrConversionCapabilities(
+        std::vector<AidlHdrConversionCapability>* hdrConversionCapabilities) {
+    const auto status =
+            mAidlComposerClient->getHdrConversionCapabilities(hdrConversionCapabilities);
+    if (!status.isOk()) {
+        hdrConversionCapabilities = {};
+        ALOGE("getHdrConversionCapabilities failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy) {
+    const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy);
+    if (!status.isOk()) {
+        ALOGE("setHdrConversionStrategy failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
 Error AidlComposer::getClientTargetProperty(
         Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
     Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 9a7ade7..a5ddf74 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -48,6 +48,8 @@
 namespace android::Hwc2 {
 
 using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::ComposerClientReader;
 using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
@@ -235,6 +237,8 @@
                                         AidlTransform* outDisplayOrientation) override;
     void onHotplugConnect(Display) override;
     void onHotplugDisconnect(Display) override;
+    Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
+    Error setHdrConversionStrategy(HdrConversionStrategy) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 1c2b8b5..82b677e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,8 @@
 #include <utils/StrongPointer.h>
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -288,6 +290,10 @@
     virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0;
     virtual void onHotplugConnect(Display) = 0;
     virtual void onHotplugDisconnect(Display) = 0;
+    virtual Error getHdrConversionCapabilities(
+            std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
+    virtual Error setHdrConversionStrategy(
+            ::aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 10fde2a..7dde6b4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -70,6 +70,8 @@
 #define RETURN_IF_HWC_ERROR(error, displayId, ...) \
     RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
 
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 namespace hal = android::hardware::graphics::composer::hal;
@@ -97,6 +99,7 @@
     loadCapabilities();
     loadLayerMetadataSupport();
     loadOverlayProperties();
+    loadHdrConversionCapabilities();
 
     if (mRegisteredCallback) {
         ALOGW("Callback already registered. Ignored extra registration attempt.");
@@ -787,6 +790,18 @@
     return displayModeId;
 }
 
+std::vector<HdrConversionCapability> HWComposer::getHdrConversionCapabilities() const {
+    return mHdrConversionCapabilities;
+}
+
+status_t HWComposer::setHdrConversionStrategy(HdrConversionStrategy hdrConversionStrategy) {
+    const auto error = mComposer->setHdrConversionStrategy(hdrConversionStrategy);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in setting HDR conversion strategy %s", to_string(error).c_str());
+    }
+    return NO_ERROR;
+}
+
 status_t HWComposer::getDisplayDecorationSupport(
         PhysicalDisplayId displayId,
         std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
@@ -979,6 +994,14 @@
     mComposer->getOverlaySupport(&mOverlayProperties);
 }
 
+void HWComposer::loadHdrConversionCapabilities() {
+    const auto error = mComposer->getHdrConversionCapabilities(&mHdrConversionCapabilities);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in fetching HDR conversion capabilities %s", to_string(error).c_str());
+        mHdrConversionCapabilities = {};
+    }
+}
+
 status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId,
                                          std::chrono::milliseconds timeout) {
     ATRACE_CALL();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 78d4a68..f6155d2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -44,6 +44,8 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -286,6 +288,10 @@
     virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0;
     virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const = 0;
     virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 0;
+    virtual std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+    getHdrConversionCapabilities() const = 0;
+    virtual status_t setHdrConversionStrategy(
+            aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -437,6 +443,10 @@
     status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override;
     bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const override;
     Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const override;
+    std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+    getHdrConversionCapabilities() const override;
+    status_t setHdrConversionStrategy(
+            aidl::android::hardware::graphics::common::HdrConversionStrategy) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
@@ -490,12 +500,16 @@
     void loadCapabilities();
     void loadLayerMetadataSupport();
     void loadOverlayProperties();
+    void loadHdrConversionCapabilities();
 
     std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
 
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
     aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties;
+    std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+            mHdrConversionCapabilities = {};
+
     std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
     bool mRegisteredCallback = false;
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c9e1e79..6fdb2d7 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -36,6 +36,8 @@
 #include <algorithm>
 #include <cinttypes>
 
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
 using aidl::android::hardware::graphics::composer3::DimmingStage;
@@ -1348,6 +1350,14 @@
     return Error::UNSUPPORTED;
 }
 
+Error HidlComposer::getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy) {
+    return Error::UNSUPPORTED;
+}
+
 Error HidlComposer::getClientTargetProperty(
         Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
     IComposerClient::ClientTargetProperty property;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 921add5..8280af2 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -343,6 +343,11 @@
                                         AidlTransform* outDisplayOrientation) override;
     void onHotplugConnect(Display) override;
     void onHotplugDisconnect(Display) override;
+    Error getHdrConversionCapabilities(
+            std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>*)
+            override;
+    Error setHdrConversionStrategy(
+            aidl::android::hardware::graphics::common::HdrConversionStrategy) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index c2109b3..8629671 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -177,7 +177,7 @@
     }
 
     mStalledTransactions.push_back(transactionId);
-    listener->onTransactionQueueStalled(reason);
+    listener->onTransactionQueueStalled(String8(reason.c_str()));
 }
 
 void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 475ff1b..a06b870 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -29,7 +29,6 @@
 namespace android {
 
 class TestableSurfaceFlinger;
-using gui::IListenerHash;
 namespace surfaceflinger::frontend {
 
 class TransactionHandler {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1f159ae..de9ea04 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2604,12 +2604,9 @@
         return;
     }
     ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
-    std::optional<os::ParcelFileDescriptor> fenceFd;
-    if (releaseFence) {
-        fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
-    }
-    listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
-                              static_cast<int32_t>(currentMaxAcquiredBufferCount));
+    listener->onReleaseBuffer({buffer->getId(), framenumber},
+                              releaseFence ? releaseFence : Fence::NO_FENCE,
+                              currentMaxAcquiredBufferCount);
 }
 
 void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 771c09e..2d8b9c1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1540,6 +1540,80 @@
     return future.get();
 }
 
+status_t SurfaceFlinger::getHdrConversionCapabilities(
+        std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) const {
+    bool hdrOutputConversionSupport;
+    getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    if (hdrOutputConversionSupport == false) {
+        ALOGE("hdrOutputConversion is not supported by this device.");
+        return INVALID_OPERATION;
+    }
+    const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities();
+    for (auto capability : aidlConversionCapability) {
+        gui::HdrConversionCapability tempCapability;
+        tempCapability.sourceType = static_cast<int>(capability.sourceType.hdr);
+        tempCapability.outputType = static_cast<int>(capability.outputType->hdr);
+        tempCapability.addsLatency = capability.addsLatency;
+        hdrConversionCapabilities->push_back(tempCapability);
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setHdrConversionStrategy(
+        const gui::HdrConversionStrategy& hdrConversionStrategy) {
+    bool hdrOutputConversionSupport;
+    getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    if (hdrOutputConversionSupport == false) {
+        ALOGE("hdrOutputConversion is not supported by this device.");
+        return INVALID_OPERATION;
+    }
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
+        using AidlHdrConversionStrategy =
+                aidl::android::hardware::graphics::common::HdrConversionStrategy;
+        using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+        AidlHdrConversionStrategy aidlConversionStrategy;
+        switch (hdrConversionStrategy.getTag()) {
+            case GuiHdrConversionStrategyTag::passthrough: {
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>(
+                        hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>());
+                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+            }
+            case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: {
+                auto autoHdrTypes =
+                        hdrConversionStrategy
+                                .get<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>();
+                std::vector<aidl::android::hardware::graphics::common::Hdr> aidlAutoHdrTypes;
+                for (auto type : autoHdrTypes) {
+                    aidlAutoHdrTypes.push_back(
+                            static_cast<aidl::android::hardware::graphics::common::Hdr>(type));
+                }
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+                        aidlAutoHdrTypes);
+                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+            }
+            case GuiHdrConversionStrategyTag::forceHdrConversion: {
+                auto forceHdrConversion =
+                        hdrConversionStrategy
+                                .get<GuiHdrConversionStrategyTag::forceHdrConversion>();
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>(
+                        static_cast<aidl::android::hardware::graphics::common::Hdr>(
+                                forceHdrConversion));
+                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+            }
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const {
+    auto future = mScheduler->schedule([this] {
+        return getHwComposer().hasCapability(Capability::HDR_OUTPUT_CONVERSION_CONFIG);
+    });
+
+    *outSupport = future.get();
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
     const char* const whence = __func__;
     static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
@@ -7160,7 +7234,8 @@
                                    layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
         ALOGD("%s", errorMessage.c_str());
         if (bufferData.releaseBufferListener) {
-            bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
+            bufferData.releaseBufferListener->onTransactionQueueStalled(
+                    String8(errorMessage.c_str()));
         }
         return nullptr;
     }
@@ -7178,7 +7253,7 @@
 
             if (bufferData.releaseBufferListener) {
                 bufferData.releaseBufferListener->onTransactionQueueStalled(
-                        "Buffer processing hung due to full buffer cache");
+                        String8("Buffer processing hung due to full buffer cache"));
             }
         }
 
@@ -7584,6 +7659,32 @@
     return binderStatusFromStatusT(status);
 }
 
+binder::Status SurfaceComposerAIDL::getHdrConversionCapabilities(
+        std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->getHdrConversionCapabilities(hdrConversionCapabilities);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setHdrConversionStrategy(
+        const gui::HdrConversionStrategy& hdrConversionStrategy) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setHdrConversionStrategy(hdrConversionStrategy);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getHdrOutputConversionSupport(bool* outMode) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->getHdrOutputConversionSupport(outMode);
+    }
+    return binderStatusFromStatusT(status);
+}
+
 binder::Status SurfaceComposerAIDL::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     status_t status = checkAccessPermission();
     if (status != OK) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 064a1f1..33f0402 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,7 +26,6 @@
 #include <android/gui/DisplayStatInfo.h>
 #include <android/gui/DisplayState.h>
 #include <android/gui/ISurfaceComposerClient.h>
-#include <android/gui/ITransactionCompletedListener.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <ftl/future.h>
@@ -35,8 +34,8 @@
 #include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerDebugInfo.h>
-
 #include <gui/LayerState.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
@@ -127,9 +126,7 @@
 using gui::CaptureArgs;
 using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
-using gui::ITransactionCompletedListener;
 using gui::LayerCaptureArgs;
-
 using gui::ScreenCaptureResults;
 
 namespace frametimeline {
@@ -534,6 +531,10 @@
     status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
     status_t getOverlaySupport(gui::OverlayProperties* outProperties) const;
     status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
+    status_t getHdrConversionCapabilities(
+            std::vector<gui::HdrConversionCapability>* hdrConversionCapaabilities) const;
+    status_t setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy);
+    status_t getHdrOutputConversionSupport(bool* outSupport) const;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
     void setPowerMode(const sp<IBinder>& displayToken, int mode);
@@ -1434,6 +1435,11 @@
     binder::Status clearBootDisplayMode(const sp<IBinder>& display) override;
     binder::Status getBootDisplayModeSupport(bool* outMode) override;
     binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override;
+    binder::Status getHdrConversionCapabilities(
+            std::vector<gui::HdrConversionCapability>*) override;
+    binder::Status setHdrConversionStrategy(
+            const gui::HdrConversionStrategy& hdrConversionStrategy) override;
+    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 captureDisplay(const DisplayCaptureArgs&,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index c09bcce..61ff9bc 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,27 +26,14 @@
 #include <unordered_set>
 
 #include <android-base/thread_annotations.h>
-#include <android/gui/ITransactionCompletedListener.h>
-
 #include <binder/IBinder.h>
-#include <gui/ListenerStats.h>
-#include <gui/ReleaseCallbackId.h>
-#include <renderengine/RenderEngine.h>
+#include <ftl/future.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <ui/Fence.h>
 #include <ui/FenceResult.h>
 
 namespace android {
 
-using gui::CallbackId;
-using gui::FrameEventHistoryStats;
-using gui::IListenerHash;
-using gui::ITransactionCompletedListener;
-using gui::JankData;
-using gui::ListenerCallbacks;
-using gui::ListenerStats;
-using gui::ReleaseCallbackId;
-using gui::TransactionStats;
-
 class CallbackHandle : public RefBase {
 public:
     CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
index 225ad16..ac5e927 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -96,5 +96,23 @@
     EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
 }
 
+TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) {
+    ftl::FakeGuard guard(kMainThreadContext);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = -1.0f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 8f89a8c..afbc57a 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -147,6 +147,7 @@
                             }),
                             Return(hardware::graphics::composer::V2_4::Error::NONE)));
     EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE));
+    EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::NONE));
 
     EXPECT_CALL(*mHal, registerCallback(_));
 
@@ -165,6 +166,7 @@
     EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
             .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
     EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED));
+    EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::UNSUPPORTED));
     EXPECT_CALL(*mHal, registerCallback(_));
 
     mHwc.setCallback(mCallback);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2f16b7b..5e29fe7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -145,6 +145,11 @@
     MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
     MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
     MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*));
+    MOCK_METHOD1(getHdrConversionCapabilities,
+                 Error(std::vector<
+                         aidl::android::hardware::graphics::common::HdrConversionCapability>*));
+    MOCK_METHOD1(setHdrConversionStrategy,
+                 Error(aidl::android::hardware::graphics::common::HdrConversionStrategy));
     MOCK_METHOD2(getSupportedContentTypes,
                  V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
     MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));