CameraITS: Add physical camera metadata support

- Serialize physical camera metadata for logical multi-camera if physical
camera is requested.
- Update CTS test to use getPhysicalResultMetadata call
- Remove redundant request and static metadata from captureResults.

Test: Run hacked test_yuv_plus_raw to stream physical streams
Bug: 66697407
Change-Id: I8fdfd9992fc988de59f84a84f3cb99383abf1a01
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 3e3cc02..7a20877 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -756,6 +756,7 @@
                 "rawStats":[], "dng":[], "jpeg":[]}
         yuv_bufs = {size:[] for size in yuv_sizes}
         mds = []
+        physical_mds = []
         widths = None
         heights = None
         while nbufs < ncap*nsurf or len(mds) < ncap:
@@ -772,6 +773,7 @@
                 nbufs += 1
             elif jsonObj['tag'] == 'captureResults':
                 mds.append(jsonObj['objValue']['captureResult'])
+                physical_mds.append(jsonObj['objValue']['physicalResults'])
                 outputs = jsonObj['objValue']['outputs']
                 widths = [out['width'] for out in outputs]
                 heights = [out['height'] for out in outputs]
@@ -791,7 +793,13 @@
                 obj["width"] = widths[j]
                 obj["height"] = heights[j]
                 obj["format"] = fmt
-                obj["metadata"] = mds[i]
+                if j in physical_cam_ids:
+                    for physical_md in physical_mds[i]:
+                        if physical_cam_ids[j] in physical_md:
+                            obj["metadata"] = physical_md[physical_cam_ids[j]]
+                            break
+                else:
+                    obj["metadata"] = mds[i]
 
                 if j in physical_cam_ids:
                     obj["data"] = physical_buffers[physical_cam_ids[j]][i]
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 8677941..6b8e9b2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -86,8 +86,10 @@
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingDeque;
@@ -211,6 +213,9 @@
     private HandlerThread mSensorThread = null;
     private Handler mSensorHandler = null;
 
+    private static final int SERIALIZER_SURFACES_ID = 2;
+    private static final int SERIALIZER_PHYSICAL_METADATA_ID = 3;
+
     public interface CaptureCallback {
         void onCaptureAvailable(Image capture, String physicalCameraId);
     }
@@ -420,9 +425,20 @@
                             jsonObj.put("captureResult", ItsSerializer.serialize(
                                     (CaptureResult)obj));
                         } else if (obj instanceof JSONArray) {
-                            jsonObj.put("outputs", (JSONArray)obj);
+                            if (tag == "captureResults") {
+                                if (i == SERIALIZER_SURFACES_ID) {
+                                    jsonObj.put("outputs", (JSONArray)obj);
+                                } else if (i == SERIALIZER_PHYSICAL_METADATA_ID) {
+                                    jsonObj.put("physicalResults", (JSONArray)obj);
+                                } else {
+                                    throw new ItsException(
+                                            "Unsupported JSONArray for captureResults");
+                                }
+                            } else {
+                                jsonObj.put("outputs", (JSONArray)obj);
+                            }
                         } else {
-                            throw new ItsException("Invalid object received for serialiation");
+                            throw new ItsException("Invalid object received for serialization");
                         }
                     }
                     if (tag == null) {
@@ -750,7 +766,7 @@
 
         public void sendResponseCaptureResult(CameraCharacteristics props,
                                               CaptureRequest request,
-                                              CaptureResult result,
+                                              TotalCaptureResult result,
                                               ImageReader[] readers)
                 throws ItsException {
             try {
@@ -788,12 +804,19 @@
                     jsonSurfaces.put(jsonSurface);
                 }
 
-                Object objs[] = new Object[5];
+                Map<String, CaptureResult> physicalMetadata =
+                        result.getPhysicalCameraResults();
+                JSONArray jsonPhysicalMetadata = new JSONArray();
+                for (Map.Entry<String, CaptureResult> pair : physicalMetadata.entrySet()) {
+                    JSONObject jsonOneMetadata = new JSONObject();
+                    jsonOneMetadata.put(pair.getKey(), ItsSerializer.serialize(pair.getValue()));
+                    jsonPhysicalMetadata.put(jsonOneMetadata);
+                }
+                Object objs[] = new Object[4];
                 objs[0] = "captureResults";
-                objs[1] = props;
-                objs[2] = request;
-                objs[3] = result;
-                objs[4] = jsonSurfaces;
+                objs[1] = result;
+                objs[SERIALIZER_SURFACES_ID] = jsonSurfaces;
+                objs[SERIALIZER_PHYSICAL_METADATA_ID] = jsonPhysicalMetadata;
                 mSerializerQueue.put(objs);
             } catch (org.json.JSONException e) {
                 throw new ItsException("JSON error: ", e);
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 05aa260..6621cef 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -52,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.mockito.Mockito.*;
 
@@ -505,18 +506,14 @@
                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
             logicalTimestamps[i] = totalCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP);
         }
