Merge changes Ibb632d4a,I95939670

* changes:
  SF: pass a render rate to VsyncTracker instead of a divisor
  SF: vsync divisors should use the same reference point
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index f76e73d..79c59f2 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -521,6 +521,45 @@
                                                  __INTRODUCED_IN(29);
 
 /**
+ * Sets the desired extended range brightness for the layer. This only applies for layers whose
+ * dataspace has RANGE_EXTENDED set on it.
+ *
+ * @param surface_control The layer whose extended range brightness is being specified
+ * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as
+ *                           peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the
+ *                           buffer was rendered with a target SDR whitepoint of 100nits and a max
+ *                           display brightness of 200nits, this should be set to 2.0f.
+ *
+ *                           Default value is 1.0f.
+ *
+ *                           Transfer functions that encode their own brightness ranges, such as
+ *                           HLG or PQ, should also set this to 1.0f and instead communicate
+ *                           extended content brightness information via metadata such as CTA861_3
+ *                           or SMPTE2086.
+ *
+ *                           Must be finite && >= 1.0f
+ *
+ * @param desiredRatio The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ *                     targetSdrWhitePointInNits. This can be used to communicate the max desired
+ *                     brightness range. This is similar to the "max luminance" value in other
+ *                     HDR metadata formats, but represented as a ratio of the target SDR whitepoint
+ *                     to the max display brightness. The system may not be able to, or may choose
+ *                     not to, deliver the requested range.
+ *
+ *                     If unspecified, the system will attempt to provide the best range it can
+ *                     for the given ambient conditions & device state. However, voluntarily
+ *                     reducing the requested range can help improve battery life as well as can
+ *                     improve quality by ensuring greater bit depth is allocated to the luminance
+ *                     range in use.
+ *
+ *                     Must be finite && >= 1.0f
+ */
+void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transaction,
+                                            ASurfaceControl* surface_control,
+                                            float currentBufferRatio,
+                                            float desiredRatio) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
  * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
  * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 3fae4e6..68ebf75 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <memory>
 #include <mutex>
+#include <string>
 #include <unordered_map>
 
 #include <android-base/thread_annotations.h>
@@ -73,6 +74,7 @@
 
 private:
     const nsecs_t mPredictionTimestampOffsetNanos;
+    const std::string mModelPath;
     const std::function<bool()> mCheckMotionPredictionEnabled;
 
     std::unique_ptr<TfLiteMotionPredictorModel> mModel;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index b84a9d3..c4c8ffb 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -518,6 +518,10 @@
             enabled: false,
         },
     },
+    visibility: [
+        ":__subpackages__",
+        "//system/tools/aidl:__subpackages__",
+    ],
 }
 
 // TODO(b/184872979): remove once the Rust API is created.
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 1ea13f9..b27f102 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -1036,8 +1036,8 @@
                 return DEAD_OBJECT;
             }
 
-            if (it->second.asyncTodo.size() != 0 &&
-                it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+            if (it->second.asyncTodo.size() == 0) return OK;
+            if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
                 LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64,
                                it->second.asyncNumber, addr);
 
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 3ebbed6..42d226b 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -57,6 +57,15 @@
 // could not be started.
 [[nodiscard]] ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd);
 
+// Starts an RPC server on a given IP address+port and a given IBinder object.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+// Does not take ownership of `service`.
+// Returns an opaque handle to the running service instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address,
+                                             unsigned int port);
+
 // Sets the list of supported file descriptor transport modes of this RPC server.
 void ARpcServer_setSupportedFileDescriptorTransportModes(
         ARpcServer* handle,
@@ -98,6 +107,10 @@
 AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* session,
                                                      int bootstrapFd);
 
+// Connects to an RPC server over an INET socket at a given IP address on a given port.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsigned int port);
+
 // Connects to an RPC server with preconnected file descriptors.
 //
 // requestFd should connect to the server and return a valid file descriptor, or
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index e7943dd..daff8c1 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -145,6 +145,17 @@
     return createObjectHandle<ARpcServer>(server);
 }
 
+ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) {
+    auto server = RpcServer::make();
+    if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) {
+        LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port "
+                   << port << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    server->setRootObject(AIBinder_toPlatformBinder(service));
+    return createObjectHandle<ARpcServer>(server);
+}
+
 void ARpcServer_setSupportedFileDescriptorTransportModes(
         ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
         size_t modes_len) {
@@ -222,6 +233,16 @@
     return AIBinder_fromPlatformBinder(session->getRootObject());
 }
 
+AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) {
+    auto session = handleToStrongPointer<RpcSession>(handle);
+    if (status_t status = session->setupInetClient(address, port); status != OK) {
+        LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port "
+                   << port << " error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+
 AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
                                               void* param) {
     auto session = handleToStrongPointer<RpcSession>(handle);
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index 1bc2416..63679c2 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -2,6 +2,7 @@
   global:
     ARpcServer_free;
     ARpcServer_join;
+    ARpcServer_newInet;
     ARpcServer_newInitUnixDomain;
     ARpcServer_newVsock;
     ARpcServer_shutdown;
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 761b306..c87876a 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -102,6 +102,29 @@
         }
     }
 
+    /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+    /// IP address and port.
+    pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> {
+        let address = match CString::new(address) {
+            Ok(s) => s,
+            Err(e) => {
+                log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
+                return Err(Error::from(ErrorKind::InvalidInput));
+            }
+        };
+        let service = service.as_native_mut();
+
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        unsafe {
+            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet(
+                service,
+                address.as_ptr(),
+                port,
+            ))
+        }
+    }
+
     unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
         if ptr.is_null() {
             return Err(Error::new(ErrorKind::Other, "Failed to start server"));
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 62fedb1..0b517cf 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -144,6 +144,32 @@
         Self::get_interface(service)
     }
 
+    /// Connects to an RPC Binder server over inet socket at the given address and port.
+    pub fn setup_inet_client<T: FromIBinder + ?Sized>(
+        &self,
+        address: &str,
+        port: u32,
+    ) -> Result<Strong<T>, StatusCode> {
+        let address = match CString::new(address) {
+            Ok(s) => s,
+            Err(e) => {
+                log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
+                return Err(StatusCode::BAD_VALUE);
+            }
+        };
+
+        // SAFETY: AIBinder returned by ARpcSession_setupInet has correct reference
+        // count, and the ownership can safely be taken by new_spibinder.
+        let service = unsafe {
+            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupInet(
+                self.as_ptr(),
+                address.as_ptr(),
+                port,
+            ))
+        };
+        Self::get_interface(service)
+    }
+
     /// Connects to an RPC Binder server, using the given callback to get (and
     /// take ownership of) file descriptors already connected to it.
     pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..cefb9a7 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -63,7 +63,8 @@
                                  Vector<ComposerState>& state, const Vector<DisplayState>& displays,
                                  uint32_t flags, const sp<IBinder>& applyToken,
                                  const InputWindowCommands& commands, int64_t desiredPresentTime,
-                                 bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+                                 bool isAutoTimestamp,
+                                 const std::vector<client_cache_t>& uncacheBuffers,
                                  bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override {
@@ -87,8 +88,11 @@
         SAFE_PARCEL(commands.write, data);
         SAFE_PARCEL(data.writeInt64, desiredPresentTime);
         SAFE_PARCEL(data.writeBool, isAutoTimestamp);
-        SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
-        SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
+        for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
+            SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+            SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+        }
         SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
 
         SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
@@ -158,11 +162,14 @@
             SAFE_PARCEL(data.readInt64, &desiredPresentTime);
             SAFE_PARCEL(data.readBool, &isAutoTimestamp);
 
-            client_cache_t uncachedBuffer;
+            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+            std::vector<client_cache_t> uncacheBuffers(count);
             sp<IBinder> tmpBinder;
-            SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
-            uncachedBuffer.token = tmpBinder;
-            SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
+            for (size_t i = 0; i < count; i++) {
+                SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+                uncacheBuffers[i].token = tmpBinder;
+                SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
+            }
 
             bool hasListenerCallbacks = false;
             SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
@@ -182,7 +189,7 @@
 
             return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
                                        inputWindowCommands, desiredPresentTime, isAutoTimestamp,
-                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+                                       uncacheBuffers, hasListenerCallbacks, listenerCallbacks,
                                        transactionId);
         }
         default: {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7498834..c508917 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -633,11 +633,13 @@
         return NO_ERROR;
     }
 
-    uint64_t cache(const sp<GraphicBuffer>& buffer) {
+    uint64_t cache(const sp<GraphicBuffer>& buffer,
+                   std::optional<client_cache_t>& outUncacheBuffer) {
         std::lock_guard<std::mutex> lock(mMutex);
 
         if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) {
-            evictLeastRecentlyUsedBuffer();
+            outUncacheBuffer = findLeastRecentlyUsedBuffer();
+            mBuffers.erase(outUncacheBuffer->id);
         }
 
         buffer->addDeathCallback(removeDeadBufferCallback, nullptr);
@@ -648,16 +650,13 @@
 
     void uncache(uint64_t cacheId) {
         std::lock_guard<std::mutex> lock(mMutex);
-        uncacheLocked(cacheId);
-    }
-
-    void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) {
-        mBuffers.erase(cacheId);
-        SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+        if (mBuffers.erase(cacheId)) {
+            SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+        }
     }
 
 private:
-    void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+    client_cache_t findLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
         auto itr = mBuffers.begin();
         uint64_t minCounter = itr->second;
         auto minBuffer = itr;
@@ -671,7 +670,8 @@
             }
             itr++;
         }
-        uncacheLocked(minBuffer->first);
+
+        return {.token = getToken(), .id = minBuffer->first};
     }
 
     uint64_t getCounter() REQUIRES(mMutex) {
@@ -809,6 +809,18 @@
     InputWindowCommands inputWindowCommands;
     inputWindowCommands.read(*parcel);
 
+    count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
+    std::vector<client_cache_t> uncacheBuffers(count);
+    for (size_t i = 0; i < count; i++) {
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        uncacheBuffers[i].token = tmpBinder;
+        SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
+    }
+
     // Parsing was successful. Update the object.
     mId = transactionId;
     mTransactionNestCount = transactionNestCount;
@@ -823,6 +835,7 @@
     mComposerStates = composerStates;
     mInputWindowCommands = inputWindowCommands;
     mApplyToken = applyToken;
+    mUncacheBuffers = std::move(uncacheBuffers);
     return NO_ERROR;
 }
 
@@ -874,6 +887,13 @@
     }
 
     mInputWindowCommands.write(*parcel);
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+    }
+
     return NO_ERROR;
 }
 
@@ -940,6 +960,10 @@
         }
     }
 
+    for (const auto& cacheId : other.mUncacheBuffers) {
+        mUncacheBuffers.push_back(cacheId);
+    }
+
     mInputWindowCommands.merge(other.mInputWindowCommands);
 
     mMayContainBuffer |= other.mMayContainBuffer;
@@ -958,6 +982,7 @@
     mDisplayStates.clear();
     mListenerCallbacks.clear();
     mInputWindowCommands.clear();
+    mUncacheBuffers.clear();
     mMayContainBuffer = false;
     mTransactionNestCount = 0;
     mAnimation = false;
@@ -980,10 +1005,10 @@
     uncacheBuffer.token = BufferCache::getInstance().getToken();
     uncacheBuffer.id = cacheId;
     Vector<ComposerState> composerStates;
-    status_t status =
-            sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
-                                    ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(),
-                                    {}, systemTime(), true, uncacheBuffer, false, {}, generateId());
+    status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+                                              ISurfaceComposer::eOneWay,
+                                              Transaction::getDefaultApplyToken(), {}, systemTime(),
+                                              true, {uncacheBuffer}, false, {}, generateId());
     if (status != NO_ERROR) {
         ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
                         strerror(-status));
@@ -1021,7 +1046,11 @@
             s->bufferData->buffer = nullptr;
         } else {
             // Cache-miss. Include the buffer and send the new cacheId.
-            cacheId = BufferCache::getInstance().cache(s->bufferData->buffer);
+            std::optional<client_cache_t> uncacheBuffer;
+            cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
+            if (uncacheBuffer) {
+                mUncacheBuffers.push_back(*uncacheBuffer);
+            }
         }
         s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
         s->bufferData->cachedBuffer.token = BufferCache::getInstance().getToken();
@@ -1154,8 +1183,7 @@
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
                             mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
-                            {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
-                            hasListenerCallbacks, listenerCallbacks, mId);
+                            mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId);
     mId = generateId();
 
     // Clear the current states and flags
@@ -2629,9 +2657,17 @@
 }
 
 status_t SurfaceComposerClient::setHdrConversionStrategy(
-        gui::HdrConversionStrategy hdrConversionStrategy) {
-    binder::Status status = ComposerServiceAIDL::getComposerService()->setHdrConversionStrategy(
-            hdrConversionStrategy);
+        gui::HdrConversionStrategy hdrConversionStrategy, ui::Hdr* outPreferredHdrOutputType) {
+    int hdrType;
+    binder::Status status = ComposerServiceAIDL::getComposerService()
+                                    ->setHdrConversionStrategy(hdrConversionStrategy, &hdrType);
+    *outPreferredHdrOutputType = static_cast<ui::Hdr>(hdrType);
+    return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::getHdrOutputConversionSupport(bool* isSupported) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->getHdrOutputConversionSupport(isSupported);
     return statusTFromBinderStatus(status);
 }
 
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 9812142..aa58e2e 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -191,10 +191,12 @@
 
      /**
       * Sets the HDR conversion strategy of the device.
+      * Returns the preferred HDR output type of the device, in case when HdrConversionStrategy has
+      * autoAllowedHdrTypes set. Returns Hdr::INVALID in other cases.
       *
       * Requires the ACCESS_SURFACE_FLINGER permission.
       */
-     void setHdrConversionStrategy(in HdrConversionStrategy hdrConversionStrategy);
+     int setHdrConversionStrategy(in HdrConversionStrategy hdrConversionStrategy);
 
      /**
       * Gets whether HDR output conversion operations are supported on the device.
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index f01c2a9..8c003d8 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -93,8 +93,8 @@
     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, setHdrConversionStrategy,
+                (const gui::HdrConversionStrategy&, int32_t*), (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));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 045cc2a..ae56f9f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -113,8 +113,9 @@
             const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
             const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-            bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
+            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
+            bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+            uint64_t transactionId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 40c0d71..44e78ec 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -192,8 +192,12 @@
 
     // 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 HDR conversion strategy for the device. in case when HdrConversionStrategy has
+    // autoAllowedHdrTypes set. Returns Hdr::INVALID in other cases.
+    static status_t setHdrConversionStrategy(gui::HdrConversionStrategy hdrConversionStrategy,
+                                             ui::Hdr* outPreferredHdrOutputType);
+    // Returns whether HDR conversion is supported by the device.
+    static status_t getHdrOutputConversionSupport(bool* isSupported);
 
     // Sets the frame rate of a particular app (uid). This is currently called
     // by GameManager.
@@ -413,6 +417,7 @@
         SortedVector<DisplayState> mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
+        std::vector<client_cache_t> mUncacheBuffers;
 
         uint64_t mId;
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index be1ef8e..fccc408 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -701,7 +701,7 @@
                                  const sp<IBinder>& /*applyToken*/,
                                  const InputWindowCommands& /*inputWindowCommands*/,
                                  int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-                                 const client_cache_t& /*cachedBuffer*/,
