Camera: Fix focal length/aperture tests for foldables

For foldable devices, depending on fold/unfold position,
the focal length of the camera may not be the same as
the supported focal lengths in the camera characteristics.

Adjust the test such that if the camera is a logical multi-camera,
the focal length/aperture is allowed to a value supported by
the active physical camera.

This adjustment is necessary for foldables because the "default"
focal length or aperture of a logical camera may be different
depending on the fold/unfold state.

Test: Run Camera CTS on foldable device
Bug: 254611744
Change-Id: I21a97cd51b3add62972f60e2013512ab07e8bd2c
diff --git a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 58fb31e..c999acb 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -1113,8 +1113,8 @@
                 // Verify output image's and result's JPEG EXIF data.
                 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
                 verifyJpegKeys(image, reprocessResults[i], reprocessOutputSize,
-                        testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector,
-                        mDebugFileNameBase, ImageFormat.JPEG);
+                        testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mAllStaticInfo,
+                        mCollector, mDebugFileNameBase, ImageFormat.JPEG);
                 image.close();
 
             }
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index 5f2f961..4cc7c34 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -54,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 import junit.framework.Assert;
 
@@ -670,25 +671,42 @@
             waitForResultValue(resultListener, CaptureResult.LENS_STATE,
                     CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
             result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
-            mCollector.expectEquals("Focal length in preview result and request should be the same",
-                    previewRequest.get(CaptureRequest.LENS_FOCAL_LENGTH),
-                    result.get(CaptureResult.LENS_FOCAL_LENGTH));
+            Float focalLengthInResult = result.get(CaptureResult.LENS_FOCAL_LENGTH);
+            Set<Float> validFocalLengths = getAvailableFocalLengthsForResult(
+                    result, mStaticInfo, mAllStaticInfo);
+            if (focalLengths.length > 1) {
+                mCollector.expectEquals(
+                        "Focal length in preview result and request should be the same",
+                        previewRequest.get(CaptureRequest.LENS_FOCAL_LENGTH),
+                        focalLengthInResult);
+            } else {
+                mCollector.expectTrue(
+                        "Focal length in preview result should be a supported value",
+                        validFocalLengths.contains(focalLengthInResult));
+            }
 
             stillRequest.set(CaptureRequest.LENS_FOCAL_LENGTH, focalLength);
             CaptureRequest request = stillRequest.build();
             resultListener = new SimpleCaptureCallback();
             mSession.capture(request, resultListener, mHandler);
             result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
-            mCollector.expectEquals(
-                    "Focal length in still capture result and request should be the same",
-                    stillRequest.get(CaptureRequest.LENS_FOCAL_LENGTH),
-                    result.get(CaptureResult.LENS_FOCAL_LENGTH));
+            focalLengthInResult = result.get(CaptureResult.LENS_FOCAL_LENGTH);
+            if (focalLengths.length > 1) {
+                mCollector.expectEquals(
+                        "Focal length in still capture result and request should be the same",
+                        stillRequest.get(CaptureRequest.LENS_FOCAL_LENGTH),
+                        result.get(CaptureResult.LENS_FOCAL_LENGTH));
+            } else {
+                mCollector.expectTrue(
+                        "Focal length in still capture result should be a supported value",
+                        validFocalLengths.contains(focalLengthInResult));
+            }
 
             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
 
             validateJpegCapture(image, maxStillSz);
             verifyJpegKeys(image, result, maxStillSz, thumbnailSize, exifTestData,
-                    mStaticInfo, mCollector, mDebugFileNameBase, ImageFormat.JPEG);
+                    mStaticInfo, mAllStaticInfo, mCollector, mDebugFileNameBase, ImageFormat.JPEG);
         }
     }
 
@@ -1327,7 +1345,7 @@
             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
 
             verifyJpegKeys(image, stillResult, stillSize, testThumbnailSizes[i], EXIF_TEST_DATA[i],
-                    mStaticInfo, mCollector, mDebugFileNameBase, format);
+                    mStaticInfo, mAllStaticInfo, mCollector, mDebugFileNameBase, format);
 
             // Free image resources
             image.close();
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index b022037..37f6fa4 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -3103,13 +3103,15 @@
      * @param expectedThumbnailSize The expected thumbnail size.
      * @param expectedExifData The expected EXIF data
      * @param staticInfo The static metadata for the camera device.