-        // Make sure that requesting a physical camera key for a logical stream request
-        // throws exception.
-        try {
-            TotalCaptureResult totalCaptureResult =
-                    simpleResultListener.getTotalCaptureResult(
-                    CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
-            long timestamp = totalCaptureResult.getPhysicalCameraKey(
-                    CaptureResult.SENSOR_TIMESTAMP, physicalCameraIds.get(0));
-            fail("No exception for invalid physical camera Id for TotalCaptureResult");
-        } catch (IllegalArgumentException e) {
-           // expected
-        }
+        // Make sure that a logical stream request wont' generate physical result metadata.
+        TotalCaptureResult totalCaptureResult =
+                simpleResultListener.getTotalCaptureResult(
+                CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+        Map<String, CaptureResult> physicalResults =
+                totalCaptureResult.getPhysicalCameraResults();
+        assertTrue("Logical stream request must not generate physical result metadata",
+                physicalResults.isEmpty());
 
         double logicalAvgDurationMs = (logicalTimestamps[NUM_FRAMES_CHECKED-1] -
                 logicalTimestamps[0])/(NS_PER_MS*(NUM_FRAMES_CHECKED-1));
@@ -536,30 +533,28 @@
             physicalTimestamps[i] = new long[NUM_FRAMES_CHECKED];
         }
         for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
-            TotalCaptureResult totalCaptureResult =
+            TotalCaptureResult totalCaptureResultDual =
                     simpleResultListenerDual.getTotalCaptureResult(
                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
-            logicalTimestamps2[i] = totalCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP);
+            logicalTimestamps2[i] = totalCaptureResultDual.get(CaptureResult.SENSOR_TIMESTAMP);
 
             int index = 0;
+            Map<String, CaptureResult> physicalResultsDual =
+                    totalCaptureResultDual.getPhysicalCameraResults();
             for (String physicalId : physicalCameraIds) {
-                 physicalTimestamps[index][i] = totalCaptureResult.getPhysicalCameraKey(
-                         CaptureResult.SENSOR_TIMESTAMP, physicalId);
+                 physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
+                         CaptureResult.SENSOR_TIMESTAMP);
                  index++;
             }
         }
-        // Make sure that requesting an invalid physical camera key throws exception.
-        try {
-            String invalidStringId = "InvalidCamera";
-            TotalCaptureResult totalCaptureResult =
-                    simpleResultListener.getTotalCaptureResult(
-                    CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
-           long timestamp = totalCaptureResult.getPhysicalCameraKey(
-                    CaptureResult.SENSOR_TIMESTAMP, invalidStringId);
-           fail("No exception for invalid physical camera Id for TotalCaptureResult");
-        } catch (IllegalArgumentException e) {
-           // expected
-        }
+        // Verify the size of the physical result metadata map
+        TotalCaptureResult totalCaptureResultDual =
+                simpleResultListenerDual.getTotalCaptureResult(
+                CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+        Map<String, CaptureResult> physicalResultsDual =
+                totalCaptureResultDual.getPhysicalCameraResults();
+        assertTrue("Stream request must generate 2 physical result metadata",
+                physicalResultsDual.size() == 2);
 
         // Check timestamp monolithity for individual camera and across cameras
         for (int i = 0; i < NUM_FRAMES_CHECKED-1; i++) {