+                                 const std::vector<client_cache_t>& /*cachedBuffer*/,
                                  bool /*hasListenerCallbacks*/,
                                  const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
                                  uint64_t /*transactionId*/) override {
@@ -827,7 +827,8 @@
     }
 
     binder::Status setHdrConversionStrategy(
-            const gui::HdrConversionStrategy& /*hdrConversionStrategy*/) override {
+            const gui::HdrConversionStrategy& /*hdrConversionStrategy*/,
+            int32_t* /*outPreferredHdrOutputType*/) override {
         return binder::Status::ok();
     }
 
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 0f889e8..7d11ef2 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -65,9 +65,8 @@
 MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath,
                                  std::function<bool()> checkMotionPredictionEnabled)
       : mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
-        mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
-        mModel(TfLiteMotionPredictorModel::create(modelPath == nullptr ? DEFAULT_MODEL_PATH
-                                                                       : modelPath)) {}
+        mModelPath(modelPath == nullptr ? DEFAULT_MODEL_PATH : modelPath),
+        mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
 
 void MotionPredictor::record(const MotionEvent& event) {
     if (!isPredictionAvailable(event.getDeviceId(), event.getSource())) {
@@ -76,6 +75,11 @@
         return;
     }
 
+    // Initialise the model now that it's likely to be used.
+    if (!mModel) {
+        mModel = TfLiteMotionPredictorModel::create(mModelPath.c_str());
+    }
+
     TfLiteMotionPredictorBuffers& buffers =
             mDeviceBuffers.try_emplace(event.getDeviceId(), mModel->inputLength()).first->second;
 
@@ -130,6 +134,7 @@
             continue;
         }
 
+        LOG_ALWAYS_FATAL_IF(!mModel);
         buffer.copyTo(*mModel);
         LOG_ALWAYS_FATAL_IF(!mModel->invoke());
 
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index ee112e8..78d1912 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -29,9 +29,9 @@
     local_include_dirs: ["include"],
 
     srcs: [
-        "recoverymap.cpp",
+        "jpegr.cpp",
         "recoverymapmath.cpp",
-        "recoverymaputils.cpp",
+        "jpegrutils.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h
index 485869d..8748237 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoderhelper.h
@@ -25,7 +25,7 @@
 }
 #include <utils/Errors.h>
 #include <vector>
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 /*
  * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
  * This class is not thread-safe.
@@ -115,6 +115,6 @@
     // Position of EXIF package, default value is -1 which means no EXIF package appears.
     size_t mExifPos;
 };
-} /* namespace android  */
+} /* namespace android::jpegrecoverymap  */
 
 #endif // ANDROID_JPEGRECOVERYMAP_JPEGDECODERHELPER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h
index f087b55..8b82b2b 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoderhelper.h
@@ -28,7 +28,7 @@
 #include <utils/Errors.h>
 #include <vector>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 /*
  * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
@@ -90,6 +90,6 @@
     std::vector<JOCTET> mResultBuffer;
 };
 
-} /* namespace android  */
+} /* namespace android::jpegrecoverymap  */
 
 #endif // ANDROID_JPEGRECOVERYMAP_JPEGENCODERHELPER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
similarity index 97%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
rename to libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index aee6602..5455ba6 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
-#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
+#ifndef ANDROID_JPEGRECOVERYMAP_JPEGR_H
+#define ANDROID_JPEGRECOVERYMAP_JPEGR_H
 
 #include "jpegrerrorcode.h"
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 // Color gamuts for image data
 typedef enum {
@@ -88,6 +88,8 @@
   uint32_t version;
   // Max Content Boost for the map
   float maxContentBoost;
+  // Min Content Boost for the map
+  float minContentBoost;
 };
 
 typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
@@ -96,7 +98,7 @@
 typedef struct jpegr_metadata* jr_metadata_ptr;
 typedef struct jpegr_info_struct* jr_info_ptr;
 
-class RecoveryMap {
+class JpegR {
 public:
     /*
      * Encode API-0
@@ -219,17 +221,7 @@
     */
     status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
                           jr_info_ptr jpegr_info);
-private:
-    /*
-     * This method is called in the encoding pipeline. It will encode the recovery map.
-     *
-     * @param uncompressed_recovery_map uncompressed recovery map
-     * @param dest encoded recover map
-     * @return NO_ERROR if encoding succeeds, error code if error occurs.
-     */
-    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
-                               jr_compressed_ptr dest);
-
+protected:
     /*
      * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
      * 10-bit yuv images as input, and calculate the uncompressed recovery map. The input images
@@ -265,6 +257,17 @@
                               jr_metadata_ptr metadata,
                               jr_uncompressed_ptr dest);
 
+private:
+    /*
+     * This method is called in the encoding pipeline. It will encode the recovery map.
+     *
+     * @param uncompressed_recovery_map uncompressed recovery map
+     * @param dest encoded recover map
+     * @return NO_ERROR if encoding succeeds, error code if error occurs.
+     */
+    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+                               jr_compressed_ptr dest);
+
     /*
      * This methoud is called to separate primary image and recovery map image from JPEGR
      *
@@ -320,6 +323,6 @@
                      jr_uncompressed_ptr dest);
 };
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
 
-#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
+#endif // ANDROID_JPEGRECOVERYMAP_JPEGR_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 699c0d3..f730343 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -16,7 +16,7 @@
 
 #include <utils/Errors.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 enum {
     // status_t map for errors in the media framework
@@ -48,4 +48,4 @@
     ERROR_JPEGR_TONEMAP_ERROR           = JPEGR_RUNTIME_ERROR_BASE - 5,
 };
 
-}  // namespace android::recoverymap
+}  // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
similarity index 83%
rename from libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
rename to libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
index de29a33..581806c 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
-#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#ifndef ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
+#define ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
 
-#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/jpegr.h>
 
 #include <sstream>
 #include <stdint.h>
 #include <string>
 #include <cstdio>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 struct jpegr_metadata;
 
@@ -56,6 +56,7 @@
  * below is an example of the XMP metadata that this function generates where
  * secondary_image_length = 1000
  * max_content_boost = 8.0
+ * min_content_boost = 0.5
  *
  * <x:xmpmeta
  *   xmlns:x="adobe:ns:meta/"
@@ -70,16 +71,17 @@
  *         <rdf:Seq>
  *           <rdf:li>
  *             <Container:Item
- *              Item:Semantic="Primary"
- *              Item:Mime="image/jpeg"
- *              RecoveryMap:Version="1"
- *              RecoveryMap:MaxContentBoost="8.0"/>
+ *               Item:Semantic="Primary"
+ *               Item:Mime="image/jpeg"/>
  *           </rdf:li>
  *           <rdf:li>
  *             <Container:Item
  *               Item:Semantic="RecoveryMap"
  *               Item:Mime="image/jpeg"
- *               Item:Length="1000"/>
+ *               Item:Length="1000"
+ *               RecoveryMap:Version="1"
+ *               RecoveryMap:MaxContentBoost="8.0"
+ *               RecoveryMap:MinContentBoost="0.5"/>
  *           </rdf:li>
  *         </rdf:Seq>
  *       </Container:Directory>
@@ -92,6 +94,6 @@
  * @return XMP metadata in type of string
  */
 std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
-}
+}  // namespace android::jpegrecoverymap
 
-#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index 0695bb7..c12cee9 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -20,9 +20,9 @@
 #include <cmath>
 #include <stdint.h>
 
-#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/jpegr.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
 
@@ -118,11 +118,12 @@
 constexpr size_t kRecoveryFactorPrecision = 10;
 constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
 struct RecoveryLUT {
-  RecoveryLUT(float hdrRatio) {
-    float increment = 2.0 / kRecoveryFactorNumEntries;
-    float value = -1.0f;
-    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++, value += increment) {
-      mRecoveryTable[idx] = pow(hdrRatio, value);
+  RecoveryLUT(jr_metadata_ptr metadata) {
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
+      float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
+                     + log2(metadata->maxContentBoost) * value;
+      mRecoveryTable[idx] = exp2(logBoost);
     }
   }
 
@@ -130,10 +131,10 @@
   }
 
   float getRecoveryFactor(float recovery) {
-    uint32_t value = static_cast<uint32_t>(((recovery + 1.0f) / 2.0f) * kRecoveryFactorNumEntries);
+    uint32_t idx = static_cast<uint32_t>(recovery * (kRecoveryFactorNumEntries - 1));
     //TODO() : Remove once conversion modules have appropriate clamping in place
-    value = CLIP3(value, 0, kRecoveryFactorNumEntries - 1);
-    return mRecoveryTable[value];
+    idx = CLIP3(idx, 0, kRecoveryFactorNumEntries - 1);
+    return mRecoveryTable[idx];
   }
 
 private:
@@ -219,6 +220,9 @@
 float srgbInvOetfLUT(float e_gamma);
 Color srgbInvOetfLUT(Color e_gamma);
 
+constexpr size_t kSrgbInvOETFPrecision = 10;
+constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision;
+
 ////////////////////////////////////////////////////////////////////////////////
 // Display-P3 transformations
 
@@ -260,6 +264,9 @@
 float hlgOetfLUT(float e);
 Color hlgOetfLUT(Color e);
 
+constexpr size_t kHlgOETFPrecision = 10;
+constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
+
 /*
  * Convert from HLG to scene luminance.
  *
@@ -270,6 +277,9 @@
 float hlgInvOetfLUT(float e_gamma);
 Color hlgInvOetfLUT(Color e_gamma);
 
+constexpr size_t kHlgInvOETFPrecision = 10;
+constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
+
 /*
  * Convert from scene luminance to PQ.
  *
@@ -280,6 +290,9 @@
 float pqOetfLUT(float e);
 Color pqOetfLUT(Color e);
 
+constexpr size_t kPqOETFPrecision = 10;
+constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
+
 /*
  * Convert from PQ to scene luminance in nits.
  *
@@ -290,6 +303,9 @@
 float pqInvOetfLUT(float e_gamma);
 Color pqInvOetfLUT(Color e_gamma);
 
+constexpr size_t kPqInvOETFPrecision = 10;
+constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
+
 
 ////////////////////////////////////////////////////////////////////////////////
 // Color space conversions
@@ -326,13 +342,13 @@
  * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
  * luminances in linear space, and the hdr ratio to encode against.
  */
-uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio);
+uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata);
 
 /*
  * Calculates the linear luminance in nits after applying the given recovery
  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
  */
-Color applyRecovery(Color e, float recovery, float hdr_ratio);
+Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata);
 Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);
 
 /*
@@ -376,6 +392,6 @@
  */
 uint32_t colorToRgba1010102(Color e_gamma);
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
 
 #endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/jpegdecoderhelper.cpp b/libs/jpegrecoverymap/jpegdecoderhelper.cpp
index 0754ec9..d36bbf8 100644
--- a/libs/jpegrecoverymap/jpegdecoderhelper.cpp
+++ b/libs/jpegrecoverymap/jpegdecoderhelper.cpp
@@ -24,7 +24,7 @@
 
 using namespace std;
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 const uint32_t kAPP0Marker = JPEG_APP0;      // JFIF
 const uint32_t kAPP1Marker = JPEG_APP0 + 1;  // EXIF, XMP
@@ -413,4 +413,4 @@
     return true;
 }
 
-} // namespace android
+} // namespace jpegrecoverymap
diff --git a/libs/jpegrecoverymap/jpegencoderhelper.cpp b/libs/jpegrecoverymap/jpegencoderhelper.cpp
index 54b184d..586cd34 100644
--- a/libs/jpegrecoverymap/jpegencoderhelper.cpp
+++ b/libs/jpegrecoverymap/jpegencoderhelper.cpp
@@ -20,7 +20,7 @@
 
 #include <errno.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 // The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
 struct destination_mgr {
@@ -236,4 +236,4 @@
     return true;
 }
 
-} // namespace android
+} // namespace jpegrecoverymap
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/jpegr.cpp
similarity index 89%
rename from libs/jpegrecoverymap/recoverymap.cpp
rename to libs/jpegrecoverymap/jpegr.cpp
index 218c430..828af2d 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/jpegr.h>
 #include <jpegrecoverymap/jpegencoderhelper.h>
 #include <jpegrecoverymap/jpegdecoderhelper.h>
 #include <jpegrecoverymap/recoverymapmath.h>
-#include <jpegrecoverymap/recoverymaputils.h>
+#include <jpegrecoverymap/jpegrutils.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/jpeg/jpeg_info.h>
@@ -27,7 +27,9 @@
 #include <image_io/base/data_segment_data_source.h>
 #include <utils/Log.h>
 #include "SkColorSpace.h"
+#include "SkData.h"
 #include "SkICC.h"
+#include "SkRefCnt.h"
 
 #include <map>
 #include <memory>
@@ -43,7 +45,7 @@
 using namespace std;
 using namespace photos_editing_formats::image_io;
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 #define USE_SRGB_INVOETF_LUT 1
 #define USE_HLG_OETF_LUT 1
@@ -86,14 +88,15 @@
   return cpuCoreCount;
 }
 
-static const map<recoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut {
+static const map<jpegrecoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut {
     {JPEGR_COLORGAMUT_BT709,     SkNamedGamut::kSRGB},
     {JPEGR_COLORGAMUT_P3,        SkNamedGamut::kDisplayP3},
     {JPEGR_COLORGAMUT_BT2100,    SkNamedGamut::kRec2020},
 };
 
 static const map<
-        recoverymap::jpegr_transfer_function, skcms_TransferFunction> jrTransFunc_to_skTransFunc {
+        jpegrecoverymap::jpegr_transfer_function,
+        skcms_TransferFunction> jrTransFunc_to_skTransFunc {
     {JPEGR_TF_SRGB,        SkNamedTransferFn::kSRGB},
     {JPEGR_TF_LINEAR,      SkNamedTransferFn::kLinear},
     {JPEGR_TF_HLG,         SkNamedTransferFn::kHLG},
@@ -101,7 +104,7 @@
 };
 
 /* Encode API-0 */
-status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jpegr_transfer_function hdr_tf,
                                   jr_compressed_ptr dest,
                                   int quality,
@@ -163,7 +166,7 @@
 }
 
 /* Encode API-1 */
-status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_uncompressed_ptr uncompressed_yuv_420_image,
                                   jpegr_transfer_function hdr_tf,
                                   jr_compressed_ptr dest,
@@ -227,7 +230,7 @@
 }
 
 /* Encode API-2 */
-status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_uncompressed_ptr uncompressed_yuv_420_image,
                                   jr_compressed_ptr compressed_jpeg_image,
                                   jpegr_transfer_function hdr_tf,
@@ -272,7 +275,7 @@
 }
 
 /* Encode API-3 */
-status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
                                   jr_compressed_ptr compressed_jpeg_image,
                                   jpegr_transfer_function hdr_tf,
                                   jr_compressed_ptr dest) {
@@ -324,7 +327,7 @@
   return NO_ERROR;
 }
 
