Merge "Add dEQP level 2023 prebuilts" into udc-dev
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
index 27b38c3..6c0a2f5 100644
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ b/libs/ultrahdr/fuzzer/Android.bp
@@ -24,7 +24,17 @@
 cc_defaults {
     name: "ultrahdr_fuzzer_defaults",
     host_supported: true,
-    static_libs: ["liblog"],
+    shared_libs: [
+        "libimage_io",
+        "libjpeg",
+    ],
+    static_libs: [
+        "libjpegdecoder",
+        "libjpegencoder",
+        "libultrahdr",
+        "libutils",
+        "liblog",
+    ],
     target: {
         darwin: {
             enabled: false,
@@ -37,6 +47,8 @@
         description: "The fuzzers target the APIs of jpeg hdr",
         service_privilege: "constrained",
         users: "multi_user",
+        fuzzed_code_usage: "future_version",
+        vector: "local_no_privileges_required",
     },
 }
 
@@ -46,20 +58,12 @@
     srcs: [
         "ultrahdr_enc_fuzzer.cpp",
     ],
-    shared_libs: [
-        "libimage_io",
-        "libjpeg",
-        "liblog",
-    ],
-    static_libs: [
-        "libjpegdecoder",
-        "libjpegencoder",
-        "libultrahdr",
-        "libutils",
-    ],
-    fuzz_config: {
-        fuzzed_code_usage: "future_version",
-        vector: "local_no_privileges_required",
-    },
 }
 
+cc_fuzz {
+    name: "ultrahdr_dec_fuzzer",
+    defaults: ["ultrahdr_fuzzer_defaults"],
+    srcs: [
+        "ultrahdr_dec_fuzzer.cpp",
+    ],
+}
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
new file mode 100644
index 0000000..ad1d57a
--- /dev/null
+++ b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// System include files
+#include <fuzzer/FuzzedDataProvider.h>
+#include <iostream>
+#include <vector>
+
+// User include files
+#include "ultrahdr/jpegr.h"
+
+using namespace android::ultrahdr;
+
+// Transfer functions for image data, sync with ultrahdr.h
+const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
+const int kOfMax = ULTRAHDR_OUTPUT_MAX;
+
+class UltraHdrDecFuzzer {
+public:
+    UltraHdrDecFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    void process();
+
+private:
+    FuzzedDataProvider mFdp;
+};
+
+void UltraHdrDecFuzzer::process() {
+    // hdr_of
+    auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
+    auto buffer = mFdp.ConsumeRemainingBytes<uint8_t>();
+    jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
+                                     ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+    std::vector<uint8_t> iccData(0);
+    std::vector<uint8_t> exifData(0);
+    jpegr_info_struct info{0, 0, &iccData, &exifData};
+    JpegR jpegHdr;
+    (void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+//#define DUMP_PARAM
+#ifdef DUMP_PARAM
+    std::cout << "input buffer size " << jpegImgR.length << std::endl;
+    std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
+#endif
+    size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+    jpegr_uncompressed_struct decodedJpegR;
+    auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+    decodedJpegR.data = decodedRaw.get();
+    ultrahdr_metadata_struct metadata;
+    jpegr_uncompressed_struct decodedGainMap{};
+    (void)jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+                              mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
+                              &decodedGainMap, &metadata);
+    if (decodedGainMap.data) free(decodedGainMap.data);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    UltraHdrDecFuzzer fuzzHandle(data, size);
+    fuzzHandle.process();
+    return 0;
+}
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
index 472699b..acb9b79 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -45,7 +45,7 @@
 
 // Transfer functions for image data, sync with ultrahdr.h
 const int kTfMin = ULTRAHDR_TF_UNSPECIFIED + 1;
-const int kTfMax = ULTRAHDR_TF_MAX;
+const int kTfMax = ULTRAHDR_TF_PQ;
 
 // Transfer functions for image data, sync with ultrahdr.h
 const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
@@ -55,12 +55,9 @@
 const int kQfMin = 0;
 const int kQfMax = 100;
 
