Merge "Create a VsyncSchedule per display"
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index 42e9e0f..a7bc018 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -58,6 +58,13 @@
     exit(0);
 }
 
+[[noreturn]] static void* dumpstate_thread_retrieve(void* data) {
+    std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
+    ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package);
+    MYLOGD("Finished retrieving a bugreport. Exiting.\n");
+    exit(0);
+}
+
 [[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) {
     listener->onError(error_code);
     exit(0);
@@ -192,6 +199,41 @@
     return binder::Status::ok();
 }
 
+binder::Status DumpstateService::retrieveBugreport(
+    int32_t calling_uid, const std::string& calling_package,
+    android::base::unique_fd bugreport_fd,
+    const std::string& bugreport_file,
+    const sp<IDumpstateListener>& listener) {
+
+    ds_ = &(Dumpstate::GetInstance());
+    DumpstateInfo* ds_info = new DumpstateInfo();
+    ds_info->ds = ds_;
+    ds_info->calling_uid = calling_uid;
+    ds_info->calling_package = calling_package;
+    ds_->listener_ = listener;
+    std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
+    // Use a /dev/null FD when initializing options since none is provided.
+    android::base::unique_fd devnull_fd(
+        TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+    options->Initialize(Dumpstate::BugreportMode::BUGREPORT_DEFAULT,
+                        0, bugreport_fd, devnull_fd, false);
+
+    if (bugreport_fd.get() == -1) {
+        MYLOGE("Invalid filedescriptor");
+        signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+    }
+    ds_->SetOptions(std::move(options));
+    ds_->path_ = bugreport_file;
+    pthread_t thread;
+    status_t err = pthread_create(&thread, nullptr, dumpstate_thread_retrieve, ds_info);
+    if (err != 0) {
+        MYLOGE("Could not create a thread");
+        signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+    }
+    return binder::Status::ok();
+}
+
 status_t DumpstateService::dump(int fd, const Vector<String16>&) {
     std::lock_guard<std::mutex> lock(lock_);
     if (ds_ == nullptr) {
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 997999c..dd73319 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -46,6 +46,13 @@
                                   int bugreport_flags, const sp<IDumpstateListener>& listener,
                                   bool is_screenshot_requested) override;
 
+    binder::Status retrieveBugreport(int32_t calling_uid,
+                                     const std::string& calling_package,
+                                     android::base::unique_fd bugreport_fd,
+                                     const std::string& bugreport_file,
+                                     const sp<IDumpstateListener>& listener)
+                                     override;
+
     binder::Status cancelBugreport(int32_t calling_uid,
                                    const std::string& calling_package) override;
 
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index d4323af..0dc8f5a 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -50,7 +50,10 @@
     const int BUGREPORT_MODE_DEFAULT = 6;
 
     // Use pre-dumped data.
-    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;
+    const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1;
+
+    // Defer user consent.
+    const int BUGREPORT_FLAG_DEFER_CONSENT = 0x2;
 
     /**
      * Speculatively pre-dumps UI data for a bugreport request that might come later.
@@ -100,4 +103,22 @@
      * @param callingPackage package of the original application that requested the cancellation.
      */
     void cancelBugreport(int callingUid, @utf8InCpp String callingPackage);
+
+    /**
+     * Retrieves a previously generated bugreport.
+     *
+     * <p>The caller must have previously generated a bugreport using
+     * {@link #startBugreport} with the {@link BUGREPORT_FLAG_DEFER_CONSENT}
+     * flag set.
+     *
+     * @param callingUid UID of the original application that requested the report.
+     * @param callingPackage package of the original application that requested the report.
+     * @param bugreportFd the file to which the zipped bugreport should be written
+     * @param bugreportFile the path of the bugreport file
+     * @param listener callback for updates; optional
+     */
+    void retrieveBugreport(int callingUid, @utf8InCpp String callingPackage,
+                           FileDescriptor bugreportFd,
+                           @utf8InCpp String bugreportFile,
+                           IDumpstateListener listener);
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 50c1624..e8891d3 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -50,6 +50,9 @@
     /* There is currently a bugreport running. The caller should try again later. */
     const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5;
 
+    /* There is no bugreport to retrieve for the given caller. */
+    const int BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE = 6;
+
     /**
      * Called on an error condition with one of the error codes listed above.
      */
@@ -57,8 +60,10 @@
 
     /**
      * Called when taking bugreport finishes successfully.
+     *
+     * @param bugreportFile The location of the bugreport file
      */
-    oneway void onFinished();
+    oneway void onFinished(@utf8InCpp String bugreportFile);
 
     /**
      * Called when screenshot is taken.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d77b458..ecafcfc 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2825,6 +2825,7 @@
                                         const android::base::unique_fd& screenshot_fd_in,
                                         bool is_screenshot_requested) {
     this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
+    this->is_consent_deferred = bugreport_flags & BugreportFlag::BUGREPORT_FLAG_DEFER_CONSENT;
     // Duplicate the fds because the passed in fds don't outlive the binder transaction.
     bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
     screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2907,10 +2908,64 @@
 
 Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
     Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
-    if (listener_ != nullptr) {
+    HandleRunStatus(status);
+    return status;
+}
+
+Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package) {
+    Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package);
+    HandleRunStatus(status);
+    return status;
+}
+
+Dumpstate::RunStatus  Dumpstate::RetrieveInternal(int32_t calling_uid,
+                                                  const std::string& calling_package) {
+  consent_callback_ = new ConsentCallback();
+  const String16 incidentcompanion("incidentcompanion");
+  sp<android::IBinder> ics(
+      defaultServiceManager()->checkService(incidentcompanion));
+  android::String16 package(calling_package.c_str());
+  if (ics != nullptr) {
+    MYLOGD("Checking user consent via incidentcompanion service\n");
+    android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+        calling_uid, package, String16(), String16(),
+        0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
+  } else {
+    MYLOGD(
+        "Unable to check user consent; incidentcompanion service unavailable\n");
+    return RunStatus::USER_CONSENT_TIMED_OUT;
+  }
+  UserConsentResult consent_result = consent_callback_->getResult();
+  int timeout_ms = 30 * 1000;
+  while (consent_result == UserConsentResult::UNAVAILABLE &&
+      consent_callback_->getElapsedTimeMs() < timeout_ms) {
+    sleep(1);
+    consent_result = consent_callback_->getResult();
+  }
+  if (consent_result == UserConsentResult::DENIED) {
+    return RunStatus::USER_CONSENT_DENIED;
+  }
+  if (consent_result == UserConsentResult::UNAVAILABLE) {
+    MYLOGD("Canceling user consent request via incidentcompanion service\n");
+    android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization(
+        consent_callback_.get());
+    return RunStatus::USER_CONSENT_TIMED_OUT;
+  }
+
+  bool copy_succeeded =
+      android::os::CopyFileToFd(path_, options_->bugreport_fd.get());
+  if (copy_succeeded) {
+    android::os::UnlinkAndLogOnError(path_);
+  }
+  return copy_succeeded ? Dumpstate::RunStatus::OK
+                        : Dumpstate::RunStatus::ERROR;
+}
+
+void Dumpstate::HandleRunStatus(Dumpstate::RunStatus status) {
+      if (listener_ != nullptr) {
         switch (status) {
             case Dumpstate::RunStatus::OK:
-                listener_->onFinished();
+                listener_->onFinished(path_.c_str());
                 break;
             case Dumpstate::RunStatus::HELP:
                 break;
@@ -2928,9 +2983,7 @@
                 break;
         }
     }
-    return status;
 }
-
 void Dumpstate::Cancel() {
     CleanupTmpFiles();
     android::os::UnlinkAndLogOnError(log_path_);
@@ -3181,7 +3234,7 @@
 
     // Share the final file with the caller if the user has consented or Shell is the caller.
     Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
-    if (CalledByApi()) {
+    if (CalledByApi() && !options_->is_consent_deferred) {
         status = CopyBugreportIfUserConsented(calling_uid);
         if (status != Dumpstate::RunStatus::OK &&
             status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
@@ -3270,6 +3323,15 @@
         return;
     }
 
+    // Include the proto logging from WMShell.
+    RunCommand(
+        // Empty name because it's not intended to be classified as a bugreport section.
+        // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope"
+        // in the bugreport.
+        "", {"dumpsys", "activity", "service", "SystemUIService",
+             "WMShell", "protolog", "save-for-bugreport"},
+        CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+
     // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
     for (const auto& service : {"input_method", "window"}) {
         RunCommand(
@@ -3326,9 +3388,11 @@
 }
 
 void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
-    if (multiuser_get_app_id(calling_uid) == AID_SHELL || !CalledByApi()) {
-        // No need to get consent for shell triggered dumpstates, or not through
-        // bugreporting API (i.e. no fd to copy back).
+    if (multiuser_get_app_id(calling_uid) == AID_SHELL ||
+        !CalledByApi() || options_->is_consent_deferred) {
+        // No need to get consent for shell triggered dumpstates, or not
+        // through bugreporting API (i.e. no fd to copy back), or when consent
+        // is deferred.
         return;
     }
     consent_callback_ = new ConsentCallback();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 9f894b5..8a31c31 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -207,7 +207,9 @@
     // The flags used to customize bugreport requests.
     enum BugreportFlag {
         BUGREPORT_USE_PREDUMPED_UI_DATA =
-          android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
+          android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
+        BUGREPORT_FLAG_DEFER_CONSENT =
+          android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT
     };
 
     static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
@@ -353,6 +355,15 @@
      */
     RunStatus Run(int32_t calling_uid, const std::string& calling_package);
 
+    /*
+     * Entry point for retrieving a previous-generated bugreport.
+     *
+     * Initialize() dumpstate before calling this method.
+     */
+    RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package);
+
+
+
     RunStatus ParseCommandlineAndRun(int argc, char* argv[]);
 
     /* Deletes in-progress files */
@@ -396,6 +407,7 @@
         bool progress_updates_to_socket = false;
         bool do_screenshot = false;
         bool is_screenshot_copied = false;
+        bool is_consent_deferred = false;
         bool is_remote_mode = false;
         bool show_header_only = false;
         bool telephony_only = false;
@@ -548,6 +560,7 @@
 
   private:
     RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
+    RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package);
 
     RunStatus DumpstateDefaultAfterCritical();
     RunStatus dumpstate();
@@ -572,6 +585,8 @@
 
     RunStatus HandleUserConsentDenied();
 
+    void HandleRunStatus(RunStatus status);
+
     // Copies bugreport artifacts over to the caller's directories provided there is user consent or
     // called by Shell.
     RunStatus CopyBugreportIfUserConsented(int32_t calling_uid);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index b091c8e..ccf64fe 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -160,7 +160,7 @@
         return binder::Status::ok();
     }
 