-status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
                                    jr_info_ptr jpegr_info) {
   if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -345,7 +348,7 @@
 }
 
 /* Decode API */
-status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
+status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                                   jr_uncompressed_ptr dest,
                                   jr_exif_ptr exif,
                                   bool request_sdr) {
@@ -405,7 +408,7 @@
   return NO_ERROR;
 }
 
-status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+status_t JpegR::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
                                           jr_compressed_ptr dest) {
   if (uncompressed_recovery_map == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -489,7 +492,7 @@
   mQueuedAllJobs = false;
 }
 
-status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                           jr_uncompressed_ptr uncompressed_p010_image,
                                           jpegr_transfer_function hdr_tf,
                                           jr_metadata_ptr metadata,
@@ -553,6 +556,9 @@
       return ERROR_JPEGR_INVALID_TRANS_FUNC;
   }
 
+  metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
+  metadata->minContentBoost = 1.0f;
+
   ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
       uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
 
@@ -573,40 +579,10 @@
   }
 
   std::mutex mutex;
-  float hdr_y_nits_max = 0.0f;
-  double hdr_y_nits_avg = 0.0f;
   const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
   size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
   JobQueue jobQueue;
 
-  std::function<void()> computeMetadata = [uncompressed_p010_image, hdrInvOetf,
-                                           hdrGamutConversionFn, luminanceFn, hdr_white_nits,
-                                           threads, &mutex, &hdr_y_nits_avg,
-                                           &hdr_y_nits_max, &jobQueue]() -> void {
-    size_t rowStart, rowEnd;
-    float hdr_y_nits_max_th = 0.0f;
-    double hdr_y_nits_avg_th = 0.0f;
-    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
-      for (size_t y = rowStart; y < rowEnd; ++y) {
-        for (size_t x = 0; x < uncompressed_p010_image->width; ++x) {
-          Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
-          Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
-          Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
-          hdr_rgb = hdrGamutConversionFn(hdr_rgb);
-          float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
-
-          hdr_y_nits_avg_th += hdr_y_nits;
-          if (hdr_y_nits > hdr_y_nits_max_th) {
-            hdr_y_nits_max_th = hdr_y_nits;
-          }
-        }
-      }
-    }
-    std::unique_lock<std::mutex> lock{mutex};
-    hdr_y_nits_avg += hdr_y_nits_avg_th;
-    hdr_y_nits_max = std::max(hdr_y_nits_max, hdr_y_nits_max_th);
-  };
-
   std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
                                        metadata, dest, hdrInvOetf, hdrGamutConversionFn,
                                        luminanceFn, hdr_white_nits, &jobQueue]() -> void {
@@ -634,33 +610,14 @@
 
           size_t pixel_idx = x + y * dest_map_stride;
           reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
-              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost);
+              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata);
         }
       }
     }
   };
 
-  std::vector<std::thread> workers;
-  for (int th = 0; th < threads - 1; th++) {
-    workers.push_back(std::thread(computeMetadata));
-  }
-
-  // compute metadata
-  for (size_t rowStart = 0; rowStart < image_height;) {
-    size_t rowEnd = std::min(rowStart + rowStep, image_height);
-    jobQueue.enqueueJob(rowStart, rowEnd);
-    rowStart = rowEnd;
-  }
-  jobQueue.markQueueForEnd();
-  computeMetadata();
-  std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
-  workers.clear();
-  hdr_y_nits_avg /= image_width * image_height;
-
-  metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits;
-
   // generate map
-  jobQueue.reset();
+  std::vector<std::thread> workers;
   for (int th = 0; th < threads - 1; th++) {
     workers.push_back(std::thread(generateMap));
   }
@@ -679,7 +636,7 @@
   return NO_ERROR;
 }
 
-status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                        jr_uncompressed_ptr uncompressed_recovery_map,
                                        jr_metadata_ptr metadata,
                                        jr_uncompressed_ptr dest) {
@@ -693,7 +650,7 @@
   dest->width = uncompressed_yuv_420_image->width;
   dest->height = uncompressed_yuv_420_image->height;
   ShepardsIDW idwTable(kMapDimensionScaleFactor);
-  RecoveryLUT recoveryLUT(metadata->maxContentBoost);
+  RecoveryLUT recoveryLUT(metadata);
 
   JobQueue jobQueue;
   std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
@@ -729,13 +686,12 @@
           if (map_scale_factor != floorf(map_scale_factor)) {
             recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y);
           } else {
-            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y,
-                                idwTable);
+            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, idwTable);
           }
 #if USE_APPLY_RECOVERY_LUT
           Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT);
 #else
-          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
+          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
 #endif
           Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
           uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
@@ -764,7 +720,7 @@
   return NO_ERROR;
 }
 
-status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
                                                         jr_compressed_ptr primary_image,
                                                         jr_compressed_ptr recovery_map) {
   if (compressed_jpegr_image == nullptr) {
@@ -814,7 +770,7 @@
 }
 
 
-status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
                                          jr_compressed_ptr dest) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -845,7 +801,7 @@
 // Exif 2.2 spec for EXIF marker
 // Adobe XMP spec part 3 for XMP marker
 // ICC v4.3 spec for ICC
-status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
+status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
                                         jr_compressed_ptr compressed_recovery_map,
                                         jr_exif_ptr exif,
                                         jr_metadata_ptr metadata,
@@ -908,7 +864,7 @@
   return NO_ERROR;
 }
 
