Camera: Improve monochrome camera capability support

- Add dummy CFA pattern for monochrome camera.
- Handle monochrome camera in DngCreator.
- Fix up static and dynamic metadata tags related to monochrome camera
for older version of devices.

Test: Camera CTS
Test: Capture a DNG file and inspect with LightRoom
Bug: 70216652
Change-Id: I68d2b3d77b7f81bdc9e4129c2a8af10a4f18db3b
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 90d9888..f982f67 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -79,7 +79,8 @@
         mNextReprocessShutterFrameNumber(0),
         mListener(NULL),
         mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID),
-        mLastTemplateId(-1)
+        mLastTemplateId(-1),
+        mNeedFixupMonochromeTags(false)
 {
     ATRACE_CALL();
     ALOGV("%s: Created device for camera %s", __FUNCTION__, mId.string());
@@ -188,6 +189,28 @@
         mTagMonitor.parseTagsToMonitor(String8(monitorTags));
     }
 
+    // Metadata tags needs fixup for monochrome camera device version less
+    // than 3.5.
+    hardware::hidl_version maxVersion{0,0};
+    res = manager->getHighestSupportedVersion(mId.string(), &maxVersion);
+    if (res != OK) {
+        ALOGE("%s: Error in getting camera device version id: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+    int deviceVersion = HARDWARE_DEVICE_API_VERSION(
+            maxVersion.get_major(), maxVersion.get_minor());
+
+    bool isMonochrome = false;
+    camera_metadata_entry_t entry = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < entry.count; i++) {
+        uint8_t capability = entry.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
+            isMonochrome = true;
+        }
+    }
+    mNeedFixupMonochromeTags = (isMonochrome && deviceVersion < CAMERA_DEVICE_API_VERSION_3_5);
+
     return initializeCommonLocked();
 }
 
@@ -3276,6 +3299,13 @@
     captureResult.mResultExtras = resultExtras;
     captureResult.mMetadata = partialResult;
 
+    // Fix up result metadata for monochrome camera.
+    status_t res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+    if (res != OK) {
+        SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+        return;
+    }
+
     insertResultLocked(&captureResult, frameNumber);
 }
 
@@ -3347,6 +3377,21 @@
                 frameNumber, strerror(res), res);
         return;
     }
+    // Fix up result metadata for monochrome camera.
+    res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+    if (res != OK) {
+        SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+        return;
+    }
+    for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+        String8 cameraId8(physicalMetadata.mPhysicalCameraId);
+        res = fixupMonochromeTags(mPhysicalDeviceInfoMap.at(cameraId8.c_str()),
+                physicalMetadata.mPhysicalCameraMetadata);
+        if (res != OK) {
+            SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+            return;
+        }
+    }
 
     mTagMonitor.monitorMetadata(TagMonitor::RESULT,
             frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
@@ -3519,7 +3564,7 @@
             if (shutterTimestamp == 0) {
                 request.pendingMetadata = result->result;
                 request.collectedPartialResult = collectedPartialResult;
-           } else if (request.hasCallback) {
+            } else if (request.hasCallback) {
                 CameraMetadata metadata;
                 metadata = result->result;
                 sendCaptureResult(metadata, request.resultExtras,
@@ -6298,4 +6343,75 @@
     return false;
 }
 
+status_t Camera3Device::fixupMonochromeTags(const CameraMetadata& deviceInfo,
+        CameraMetadata& resultMetadata) {
+    status_t res = OK;
+    if (!mNeedFixupMonochromeTags) {
+        return res;
+    }
+
+    // Remove tags that are not applicable to monochrome camera.
+    int32_t tagsToRemove[] = {
+           ANDROID_SENSOR_GREEN_SPLIT,
+           ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+           ANDROID_COLOR_CORRECTION_MODE,
+           ANDROID_COLOR_CORRECTION_TRANSFORM,
+           ANDROID_COLOR_CORRECTION_GAINS,
+    };
+    for (auto tag : tagsToRemove) {
+        res = resultMetadata.erase(tag);
+        if (res != OK) {
+            ALOGE("%s: Failed to remove tag %d for monochrome camera", __FUNCTION__, tag);
+            return res;
+        }
+    }
+
+    // ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL
+    camera_metadata_entry blEntry = resultMetadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+    for (size_t i = 1; i < blEntry.count; i++) {
+        blEntry.data.f[i] = blEntry.data.f[0];
+    }
+
+    // ANDROID_SENSOR_NOISE_PROFILE
+    camera_metadata_entry npEntry = resultMetadata.find(ANDROID_SENSOR_NOISE_PROFILE);
+    if (npEntry.count > 0 && npEntry.count % 2 == 0) {
+        double np[] = {npEntry.data.d[0], npEntry.data.d[1]};
+        res = resultMetadata.update(ANDROID_SENSOR_NOISE_PROFILE, np, 2);
+        if (res != OK) {
+             ALOGE("%s: Failed to update SENSOR_NOISE_PROFILE: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+    }
+
+    // ANDROID_STATISTICS_LENS_SHADING_MAP
+    camera_metadata_ro_entry lsSizeEntry = deviceInfo.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+    camera_metadata_entry lsEntry = resultMetadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+    if (lsSizeEntry.count == 2 && lsEntry.count > 0
+            && (int32_t)lsEntry.count == 4 * lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]) {
+        for (int32_t i = 0; i < lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]; i++) {
+            lsEntry.data.f[4*i+1] = lsEntry.data.f[4*i];
+            lsEntry.data.f[4*i+2] = lsEntry.data.f[4*i];
+            lsEntry.data.f[4*i+3] = lsEntry.data.f[4*i];
+        }
+    }
+
+    // ANDROID_TONEMAP_CURVE_BLUE
+    // ANDROID_TONEMAP_CURVE_GREEN
+    // ANDROID_TONEMAP_CURVE_RED
+    camera_metadata_entry tcbEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_BLUE);
+    camera_metadata_entry tcgEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_GREEN);
+    camera_metadata_entry tcrEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_RED);
+    if (tcbEntry.count > 0
+            && tcbEntry.count == tcgEntry.count
+            && tcbEntry.count == tcrEntry.count) {
+        for (size_t i = 0; i < tcbEntry.count; i++) {
+            tcbEntry.data.f[i] = tcrEntry.data.f[i];
+            tcgEntry.data.f[i] = tcrEntry.data.f[i];
+        }
+    }
+
+    return res;
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 4f5be6b..3f291ba 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -1324,6 +1324,9 @@
         int  mRequestBufferStatusId;
     } mRequestBufferSM;
 
+    // Fix up result metadata for monochrome camera.
+    bool mNeedFixupMonochromeTags;
+    status_t fixupMonochromeTags(const CameraMetadata& deviceInfo, CameraMetadata& resultMetadata);
 }; // class Camera3Device
 
 }; // namespace android