+     * @param allStaticInfo The camera Id to static metadata map for all cameras.
      * @param blobFilename The filename to dump the jpeg/heic to.
      * @param collector The camera error collector to collect errors.
      * @param format JPEG/HEIC format
      */
     public static void verifyJpegKeys(Image image, CaptureResult captureResult, Size expectedSize,
             Size expectedThumbnailSize, ExifTestData expectedExifData, StaticMetadata staticInfo,
-            CameraErrorCollector collector, String debugFileNameBase, int format) throws Exception {
+            HashMap<String, StaticMetadata> allStaticInfo, CameraErrorCollector collector,
+            String debugFileNameBase, int format) throws Exception {
 
         basicValidateBlobImage(image, expectedSize, format);
 
@@ -3163,8 +3165,8 @@
 
         // Validate other exif tags for all non-legacy devices
         if (!staticInfo.isHardwareLevelLegacy()) {
-            verifyJpegExifExtraTags(exif, expectedSize, captureResult, staticInfo, collector,
-                    expectedExifData);
+            verifyJpegExifExtraTags(exif, expectedSize, captureResult, staticInfo, allStaticInfo,
+                    collector, expectedExifData);
         }
     }
 
@@ -3190,62 +3192,148 @@
     }
 
     /**
+     * Get all of the supported focal lengths for capture result.
+     *
+     * If the camera is a logical camera, return the focal lengths of the logical camera
+     * and its active physical camera.
+     *
+     * If the camera isn't a logical camera, return the focal lengths supported by the
+     * single camera.
+     */
+    public static Set<Float> getAvailableFocalLengthsForResult(CaptureResult result,
+            StaticMetadata staticInfo,
+            HashMap<String, StaticMetadata> allStaticInfo) {
+        Set<Float> focalLengths = new HashSet<Float>();
+        float[] supportedFocalLengths = staticInfo.getAvailableFocalLengthsChecked();
+        for (float focalLength : supportedFocalLengths) {
+            focalLengths.add(focalLength);
+        }
+
+        if (staticInfo.isLogicalMultiCamera()) {
+            boolean activePhysicalCameraIdSupported =
+                    staticInfo.isActivePhysicalCameraIdSupported();
+            Set<String> physicalCameraIds;
+            if (activePhysicalCameraIdSupported) {
+                String activePhysicalCameraId = result.get(
+                        CaptureResult.LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
+                physicalCameraIds = new HashSet<String>();
+                physicalCameraIds.add(activePhysicalCameraId);
+            } else {
+                physicalCameraIds = staticInfo.getCharacteristics().getPhysicalCameraIds();
+            }
+
+            for (String physicalCameraId : physicalCameraIds) {
+                StaticMetadata physicalStaticInfo = allStaticInfo.get(physicalCameraId);
+                if (physicalStaticInfo != null) {
+                    float[] focalLengthsArray =
+                            physicalStaticInfo.getAvailableFocalLengthsChecked();
+                    for (float focalLength: focalLengthsArray) {
+                        focalLengths.add(focalLength);
+                    }
+                }
+            }
+        }
+
+        return focalLengths;
+    }
+
+    /**
      * Validate and return the focal length.
      *
      * @param result Capture result to get the focal length
+     * @param supportedFocalLengths Valid focal lengths to check the result focal length against
+     * @param collector The camera error collector
      * @return Focal length from capture result or -1 if focal length is not available.
      */
-    private static float validateFocalLength(CaptureResult result, StaticMetadata staticInfo,
-            CameraErrorCollector collector) {
-        float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked();
+    private static float validateFocalLength(CaptureResult result,
+            Set<Float> supportedFocalLengths, CameraErrorCollector collector) {
         Float resultFocalLength = result.get(CaptureResult.LENS_FOCAL_LENGTH);
         if (collector.expectTrue("Focal length is invalid",
                 resultFocalLength != null && resultFocalLength > 0)) {
-            List<Float> focalLengthList =
-                    Arrays.asList(CameraTestUtils.toObject(focalLengths));
             collector.expectTrue("Focal length should be one of the available focal length",
-                    focalLengthList.contains(resultFocalLength));
+                    supportedFocalLengths.contains(resultFocalLength));
             return resultFocalLength;
         }
         return -1;
     }
 
     /**
+     * Get all of the supported apertures for capture result.
+     *
+     * If the camera is a logical camera, return the apertures of the logical camera
+     * and its active physical camera.
+     *
+     * If the camera isn't a logical camera, return the apertures supported by the
+     * single camera.
+     */
+    private static Set<Float> getAvailableAperturesForResult(CaptureResult result,
+            StaticMetadata staticInfo, HashMap<String, StaticMetadata> allStaticInfo) {
+        Set<Float> allApertures = new HashSet<Float>();
+        float[] supportedApertures = staticInfo.getAvailableAperturesChecked();
+        for (float aperture : supportedApertures) {
+            allApertures.add(aperture);
+        }
+
+        if (staticInfo.isLogicalMultiCamera()) {
+            boolean activePhysicalCameraIdSupported =
+                    staticInfo.isActivePhysicalCameraIdSupported();
+            Set<String> physicalCameraIds;
+            if (activePhysicalCameraIdSupported) {
+                String activePhysicalCameraId = result.get(
+                        CaptureResult.LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
+                physicalCameraIds = new HashSet<String>();
+                physicalCameraIds.add(activePhysicalCameraId);
+            } else {
+                physicalCameraIds = staticInfo.getCharacteristics().getPhysicalCameraIds();
+            }
+
+            for (String physicalCameraId : physicalCameraIds) {
+                StaticMetadata physicalStaticInfo = allStaticInfo.get(physicalCameraId);
+                if (physicalStaticInfo != null) {
+                    float[] apertures = physicalStaticInfo.getAvailableAperturesChecked();
+                    for (float aperture: apertures) {
+                        allApertures.add(aperture);
+                    }
+                }
+            }
+        }
+
+        return allApertures;
+    }
+
+    /**
      * Validate and return the aperture.
      *
      * @param result Capture result to get the aperture
      * @return Aperture from capture result or -1 if aperture is not available.
      */
-    private static float validateAperture(CaptureResult result, StaticMetadata staticInfo,
-            CameraErrorCollector collector) {
-        float[] apertures = staticInfo.getAvailableAperturesChecked();
+    private static float validateAperture(CaptureResult result,
+            Set<Float> supportedApertures, CameraErrorCollector collector) {
         Float resultAperture = result.get(CaptureResult.LENS_APERTURE);
         if (collector.expectTrue("Capture result aperture is invalid",
                 resultAperture != null && resultAperture > 0)) {
-            List<Float> apertureList =
-                    Arrays.asList(CameraTestUtils.toObject(apertures));
             collector.expectTrue("Aperture should be one of the available apertures",
-                    apertureList.contains(resultAperture));
+                    supportedApertures.contains(resultAperture));
             return resultAperture;
         }
         return -1;
     }
 
     /**
-     * Return the closest value in an array of floats.
+     * Return the closest value in a Set of floats.
      */
-    private static float getClosestValueInArray(float[] values, float target) {
-        int minIdx = 0;
-        float minDistance = Math.abs(values[0] - target);
-        for(int i = 0; i < values.length; i++) {
-            float distance = Math.abs(values[i] - target);
+    private static float getClosestValueInSet(Set<Float> values, float target) {
+        float minDistance = Float.MAX_VALUE;
+        float closestValue = -1.0f;
+        for(float value : values) {
+            float distance = Math.abs(value - target);
             if (minDistance > distance) {
                 minDistance = distance;
-                minIdx = i;
+                closestValue = value;
             }
         }
 
-        return values[minIdx];
+        return closestValue;
     }
 
     /**
@@ -3265,8 +3353,9 @@
      * Verify extra tags in JPEG EXIF
      */
     private static void verifyJpegExifExtraTags(ExifInterface exif, Size jpegSize,
-            CaptureResult result, StaticMetadata staticInfo, CameraErrorCollector collector,
-            ExifTestData expectedExifData)
+            CaptureResult result, StaticMetadata staticInfo,
+            HashMap<String, StaticMetadata> allStaticInfo,
+            CameraErrorCollector collector, ExifTestData expectedExifData)
             throws ParseException {
         /**
          * TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION.
@@ -3349,15 +3438,16 @@
         boolean isExternalCamera = staticInfo.isExternalCamera();
         if (!isExternalCamera) {
             // TAG_FOCAL_LENGTH.
-            float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked();
+            Set<Float> focalLengths = getAvailableFocalLengthsForResult(
+                    result, staticInfo, allStaticInfo);
             float exifFocalLength = (float)exif.getAttributeDouble(
                         ExifInterface.TAG_FOCAL_LENGTH, -1);
             collector.expectEquals("Focal length should match",
-                    getClosestValueInArray(focalLengths, exifFocalLength),
+                    getClosestValueInSet(focalLengths, exifFocalLength),
                     exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN);
             // More checks for focal length.
             collector.expectEquals("Exif focal length should match capture result",
-                    validateFocalLength(result, staticInfo, collector),
+                    validateFocalLength(result, focalLengths, collector),
                     exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN);
 
             // TAG_EXPOSURE_TIME
@@ -3381,15 +3471,16 @@
             String exifAperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
             collector.expectNotNull("Exif TAG_APERTURE shouldn't be null", exifAperture);
             if (staticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)) {
-                float[] apertures = staticInfo.getAvailableAperturesChecked();
+                Set<Float> apertures = getAvailableAperturesForResult(
+                        result, staticInfo, allStaticInfo);
                 if (exifAperture != null) {
                     float apertureValue = Float.parseFloat(exifAperture);
                     collector.expectEquals("Aperture value should match",
-                            getClosestValueInArray(apertures, apertureValue),
+                            getClosestValueInSet(apertures, apertureValue),
                             apertureValue, EXIF_APERTURE_ERROR_MARGIN);
                     // More checks for aperture.
                     collector.expectEquals("Exif aperture length should match capture result",
-                            validateAperture(result, staticInfo, collector),
+                            validateAperture(result, apertures, collector),
                             apertureValue, EXIF_APERTURE_ERROR_MARGIN);
                 }
             }