-status_t RecoveryMap::toneMap(jr_uncompressed_ptr src,
+status_t JpegR::toneMap(jr_uncompressed_ptr src,
                               jr_uncompressed_ptr dest) {
   if (src == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -945,4 +901,4 @@
   return NO_ERROR;
 }
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp
similarity index 87%
rename from libs/jpegrecoverymap/recoverymaputils.cpp
rename to libs/jpegrecoverymap/jpegrutils.cpp
index 40956bd..49526c8 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/jpegrutils.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/recoverymaputils.h>
+#include <jpegrecoverymap/jpegrutils.h>
 #include <image_io/xml/xml_reader.h>
 #include <image_io/xml/xml_writer.h>
 #include <image_io/base/message_handler.h>
@@ -25,7 +25,7 @@
 using namespace photos_editing_formats::image_io;
 using namespace std;
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 /*
  * Helper function used for generating XMP metadata.
@@ -95,6 +95,8 @@
             if (context.BuildTokenValue(&val)) {
                 if (!val.compare(maxContentBoostAttrName)) {
                     lastAttributeName = maxContentBoostAttrName;
+                } else if (!val.compare(minContentBoostAttrName)) {
+                    lastAttributeName = minContentBoostAttrName;
                 } else {
                     lastAttributeName = "";
                 }
@@ -109,6 +111,8 @@
             if (context.BuildTokenValue(&val, true)) {
                 if (!lastAttributeName.compare(maxContentBoostAttrName)) {
                     maxContentBoostStr = val;
+                } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
+                    minContentBoostStr = val;
                 }
             }
         }
@@ -130,10 +134,27 @@
         }
     }
 
+    bool getMinContentBoost(float* min_content_boost) {
+        if (gContainerItemState == Done) {
+            stringstream ss(minContentBoostStr);
+            float val;
+            if (ss >> val) {
+                *min_content_boost = val;
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
 private:
     static const string gContainerItemName;
     static const string maxContentBoostAttrName;
     string              maxContentBoostStr;
+    static const string minContentBoostAttrName;
+    string              minContentBoostStr;
     string              lastAttributeName;
     ParseState          gContainerItemState;
 };
@@ -169,10 +190,12 @@
 
 // RecoveryMap XMP constants - element and attribute names
 const string kMapMaxContentBoost  = Name(kRecoveryMapPrefix, "MaxContentBoost");
+const string kMapMinContentBoost  = Name(kRecoveryMapPrefix, "MinContentBoost");
 const string kMapVersion          = Name(kRecoveryMapPrefix, "Version");
 
 // RecoveryMap XMP constants - names for XMP handlers
 const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;
+const string XMPXmlHandler::minContentBoostAttrName = kMapMinContentBoost;
 
 bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
     string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -213,6 +236,10 @@
         return false;
     }
 
+    if (!handler.getMinContentBoost(&metadata->minContentBoost)) {
+        return false;
+    }
+
     return true;
 }
 
@@ -235,16 +262,17 @@
   size_t item_depth = writer.StartWritingElements(kLiItem);
   writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
   writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
-  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
-  writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
   writer.FinishWritingElementsToDepth(item_depth);
   writer.StartWritingElements(kLiItem);
   writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
   writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
   writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
+  writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
+  writer.WriteAttributeNameAndValue(kMapMinContentBoost, metadata.minContentBoost);
   writer.FinishWriting();
 
   return ss.str();
 }
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 4f21ac6..7812e18 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -18,67 +18,48 @@
 #include <vector>
 #include <jpegrecoverymap/recoverymapmath.h>
 
-namespace android::recoverymap {
-
-constexpr size_t kPqOETFPrecision = 10;
-constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
+namespace android::jpegrecoverymap {
 
 static const std::vector<float> kPqOETF = [] {
     std::vector<float> result;
-    float increment = 1.0 / kPqOETFNumEntries;
-    float value = 0.0f;
-    for (int idx = 0; idx < kPqOETFNumEntries; idx++, value += increment) {
+    for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
       result.push_back(pqOetf(value));
     }
     return result;
 }();
 
-constexpr size_t kPqInvOETFPrecision = 10;
-constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
-
 static const std::vector<float> kPqInvOETF = [] {
     std::vector<float> result;
-    float increment = 1.0 / kPqInvOETFNumEntries;
-    float value = 0.0f;
-    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++, value += increment) {
+    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
       result.push_back(pqInvOetf(value));
     }
     return result;
 }();
 
-constexpr size_t kHlgOETFPrecision = 10;
-constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
-
 static const std::vector<float> kHlgOETF = [] {
     std::vector<float> result;
-    float increment = 1.0 / kHlgOETFNumEntries;
-    float value = 0.0f;
-    for (int idx = 0; idx < kHlgOETFNumEntries; idx++, value += increment) {
+    for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
       result.push_back(hlgOetf(value));
     }
     return result;
 }();
 
-constexpr size_t kHlgInvOETFPrecision = 10;
-constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
-
 static const std::vector<float> kHlgInvOETF = [] {
     std::vector<float> result;
-    float increment = 1.0 / kHlgInvOETFNumEntries;
-    float value = 0.0f;
-    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++, value += increment) {
+    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
       result.push_back(hlgInvOetf(value));
     }
     return result;
 }();
 
-constexpr size_t kSRGBInvOETFPrecision = 10;
-constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision;
-static const std::vector<float> kSRGBInvOETF = [] {
+static const std::vector<float> kSrgbInvOETF = [] {
     std::vector<float> result;
-    float increment = 1.0 / kSRGBInvOETFNumEntries;
-    float value = 0.0f;
-    for (int idx = 0; idx < kSRGBInvOETFNumEntries; idx++, value += increment) {
+    for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
       result.push_back(srgbInvOetf(value));
     }
     return result;
@@ -182,10 +163,10 @@
 
 // See IEC 61966-2-1, Equations F.5 and F.6.
 float srgbInvOetfLUT(float e_gamma) {
-  uint32_t value = static_cast<uint32_t>(e_gamma * kSRGBInvOETFNumEntries);
+  uint32_t value = static_cast<uint32_t>(e_gamma * kSrgbInvOETFNumEntries);
   //TODO() : Remove once conversion modules have appropriate clamping in place
-  value = CLIP3(value, 0, kSRGBInvOETFNumEntries - 1);
-  return kSRGBInvOETF[value];
+  value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1);
+  return kSrgbInvOETF[value];
 }
 
 Color srgbInvOetfLUT(Color e_gamma) {
@@ -461,21 +442,24 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // Recovery map calculations
-
-uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
+uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata) {
   float gain = 1.0f;
   if (y_sdr > 0.0f) {
     gain = y_hdr / y_sdr;
   }
 
-  if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio;
-  if (gain > hdr_ratio) gain = hdr_ratio;
+  if (gain < metadata->minContentBoost) gain = metadata->minContentBoost;
+  if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost;
 
-  return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f  + 127.5f);
+  return static_cast<uint8_t>((log2(gain) - log2(metadata->minContentBoost))
+                            / (log2(metadata->maxContentBoost) - log2(metadata->minContentBoost))
+                            * 255.0f);
 }
 
-Color applyRecovery(Color e, float recovery, float hdr_ratio) {
-  float recoveryFactor = pow(hdr_ratio, recovery);
+Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata) {
+  float logBoost = log2(metadata->minContentBoost) * (1.0f - recovery)
+                 + log2(metadata->maxContentBoost) * recovery;
+  float recoveryFactor = exp2(logBoost);
   return e * recoveryFactor;
 }
 
@@ -550,7 +534,7 @@
 }
 
 static float mapUintToFloat(uint8_t map_uint) {
-  return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
+  return static_cast<float>(map_uint) / 255.0f;
 }
 
 static float pythDistance(float x_diff, float y_diff) {
@@ -558,9 +542,9 @@
 }
 
 // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
-float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
-  float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
-  float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
+float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y) {
+  float x_map = static_cast<float>(x) / map_scale_factor;
+  float y_map = static_cast<float>(y) / map_scale_factor;
 
   size_t x_lower = static_cast<size_t>(floor(x_map));
   size_t x_upper = x_lower + 1;
@@ -647,4 +631,4 @@
        | (0x3 << 30);  // Set alpha to 1.0
 }
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index e416db9..5a4edb2 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -25,7 +25,7 @@
     name: "libjpegrecoverymap_test",
     test_suites: ["device-tests"],
     srcs: [
-        "recoverymap_test.cpp",
+        "jpegr_test.cpp",
         "recoverymapmath_test.cpp",
     ],
     shared_libs: [
diff --git a/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp b/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp
index 278bf3b..2f32a56 100644
--- a/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegdecoderhelper_test.cpp
@@ -20,7 +20,7 @@
 
 #include <fcntl.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 #define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
 #define YUV_IMAGE_SIZE 20193
@@ -99,4 +99,4 @@
     ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
 }
 
-}
\ No newline at end of file
+}  // namespace android::jpegrecoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp b/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp
index 532688d..095ac2f 100644
--- a/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegencoderhelper_test.cpp
@@ -20,7 +20,7 @@
 
 #include <fcntl.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 #define VALID_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
 #define VALID_IMAGE_WIDTH 320
@@ -121,5 +121,5 @@
     ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
 }
 
-}
+}  // namespace android::jpegrecoverymap
 
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
similarity index 66%
rename from libs/jpegrecoverymap/tests/recoverymap_test.cpp
rename to libs/jpegrecoverymap/tests/jpegr_test.cpp
index 3e9a76d..7a3133d 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/jpegr.h>
+#include <jpegrecoverymap/jpegrutils.h>
 #include <jpegrecoverymap/recoverymapmath.h>
-#include <jpegrecoverymap/recoverymaputils.h>
 #include <fcntl.h>
 #include <fstream>
 #include <gtest/gtest.h>
+#include <sys/time.h>
 #include <utils/Log.h>
 
 #define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
@@ -33,29 +34,26 @@
 #define SAVE_DECODING_RESULT true
 #define SAVE_INPUT_RGBA true
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
-class RecoveryMapTest : public testing::Test {
-public:
-  RecoveryMapTest();
-  ~RecoveryMapTest();
-protected:
-  virtual void SetUp();
-  virtual void TearDown();
-
-  struct jpegr_uncompressed_struct mRawP010Image;
-  struct jpegr_uncompressed_struct mRawYuv420Image;
-  struct jpegr_compressed_struct mJpegImage;
+struct Timer {
+  struct timeval StartingTime;
+  struct timeval EndingTime;
+  struct timeval ElapsedMicroseconds;
 };
 
-RecoveryMapTest::RecoveryMapTest() {}
-RecoveryMapTest::~RecoveryMapTest() {}
+void timerStart(Timer *t) {
+  gettimeofday(&t->StartingTime, nullptr);
+}
 
-void RecoveryMapTest::SetUp() {}
-void RecoveryMapTest::TearDown() {
-  free(mRawP010Image.data);
-  free(mRawYuv420Image.data);
-  free(mJpegImage.data);
+void timerStop(Timer *t) {
+  gettimeofday(&t->EndingTime, nullptr);
+}
+
+int64_t elapsedTime(Timer *t) {
+  t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec;
+  t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec;
+  return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;
 }
 
 static size_t getFileSize(int fd) {
@@ -89,21 +87,96 @@
   return true;
 }
 
-TEST_F(RecoveryMapTest, build) {
-  // Force all of the recovery map lib to be linked by calling all public functions.
-  RecoveryMap recovery_map;
-  recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
-  recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
-                           nullptr, 0, nullptr);
-  recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
-                           nullptr);
-  recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
-  recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
+class JpegRTest : public testing::Test {
+public:
+  JpegRTest();
+  ~JpegRTest();
+
+protected:
+  virtual void SetUp();
+  virtual void TearDown();
+
+  struct jpegr_uncompressed_struct mRawP010Image;
+  struct jpegr_uncompressed_struct mRawYuv420Image;
+  struct jpegr_compressed_struct mJpegImage;
+};
+
+JpegRTest::JpegRTest() {}
+JpegRTest::~JpegRTest() {}
+
+void JpegRTest::SetUp() {}
+void JpegRTest::TearDown() {
+  free(mRawP010Image.data);
+  free(mRawYuv420Image.data);
+  free(mJpegImage.data);
 }
 
-TEST_F(RecoveryMapTest, writeXmpThenRead) {
+class JpegRBenchmark : public JpegR {
+public:
+ void BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
+                                   jr_metadata_ptr metadata, jr_uncompressed_ptr map);
+ void BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+                                jr_metadata_ptr metadata, jr_uncompressed_ptr dest);
+private:
+ const int kProfileCount = 10;
+};
+
+void JpegRBenchmark::BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image,
+                                                        jr_uncompressed_ptr p010Image,
+                                                        jr_metadata_ptr metadata,
+                                                        jr_uncompressed_ptr map) {
+  ASSERT_EQ(yuv420Image->width, p010Image->width);
+  ASSERT_EQ(yuv420Image->height, p010Image->height);
+
+  Timer genRecMapTime;
+
+  timerStart(&genRecMapTime);
+  for (auto i = 0; i < kProfileCount; i++) {
+      ASSERT_EQ(OK, generateRecoveryMap(
+          yuv420Image, p010Image, jpegr_transfer_function::JPEGR_TF_HLG, metadata, map));
+      if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
+  }
+  timerStop(&genRecMapTime);
+
+  ALOGE("Generate Recovery Map:- Res = %i x %i, time = %f ms",
+        yuv420Image->width, yuv420Image->height,
+        elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f));
+
+}
+
+void JpegRBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image,
+                                                     jr_uncompressed_ptr map,
+                                                     jr_metadata_ptr metadata,
+                                                     jr_uncompressed_ptr dest) {
+  Timer applyRecMapTime;
+
+  timerStart(&applyRecMapTime);
+  for (auto i = 0; i < kProfileCount; i++) {
+      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
+  }
+  timerStop(&applyRecMapTime);
+
+  ALOGE("Apply Recovery Map:- Res = %i x %i, time = %f ms",
+        yuv420Image->width, yuv420Image->height,
+        elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f));
+}
+
+TEST_F(JpegRTest, build) {
+  // Force all of the recovery map lib to be linked by calling all public functions.
+  JpegR jpegRCodec;
+  jpegRCodec.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+                         nullptr, 0, nullptr);
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+                         nullptr);
+  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
+  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false);
+}
+
+TEST_F(JpegRTest, writeXmpThenRead) {
   jpegr_metadata metadata_expected;
   metadata_expected.maxContentBoost = 1.25;
+  metadata_expected.minContentBoost = 0.75;
   int length_expected = 1000;
   const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
   const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
@@ -120,10 +193,11 @@
   jpegr_metadata metadata_read;
   EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
   ASSERT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
+  ASSERT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
 }
 
 /* Test Encode API-0 and decode */
-TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+TEST_F(JpegRTest, encodeFromP010ThenDecode) {
   int ret;
 
   // Load input files.
@@ -134,12 +208,12 @@
   mRawP010Image.height = TEST_IMAGE_HEIGHT;
   mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
 
-  RecoveryMap recoveryMap;
+  JpegR jpegRCodec;
 
   jpegr_compressed_struct jpegR;
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
-  ret = recoveryMap.encodeJPEGR(
+  ret = jpegRCodec.encodeJPEGR(
       &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
@@ -157,7 +231,7 @@
   jpegr_uncompressed_struct decodedJpegR;
   int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
   decodedJpegR.data = malloc(decodedJpegRSize);
-  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -176,7 +250,7 @@
 }
 
 /* Test Encode API-1 and decode */
-TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
+TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
   int ret;
 
   // Load input files.
@@ -194,12 +268,12 @@
   mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
   mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
 
-  RecoveryMap recoveryMap;
+  JpegR jpegRCodec;
 
   jpegr_compressed_struct jpegR;
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
-  ret = recoveryMap.encodeJPEGR(
+  ret = jpegRCodec.encodeJPEGR(
       &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
       DEFAULT_JPEG_QUALITY, nullptr);
   if (ret != OK) {
@@ -218,7 +292,7 @@
   jpegr_uncompressed_struct decodedJpegR;
   int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
   decodedJpegR.data = malloc(decodedJpegRSize);
-  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -237,7 +311,7 @@
 }
 
 /* Test Encode API-2 and decode */
-TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
+TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
   int ret;
 
   // Load input files.
@@ -260,12 +334,12 @@
   }
   mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
 
-  RecoveryMap recoveryMap;
+  JpegR jpegRCodec;
 
   jpegr_compressed_struct jpegR;
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
-  ret = recoveryMap.encodeJPEGR(
+  ret = jpegRCodec.encodeJPEGR(
       &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
@@ -283,7 +357,7 @@
   jpegr_uncompressed_struct decodedJpegR;
   int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
   decodedJpegR.data = malloc(decodedJpegRSize);
-  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -302,7 +376,7 @@
 }
 
 /* Test Encode API-3 and decode */
-TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
+TEST_F(JpegRTest, encodeFromJpegThenDecode) {
   int ret;
 
   // Load input files.
@@ -341,12 +415,12 @@
   }
   mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
 
-  RecoveryMap recoveryMap;
+  JpegR jpegRCodec;
 
   jpegr_compressed_struct jpegR;
   jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
   jpegR.data = malloc(jpegR.maxLength);
-  ret = recoveryMap.encodeJPEGR(
+  ret = jpegRCodec.encodeJPEGR(
       &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
@@ -364,7 +438,7 @@
   jpegr_uncompressed_struct decodedJpegR;
   int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
   decodedJpegR.data = malloc(decodedJpegRSize);
-  ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
   if (ret != OK) {
     FAIL() << "Error code is " << ret;
   }
@@ -382,4 +456,46 @@
   free(decodedJpegR.data);
 }
 
+TEST_F(JpegRTest, ProfileRecoveryMapFuncs) {
+  const size_t kWidth = TEST_IMAGE_WIDTH;
+  const size_t kHeight = TEST_IMAGE_HEIGHT;
+
+  // Load input files.
+  if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+  }
+  mRawP010Image.width = kWidth;
+  mRawP010Image.height = kHeight;
+  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+  if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+    FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+  }
+  mRawYuv420Image.width = kWidth;
+  mRawYuv420Image.height = kHeight;
+  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+  JpegRBenchmark benchmark;
+
+  jpegr_metadata metadata = { .version = 1,
+                              .maxContentBoost = 8.0f,
+                              .minContentBoost = 1.0f / 8.0f };
+
+  jpegr_uncompressed_struct map = { .data = NULL,
+                                    .width = 0,
+                                    .height = 0,
+                                    .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };
+
+  benchmark.BenchmarkGenerateRecoveryMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);
+
+  const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4;
+  auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
+  jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
+                                     .width = 0,
+                                     .height = 0,
+                                     .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED };
+
+  benchmark.BenchmarkApplyRecoveryMap(&mRawYuv420Image, &map, &metadata, &dest);
+}
+
 } // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 2eec95f..6c61ff1 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -19,7 +19,7 @@
 #include <gmock/gmock.h>
 #include <jpegrecoverymap/recoverymapmath.h>
 
-namespace android::recoverymap {
+namespace android::jpegrecoverymap {
 
 class RecoveryMapMathTest : public testing::Test {
 public:
@@ -42,7 +42,7 @@
   }
 
   float Map(uint8_t e) {
-    return (static_cast<float>(e) - 127.5f) / 127.5f;
+    return static_cast<float>(e) / 255.0f;
   }
 
   Color ColorMin(Color e1, Color e2) {
@@ -88,10 +88,10 @@
     return luminance_scaled * scale_factor;
   }
 
-  Color Recover(Color yuv_gamma, float recovery, float max_content_boost) {
+  Color Recover(Color yuv_gamma, float recovery, jr_metadata_ptr metadata) {
     Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
     Color rgb = srgbInvOetf(rgb_gamma);
-    return applyRecovery(rgb, recovery, max_content_boost);
+    return applyRecovery(rgb, recovery, metadata);
   }
 
   jpegr_uncompressed_struct Yuv420Image() {
@@ -518,59 +518,95 @@
 }
 
 TEST_F(RecoveryMapMathTest, PqInvOetfLUT) {
-    float increment = 1.0 / 1024.0;
-    float value = 0.0f;
-    for (int idx = 0; idx < 1024; idx++, value += increment) {
+    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
       EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
     }
 }
 
 TEST_F(RecoveryMapMathTest, HlgInvOetfLUT) {
-    float increment = 1.0 / 1024.0;
-    float value = 0.0f;
-    for (int idx = 0; idx < 1024; idx++, value += increment) {
+    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
       EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
     }
 }
 
 TEST_F(RecoveryMapMathTest, pqOetfLUT) {
-    float increment = 1.0 / 1024.0;
-    float value = 0.0f;
-    for (int idx = 0; idx < 1024; idx++, value += increment) {
+    for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
       EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
     }
 }
 
 TEST_F(RecoveryMapMathTest, hlgOetfLUT) {
-    float increment = 1.0 / 1024.0;
-    float value = 0.0f;
-    for (int idx = 0; idx < 1024; idx++, value += increment) {
+    for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
       EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
     }
 }
 
 TEST_F(RecoveryMapMathTest, srgbInvOetfLUT) {
-    float increment = 1.0 / 1024.0;
-    float value = 0.0f;
-    for (int idx = 0; idx < 1024; idx++, value += increment) {
+    for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
       EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
     }
 }
 
 TEST_F(RecoveryMapMathTest, applyRecoveryLUT) {
-  float increment = 2.0 / kRecoveryFactorNumEntries;
-  for (float hdrRatio = 1.0f; hdrRatio <= 10.0f; hdrRatio += 1.0f)  {
-    RecoveryLUT recoveryLUT(hdrRatio);
-    for (float value = -1.0f; value <= -1.0f; value += increment) {
-      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, hdrRatio),
+  for (int boost = 1; boost <= 10; boost++) {
+    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
+                                .minContentBoost = 1.0f / static_cast<float>(boost) };
+    RecoveryLUT recoveryLUT(&metadata);
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
                       applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
-      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, hdrRatio),
+      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
                       applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
-      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, hdrRatio),
+      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
                       applyRecoveryLUT(RgbRed(), value, recoveryLUT));
-      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, hdrRatio),
+      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
                       applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
-      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, hdrRatio),
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
+                      applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+    }
+  }
+
+  for (int boost = 1; boost <= 10; boost++) {
+    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
+                                .minContentBoost = 1.0f };
+    RecoveryLUT recoveryLUT(&metadata);
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
+                      applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
+                      applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
+                      applyRecoveryLUT(RgbRed(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
+                      applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
+                      applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+    }
+  }
+
+  for (int boost = 1; boost <= 10; boost++) {
+    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost),
+                                .minContentBoost = 1.0f / pow(static_cast<float>(boost),
+                                                              1.0f / 3.0f) };
+    RecoveryLUT recoveryLUT(&metadata);
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) {
+      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1);
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),
+                      applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),
+                      applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),
+                      applyRecoveryLUT(RgbRed(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),
+                      applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),
                       applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
     }
   }
@@ -623,60 +659,121 @@
 }
 
 TEST_F(RecoveryMapMathTest, EncodeRecovery) {
-  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127);
-  EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127);
-  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0);
-  EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0);
+  jpegr_metadata metadata = { .maxContentBoost = 4.0f,
+                              .minContentBoost = 1.0f / 4.0f };
 
-  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127);
-  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255);
-  EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255);
-  EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0);
-  EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0);
-  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191);
-  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63);
+  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127);
+  EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(0.5f, 0.0f, &metadata), 0);
 
-  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255);
-  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0);
-  EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191);
-  EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63);
+  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 127);
+  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(1.0f, 5.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(4.0f, 1.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(4.0f, 0.5f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 191);
+  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 63);
 
-  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255);
-  EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0);
-  EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191);
-  EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63);
+  metadata.maxContentBoost = 2.0f;
+  metadata.minContentBoost = 1.0f / 2.0f;
+
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, &metadata), 191);
+  EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, &metadata), 63);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 1.0f / 8.0f;
+
+  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(8.0f, 1.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, &metadata), 191);
+  EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, &metadata), 63);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 1.0f;
+
+  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
+
+  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 0);
+  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 170);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 85);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 0.5f;
+
+  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 63);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0);
+
+  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 63);
+  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255);
+  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 191);
+  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 127);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.7071f, &metadata), 31);
+  EXPECT_EQ(encodeRecovery(1.0f, 0.5f, &metadata), 0);
 }
 
 TEST_F(RecoveryMapMathTest, ApplyRecovery) {
-  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack());
-  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack());
-  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack());
+  jpegr_metadata metadata = { .maxContentBoost = 4.0f,
+                              .minContentBoost = 1.0f / 4.0f };
 
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite());
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack());
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack());
+  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, &metadata), RgbBlack());
 
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite());
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f);
 
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite());
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f);
-  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f);
+  metadata.maxContentBoost = 2.0f;
+  metadata.minContentBoost = 1.0f / 2.0f;
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 1.0f / 8.0f;
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 1.0f;
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 0.5f;
+
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite());
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
 
   Color e = {{{ 0.0f, 0.5f, 1.0f }}};
