Camera: use compact copy when necessary

When camera HAL sends redundant metadata (reserved space >> actual
used space), create a compact copy to save IPC overhead.

Test: Camera CTS/camera app smoke test
Bug: 79838637
Change-Id: I02657deb377c43c65d111d1fc894eb3c20ad3b8f
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 9217a82..dc4e0f0 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -306,7 +306,7 @@
             return ret;
         }
         CameraMetadata m;
-        m = rawInfo.static_camera_characteristics;
+        m.append(rawInfo.static_camera_characteristics);
         deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
         cameraInfo = rawInfo;
         cameraInfo.static_camera_characteristics = m.release();
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index 1cef882..f33da13 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -35,6 +35,13 @@
 // Size of result metadata fast message queue. Change to 0 to always use hwbinder buffer.
 static constexpr size_t CAMERA_RESULT_METADATA_QUEUE_SIZE  = 1 << 20 /* 1MB */;
 
+// Metadata sent by HAL will be replaced by a compact copy
+// if their (total size >= compact size + METADATA_SHRINK_ABS_THRESHOLD &&
+//           total_size >= compact size * METADATA_SHRINK_REL_THRESHOLD)
+// Heuristically picked by size of one page
+static constexpr int METADATA_SHRINK_ABS_THRESHOLD = 4096;
+static constexpr int METADATA_SHRINK_REL_THRESHOLD = 2;
+
 HandleImporter CameraDeviceSession::sHandleImporter;
 const int CameraDeviceSession::ResultBatcher::NOT_BATCHED;
 
@@ -780,13 +787,11 @@
                 mOverridenRequest.update(
                         ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
                         defaultBoost, 1);
-                const camera_metadata_t *metaBuffer =
-                        mOverridenRequest.getAndLock();
-                convertToHidl(metaBuffer, outMetadata);
-                mOverridenRequest.unlock(metaBuffer);
-            } else {
-                convertToHidl(rawRequest, outMetadata);
             }
+            const camera_metadata_t *metaBuffer =
+                    mOverridenRequest.getAndLock();
+            convertToHidl(metaBuffer, outMetadata);
+            mOverridenRequest.unlock(metaBuffer);
         }
     }
     return status;
@@ -1362,6 +1367,62 @@
     return OK;
 }
 
+// Static helper method to copy/shrink capture result metadata sent by HAL
+void CameraDeviceSession::sShrinkCaptureResult(
+        camera3_capture_result* dst, const camera3_capture_result* src,
+        std::vector<::android::hardware::camera::common::V1_0::helper::CameraMetadata>* mds,
+        std::vector<const camera_metadata_t*>* physCamMdArray,
+        bool handlePhysCam) {
+    *dst = *src;
+    if (sShouldShrink(src->result)) {
+        mds->emplace_back(sCreateCompactCopy(src->result));
+        dst->result = mds->back().getAndLock();
+    }
+
+    if (handlePhysCam) {
+        // First determine if we need to create new camera_metadata_t* array
+        bool needShrink = false;
+        for (uint32_t i = 0; i < src->num_physcam_metadata; i++) {
+            if (sShouldShrink(src->physcam_metadata[i])) {
+                needShrink = true;
+            }
+        }
+
+        if (!needShrink) return;
+
+        physCamMdArray->reserve(src->num_physcam_metadata);
+        dst->physcam_metadata = physCamMdArray->data();
+        for (uint32_t i = 0; i < src->num_physcam_metadata; i++) {
+            if (sShouldShrink(src->physcam_metadata[i])) {
+                mds->emplace_back(sCreateCompactCopy(src->physcam_metadata[i]));
+                dst->physcam_metadata[i] = mds->back().getAndLock();
+            } else {
+                dst->physcam_metadata[i] = src->physcam_metadata[i];
+            }
+        }
+    }
+}
+
+bool CameraDeviceSession::sShouldShrink(const camera_metadata_t* md) {
+    size_t compactSize = get_camera_metadata_compact_size(md);
+    size_t totalSize = get_camera_metadata_size(md);
+    if (totalSize >= compactSize + METADATA_SHRINK_ABS_THRESHOLD &&
+            totalSize >= compactSize * METADATA_SHRINK_REL_THRESHOLD) {
+        ALOGV("Camera metadata should be shrunk from %zu to %zu", totalSize, compactSize);
+        return true;
+    }
+    return false;
+}
+
+camera_metadata_t* CameraDeviceSession::sCreateCompactCopy(const camera_metadata_t* src) {
+    size_t compactSize = get_camera_metadata_compact_size(src);
+    void* buffer = calloc(1, compactSize);
+    if (buffer == nullptr) {
+        ALOGE("%s: Allocating %zu bytes failed", __FUNCTION__, compactSize);
+    }
+    return copy_camera_metadata(buffer, compactSize, src);
+}
+
 /**
  * Static callback forwarding methods from HAL to instance
  */
@@ -1372,7 +1433,13 @@
             const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
 
     CaptureResult result = {};