-// seed
-const unsigned kSeed = 0x7ab7;
-
-class JpegHDRFuzzer {
+class UltraHdrEncFuzzer {
 public:
-    JpegHDRFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
     void process();
     void fillP010Buffer(uint16_t* data, int width, int height, int stride);
     void fill420Buffer(uint8_t* data, int size);
@@ -69,7 +66,7 @@
     FuzzedDataProvider mFdp;
 };
 
-void JpegHDRFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
+void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
     uint16_t* tmp = data;
     std::vector<uint16_t> buffer(16);
     for (int i = 0; i < buffer.size(); i++) {
@@ -78,22 +75,24 @@
     for (int j = 0; j < height; j++) {
         for (int i = 0; i < width; i += buffer.size()) {
             memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (width - i)));
-            std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(kSeed));
+            std::shuffle(buffer.begin(), buffer.end(),
+                         std::default_random_engine(std::random_device{}()));
         }
         tmp += stride;
     }
 }
 
-void JpegHDRFuzzer::fill420Buffer(uint8_t* data, int size) {
+void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int size) {
     std::vector<uint8_t> buffer(16);
     mFdp.ConsumeData(buffer.data(), buffer.size());
     for (int i = 0; i < size; i += buffer.size()) {
         memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (size - i)));
-        std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(kSeed));
+        std::shuffle(buffer.begin(), buffer.end(),
+                     std::default_random_engine(std::random_device{}()));
     }
 }
 
-void JpegHDRFuzzer::process() {
+void UltraHdrEncFuzzer::process() {
     while (mFdp.remaining_bytes()) {
         struct jpegr_uncompressed_struct p010Img {};
         struct jpegr_uncompressed_struct yuv420Img {};
@@ -256,7 +255,7 @@
                         } else if (tf == ULTRAHDR_TF_PQ) {
                             metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
                         } else {
-                            metadata.maxContentBoost = 0;
+                            metadata.maxContentBoost = 1.0f;
                         }
                         metadata.minContentBoost = 1.0f;
                         status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
@@ -265,22 +264,35 @@
             }
         }
         if (status == android::OK) {
-            jpegr_uncompressed_struct decodedJpegR;
-            auto decodedRaw = std::make_unique<uint8_t[]>(width * height * 8);
-            decodedJpegR.data = decodedRaw.get();
-            jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
-                                mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
-                                nullptr, nullptr);
             std::vector<uint8_t> iccData(0);
             std::vector<uint8_t> exifData(0);
             jpegr_info_struct info{0, 0, &iccData, &exifData};
-            jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+            status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+            if (status == android::OK) {
+                size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+                jpegr_uncompressed_struct decodedJpegR;
+                auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+                decodedJpegR.data = decodedRaw.get();
+                ultrahdr_metadata_struct metadata;
+                jpegr_uncompressed_struct decodedGainMap{};
+                status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+                                             mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX),
+                                             nullptr, of, &decodedGainMap, &metadata);
+                if (status != android::OK) {
+                    ALOGE("encountered error during decoding %d", status);
+                }
+                if (decodedGainMap.data) free(decodedGainMap.data);
+            } else {
+                ALOGE("encountered error during get jpeg info %d", status);
+            }
+        } else {
+            ALOGE("encountered error during encoding %d", status);
         }
     }
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    JpegHDRFuzzer fuzzHandle(data, size);
+    UltraHdrEncFuzzer fuzzHandle(data, size);
     fuzzHandle.process();
     return 0;
 }
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
index c807705..32d08aa 100644
--- a/libs/ultrahdr/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -180,7 +180,7 @@
 
     uint32_t total_length = text_length * 2 + sizeof(header);
     total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
 
     if (!dataStruct->write(header, sizeof(header))) {
         ALOGE("write_text_tag(): error in writing data");
@@ -204,7 +204,7 @@
             static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
             static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
     };
-    sp<DataStruct> dataStruct = new DataStruct(sizeof(data));
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(sizeof(data));
     dataStruct->write(&data, sizeof(data));
     return dataStruct;
 }
@@ -212,7 +212,7 @@
 sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* table_16) {
     int total_length = 4 + 4 + 4 + table_entries * 2;
     total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(kTAG_CurveType));     // Type
     dataStruct->write32(0);                                     // Reserved
     dataStruct->write32(Endian_SwapBE32(table_entries));  // Value count
@@ -225,7 +225,7 @@
 
 sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
     int total_length = 16;
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
     dataStruct->write32(0);                                      // Reserved
     dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
@@ -263,7 +263,7 @@
 sp<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
                                          uint32_t transfer_characteristics) {
     int total_length = 12;  // 4 + 4 + 1 + 1 + 1 + 1
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(kTAG_cicp));    // Type signature
     dataStruct->write32(0);                             // Reserved
     dataStruct->write8(color_primaries);                // Color primaries