+  metadata.maxContentBoost = 4.0f;
+  metadata.minContentBoost = 1.0f / 4.0f;
 
-  EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f);
-  EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f);
-  EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e);
-  EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f);
-  EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, &metadata), e / 4.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.25f, &metadata), e / 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, &metadata), e);
+  EXPECT_RGB_NEAR(applyRecovery(e, 0.75f, &metadata), e * 2.0f);
+  EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, &metadata), e * 4.0f);
 }
 
 TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
@@ -785,8 +882,10 @@
       // Instead of reimplementing the sampling algorithm, confirm that the
       // sample output is within the range of the min and max of the nearest
       // points.
-      EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
+      EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
                   testing::AllOf(testing::Ge(min), testing::Le(max)));
+      EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
+                sampleMap(&image, kMapScaleFactor, x, y));
     }
   }
 }
@@ -882,60 +981,89 @@
 }
 
 TEST_F(RecoveryMapMathTest, ApplyMap) {
-  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f),
+  jpegr_metadata metadata = { .maxContentBoost = 8.0f,
+                              .minContentBoost = 1.0f / 8.0f };
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
                 RgbWhite() * 8.0f);
-  EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata),
                 RgbBlack());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata),
                   RgbRed() * 8.0f);
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata),
                   RgbGreen() * 8.0f);
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata),
                   RgbBlue() * 8.0f);
 
-  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata),
                 RgbWhite() * sqrt(8.0f));
-  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata),
                 RgbBlack());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata),
                   RgbRed() * sqrt(8.0f));
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata),
                   RgbGreen() * sqrt(8.0f));
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata),
                   RgbBlue() * sqrt(8.0f));
 
-  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
                 RgbWhite());
-  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata),
                 RgbBlack());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata),
                   RgbRed());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata),
                   RgbGreen());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata),
                   RgbBlue());
 
-  EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
                 RgbWhite() / sqrt(8.0f));
-  EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata),
                 RgbBlack());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata),
                   RgbRed() / sqrt(8.0f));
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata),
                   RgbGreen() / sqrt(8.0f));
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata),
                   RgbBlue() / sqrt(8.0f));
 
-  EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
                 RgbWhite() / 8.0f);
-  EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f),
+  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata),
                 RgbBlack());
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata),
                   RgbRed() / 8.0f);
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata),
                   RgbGreen() / 8.0f);
-  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f),
+  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata),
                   RgbBlue() / 8.0f);
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 1.0f;
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
+                RgbWhite() * 8.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata),
+                RgbWhite() * 4.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata),
+                RgbWhite() * 2.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
+                RgbWhite());
+
+  metadata.maxContentBoost = 8.0f;
+  metadata.minContentBoost = 0.5f;;
+
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
+                RgbWhite() * 8.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata),
+                RgbWhite() * 4.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
+                RgbWhite() * 2.0f);
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
+                RgbWhite());
+  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
+                RgbWhite() / 2.0f);
 }
 
-} // namespace android::recoverymap
+} // namespace android::jpegrecoverymap
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 2a3dba7..495334e 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -46,8 +46,11 @@
           "include-filter": "android.view.cts.MotionEventTest",
           "include-filter": "android.view.cts.PointerCaptureTest",
           "include-filter": "android.view.cts.TooltipTest",
+          "include-filter": "android.view.cts.TouchDelegateTest",
           "include-filter": "android.view.cts.VelocityTrackerTest",
-          "include-filter": "android.view.cts.VerifyInputEventTest"
+          "include-filter": "android.view.cts.VerifyInputEventTest",
+          "include-filter": "android.view.cts.ViewTest",
+          "include-filter": "android.view.cts.ViewUnbufferedTest"
         }
       ]
     },
@@ -55,7 +58,8 @@
       "name": "CtsWidgetTestCases",
       "options": [
         {
-          "include-filter": "android.widget.cts.NumberPickerTest"
+          "include-filter": "android.widget.cts.NumberPickerTest",
+          "include-filter": "android.widget.cts.SeekBarTest"
         }
       ]
     },
@@ -138,8 +142,11 @@
           "include-filter": "android.view.cts.MotionEventTest",
           "include-filter": "android.view.cts.PointerCaptureTest",
           "include-filter": "android.view.cts.TooltipTest",
+          "include-filter": "android.view.cts.TouchDelegateTest",
           "include-filter": "android.view.cts.VelocityTrackerTest",
-          "include-filter": "android.view.cts.VerifyInputEventTest"
+          "include-filter": "android.view.cts.VerifyInputEventTest",
+          "include-filter": "android.view.cts.ViewTest",
+          "include-filter": "android.view.cts.ViewUnbufferedTest"
         }
       ]
     },
@@ -147,7 +154,8 @@
       "name": "CtsWidgetTestCases",
       "options": [
         {
-          "include-filter": "android.widget.cts.NumberPickerTest"
+          "include-filter": "android.widget.cts.NumberPickerTest",
+          "include-filter": "android.widget.cts.SeekBarTest"
         }
       ]
     },
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 398cdf9..e9c8335 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -87,14 +87,15 @@
 
 // ---------------------------------------------------------------------------
 
-RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback)
+RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<SensorCallback> callback)
   : BaseSensor(sensor), mCallback(std::move(callback)) {
 }
 
 status_t RuntimeSensor::activate(void*, bool enabled) {
     if (enabled != mEnabled) {
         mEnabled = enabled;
-        mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+        return mCallback->onConfigurationChanged(mSensor.getHandle(), mEnabled, mSamplingPeriodNs,
+                mBatchReportLatencyNs);
     }
     return OK;
 }
@@ -105,7 +106,8 @@
         mSamplingPeriodNs = samplingPeriodNs;
         mBatchReportLatencyNs = maxBatchReportLatencyNs;
         if (mEnabled) {
-            mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+            return mCallback->onConfigurationChanged(mSensor.getHandle(), mEnabled,
+                    mSamplingPeriodNs, mBatchReportLatencyNs);
         }
     }
     return OK;
@@ -115,7 +117,8 @@
     if (mSamplingPeriodNs != ns) {
         mSamplingPeriodNs = ns;
         if (mEnabled) {
-            mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+            return mCallback->onConfigurationChanged(mSensor.getHandle(), mEnabled,
+                    mSamplingPeriodNs, mBatchReportLatencyNs);
         }
     }
     return OK;
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 5ee5e12..c446d61 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -108,12 +108,12 @@
 public:
     static constexpr int DEFAULT_DEVICE_ID = 0;
 
-    class StateChangeCallback : public virtual RefBase {
+    class SensorCallback : public virtual RefBase {
       public:
-        virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs,
-                                    int64_t batchReportLatencyNs) = 0;
+        virtual status_t onConfigurationChanged(int handle, bool enabled, int64_t samplingPeriodNs,
+                                                int64_t batchReportLatencyNs) = 0;
     };
-    RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback);
+    RuntimeSensor(const sensor_t& sensor, sp<SensorCallback> callback);
     virtual status_t activate(void* ident, bool enabled) override;
     virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
                            int64_t maxBatchReportLatencyNs) override;
@@ -125,7 +125,7 @@
     bool mEnabled = false;
     int64_t mSamplingPeriodNs = 0;
     int64_t mBatchReportLatencyNs = 0;
-    sp<StateChangeCallback> mCallback;
+    sp<SensorCallback> mCallback;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 5c98614..3a0329c 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -116,16 +116,17 @@
     return nextHandle++;
 }
 
-class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback {
+class RuntimeSensorCallbackProxy : public RuntimeSensor::SensorCallback {
  public:
-    RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback)
+    RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorCallback> callback)
         : mCallback(std::move(callback)) {}
-    void onStateChanged(bool enabled, int64_t samplingPeriodNs,
-                        int64_t batchReportLatencyNs) override {
-        mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs);
+    status_t onConfigurationChanged(int handle, bool enabled, int64_t samplingPeriodNs,
+                                    int64_t batchReportLatencyNs) override {
+        return mCallback->onConfigurationChanged(handle, enabled, samplingPeriodNs,
+                batchReportLatencyNs);
     }
  private:
-    sp<SensorService::RuntimeSensorStateChangeCallback> mCallback;
+    sp<SensorService::RuntimeSensorCallback> mCallback;
 };
 
 } // namespace
@@ -166,7 +167,7 @@
 }
 
 int SensorService::registerRuntimeSensor(
-    const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) {
+        const sensor_t& sensor, int deviceId, sp<RuntimeSensorCallback> callback) {
     int handle = 0;
     while (handle == 0 || !mSensors.isNewHandle(handle)) {
         handle = nextRuntimeSensorHandle();
@@ -179,7 +180,7 @@
     ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s",
             handle, sensor.type, sensor.name);
 
-    sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback(
+    sp<RuntimeSensor::SensorCallback> runtimeSensorCallback(
         new RuntimeSensorCallbackProxy(std::move(callback)));
     sensor_t runtimeSensor = sensor;
     // force the handle to be consistent
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 0798279..3f6a895 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -147,12 +147,13 @@
         virtual void onProximityActive(bool isActive) = 0;
     };
 
-    class RuntimeSensorStateChangeCallback : public virtual RefBase {
+    class RuntimeSensorCallback : public virtual RefBase {
     public:
         // Note that the callback is invoked from an async thread and can interact with the
         // SensorService directly.
-        virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos,
-                                    int64_t batchReportLatencyNanos) = 0;
+        virtual status_t onConfigurationChanged(int handle, bool enabled,
+                                                int64_t samplingPeriodNanos,
+                                                int64_t batchReportLatencyNanos) = 0;
     };
 
     static char const* getServiceName() ANDROID_API { return "sensorservice"; }
@@ -182,7 +183,7 @@
     status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
 
     int registerRuntimeSensor(const sensor_t& sensor, int deviceId,
-                              sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API;
+                              sp<RuntimeSensorCallback> callback) ANDROID_API;
     status_t unregisterRuntimeSensor(int handle) ANDROID_API;
     status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
 
diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp
index cab53c1..9ef4ca8 100644
--- a/services/sensorservice/aidl/DirectReportChannel.cpp
+++ b/services/sensorservice/aidl/DirectReportChannel.cpp
@@ -32,7 +32,7 @@
         int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
         int32_t* _aidl_return) {
     int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate));
-    if (token <= 0) {
+    if (token < 0) {
         return ndk::ScopedAStatus::fromServiceSpecificError(token);
     }
     *_aidl_return = token;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 4777f13..a8322d8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -97,6 +97,8 @@
     std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
 
     std::vector<BorderRenderInfo> borderInfoList;
+
+    bool hasTrustedPresentationListener = false;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 5bb2497..32684fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -212,6 +212,8 @@
     float currentSdrHdrRatio = 1.f;
     float desiredSdrHdrRatio = 1.f;
 
+    bool isInternalDisplayOverlay = false;
+
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 52ebd9e..a3d8639 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -153,6 +153,10 @@
         Region aboveOpaqueLayers;
         // The region of the output which should be considered dirty
         Region dirtyRegion;
+        // The region of the output which is covered by layers, excluding display overlays. This
+        // only has a value if there's something needing it, like when a TrustedPresentationListener
+        // is set
+        std::optional<Region> aboveCoveredLayersExcludingOverlays;
     };
 
     virtual ~Output();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b86782f..7b0af3a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -63,9 +63,15 @@
     // The portion of the layer that is not obscured and is also opaque
     Region visibleNonTransparentRegion;
 
-    // The portion of the layer that is obscured by opaque layers on top
+    // The portion of the layer that is obscured by all layers on top. This includes transparent and
+    // opaque.
     Region coveredRegion;
 
+    // The portion of the layer that is obscured by all layers on top excluding display overlays.
+    // This only has a value if there's something needing it, like when a
+    // TrustedPresentationListener is set.
+    std::optional<Region> coveredRegionExcludingDisplayOverlays;
+
     // The visibleRegion transformed to output space
     Region outputSpaceVisibleRegion;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 403385e..7d94316 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -31,6 +31,7 @@
 #include <ftl/future.h>
 #include <gui/TraceUtils.h>
 
+#include <optional>
 #include <thread>
 
 #include "renderengine/ExternalTexture.h"
@@ -480,6 +481,9 @@
 
     // Process the layers to determine visibility and coverage
     compositionengine::Output::CoverageState coverage{layerFESet};
+    coverage.aboveCoveredLayersExcludingOverlays = refreshArgs.hasTrustedPresentationListener
+            ? std::make_optional<Region>()
+            : std::nullopt;
     collectVisibleLayers(refreshArgs, coverage);
 
     // Compute the resulting coverage for this output, and store it for later
@@ -534,6 +538,9 @@
         return;
     }
 
+    bool computeAboveCoveredExcludingOverlays =
+            coverage.aboveCoveredLayersExcludingOverlays && !layerFEState->isInternalDisplayOverlay;
+
     /*
      * opaqueRegion: area of a surface that is fully opaque.
      */
@@ -575,6 +582,11 @@
      */
     Region shadowRegion;
 
+    /**
+     * covered region above excluding internal display overlay layers
+     */
+    std::optional<Region> coveredRegionExcludingDisplayOverlays = std::nullopt;
+
     const ui::Transform& tr = layerFEState->geomLayerTransform;
 
     // Get the visible region
@@ -647,6 +659,12 @@
     // Update accumAboveCoveredLayers for next (lower) layer
     coverage.aboveCoveredLayers.orSelf(visibleRegion);
 
+    if (CC_UNLIKELY(computeAboveCoveredExcludingOverlays)) {
+        coveredRegionExcludingDisplayOverlays =
+                coverage.aboveCoveredLayersExcludingOverlays->intersect(visibleRegion);
+        coverage.aboveCoveredLayersExcludingOverlays->orSelf(visibleRegion);
+    }
+
     // subtract the opaque region covered by the layers above us
     visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
 
@@ -733,6 +751,10 @@
             ? outputState.transform.transform(
                       transparentRegion.intersect(outputState.layerStackSpace.getContent()))
             : Region();
+    if (CC_UNLIKELY(computeAboveCoveredExcludingOverlays)) {
+        outputLayerState.coveredRegionExcludingDisplayOverlays =
+                std::move(coveredRegionExcludingDisplayOverlays);
+    }
 }
 
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 6b69ce7..1b86cd3 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -585,6 +585,7 @@
         case Composition::CURSOR:
         case Composition::DEVICE:
         case Composition::DISPLAY_DECORATION:
+        case Composition::REFRESH_RATE_INDICATOR:
             writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
             break;
         case Composition::INVALID:
@@ -780,6 +781,7 @@
         case Composition::CURSOR:
         case Composition::SIDEBAND:
         case Composition::DISPLAY_DECORATION:
+        case Composition::REFRESH_RATE_INDICATOR:
             result = (to == Composition::CLIENT || to == Composition::DEVICE);
             break;
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 2fc029f..6064126 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -151,6 +151,10 @@
                 // A for "Alpha", since the decoration is an alpha layer.
                 result.append("A");
                 break;
+            case aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR:
+                // R for "Refresh", since the layer is Refresh rate overlay.
+                result.append("R");
+                break;
         }
     }
     return result;
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 933f616..8555fd6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -115,8 +115,9 @@
     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(setHdrConversionStrategy,
+                 status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                          aidl::android::hardware::graphics::common::Hdr*));
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
     MOCK_METHOD(status_t, getSupportedContentTypes,
                 (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 9470552..4194a7e 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -208,6 +208,12 @@
         return ::ndk::ScopedAStatus::ok();
     }
 
+    ::ndk::ScopedAStatus onRefreshRateChangedDebug(
+            const RefreshRateChangedDebugData& refreshRateChangedDebugData) override {
+        mCallback.onRefreshRateChangedDebug(refreshRateChangedDebugData);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
 private:
     HWC2::ComposerCallback& mCallback;
 };
@@ -1411,8 +1417,10 @@
     return Error::NONE;
 }
 
-Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy) {
-    const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy);
+Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy,
+                                             Hdr* outPreferredHdrOutputType) {
+    const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy,
+                                                                      outPreferredHdrOutputType);
     if (!status.isOk()) {
         ALOGE("setHdrConversionStrategy failed %s", status.getDescription().c_str());
         return static_cast<Error>(status.getServiceSpecificError());
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index a5ddf74..d163ff2 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -238,7 +238,7 @@
     void onHotplugConnect(Display) override;
     void onHotplugDisconnect(Display) override;
     Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
-    Error setHdrConversionStrategy(HdrConversionStrategy) override;
+    Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) 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 82b677e..9b9b7fd 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -293,7 +293,7 @@
     virtual Error getHdrConversionCapabilities(
             std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
     virtual Error setHdrConversionStrategy(
-            ::aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0;
+            ::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c1c7070..23dd3e5 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -44,6 +44,7 @@
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
+#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 
 namespace android {
 
@@ -63,6 +64,8 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+
 // Implement this interface to receive hardware composer events.
 //
 // These callback functions will generally be called on a hwbinder thread, but
@@ -77,6 +80,7 @@
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
     virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
     virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0;
+    virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0;
 
 protected:
     ~ComposerCallback() = default;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6d94079..470bf76 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -796,10 +796,14 @@
     return mHdrConversionCapabilities;
 }
 
-status_t HWComposer::setHdrConversionStrategy(HdrConversionStrategy hdrConversionStrategy) {
-    const auto error = mComposer->setHdrConversionStrategy(hdrConversionStrategy);
+status_t HWComposer::setHdrConversionStrategy(
+        HdrConversionStrategy hdrConversionStrategy,
+        aidl::android::hardware::graphics::common::Hdr* outPreferredHdrOutputType) {
+    const auto error =
+            mComposer->setHdrConversionStrategy(hdrConversionStrategy, outPreferredHdrOutputType);
     if (error != hal::Error::NONE) {
         ALOGE("Error in setting HDR conversion strategy %s", to_string(error).c_str());
+        return INVALID_OPERATION;
     }
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index acebfb2..95568eb 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -44,6 +44,7 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/Hdr.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>
@@ -294,7 +295,8 @@
     virtual std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
     getHdrConversionCapabilities() const = 0;
     virtual status_t setHdrConversionStrategy(
-            aidl::android::hardware::graphics::common::HdrConversionStrategy) = 0;
+            aidl::android::hardware::graphics::common::HdrConversionStrategy,
+            aidl::android::hardware::graphics::common::Hdr*) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -449,7 +451,8 @@
     std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
     getHdrConversionCapabilities() const override;
     status_t setHdrConversionStrategy(
-            aidl::android::hardware::graphics::common::HdrConversionStrategy) override;
+            aidl::android::hardware::graphics::common::HdrConversionStrategy,
+            aidl::android::hardware::graphics::common::Hdr*) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 537d545..bf3089f 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -113,6 +113,8 @@
             return "Sideband";
         case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
             return "DisplayDecoration";
+        case aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR:
+            return "RefreshRateIndicator";
         default:
             return "Unknown";
     }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 6fdb2d7..9bc62b6 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1354,7 +1354,7 @@
     return Error::UNSUPPORTED;
 }
 
-Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy) {
+Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy, Hdr*) {
     return Error::UNSUPPORTED;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 8280af2..2bab1fe 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -346,8 +346,8 @@
     Error getHdrConversionCapabilities(
             std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>*)
             override;
-    Error setHdrConversionStrategy(
-            aidl::android::hardware::graphics::common::HdrConversionStrategy) override;
+    Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                                   Hdr*) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index a4fac1c..afe557e 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -19,6 +19,7 @@
 #define LOG_TAG "LayerHierarchy"
 
 #include "LayerHierarchy.h"
+#include "LayerLog.h"
 #include "SwapErase.h"
 
 namespace android::surfaceflinger::frontend {
@@ -259,6 +260,7 @@
 }
 
 void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+    LLOGV(layer->id, "");
     LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
     if (!hierarchy) {
         // Layer was never part of the hierarchy if it was created and destroyed in the same
@@ -408,29 +410,32 @@
     if (id == UNASSIGNED_LAYER_ID) {
         return "TraversalPath{ROOT}";
     }
-    std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+    std::stringstream ss;
+    ss << "TraversalPath{.id = " << id;
 
-    if (!mirrorRootIds.empty()) {
-        debugString += ", .mirrorRootIds=";
-        for (auto rootId : mirrorRootIds) {
-            debugString += std::to_string(rootId) + ",";
-        }
+    if (mirrorRootId != UNASSIGNED_LAYER_ID) {
+        ss << ", .mirrorRootId=" << mirrorRootId;
     }
 
     if (!relativeRootIds.empty()) {
-        debugString += ", .relativeRootIds=";
+        ss << ", .relativeRootIds=";
         for (auto rootId : relativeRootIds) {
-            debugString += std::to_string(rootId) + ",";
+            ss << rootId << ",";
         }
     }
 
     if (hasRelZLoop()) {
-        debugString += ", hasRelZLoop=true invalidRelativeRootId=";
-        debugString += std::to_string(invalidRelativeRootId) + ",";
+        ss << "hasRelZLoop=true invalidRelativeRootId=" << invalidRelativeRootId << ",";
     }
+    ss << "}";
+    return ss.str();
+}
 
-    debugString += "}";
-    return debugString;
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::getMirrorRoot() const {
+    LOG_ALWAYS_FATAL_IF(!isClone(), "Cannot get mirror root of a non cloned node");
+    TraversalPath mirrorRootPath = *this;
+    mirrorRootPath.id = mirrorRootId;
+    return mirrorRootPath;
 }
 
 // Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
@@ -438,16 +443,13 @@
 LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
                                                                    uint32_t layerId,
                                                                    LayerHierarchy::Variant variant)
-      : mTraversalPath(traversalPath),
-        mParentId(traversalPath.id),
-        mParentVariant(traversalPath.variant),
-        mParentDetached(traversalPath.detached) {
+      : mTraversalPath(traversalPath), mParentPath(traversalPath) {
     // Update the traversal id with the child layer id and variant. Parent id and variant are
     // stored to reset the id upon destruction.
     traversalPath.id = layerId;
     traversalPath.variant = variant;
     if (variant == LayerHierarchy::Variant::Mirror) {
-        traversalPath.mirrorRootIds.emplace_back(layerId);
+        traversalPath.mirrorRootId = layerId;
     } else if (variant == LayerHierarchy::Variant::Relative) {
         if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
                       layerId) != traversalPath.relativeRootIds.end()) {
@@ -462,16 +464,16 @@
     // Reset the traversal id to its original parent state using the state that was saved in
     // the constructor.
     if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
-        mTraversalPath.mirrorRootIds.pop_back();
+        mTraversalPath.mirrorRootId = mParentPath.mirrorRootId;
     } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
         mTraversalPath.relativeRootIds.pop_back();
     }
     if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
         mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
     }
-    mTraversalPath.id = mParentId;
-    mTraversalPath.variant = mParentVariant;
-    mTraversalPath.detached = mParentDetached;
+    mTraversalPath.id = mParentPath.id;
+    mTraversalPath.variant = mParentPath.variant;
+    mTraversalPath.detached = mParentPath.detached;
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index ca8d301..2ab897b 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -50,12 +50,32 @@
         ftl_last = Mirror,
     };
     // Represents a unique path to a node.
+    // The layer hierarchy is represented as a graph. Each node can be visited by multiple parents.
+    // This allows us to represent mirroring in an efficient way. See the example below:
+    // root
+    // ├─ A {Traversal path id = 1}
+    // ├─ B {Traversal path id = 2}
+    // │  ├─ C {Traversal path id = 3}
+    // │  ├─ D {Traversal path id = 4}
+    // │  └─ E {Traversal path id = 5}
+    // ├─ F (Mirrors B) {Traversal path id = 6}
+    // └─ G (Mirrors F) {Traversal path id = 7}
+    //
+    // C, D and E can be traversed via B or via F then B or via G then F then B.
+    // Depending on how the node is reached, its properties such as geometry or visibility might be
+    // different. And we can uniquely identify the node by keeping track of the nodes leading up to
+    // it. But to be more efficient we only need to track the nodes id and the top mirror root path.
+    // So C for example, would have the following unique traversal paths:
+    //  - {Traversal path id = 3}
+    //  - {Traversal path id = 3, mirrorRootId = 6}
+    //  - {Traversal path id = 3, mirrorRootId = 7}
+
     struct TraversalPath {
         uint32_t id;
         LayerHierarchy::Variant variant;
         // Mirrored layers can have a different geometry than their parents so we need to track
         // the mirror roots in the traversal.
-        ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+        uint32_t mirrorRootId = UNASSIGNED_LAYER_ID;
         // Relative layers can be visited twice, once by their parent and then once again by
         // their relative parent. We keep track of the roots here to detect any loops in the
         // hierarchy. If a relative root already exists in the list while building the
@@ -73,10 +93,11 @@
         // Returns true if the node or its parents are not Detached.
         bool isAttached() const { return !detached; }
         // Returns true if the node is a clone.
-        bool isClone() const { return !mirrorRootIds.empty(); }
+        bool isClone() const { return mirrorRootId != UNASSIGNED_LAYER_ID; }
+        TraversalPath getMirrorRoot() const;
 
         bool operator==(const TraversalPath& other) const {
-            return id == other.id && mirrorRootIds == other.mirrorRootIds;
+            return id == other.id && mirrorRootId == other.mirrorRootId;
         }
         std::string toString() const;
 
@@ -93,9 +114,7 @@
 
     private:
         TraversalPath& mTraversalPath;
-        uint32_t mParentId;
-        LayerHierarchy::Variant mParentVariant;
-        bool mParentDetached;
+        TraversalPath mParentPath;
     };
     LayerHierarchy(RequestedLayerState* layer);
 
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 547a852..66197be 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -22,6 +22,7 @@
 #include "LayerLifecycleManager.h"
 #include "Layer.h" // temporarily needed for LayerHandle
 #include "LayerHandle.h"
+#include "LayerLog.h"
 #include "SwapErase.h"
 
 namespace android::surfaceflinger::frontend {
@@ -36,6 +37,7 @@
     mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
     for (auto& newLayer : newLayers) {
         RequestedLayerState& layer = *newLayer.get();
+        LLOGV(layer.id, "%s layer %s", __func__, layer.getDebugStringShort().c_str());
         auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
         if (!inserted) {
             LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
@@ -146,7 +148,7 @@
     while (it != mLayers.end()) {
         RequestedLayerState* layer = it->get();
         if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
-            ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str());
+            LLOGV(layer->id, "destroyed layer %s", layer->getDebugStringShort().c_str());
             std::iter_swap(it, mLayers.end() - 1);
             mDestroyedLayers.emplace_back(std::move(mLayers.back()));
             if (it == mLayers.end() - 1) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 3ed24b2..c9aeb24 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -259,8 +259,8 @@
     }
 }
 
-void updateVisibility(LayerSnapshot& snapshot) {
-    snapshot.isVisible = snapshot.getIsVisible();
+void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+    snapshot.isVisible = visible;
 
     // TODO(b/238781169) we are ignoring this compat for now, since we will have
     // to remove any optimization based on visibility.
@@ -273,9 +273,9 @@
     // We are just using these layers for occlusion detection in
     // InputDispatcher, and obviously if they aren't visible they can't occlude
     // anything.
-    const bool visible =
+    const bool visibleForInput =
             (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
-    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
 }
 
 bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
@@ -521,7 +521,7 @@
                 }
 
                 if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
-                    updateVisibility(*snapshot);
+                    updateVisibility(*snapshot, snapshot->getIsVisible());
                     size_t oldZ = snapshot->globalZ;
                     size_t newZ = globalZ++;
                     snapshot->globalZ = newZ;
@@ -539,7 +539,8 @@
     mNumInterestingSnapshots = (int)globalZ;
     while (globalZ < mSnapshots.size()) {
         mSnapshots[globalZ]->globalZ = globalZ;
-        updateVisibility(*mSnapshots[globalZ]);
+        /* mark unreachable snapshots as explicitly invisible */
+        updateVisibility(*mSnapshots[globalZ], false);
         globalZ++;
     }
 }
@@ -634,11 +635,15 @@
     const bool forceUpdate = newSnapshot || args.forceUpdate ||
             snapshot.changes.any(RequestedLayerState::Changes::Visibility |
                                  RequestedLayerState::Changes::Created);
+    snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
+            ? parentSnapshot.outputFilter.layerStack
+            : requested.layerStack;
+
     uint32_t displayRotationFlags =
             getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
 
     // always update the buffer regardless of visibility
-    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
+    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
         snapshot.acquireFence =
                 (requested.externalTexture &&
                  requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
@@ -727,9 +732,13 @@
     if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
         snapshot.color.rgb = requested.getColor().rgb;
         snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
-        snapshot.backgroundBlurRadius =
-                args.supportsBlur ? static_cast<int>(requested.backgroundBlurRadius) : 0;
+        snapshot.backgroundBlurRadius = args.supportsBlur
+                ? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
+                : 0;
         snapshot.blurRegions = requested.blurRegions;
+        for (auto& region : snapshot.blurRegions) {
+            region.alpha = region.alpha * snapshot.color.a;
+        }
         snapshot.hdrMetadata = requested.hdrMetadata;
     }
 