-    binder::Status onFinished() override {
+    binder::Status onFinished([[maybe_unused]] const std::string& bugreport_file) override {
         std::lock_guard<std::mutex> lock(lock_);
         is_finished_ = true;
         dprintf(out_fd_, "\rFinished");
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 1ffcafa..87f9254 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -71,7 +71,7 @@
   public:
     MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
     MOCK_METHOD1(onError, binder::Status(int32_t error_code));
-    MOCK_METHOD0(onFinished, binder::Status());
+    MOCK_METHOD1(onFinished, binder::Status(const std::string& bugreport_file));
     MOCK_METHOD1(onScreenshotTaken, binder::Status(bool success));
     MOCK_METHOD0(onUiIntensiveBugreportDumpsFinished, binder::Status());
 
@@ -486,6 +486,20 @@
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
+TEST_F(DumpOptionsTest, InitializeBugreportFlags) {
+    int flags = Dumpstate::BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA |
+                Dumpstate::BugreportFlag::BUGREPORT_FLAG_DEFER_CONSENT;
+    options_.Initialize(
+      Dumpstate::BugreportMode::BUGREPORT_FULL, flags, fd, fd, true);
+    EXPECT_TRUE(options_.is_consent_deferred);
+    EXPECT_TRUE(options_.use_predumped_ui_data);
+
+    options_.Initialize(
+      Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
+    EXPECT_FALSE(options_.is_consent_deferred);
+    EXPECT_FALSE(options_.use_predumped_ui_data);
+}
+
 class DumpstateTest : public DumpstateBaseTest {
   public:
     void SetUp() {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b27f102..1ea13f9 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -1036,8 +1036,8 @@
                 return DEAD_OBJECT;
             }
 
-            if (it->second.asyncTodo.size() == 0) return OK;
-            if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
+            if (it->second.asyncTodo.size() != 0 &&
+                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/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 18ce316..109da75 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -117,7 +117,15 @@
         *ctx_p = channelContext;
     };
 
-    base::unique_fd clientFd(chan);
+    // We need to duplicate the channel handle here because the tipc library
+    // owns the original handle and closes is automatically on channel cleanup.
+    // We use dup() because Trusty does not have fcntl().
+    // NOLINTNEXTLINE(android-cloexec-dup)
+    handle_t chanDup = dup(chan);
+    if (chanDup < 0) {
+        return chanDup;
+    }
+    base::unique_fd clientFd(chanDup);
     android::RpcTransportFd transportFd(std::move(clientFd));
 
     std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
diff --git a/libs/binder/trusty/include_mock/lib/tipc/tipc.h b/libs/binder/trusty/include_mock/lib/tipc/tipc.h
index f295be4..ead9f9c 100644
--- a/libs/binder/trusty/include_mock/lib/tipc/tipc.h
+++ b/libs/binder/trusty/include_mock/lib/tipc/tipc.h
@@ -15,7 +15,9 @@
  */
 #pragma once
 
-__BEGIN_DECLS
+#if defined(__cplusplus)
+extern "C" {
+#endif
 
 struct tipc_hset;
 
@@ -26,4 +28,6 @@
     return 0;
 }
 
-__END_DECLS
+#if defined(__cplusplus)
+}
+#endif
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 01318dc..2c4b3bf 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -41,6 +41,8 @@
         "libjpegdecoder",
         "liblog",
     ],
+
+    static_libs: ["libskia"],
 }
 
 cc_library {
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 696be1b..1a4b679 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -34,6 +34,7 @@
   JPEGR_TF_LINEAR = 0,
   JPEGR_TF_HLG = 1,
   JPEGR_TF_PQ = 2,
+  JPEGR_TF_SRGB = 3,
 } jpegr_transfer_function;
 
 struct jpegr_info_struct {
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index c36a363..8696851 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -28,13 +28,6 @@
 
 struct jpegr_metadata;
 
-// If the EXIF package doesn't exist in the input JPEG, we'll create one with one entry
-// where the length is represented by this value.
-const size_t PSEUDO_EXIF_PACKAGE_LENGTH = 28;
-// If the EXIF package exists in the input JPEG, we'll add an "JR" entry where the length is
-// represented by this value.
-const size_t EXIF_J_R_ENTRY_LENGTH = 12;
-
 /*
  * Helper function used for writing data to destination.
  *
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 30aa846..e06bd24 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -26,7 +26,10 @@
 #include <image_io/jpeg/jpeg_info_builder.h>
 #include <image_io/base/data_segment_data_source.h>
 #include <utils/Log.h>
+#include "SkColorSpace.h"
+#include "SkICC.h"
 
+#include <map>
 #include <memory>
 #include <sstream>
 #include <string>
@@ -93,30 +96,19 @@
   return cpuCoreCount;
 }
 
-/*
- * Helper function copies the JPEG image from without EXIF.
- *
- * @param dest destination of the data to be written.
- * @param source source of data being written.
- * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos().
- *                 (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>).
- * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize().
- */
-void copyJpegWithoutExif(jr_compressed_ptr dest,
-                         jr_compressed_ptr source,
-                         size_t exif_pos,
-                         size_t exif_size) {
-  memcpy(dest, source, sizeof(jpegr_compressed_struct));
+static const map<recoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut {
+    {JPEGR_COLORGAMUT_BT709,     SkNamedGamut::kSRGB},
+    {JPEGR_COLORGAMUT_P3,        SkNamedGamut::kDisplayP3},
+    {JPEGR_COLORGAMUT_BT2100,    SkNamedGamut::kRec2020},
+};
 
-  const size_t exif_offset = 4; //exif_pos has 4 bypes offset to the FF sign
-  dest->length = source->length - exif_size - exif_offset;
-  dest->data = malloc(dest->length);
-
-  memcpy(dest->data, source->data, exif_pos - exif_offset);
-  memcpy((uint8_t*)dest->data + exif_pos - exif_offset,
-         (uint8_t*)source->data + exif_pos + exif_size,
-         source->length - exif_pos - exif_size);
-}
+static const map<
+        recoverymap::jpegr_transfer_function, skcms_TransferFunction> jrTransFunc_to_skTransFunc {
+    {JPEGR_TF_SRGB,        SkNamedTransferFn::kSRGB},
+    {JPEGR_TF_LINEAR,      SkNamedTransferFn::kLinear},
+    {JPEGR_TF_HLG,         SkNamedTransferFn::kHLG},
+    {JPEGR_TF_PQ,          SkNamedTransferFn::kPQ},
+};
 
 /* Encode API-0 */
 status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
@@ -164,11 +156,15 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
+  sk_sp<SkData> icc = SkWriteICCProfile(
+          jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
+          jrGamut_to_skGamut.at(uncompressed_yuv_420_image.colorGamut));
+
   JpegEncoder jpeg_encoder;
-  // TODO: determine ICC data based on color gamut information
   if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
                                   uncompressed_yuv_420_image.width,
-                                  uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+                                  uncompressed_yuv_420_image.height, quality,
+                                  icc.get()->data(), icc.get()->size())) {
     return ERROR_JPEGR_ENCODE_ERROR;
   }
   jpegr_compressed_struct jpeg;