-    status_t ret = d->constructCaptureResult(result, hal_result);
+    camera3_capture_result shadowResult;
+    bool handlePhysCam = (d->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
+    std::vector<::android::hardware::camera::common::V1_0::helper::CameraMetadata> compactMds;
+    std::vector<const camera_metadata_t*> physCamMdArray;
+    sShrinkCaptureResult(&shadowResult, hal_result, &compactMds, &physCamMdArray, handlePhysCam);
+
+    status_t ret = d->constructCaptureResult(result, &shadowResult);
     if (ret == OK) {
         d->mResultBatcher.processCaptureResult(result);
     }
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index 269cc06..af90e5a 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -141,7 +141,7 @@
     };
 
     camera3_device_t* mDevice;
-    uint32_t mDeviceVersion;
+    const uint32_t mDeviceVersion;
     bool mIsAELockAvailable;
     bool mDerivePostRawSensKey;
     uint32_t mNumPartialResults;
@@ -329,6 +329,17 @@
 
     status_t constructCaptureResult(CaptureResult& result,
                                 const camera3_capture_result *hal_result);
+
+    // Static helper method to copy/shrink capture result metadata sent by HAL
+    // Temporarily allocated metadata copy will be hold in mds
+    static void sShrinkCaptureResult(
+            camera3_capture_result* dst, const camera3_capture_result* src,
+            std::vector<::android::hardware::camera::common::V1_0::helper::CameraMetadata>* mds,
+            std::vector<const camera_metadata_t*>* physCamMdArray,
+            bool handlePhysCam);
+    static bool sShouldShrink(const camera_metadata_t* md);
+    static camera_metadata_t* sCreateCompactCopy(const camera_metadata_t* src);
+
 private:
 
     struct TrampolineSessionInterface_3_2 : public ICameraDeviceSession {
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index 9722c75..6a18161 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -479,31 +479,40 @@
             const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
 
     CaptureResult result = {};
-    status_t ret = d->constructCaptureResult(result.v3_2, hal_result);
+    camera3_capture_result shadowResult;
+    bool handlePhysCam = (d->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
+    std::vector<::android::hardware::camera::common::V1_0::helper::CameraMetadata> compactMds;
+    std::vector<const camera_metadata_t*> physCamMdArray;
+    sShrinkCaptureResult(&shadowResult, hal_result, &compactMds, &physCamMdArray, handlePhysCam);
+
+    status_t ret = d->constructCaptureResult(result.v3_2, &shadowResult);
     if (ret != OK) {
         return;
     }
 
-    if (hal_result->num_physcam_metadata > d->mPhysicalCameraIds.size()) {
-        ALOGE("%s: Fatal: Invalid num_physcam_metadata %u", __FUNCTION__,
-                hal_result->num_physcam_metadata);
-        return;
-    }
-    result.physicalCameraMetadata.resize(hal_result->num_physcam_metadata);
-    for (uint32_t i = 0; i < hal_result->num_physcam_metadata; i++) {
-        std::string physicalId = hal_result->physcam_ids[i];
-        if (d->mPhysicalCameraIds.find(physicalId) == d->mPhysicalCameraIds.end()) {
-            ALOGE("%s: Fatal: Invalid physcam_ids[%u]: %s", __FUNCTION__,
-                  i, hal_result->physcam_ids[i]);
+    if (handlePhysCam) {
+        if (shadowResult.num_physcam_metadata > d->mPhysicalCameraIds.size()) {
+            ALOGE("%s: Fatal: Invalid num_physcam_metadata %u", __FUNCTION__,
+                    shadowResult.num_physcam_metadata);
             return;
         }
-        V3_2::CameraMetadata physicalMetadata;
-        V3_2::implementation::convertToHidl(hal_result->physcam_metadata[i], &physicalMetadata);
-        PhysicalCameraMetadata physicalCameraMetadata = {
-                .fmqMetadataSize = 0,
-                .physicalCameraId = physicalId,
-                .metadata = physicalMetadata };
-        result.physicalCameraMetadata[i] = physicalCameraMetadata;
+        result.physicalCameraMetadata.resize(shadowResult.num_physcam_metadata);
+        for (uint32_t i = 0; i < shadowResult.num_physcam_metadata; i++) {
+            std::string physicalId = shadowResult.physcam_ids[i];
+            if (d->mPhysicalCameraIds.find(physicalId) == d->mPhysicalCameraIds.end()) {
+                ALOGE("%s: Fatal: Invalid physcam_ids[%u]: %s", __FUNCTION__,
+                      i, shadowResult.physcam_ids[i]);
+                return;
+            }
+            V3_2::CameraMetadata physicalMetadata;
+            V3_2::implementation::convertToHidl(
+                    shadowResult.physcam_metadata[i], &physicalMetadata);
+            PhysicalCameraMetadata physicalCameraMetadata = {
+                    .fmqMetadataSize = 0,
+                    .physicalCameraId = physicalId,
+                    .metadata = physicalMetadata };
+            result.physicalCameraMetadata[i] = physicalCameraMetadata;
+        }
     }
     d->mResultBatcher_3_4.processCaptureResult_3_4(result);
 }