@@ -965,7 +974,7 @@
     // touches from going outside the cloned area.
     if (path.isClone()) {
         snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
-        auto clonedRootSnapshot = getSnapshot(path.mirrorRootIds.back());
+        auto clonedRootSnapshot = getSnapshot(path.getMirrorRoot());
         if (clonedRootSnapshot) {
             const Rect rect =
                     displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index f4544fd..0902ab8 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -115,8 +115,8 @@
     struct TraversalPathHash {
         std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
             uint32_t hashCode = key.id * 31;
-            for (auto mirrorRoot : key.mirrorRootIds) {
-                hashCode += mirrorRoot * 31;
+            if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
+                hashCode += key.mirrorRootId * 31;
             }
             return std::hash<size_t>{}(hashCode);
         }
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index d63b126..2834084 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -144,7 +144,7 @@
     const half oldAlpha = color.a;
     const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
     const layer_state_t& clientState = resolvedComposerState.state;
-
+    const bool hadBlur = hasBlur();
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
     layer_state_t::merge(clientState);
     what = clientChanges;
@@ -174,6 +174,12 @@
                     RequestedLayerState::Changes::VisibleRegion;
         }
     }
+    if (what & (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged)) {
+        if (hadBlur != hasBlur()) {
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
+        }
+    }
     if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
         changes |= RequestedLayerState::Changes::Hierarchy;
     if (clientChanges & layer_state_t::CONTENT_CHANGES)