@@ -228,11 +224,15 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
+  sk_sp<SkData> icc = SkWriteICCProfile(
+          jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB),
+          jrGamut_to_skGamut.at(uncompressed_yuv_420_image->colorGamut));
+
   JpegEncoder jpeg_encoder;
-  // TODO: determine ICC data based on color gamut information
   if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
                                   uncompressed_yuv_420_image->width,
-                                  uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
+                                  uncompressed_yuv_420_image->height, quality,
+                                  icc.get()->data(), icc.get()->size())) {
     return ERROR_JPEGR_ENCODE_ERROR;
   }
   jpegr_compressed_struct jpeg;
@@ -574,7 +574,7 @@
 #endif
       hdr_white_nits = kPqMaxNits;
       break;
-    case JPEGR_TF_UNSPECIFIED:
+    default:
       // Should be impossible to hit after input validation.
       return ERROR_JPEGR_INVALID_TRANS_FUNC;
   }
@@ -750,7 +750,7 @@
         hdrOetf = pqOetf;
 #endif
         break;
-      case JPEGR_TF_UNSPECIFIED:
+      default:
         // Should be impossible to hit after input validation.
         hdrOetf = identityConversion;
     }
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 39445f8..cad273e 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -30,15 +30,13 @@
     ],
     shared_libs: [
         "libjpeg",
+        "libjpegrecoverymap",
         "libimage_io",
         "liblog",
     ],
     static_libs: [
         "libgmock",
         "libgtest",
-        "libjpegdecoder",
-        "libjpegencoder",
-        "libjpegrecoverymap",
     ],
 }
 