@@ -314,7 +314,7 @@
 
     int total_length = 20 + 2 * value_count;
     total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
 
     for (size_t i = 0; i < 16; ++i) {
         dataStruct->write8(i < kNumChannels ? grid_points[i] : 0);  // Grid size
@@ -372,7 +372,7 @@
             total_length += a_curves_data[i]->getLength();
         }
     }
-    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(type));             // Type signature
     dataStruct->write32(0);                                 // Reserved
     dataStruct->write8(kNumChannels);                       // Input channels
@@ -421,7 +421,7 @@
             break;
         default:
             // Should not fall here.
-            return new DataStruct(0);
+            return nullptr;
     }
 
     // Compute primaries.
@@ -546,7 +546,7 @@
     header.size = Endian_SwapBE32(profile_size);
     header.tag_count = Endian_SwapBE32(tags.size());
 
-    sp<DataStruct> dataStruct = new DataStruct(profile_size);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size);
     if (!dataStruct->write(&header, sizeof(header))) {
         ALOGE("writeIccProfile(): error in header");
         return dataStruct;
diff --git a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
index f642bad..4f2b742 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
@@ -25,6 +25,10 @@
 }
 #include <utils/Errors.h>
 #include <vector>
+
+static const int kMaxWidth = 8192;
+static const int kMaxHeight = 8192;
+
 namespace android::ultrahdr {
 /*
  * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index d6153e9..21751b4 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -20,7 +20,7 @@
 namespace android::ultrahdr {
 // Color gamuts for image data
 typedef enum {
-  ULTRAHDR_COLORGAMUT_UNSPECIFIED,
+  ULTRAHDR_COLORGAMUT_UNSPECIFIED = -1,
   ULTRAHDR_COLORGAMUT_BT709,
   ULTRAHDR_COLORGAMUT_P3,
   ULTRAHDR_COLORGAMUT_BT2100,
@@ -52,7 +52,7 @@
  */
 struct ultrahdr_metadata_struct {
   // Ultra HDR library version
-  const char* version;
+  std::string version;
   // Max Content Boost for the map
   float maxContentBoost;
   // Min Content Boost for the map
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index fac90c5..0bad4a4 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -150,6 +150,7 @@
     jpeg_decompress_struct cinfo;
     jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
     jpegrerror_mgr myerr;
+    bool status = true;
 
     cinfo.err = jpeg_std_error(&myerr.pub);
     myerr.pub.error_exit = jpegrerror_exit;
@@ -213,13 +214,21 @@
         }
     }
 
+    if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
+        // constraint on max width and max height is only due to alloc constraints
+        // tune these values basing on the target device
+        status = false;
+        goto CleanUp;
+    }
+
     mWidth = cinfo.image_width;
     mHeight = cinfo.image_height;
 
     if (decodeToRGBA) {
         if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
             // We don't intend to support decoding grayscale to RGBA
-            return false;
+            status = false;
+            goto CleanUp;
         }
         // 4 bytes per pixel
         mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
@@ -232,7 +241,8 @@
                 cinfo.comp_info[0].v_samp_factor != 2 ||
                 cinfo.comp_info[1].v_samp_factor != 1 ||
                 cinfo.comp_info[2].v_samp_factor != 1) {
-                return false;
+                status = false;
+                goto CleanUp;
             }
             mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
         } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
@@ -248,13 +258,15 @@
 
     if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
             cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
-        return false;
+        status = false;
+        goto CleanUp;
     }
 
+CleanUp:
     jpeg_finish_decompress(&cinfo);
     jpeg_destroy_decompress(&cinfo);
 
-    return true;
+    return status;
 }
 
 bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
@@ -361,7 +373,7 @@
     uint8_t* y_plane = const_cast<uint8_t*>(dest);
     uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
     uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
-    std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
     memset(empty.get(), 0, cinfo->image_width);
 
     const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
@@ -435,7 +447,7 @@
     JSAMPARRAY planes[1] {y};
 
     uint8_t* y_plane = const_cast<uint8_t*>(dest);
-    std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
     memset(empty.get(), 0, cinfo->image_width);
 
     int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp
index 10a7630..a03547b 100644
--- a/libs/ultrahdr/jpegencoderhelper.cpp
+++ b/libs/ultrahdr/jpegencoderhelper.cpp
@@ -22,6 +22,8 @@
 
 namespace android::ultrahdr {
 
+#define ALIGNM(x, m)  ((((x) + ((m) - 1)) / (m)) * (m))
+
 // The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
 struct destination_mgr {
 public:
@@ -105,12 +107,11 @@
         jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
     }
 
-    if (!compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel)) {
-        return false;
-    }
+    bool status = compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel);
     jpeg_finish_compress(&cinfo);
     jpeg_destroy_compress(&cinfo);
-    return true;
+
+    return status;
 }
 
 void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
@@ -172,9 +173,40 @@
     uint8_t* y_plane = const_cast<uint8_t*>(yuv);
     uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
     uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
-    std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
     memset(empty.get(), 0, cinfo->image_width);
 
+    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+    const bool is_width_aligned = (aligned_width == cinfo->image_width);
+    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+    uint8_t* y_plane_intrm = nullptr;
+    uint8_t* u_plane_intrm = nullptr;
+    uint8_t* v_plane_intrm = nullptr;
+    JSAMPROW y_intrm[kCompressBatchSize];
+    JSAMPROW cb_intrm[kCompressBatchSize / 2];
+    JSAMPROW cr_intrm[kCompressBatchSize / 2];
+    JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
+    if (!is_width_aligned) {
+        size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
+        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+        y_plane_intrm = buffer_intrm.get();
+        u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
+        v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
+        for (int i = 0; i < kCompressBatchSize; ++i) {
+            y_intrm[i] = y_plane_intrm + i * aligned_width;
+            memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
+        }
+        for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+            int offset_intrm = i * (aligned_width / 2);
+            cb_intrm[i] = u_plane_intrm + offset_intrm;
+            cr_intrm[i] = v_plane_intrm + offset_intrm;
+            memset(cb_intrm[i] + cinfo->image_width / 2, 0,
+                   (aligned_width - cinfo->image_width) / 2);
+            memset(cr_intrm[i] + cinfo->image_width / 2, 0,
+                   (aligned_width - cinfo->image_width) / 2);
+        }
+    }
+
     while (cinfo->next_scanline < cinfo->image_height) {
         for (int i = 0; i < kCompressBatchSize; ++i) {
             size_t scanline = cinfo->next_scanline + i;
@@ -183,6 +215,9 @@
             } else {
                 y[i] = empty.get();
             }
+            if (!is_width_aligned) {
+                memcpy(y_intrm[i], y[i], cinfo->image_width);
+            }
         }
         // cb, cr only have half scanlines
         for (int i = 0; i < kCompressBatchSize / 2; ++i) {
@@ -194,9 +229,13 @@
             } else {
                 cb[i] = cr[i] = empty.get();
             }
+            if (!is_width_aligned) {
+                memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
+                memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
+            }
         }