@@ -319,11 +325,11 @@
 }
 
 std::string RequestedLayerState::getDebugString() const {
-    return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
-            ",relativeParent=" + layerIdToString(relativeParentId) +
-            ",isRelativeOf=" + std::to_string(isRelativeOf) +
-            ",mirrorIds=" + layerIdsToString(mirrorIds) +
-            ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
+    std::stringstream debug;
+    debug << "RequestedLayerState{" << name << " parent=" << layerIdToString(parentId)
+          << " relativeParent=" << layerIdToString(relativeParentId)
+          << " mirrorId=" << layerIdsToString(mirrorIds) << " handle=" << handleAlive << " z=" << z;
+    return debug.str();
 }
 
 std::string RequestedLayerState::getDebugStringShort() const {
@@ -442,4 +448,8 @@
             windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
+bool RequestedLayerState::hasBlur() const {
+    return backgroundBlurRadius > 0 || blurRegions.size() > 0;
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 6317b95..6840b25 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -71,6 +71,7 @@
     aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
     bool hasValidRelativeParent() const;
     bool hasInputInfo() const;
+    bool hasBlur() const;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8c484f0..084d9b9 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -393,14 +393,22 @@
 
     if (!leaveState) {
         const auto outputLayer = findOutputLayerForDisplay(display);
-        if (outputLayer != nullptr && snapshot != nullptr) {
-            mLastComputedTrustedPresentationState =
-                    computeTrustedPresentationState(snapshot->geomLayerBounds,
-                                                    snapshot->sourceBounds(),
-                                                    outputLayer->getState().coveredRegion,
-                                                    snapshot->transformedBounds, snapshot->alpha,
-                                                    snapshot->geomLayerTransform,
-                                                    mTrustedPresentationThresholds);
+        if (outputLayer != nullptr) {
+            if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) {
+                Region coveredRegion =
+                        *outputLayer->getState().coveredRegionExcludingDisplayOverlays;
+                mLastComputedTrustedPresentationState =
+                        computeTrustedPresentationState(snapshot->geomLayerBounds,
+                                                        snapshot->sourceBounds(), coveredRegion,
+                                                        snapshot->transformedBounds,
+                                                        snapshot->alpha,
+                                                        snapshot->geomLayerTransform,
+                                                        mTrustedPresentationThresholds);
+            } else {
+                ALOGE("CoveredRegionExcludingDisplayOverlays was not set for %s. Don't compute "
+                      "TrustedPresentationState",
+                      getDebugName());
+            }
         }
     }
     const bool newState = mLastComputedTrustedPresentationState;
@@ -457,10 +465,15 @@
     float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight();
     fractionRendered *= boundsOverSourceW * boundsOverSourceH;
 
-    Rect coveredBounds = coveredRegion.bounds();
-    fractionRendered *= (1 -
-                         ((coveredBounds.width() / (float)screenBounds.getWidth()) *
-                          coveredBounds.height() / (float)screenBounds.getHeight()));
+    Region tJunctionFreeRegion = Region::createTJunctionFreeRegion(coveredRegion);
+    // Compute the size of all the rects since they may be disconnected.
+    float coveredSize = 0;
+    for (auto rect = tJunctionFreeRegion.begin(); rect < tJunctionFreeRegion.end(); rect++) {
+        float size = rect->width() * rect->height();
+        coveredSize += size;
+    }
+
+    fractionRendered *= (1 - (coveredSize / (screenBounds.getWidth() * screenBounds.getHeight())));
 
     if (fractionRendered < thresholds.minFractionRendered) {
         return false;
@@ -770,7 +783,7 @@
 // transaction
 // ----------------------------------------------------------------------------
 
-uint32_t Layer::doTransaction(uint32_t flags) {
+uint32_t Layer::doTransaction(uint32_t flags, nsecs_t latchTime) {
     ATRACE_CALL();
 
     // TODO: This is unfortunate.
@@ -798,23 +811,24 @@
         mFlinger->mUpdateInputInfo = true;
     }
 
-    commitTransaction(mDrawingState);
+    commitTransaction(mDrawingState, latchTime);
 
     return flags;
 }
 
-void Layer::commitTransaction(State&) {
+void Layer::commitTransaction(State&, nsecs_t currentLatchTime) {
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
     // bufferSurfaceFrameTX will be presented in latchBuffer.
     for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
         if (surfaceFrame->getPresentState() != PresentState::Presented) {
             // With applyPendingStates, we could end up having presented surfaceframes from previous
             // states
-            surfaceFrame->setPresentState(PresentState::Presented);
+            surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
             mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
         }
     }
     mDrawingState.bufferlessSurfaceFramesTX.clear();
+    mLastLatchTime = currentLatchTime;
 }
 
 uint32_t Layer::clearTransactionFlags(uint32_t mask) {
@@ -3992,6 +4006,7 @@
     snapshot->bufferSize = getBufferSize(mDrawingState);
     snapshot->externalTexture = mBufferInfo.mBuffer;
     snapshot->hasReadyFrame = hasReadyFrame();
+    snapshot->isInternalDisplayOverlay = isInternalDisplayOverlay();
     preparePerFrameCompositionState();
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bf8cc7e..2955daf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -620,7 +620,7 @@
      * doTransaction - process the transaction. This is a good place to figure
      * out which attributes of the surface have changed.
      */
-    virtual uint32_t doTransaction(uint32_t transactionFlags);
+    virtual uint32_t doTransaction(uint32_t transactionFlags, nsecs_t currentLatchTime);
 
     /*
      * Remove relative z for the layer if its relative parent is not part of the
@@ -857,7 +857,7 @@
     void preparePerFrameCompositionState();
     void preparePerFrameBufferCompositionState();
     void preparePerFrameEffectsCompositionState();
-    virtual void commitTransaction(State& stateToCommit);
+    virtual void commitTransaction(State& stateToCommit, nsecs_t currentLatchTime = 0);
     void gatherBufferInfo();
     void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c76e41f..5f95859 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -871,7 +871,7 @@
     // initialize our drawing state
     mDrawingState = mCurrentState;
 
-    // set initial conditions (e.g. unblank default device)
+    onActiveDisplayChangedLocked(nullptr, *display);
     initializeDisplays();
 
     mPowerAdvisor->init();
@@ -1580,7 +1580,8 @@
 }
 
 status_t SurfaceFlinger::setHdrConversionStrategy(
-        const gui::HdrConversionStrategy& hdrConversionStrategy) {
+        const gui::HdrConversionStrategy& hdrConversionStrategy,
+        int32_t* outPreferredHdrOutputType) {
     bool hdrOutputConversionSupport;
     getHdrOutputConversionSupport(&hdrOutputConversionSupport);
     if (hdrOutputConversionSupport == false) {
@@ -1592,11 +1593,16 @@
                 aidl::android::hardware::graphics::common::HdrConversionStrategy;
         using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
         AidlHdrConversionStrategy aidlConversionStrategy;
+        status_t status;
+        aidl::android::hardware::graphics::common::Hdr aidlPreferredHdrOutputType;
         switch (hdrConversionStrategy.getTag()) {
             case GuiHdrConversionStrategyTag::passthrough: {
                 aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>(
                         hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>());
-                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
             }
             case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: {
                 auto autoHdrTypes =
@@ -1609,7 +1615,10 @@
                 }
                 aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>(
                         aidlAutoHdrTypes);
-                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
             }
             case GuiHdrConversionStrategyTag::forceHdrConversion: {
                 auto forceHdrConversion =
@@ -1618,7 +1627,10 @@
                 aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>(
                         static_cast<aidl::android::hardware::graphics::common::Hdr>(
                                 forceHdrConversion));
-                return getHwComposer().setHdrConversionStrategy(aidlConversionStrategy);
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
             }
         }
     });
@@ -2080,6 +2092,10 @@
     mScheduler->forceNextResync();
 }
 
+void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {
+    // TODO(b/202734676) update refresh rate value on the RefreshRateOverlay
+}
+
 void SurfaceFlinger::setVsyncEnabled(bool enabled) {
     ATRACE_CALL();
 
@@ -2513,6 +2529,7 @@
     refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
     refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
     refreshArgs.expectedPresentTime = mExpectedPresentTime.ns();
+    refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
 
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
@@ -3127,24 +3144,11 @@
         const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                 getKernelIdleTimerProperties(compositionDisplay->getId());
 
-        const auto enableFrameRateOverride = [&] {
-            using Config = scheduler::RefreshRateSelector::Config;
-            if (!sysprop::enable_frame_rate_override(true)) {
-                return Config::FrameRateOverride::Disabled;
-            }
-
-            if (sysprop::frame_rate_override_for_native_rates(false)) {
-                return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
-            }
-
-            if (!sysprop::frame_rate_override_global(true)) {
-                return Config::FrameRateOverride::AppOverride;
-            }
-
-            return Config::FrameRateOverride::Enabled;
-        }();
-
-        scheduler::RefreshRateSelector::Config config =
+        using Config = scheduler::RefreshRateSelector::Config;
+        const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
+                ? Config::FrameRateOverride::Enabled
+                : Config::FrameRateOverride::Disabled;
+        Config config =
                 {.enableFrameRateOverride = enableFrameRateOverride,
                  .frameRateMultipleThreshold =
                          base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
@@ -3390,7 +3394,7 @@
 
             // TODO(b/175678251) Call a listener instead.
             if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
-                updateInternalDisplayVsyncLocked(display);
+                updateActiveDisplayVsyncLocked(*display);
             }
         }
         return;
@@ -3418,15 +3422,15 @@
             display->setDisplaySize(currentState.width, currentState.height);
 
             if (display->getId() == mActiveDisplayId) {
-                onActiveDisplaySizeChanged(display);
+                onActiveDisplaySizeChanged(*display);
             }
         }
     }
 }
 
-void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->getActiveMode().fps;
+    const Fps refreshRate = activeDisplay.getActiveMode().fps;
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3859,7 +3863,7 @@
     for (Layer* offscreenLayer : mOffscreenLayers) {
         offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
             if (layer->clearTransactionFlags(eTransactionNeeded)) {
-                layer->doTransaction(0);
+                layer->doTransaction(0, 0);
                 layer->commitChildList();
             }
         });
@@ -3895,7 +3899,7 @@
     // second frame. But layer 0's second frame could be waiting on display.
     mDrawingState.traverse([&](Layer* layer) {
         if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
-            const uint32_t flags = layer->doTransaction(0);
+            const uint32_t flags = layer->doTransaction(0, latchTime);
             if (flags & Layer::eVisibleRegion) {
                 mVisibleRegionsDirty = true;
             }
@@ -4163,7 +4167,7 @@
                                       transaction.displays, transaction.flags,
                                       transaction.inputWindowCommands,
                                       transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                      transaction.buffer, transaction.postTime,
+                                      std::move(transaction.uncacheBufferIds), transaction.postTime,
                                       transaction.permissions, transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
@@ -4251,8 +4255,9 @@
         const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+        bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        uint64_t transactionId) {
     ATRACE_CALL();
 
     uint32_t permissions =
@@ -4286,6 +4291,15 @@
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
 
+    std::vector<uint64_t> uncacheBufferIds;
+    uncacheBufferIds.reserve(uncacheBuffers.size());
+    for (const auto& uncacheBuffer : uncacheBuffers) {
+        sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+        if (buffer != nullptr) {
+            uncacheBufferIds.push_back(buffer->getId());
+        }
+    }
+
     std::vector<ResolvedComposerState> resolvedStates;
     resolvedStates.reserve(states.size());
     for (auto& state : states) {
@@ -4303,14 +4317,22 @@
         }
     }
 
-    TransactionState state{frameTimelineInfo,  resolvedStates,
-                           displays,           flags,
-                           applyToken,         inputWindowCommands,
-                           desiredPresentTime, isAutoTimestamp,
-                           uncacheBuffer,      postTime,
-                           permissions,        hasListenerCallbacks,
-                           listenerCallbacks,  originPid,
-                           originUid,          transactionId};
+    TransactionState state{frameTimelineInfo,
+                           resolvedStates,
+                           displays,
+                           flags,
+                           applyToken,
+                           inputWindowCommands,
+                           desiredPresentTime,
+                           isAutoTimestamp,
+                           std::move(uncacheBufferIds),
+                           postTime,
+                           permissions,
+                           hasListenerCallbacks,
+                           listenerCallbacks,
+                           originPid,
+                           originUid,
+                           transactionId};
 
     if (mTransactionTracing) {
         mTransactionTracing->addQueuedTransaction(state);
@@ -4333,7 +4355,7 @@
                                            Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
-                                           const client_cache_t& uncacheBuffer,
+                                           const std::vector<uint64_t>& uncacheBufferIds,
                                            const int64_t postTime, uint32_t permissions,
                                            bool hasListenerCallbacks,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -4383,11 +4405,8 @@
         ALOGE("Only privileged callers are allowed to send input commands.");
     }
 
-    if (uncacheBuffer.isValid()) {
-        sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
-        if (buffer != nullptr) {
-            mBufferIdsToUncache.push_back(buffer->getId());
-        }
+    for (uint64_t uncacheBufferId : uncacheBufferIds) {
+        mBufferIdsToUncache.push_back(uncacheBufferId);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -5203,21 +5222,15 @@
     if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
         // Turn on the display
 
-        // Activate the display (which involves a modeset to the active mode):
-        //     1) When the first (a.k.a. primary) display is powered on during boot.
-        //     2) When the inner or outer display of a foldable is powered on. This condition relies
-        //        on the above DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the
-        //        same display, then the `activeDisplay->isPoweredOn()` below is true, such that the
-        //        display is not activated every time it is powered on.
+        // Activate the display (which involves a modeset to the active mode) when the inner or
+        // outer display of a foldable is powered on. This condition relies on the above
+        // DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the same display,
+        // then the `activeDisplay->isPoweredOn()` below is true, such that the display is not
+        // activated every time it is powered on.
         //
         // TODO(b/255635821): Remove the concept of active display.
-        const bool activeDisplayChanged =
-                isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn());
-
-        static bool sPrimaryDisplay = true;
-        if (sPrimaryDisplay || activeDisplayChanged) {
-            onActiveDisplayChangedLocked(activeDisplay, display);
-            sPrimaryDisplay = false;
+        if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+            onActiveDisplayChangedLocked(activeDisplay.get(), *display);
         }
 
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
@@ -7516,35 +7529,35 @@
     mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
 }
 
-void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<const DisplayDevice>& activeDisplay) {
-    mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight());
-    getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
+void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
+    mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
+    getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
 }
 
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
-                                                  const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
+                                                  const DisplayDevice& activeDisplay) {
     ATRACE_CALL();
 
-    if (inactiveDisplay) {
-        inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+    if (inactiveDisplayPtr) {
+        inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
     }
 
-    mActiveDisplayId = activeDisplay->getPhysicalId();
-    activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+    mActiveDisplayId = activeDisplay.getPhysicalId();
+    activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
 
-    updateInternalDisplayVsyncLocked(activeDisplay);
+    updateActiveDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
     mScheduler->setLeaderDisplay(mActiveDisplayId);
 
     onActiveDisplaySizeChanged(activeDisplay);
-    mActiveDisplayTransformHint = activeDisplay->getTransformHint();
+    mActiveDisplayTransformHint = activeDisplay.getTransformHint();
 
     // The policy of the new active/leader display may have changed while it was inactive. In that
     // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
     // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
     // and the kernel idle timer of the newly active display must be toggled.
     constexpr bool kForce = true;
-    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector(), kForce);
+    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce);
 }
 
 status_t SurfaceFlinger::addWindowInfosListener(
@@ -8144,10 +8157,12 @@
 }
 
 binder::Status SurfaceComposerAIDL::setHdrConversionStrategy(
-        const gui::HdrConversionStrategy& hdrConversionStrategy) {
+        const gui::HdrConversionStrategy& hdrConversionStrategy,
+        int32_t* outPreferredHdrOutputType) {
     status_t status = checkAccessPermission();
     if (status == OK) {
-        status = mFlinger->setHdrConversionStrategy(hdrConversionStrategy);
+        status = mFlinger->setHdrConversionStrategy(hdrConversionStrategy,
+                                                    outPreferredHdrOutputType);
     }
     return binderStatusFromStatusT(status);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8866a6d..b41f414 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -105,6 +105,7 @@
 #include <vector>
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 #include "Client.h"
 
 using namespace android::surfaceflinger;
@@ -128,6 +129,7 @@
 class ScreenCapturer;
 class WindowInfosListenerInvoker;
 
+using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using frontend::TransactionHandler;
 using gui::CaptureArgs;
 using gui::DisplayCaptureArgs;
@@ -522,7 +524,8 @@
                                  uint32_t flags, const sp<IBinder>& applyToken,
                                  const InputWindowCommands& inputWindowCommands,
                                  int64_t desiredPresentTime, bool isAutoTimestamp,
-                                 const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+                                 const std::vector<client_cache_t>& uncacheBuffers,
+                                 bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override;
     void bootFinished();
@@ -555,7 +558,8 @@
     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 setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy,
+                                      int32_t*);
     status_t getHdrOutputConversionSupport(bool* outSupport) const;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
@@ -630,6 +634,7 @@
                                                const hal::VsyncPeriodChangeTimeline&) override;
     void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
     void onComposerHalVsyncIdle(hal::HWDisplayId) override;
+    void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
 
     // ICompositor overrides:
     void configure() override;
@@ -735,16 +740,14 @@
     /*
      * Transactions
      */
-    bool applyTransactionState(const FrameTimelineInfo& info,
-                               std::vector<ResolvedComposerState>& state,
-                               Vector<DisplayState>& displays, uint32_t flags,
-                               const InputWindowCommands& inputWindowCommands,
-                               const int64_t desiredPresentTime, bool isAutoTimestamp,
-                               const client_cache_t& uncacheBuffer, const int64_t postTime,
-                               uint32_t permissions, bool hasListenerCallbacks,
-                               const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               int originPid, int originUid, uint64_t transactionId)
-            REQUIRES(mStateLock);
+    bool applyTransactionState(
+            const FrameTimelineInfo& info, std::vector<ResolvedComposerState>& state,
+            Vector<DisplayState>& displays, uint32_t flags,
+            const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+            bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds,
+            const int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
+            const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+            uint64_t transactionId) REQUIRES(mStateLock);
     // Flush pending transactions that were presented after desiredPresentTime.
     // For test only
     bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
@@ -1046,13 +1049,11 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
-    // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
-    // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
-    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
-                                      const sp<DisplayDevice>& activeDisplay)
+    void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
+                                      const DisplayDevice& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
 
-    void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
+    void onActiveDisplaySizeChanged(const DisplayDevice&);
 
     /*
      * Debugging & dumpsys
@@ -1118,7 +1119,7 @@
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
-    void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
+    void updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
 
     bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
@@ -1459,8 +1460,8 @@
     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 setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy,
+                                            int32_t*) 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;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 5b73030..20fa091 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -367,14 +367,6 @@
     return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
 }
 
-bool frame_rate_override_for_native_rates(bool defaultValue) {
-    return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
-}
-
-bool frame_rate_override_global(bool defaultValue) {
-    return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue);
-}
-
 bool enable_layer_caching(bool defaultValue) {
     return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
 }
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 09629cf..080feee 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -96,10 +96,6 @@
 
 bool enable_frame_rate_override(bool defaultValue);
 
-bool frame_rate_override_for_native_rates(bool defaultValue);
-
-bool frame_rate_override_global(bool defaultValue);
-
 bool enable_layer_caching(bool defaultValue);
 
 bool enable_sdr_dimming(bool defaultValue);
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index aeca56e..6c5a8b2 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -49,7 +49,7 @@
                      const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                      const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
                      int64_t desiredPresentTime, bool isAutoTimestamp,
-                     const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+                     std::vector<uint64_t> uncacheBufferIds, int64_t postTime, uint32_t permissions,
                      bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
                      int originPid, int originUid, uint64_t transactionId)
           : frameTimelineInfo(frameTimelineInfo),
@@ -60,7 +60,7 @@
             inputWindowCommands(inputWindowCommands),
             desiredPresentTime(desiredPresentTime),
             isAutoTimestamp(isAutoTimestamp),
-            buffer(uncacheBuffer),
+            uncacheBufferIds(std::move(uncacheBufferIds)),
             postTime(postTime),
             permissions(permissions),
             hasListenerCallbacks(hasListenerCallbacks),
@@ -121,7 +121,7 @@
     InputWindowCommands inputWindowCommands;
     int64_t desiredPresentTime;
     bool isAutoTimestamp;
-    client_cache_t buffer;
+    std::vector<uint64_t> uncacheBufferIds;
     int64_t postTime;
     uint32_t permissions;
     bool hasListenerCallbacks;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
index 6a6e3db..1a951b3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
@@ -41,6 +41,7 @@
 
 namespace android::hardware::graphics::composer::hal {
 
+using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::HWC2::ComposerCallback;
@@ -99,6 +100,7 @@
     void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {}
     void onComposerHalSeamlessPossible(HWDisplayId) {}
     void onComposerHalVsyncIdle(HWDisplayId) {}
+    void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {}
 };
 
 } // namespace android::hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 7695459..609fd33 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -737,17 +737,18 @@
         return mFlinger->mTransactionHandler.mPendingTransactionQueues;
     }
 
-    auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo,
-                             Vector<ComposerState> &states, const Vector<DisplayState> &displays,
-                             uint32_t flags, const sp<IBinder> &applyToken,
-                             const InputWindowCommands &inputWindowCommands,
+    auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+                             Vector<ComposerState>& states, const Vector<DisplayState>& displays,
+                             uint32_t flags, const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, bool isAutoTimestamp,
-                             const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
-                             std::vector<ListenerCallbacks> &listenerCallbacks,
+                             const std::vector<client_cache_t>& uncacheBuffers,
+                             bool hasListenerCallbacks,
+                             std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
                                              inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
                                              listenerCallbacks, transactionId);
     }
 
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 8540c3d..bcbe21a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -445,28 +445,6 @@
     prop_name: "ro.surface_flinger.enable_frame_rate_override"
 }
 
-# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate
-# to native display refresh rates only. Before introducing this flag, native display refresh rates
-# was the default behaviour. With this flag we can control which behaviour we want explicitly.
-# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false.
-prop {
-    api_name: "frame_rate_override_for_native_rates"
-    type: Boolean
-    scope: Public
-    access: Readonly
-    prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
-}
-
-# Enables the frame rate override feature (enable_frame_rate_override) to
-# override the frame rate globally instead of only for individual apps.
-prop {
-    api_name: "frame_rate_override_global"
-    type: Boolean
-    scope: Public
-    access: Readonly
-    prop_name: "ro.surface_flinger.frame_rate_override_global"
-}
-
 # Enables Layer Caching
 prop {
     api_name: "enable_layer_caching"
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9338133..348a462 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -61,14 +61,6 @@
     prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
   }
   prop {
-    api_name: "frame_rate_override_for_native_rates"
-    prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
-  }
-  prop {
-    api_name: "frame_rate_override_global"
-    prop_name: "ro.surface_flinger.frame_rate_override_global"
-  }
-  prop {
     api_name: "has_HDR_display"
     prop_name: "ro.surface_flinger.has_HDR_display"
   }
diff --git a/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
index a843fd1..2be8d3b 100644
--- a/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
+++ b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
@@ -238,4 +238,50 @@
     EXPECT_TRUE(pch.awaitCallback());
 }
 
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_display_overlay) {
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setFlags(otherLayer, layer_state_t::eLayerSkipScreenshot,
+                      layer_state_t::eLayerSkipScreenshot)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .show(mainLayer)
+            .setPosition(mainLayer, 100, 100)
+            .setTrustedPresentationCallback(
+                    mainLayer,
+                    [&](void* context, bool state) {
+                        PresentationCallbackHelper* helper = (PresentationCallbackHelper*)context;
+                        helper->callbackArrived(state);
+                    },
+                    thresholds, &pch, mCallback)
+            .apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_non_overlapping_bounds) {
+    thresholds.minFractionRendered = 0.5;
+    auto otherLayer1 = makeLayer();
+    auto otherLayer2 = makeLayer();
+    t.show(otherLayer1)
+            .show(otherLayer2)
+            .setPosition(otherLayer1, 100, 25)
+            .setLayer(otherLayer1, INT32_MAX)
+            .setPosition(otherLayer2, 100, 175)
+            .setLayer(otherLayer2, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .show(mainLayer)
+            .setPosition(mainLayer, 100, 100)
+            .setTrustedPresentationCallback(
+                    mainLayer,
+                    [&](void* context, bool state) {
+                        PresentationCallbackHelper* helper = (PresentationCallbackHelper*)context;
+                        helper->callbackArrived(state);
+                    },
+                    thresholds, &pch, mCallback)
+            .apply();
+
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 9534f3b..da00377 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -52,6 +52,7 @@
 
 using Hwc2::Config;
 
+using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::ElementsAreArray;
@@ -155,6 +156,7 @@
                  void(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
     MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
     MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
+    MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override));
 };
 
 struct HWComposerSetCallbackTest : HWComposerTest {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index ad3bd35..43af595 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -119,7 +119,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(*mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -159,7 +159,7 @@
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(*mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
@@ -195,7 +195,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(*mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
@@ -238,7 +238,7 @@
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(*mDisplay);
 
     mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
                                         mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
@@ -315,7 +315,7 @@
     EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
     EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
 
-    mFlinger.onActiveDisplayChanged(innerDisplay);
+    mFlinger.onActiveDisplayChanged(*innerDisplay);
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -359,7 +359,7 @@
     EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
     EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
 
-    mFlinger.onActiveDisplayChanged(outerDisplay);
+    mFlinger.onActiveDisplayChanged(*outerDisplay);
 
     // No transition on the inner display.
     EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 88ddb0f..a0aaa68 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -404,13 +404,11 @@
     EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToOnPrimaryDisplay) {
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
@@ -446,13 +444,11 @@
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToOnExternalDisplay) {
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e03f6cc..89a661f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -440,12 +440,13 @@
                              uint32_t flags, const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, bool isAutoTimestamp,
-                             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+                             const std::vector<client_cache_t>& uncacheBuffers,
+                             bool hasListenerCallbacks,
                              std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
                                              inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
                                              listenerCallbacks, transactionId);
     }
 
@@ -473,7 +474,7 @@
         return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
     }
 
-    void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
+    void onActiveDisplayChanged(const DisplayDevice& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
         ftl::FakeGuard guard(kMainThreadContext);
         mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 981e9d2..859f702 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -102,7 +102,7 @@
         int64_t desiredPresentTime = 0;
         bool isAutoTimestamp = true;
         FrameTimelineInfo frameTimelineInfo;
-        client_cache_t uncacheBuffer;
+        std::vector<client_cache_t> uncacheBuffers;
         uint64_t id = static_cast<uint64_t>(-1);
         static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
     };
@@ -138,7 +138,7 @@
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transaction.id);
 
         // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
@@ -165,7 +165,7 @@
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transaction.id);
 
         nsecs_t returnedTime = systemTime();
@@ -196,7 +196,7 @@
                                      transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
                                      transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transactionA.id);
 
         // This thread should not have been blocked by the above transaction
@@ -211,7 +211,7 @@
                                      transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
                                      transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transactionB.id);
 
         // this thread should have been blocked by the above transaction
@@ -248,7 +248,7 @@
     mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
                                  mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -268,7 +268,7 @@
     mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
                                  mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -282,7 +282,7 @@
     mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
                                  empty.applyToken, empty.inputWindowCommands,
                                  empty.desiredPresentTime, empty.isAutoTimestamp,
-                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
+                                 empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
@@ -379,8 +379,7 @@
                                               transaction.applyToken,
                                               transaction.inputWindowCommands,
                                               transaction.desiredPresentTime,
-                                              transaction.isAutoTimestamp,
-                                              transaction.uncacheBuffer, systemTime(), 0,
+                                              transaction.isAutoTimestamp, {}, systemTime(), 0,
                                               mHasListenerCallbacks, mCallbacks, getpid(),
                                               static_cast<int>(getuid()), transaction.id);
             mFlinger.setTransactionStateInternal(transactionState);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5e29fe7..f28b8d8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -148,8 +148,9 @@
     MOCK_METHOD1(getHdrConversionCapabilities,
                  Error(std::vector<
                          aidl::android::hardware::graphics::common::HdrConversionCapability>*));
-    MOCK_METHOD1(setHdrConversionStrategy,
-                 Error(aidl::android::hardware::graphics::common::HdrConversionStrategy));
+    MOCK_METHOD2(setHdrConversionStrategy,
+                 Error(aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                       aidl::android::hardware::graphics::common::Hdr*));
     MOCK_METHOD2(getSupportedContentTypes,
                  V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
     MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index b6d3a0b..b544245 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -25,10 +25,8 @@
         ".",
     ],
     shared_libs: [
-        "libvulkan",
-    ],
-    whole_static_libs: [
         "libjsoncpp",
+        "libvulkan",
     ],
     export_shared_lib_headers: [
         "libvulkan",