@@ -50,10 +48,10 @@
     ],
     shared_libs: [
         "libjpeg",
+        "libjpegencoder",
         "liblog",
     ],
     static_libs: [
-        "libjpegencoder",
         "libgtest",
     ],
 }
@@ -66,10 +64,10 @@
     ],
     shared_libs: [
         "libjpeg",
+        "libjpegdecoder",
         "liblog",
     ],
     static_libs: [
-        "libjpegdecoder",
         "libgtest",
     ],
 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index e5de759..3da98d4 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -137,7 +137,6 @@
             sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE);
             if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
                 prevFence = std::move(currentFence);
-                handle->previousReleaseFence = prevFence;
             } else if (prevFence != nullptr) {
                 // If both fences are signaled or both are unsignaled, we need to merge
                 // them to get an accurate timestamp.
@@ -147,8 +146,7 @@
                     snprintf(fenceName, 32, "%.28s", handle->name.c_str());
                     sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
                     if (mergedFence->isValid()) {
-                        handle->previousReleaseFence = std::move(mergedFence);
-                        prevFence = handle->previousReleaseFence;
+                        prevFence = std::move(mergedFence);
                     }
                 } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
                     // If one fence has signaled and the other hasn't, the unsignaled
@@ -158,10 +156,11 @@
                     // by this point, they will have both signaled and only the timestamp
                     // will be slightly off; any dependencies after this point will
                     // already have been met.
-                    handle->previousReleaseFence = std::move(currentFence);
+                    prevFence = std::move(currentFence);
                 }
             }
         }
+        handle->previousReleaseFence = prevFence;
         handle->previousReleaseFences.clear();
 
         FrameEventHistoryStats eventStats(handle->frameNumber,