-
-        int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+        int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+                                            kCompressBatchSize);
         if (processed != kCompressBatchSize) {
             ALOGE("Number of processed lines does not equal input lines.");
             return false;
@@ -210,9 +249,26 @@
     JSAMPARRAY planes[1] {y};
 
     uint8_t* y_plane = const_cast<uint8_t*>(image);
-    std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
     memset(empty.get(), 0, cinfo->image_width);
 
+    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+    bool is_width_aligned = (aligned_width == cinfo->image_width);
+    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
+    uint8_t* y_plane_intrm = nullptr;
+    uint8_t* u_plane_intrm = nullptr;
+    JSAMPROW y_intrm[kCompressBatchSize];
+    JSAMPARRAY planes_intrm[]{y_intrm};
+    if (!is_width_aligned) {
+        size_t mcu_row_size = aligned_width * kCompressBatchSize;
+        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
+        y_plane_intrm = buffer_intrm.get();
+        for (int i = 0; i < kCompressBatchSize; ++i) {
+            y_intrm[i] = y_plane_intrm + i * aligned_width;
+            memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
+        }
+    }
+
     while (cinfo->next_scanline < cinfo->image_height) {
         for (int i = 0; i < kCompressBatchSize; ++i) {
             size_t scanline = cinfo->next_scanline + i;
@@ -221,8 +277,12 @@
             } else {
                 y[i] = empty.get();
             }
+            if (!is_width_aligned) {
+                memcpy(y_intrm[i], y[i], cinfo->image_width);
+            }
         }
-        int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+        int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+                                            kCompressBatchSize);
         if (processed != kCompressBatchSize / 2) {
             ALOGE("Number of processed lines does not equal input lines.");
             return false;
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index c250aa0..415255d 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -76,9 +76,9 @@
 // JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
 // and 8 x 8 for chroma.
 // Width must be 16 dividable for luma, and 8 dividable for chroma.
-// If this criteria is not ficilitated, we will pad zeros based on the required block size.
+// If this criteria is not facilitated, we will pad zeros based to each line on the
+// required block size.
 static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
-static const size_t kJpegBlockSquare = kJpegBlock * kJpegBlock;
 // JPEG compress quality (0 ~ 100) for gain map
 static const int kMapCompressQuality = 85;
 
@@ -119,6 +119,13 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
+  if (uncompressed_p010_image->width > kMaxWidth
+          || uncompressed_p010_image->height > kMaxHeight) {
+    ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d",
+          kMaxWidth, kMaxHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
   if (uncompressed_p010_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
           || uncompressed_p010_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
     ALOGE("Unrecognized p010 color gamut %d", uncompressed_p010_image->colorGamut);
@@ -145,7 +152,8 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX) {
+  if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX
+          || hdr_tf == ULTRAHDR_TF_SRGB) {
     ALOGE("Invalid hdr transfer function %d", hdr_tf);
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
@@ -228,13 +236,8 @@
   metadata.version = kJpegrVersion;
 
   jpegr_uncompressed_struct uncompressed_yuv_420_image;
-  size_t gain_map_length = uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2;
-  // Pad a pseudo chroma block (kJpegBlock / 2) x (kJpegBlock / 2)
-  // if width is not kJpegBlock aligned.
-  if (uncompressed_p010_image->width % kJpegBlock != 0) {
-    gain_map_length += kJpegBlockSquare / 4;
-  }
-  unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(gain_map_length);
+  unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
+      uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
   uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
   JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
 
@@ -509,11 +512,6 @@
     return ERROR_JPEGR_INVALID_INPUT_TYPE;
   }
 
-  if (gain_map != nullptr && gain_map->data == nullptr) {
-    ALOGE("received nullptr address for gain map data");
-    return ERROR_JPEGR_INVALID_INPUT_TYPE;
-  }
-
   if (output_format == ULTRAHDR_OUTPUT_SDR) {
     JpegDecoderHelper jpeg_decoder;
     if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
@@ -555,6 +553,11 @@
   if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
+  if ((gain_map_decoder.getDecompressedImageWidth() *
+       gain_map_decoder.getDecompressedImageHeight()) >
+      gain_map_decoder.getDecompressedImageSize()) {
+    return ERROR_JPEGR_CALCULATION_ERROR;
+  }
 
   if (gain_map != nullptr) {
     gain_map->width = gain_map_decoder.getDecompressedImageWidth();
@@ -584,6 +587,11 @@
   if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
+  if ((jpeg_decoder.getDecompressedImageWidth() *
+       jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
+      jpeg_decoder.getDecompressedImageSize()) {
+    return ERROR_JPEGR_CALCULATION_ERROR;
+  }
 
   if (exif != nullptr) {
     if (exif->data == nullptr) {
@@ -605,7 +613,6 @@
   uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
-
   JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format,
                            max_display_boost, dest));
   return NO_ERROR;
@@ -726,7 +733,7 @@
   map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
 
   ColorTransformFn hdrInvOetf = nullptr;
-  float hdr_white_nits = 0.0f;
+  float hdr_white_nits = kSdrWhiteNits;
   switch (hdr_tf) {
     case ULTRAHDR_TF_LINEAR:
       hdrInvOetf = identityConversion;
@@ -848,6 +855,20 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
+  // TODO: remove once map scaling factor is computed based on actual map dims
+  size_t image_width = uncompressed_yuv_420_image->width;
+  size_t image_height = uncompressed_yuv_420_image->height;
+  size_t map_width = image_width / kMapDimensionScaleFactor;
+  size_t map_height = image_height / kMapDimensionScaleFactor;
+  map_width = static_cast<size_t>(
+          floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
+  map_height = ((map_height + 1) >> 1) << 1;
+  if (map_width != uncompressed_gain_map->width
+   || map_height != uncompressed_gain_map->height) {
+    ALOGE("gain map dimensions and primary image dimensions are not to scale");
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
   dest->width = uncompressed_yuv_420_image->width;
   dest->height = uncompressed_yuv_420_image->height;
   ShepardsIDW idwTable(kMapDimensionScaleFactor);
@@ -1053,6 +1074,12 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
+  if (metadata->minContentBoost < 1.0f || metadata->maxContentBoost < metadata->minContentBoost) {
+    ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
+           metadata->maxContentBoost);
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
   const string nameSpace = "http://ns.adobe.com/xap/1.0/";
   const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
 
diff --git a/libs/ultrahdr/multipictureformat.cpp b/libs/ultrahdr/multipictureformat.cpp
index 7a265c6..f1679ef 100644
--- a/libs/ultrahdr/multipictureformat.cpp
+++ b/libs/ultrahdr/multipictureformat.cpp
@@ -30,7 +30,7 @@
 sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
         int secondary_image_size, int secondary_image_offset) {
     size_t mpf_size = calculateMpfSize();
-    sp<DataStruct> dataStruct = new DataStruct(mpf_size);
+    sp<DataStruct> dataStruct = sp<DataStruct>::make(mpf_size);
 
     dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
 #if USE_BIG_ENDIAN
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
index 8f18ac0..f0e1fa4 100644
--- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
@@ -108,18 +108,9 @@
     ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
 }
 
-// The width of the "unaligned" image is not 16-aligned, and will fail if encoded directly.
-// Should pass with the padding zero method.
 TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
     JpegEncoderHelper encoder;
-    const size_t paddingZeroLength = JpegEncoderHelper::kCompressBatchSize
-            * JpegEncoderHelper::kCompressBatchSize / 4;
-    std::unique_ptr<uint8_t[]> imageWithPaddingZeros(
-            new uint8_t[UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2
-            + paddingZeroLength]);
-    memcpy(imageWithPaddingZeros.get(), mUnalignedImage.buffer.get(),
-            UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2);
-    EXPECT_TRUE(encoder.compressImage(imageWithPaddingZeros.get(), mUnalignedImage.width,
+    EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), mUnalignedImage.width,
                                       mUnalignedImage.height, JPEG_QUALITY, NULL, 0));
     ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0cc7cfb..fbbb388 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -6700,6 +6700,13 @@
         for (const auto& [displayId, handles] : handlesPerDisplay) {
             setInputWindowsLocked(handles, displayId);
         }
+
+        if (update.vsyncId < mWindowInfosVsyncId) {
+            ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
+                  ", current update vsync id: %" PRId64,
+                  mWindowInfosVsyncId, update.vsyncId);
+        }
+        mWindowInfosVsyncId = update.vsyncId;
     }
     // Wake up poll loop since it may need to make new input dispatching choices.
     mLooper->wake();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8ca01b7..6b22f2f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -204,6 +204,8 @@
 
     const IdGenerator mIdGenerator;
 
+    int64_t mWindowInfosVsyncId GUARDED_BY(mLock);
+
     // With each iteration, InputDispatcher nominally processes one queued event,
     // a timeout, or a response from an input consumer.
     // This method should only be called on the input dispatcher's own thread.
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 37b68c8..f8b466c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -223,7 +223,7 @@
 }
 
 void PowerAdvisor::reportActualWorkDuration() {
-    if (!mBootFinished || !usePowerHintSession()) {
+    if (!mBootFinished || !sUseReportActualDuration || !usePowerHintSession()) {
         ALOGV("Actual work duration power hint cannot be sent, skipping");
         return;
     }
@@ -564,6 +564,9 @@
         base::GetIntProperty<int64_t>("debug.sf.hint_margin_us",
                                       ticks<std::micro>(PowerAdvisor::kDefaultTargetSafetyMargin)));
 
+const bool PowerAdvisor::sUseReportActualDuration =
+        base::GetBoolProperty(std::string("debug.adpf.use_report_actual_duration"), true);
+
 power::PowerHalController& PowerAdvisor::getPowerHal() {
     static std::once_flag halFlag;
     std::call_once(halFlag, [this] { mPowerHal->init(); });
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 7a0d426..f0d3fd8 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -269,6 +269,9 @@
     static const Duration sTargetSafetyMargin;
     static constexpr const Duration kDefaultTargetSafetyMargin{1ms};
 
+    // Whether we should send reportActualWorkDuration calls
+    static const bool sUseReportActualDuration;
+
     // How long we expect hwc to run after the present call until it waits for the fence
     static constexpr const Duration kFenceWaitStartDelayValidated{150us};
     static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2ac1db9..79378be 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2485,7 +2485,10 @@
 
         mPowerAdvisor->setFrameDelay(frameDelay);
         mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
-        mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
+
+        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+        const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod();
+        mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
     }
 
     if (mRefreshRateOverlaySpinner) {