Camera: Enable logical multi-camera API in NDK

The support inclues:
- Physical camera specific stream support,
- Physical camera result metadata, and

Test: Newly added NDK CTS test pass
Test: Newly added VNDK test pass
Bug: 120566141
Bug: 115532726
Change-Id: I939b81522ca6c518c0e54ded5d3615f9973a6a65
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 321eb08..4e9b27d 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -167,19 +167,23 @@
 }
 
 OutputConfiguration::OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+        const String16& physicalId,
         int surfaceSetID, bool isShared) {
     mGbps.push_back(gbp);
     mRotation = rotation;
     mSurfaceSetID = surfaceSetID;
     mIsDeferred = false;
     mIsShared = isShared;
+    mPhysicalCameraId = physicalId;
 }
 
 OutputConfiguration::OutputConfiguration(
         const std::vector<sp<IGraphicBufferProducer>>& gbps,
-    int rotation, int surfaceSetID,  int surfaceType, int width, int height, bool isShared)
+    int rotation, const String16& physicalCameraId, int surfaceSetID,  int surfaceType,
+    int width, int height, bool isShared)
   : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType),
-    mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared) { }
+    mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared),
+    mPhysicalCameraId(physicalCameraId) { }
 
 status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
 
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 5b117fb..95c4f39 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -65,10 +65,12 @@
     OutputConfiguration(const android::Parcel& parcel);
 
     OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+            const String16& physicalCameraId,
             int surfaceSetID = INVALID_SET_ID, bool isShared = false);
 
     OutputConfiguration(const std::vector<sp<IGraphicBufferProducer>>& gbps,
-                        int rotation, int surfaceSetID = INVALID_SET_ID,
+                        int rotation, const String16& physicalCameraId,
+                        int surfaceSetID = INVALID_SET_ID,
                         int surfaceType = OutputConfiguration::SURFACE_TYPE_UNKNOWN, int width = 0,
                         int height = 0, bool isShared = false);
 
diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp
index fd95296..540d84e 100644
--- a/camera/ndk/NdkCameraCaptureSession.cpp
+++ b/camera/ndk/NdkCameraCaptureSession.cpp
@@ -28,6 +28,8 @@
 #include <camera/NdkCameraCaptureSession.h>
 #include "impl/ACameraCaptureSession.h"
 
+#include "impl/ACameraCaptureSession.inc"
+
 using namespace android;
 
 EXPORT
@@ -82,7 +84,31 @@
         return ACAMERA_ERROR_SESSION_CLOSED;
     }
 
-    return session->capture(cbs, numRequests, requests, captureSequenceId);
+    return session->capture(
+            cbs, numRequests, requests, captureSequenceId);
+}
+
+EXPORT
+camera_status_t ACameraCaptureSession_logicalCamera_capture(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    ATRACE_CALL();
+    if (session == nullptr || requests == nullptr || numRequests < 1) {
+        ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p",
+                __FUNCTION__, session, numRequests, requests);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (session->isClosed()) {
+        ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+        *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    return session->capture(
+            lcbs, numRequests, requests, captureSequenceId);
 }
 
 EXPORT
@@ -107,6 +133,28 @@
 }
 
 EXPORT
+camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    ATRACE_CALL();
+    if (session == nullptr || requests == nullptr || numRequests < 1) {
+        ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p",
+                __FUNCTION__, session, numRequests, requests);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (session->isClosed()) {
+        ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+        *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    return session->setRepeatingRequest(lcbs, numRequests, requests, captureSequenceId);
+}
+
+EXPORT
 camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session) {
     ATRACE_CALL();
     if (session == nullptr) {
diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp
index ef05e0b..98608da 100644
--- a/camera/ndk/NdkCameraDevice.cpp
+++ b/camera/ndk/NdkCameraDevice.cpp
@@ -129,6 +129,20 @@
 }
 
 EXPORT
+camera_status_t ACaptureSessionPhysicalOutput_create(
+        ACameraWindowType* window, const char* physicalId,
+        /*out*/ACaptureSessionOutput** out) {
+    ATRACE_CALL();
+    if (window == nullptr || physicalId == nullptr || out == nullptr) {
+        ALOGE("%s: Error: bad argument. window %p, physicalId %p, out %p",
+                __FUNCTION__, window, physicalId, out);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    *out = new ACaptureSessionOutput(window, false, physicalId);
+    return ACAMERA_OK;
+}
+
+EXPORT
 camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out,
         ACameraWindowType* window) {
     ATRACE_CALL();
diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp
index 34ec2da..9a39ed8 100644
--- a/camera/ndk/NdkCameraMetadata.cpp
+++ b/camera/ndk/NdkCameraMetadata.cpp
@@ -69,3 +69,20 @@
         metadata->decStrong((void*) ACameraMetadata_free);
     }
 }
+
+EXPORT
+bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata,
+        /*out*/size_t* numPhysicalCameras, /*out*/const char*const** physicalCameraIds) {
+    ATRACE_CALL();
+    if (numPhysicalCameras == nullptr || physicalCameraIds == nullptr) {
+        ALOGE("%s: Invalid input: numPhysicalCameras %p, physicalCameraIds %p",
+                 __FUNCTION__, numPhysicalCameras, physicalCameraIds);
+        return false;
+    }
+    if (staticMetadata == nullptr) {
+        ALOGE("%s: Invalid input: staticMetadata is null.", __FUNCTION__);
+        return false;
+    }
+
+    return staticMetadata->isLogicalMultiCamera(numPhysicalCameras, physicalCameraIds);
+}
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index fb72bdb..d6f1412 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -107,47 +107,6 @@
     return ret;
 }
 
-camera_status_t
-ACameraCaptureSession::setRepeatingRequest(
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    sp<acam::CameraDevice> dev = getDeviceSp();
-    if (dev == nullptr) {
-        ALOGE("Error: Device associated with session %p has been closed!", this);
-        return ACAMERA_ERROR_SESSION_CLOSED;
-    }
-
-    camera_status_t ret;
-    dev->lockDeviceForSessionOps();
-    {
-        Mutex::Autolock _l(mSessionLock);
-        ret = dev->setRepeatingRequestsLocked(
-                this, cbs, numRequests, requests, captureSequenceId);
-    }
-    dev->unlockDevice();
-    return ret;
-}
-
-camera_status_t ACameraCaptureSession::capture(
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    sp<acam::CameraDevice> dev = getDeviceSp();
-    if (dev == nullptr) {
-        ALOGE("Error: Device associated with session %p has been closed!", this);
-        return ACAMERA_ERROR_SESSION_CLOSED;
-    }
-    camera_status_t ret;
-    dev->lockDeviceForSessionOps();
-    {
-        Mutex::Autolock _l(mSessionLock);
-        ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId);
-    }
-    dev->unlockDevice();
-    return ret;
-}
-
 camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) {
     sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h
index 133c2c8..08a9226 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -17,6 +17,7 @@
 #define _ACAMERA_CAPTURE_SESSION_H
 
 #include <set>
+#include <string>
 #include <hardware/camera3.h>
 #include <camera/NdkCameraDevice.h>
 
@@ -29,8 +30,9 @@
 using namespace android;
 
 struct ACaptureSessionOutput {
-    explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false) :
-            mWindow(window), mIsShared(isShared) {};
+    explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false,
+            const char* physicalCameraId = "") :
+            mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {};
 
     bool operator == (const ACaptureSessionOutput& other) const {
         return mWindow == other.mWindow;
@@ -49,6 +51,7 @@
     std::set<ACameraWindowType *> mSharedWindows;
     bool           mIsShared;
     int            mRotation = CAMERA3_STREAM_ROTATION_0;
+    std::string mPhysicalCameraId;
 };
 #endif
 
@@ -88,13 +91,15 @@
 
     camera_status_t abortCaptures();
 
+    template<class T>
     camera_status_t setRepeatingRequest(
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t capture(
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
diff --git a/camera/ndk/impl/ACameraCaptureSession.inc b/camera/ndk/impl/ACameraCaptureSession.inc
new file mode 100644
index 0000000..86bf8a5
--- /dev/null
+++ b/camera/ndk/impl/ACameraCaptureSession.inc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "ACameraCaptureSession.h"
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraDeviceVendor.inc"
+#else
+#include "ACameraDevice.inc"
+#endif
+
+using namespace android;
+
+template <class T>
+camera_status_t
+ACameraCaptureSession::setRepeatingRequest(
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    sp<acam::CameraDevice> dev = getDeviceSp();
+    if (dev == nullptr) {
+        ALOGE("Error: Device associated with session %p has been closed!", this);
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    camera_status_t ret;
+    dev->lockDeviceForSessionOps();
+    {
+        Mutex::Autolock _l(mSessionLock);
+        ret = dev->setRepeatingRequestsLocked(
+                this, cbs, numRequests, requests, captureSequenceId);
+    }
+    dev->unlockDevice();
+    return ret;
+}
+
+template <class T>
+camera_status_t ACameraCaptureSession::capture(
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    sp<acam::CameraDevice> dev = getDeviceSp();
+    if (dev == nullptr) {
+        ALOGE("Error: Device associated with session %p has been closed!", this);
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+    camera_status_t ret;
+    dev->lockDeviceForSessionOps();
+    {
+        Mutex::Autolock _l(mSessionLock);
+        ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId);
+    }
+    dev->unlockDevice();
+    return ret;
+}
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 657d41f..d8a5765 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -20,13 +20,14 @@
 #include <vector>
 #include <inttypes.h>
 #include <android/hardware/ICameraService.h>
-#include <camera2/SubmitInfo.h>
 #include <gui/Surface.h>
 #include "ACameraDevice.h"
 #include "ACameraMetadata.h"
 #include "ACaptureRequest.h"
 #include "ACameraCaptureSession.h"
 
+#include "ACameraCaptureSession.inc"
+
 namespace android {
 namespace acam {
 
@@ -39,6 +40,7 @@
 const char* CameraDevice::kCaptureRequestKey = "CaptureRequest";
 const char* CameraDevice::kTimeStampKey      = "TimeStamp";
 const char* CameraDevice::kCaptureResultKey  = "CaptureResult";
+const char* CameraDevice::kPhysicalCaptureResultKey = "PhysicalCaptureResult";
 const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
 const char* CameraDevice::kSequenceIdKey     = "SequenceId";
 const char* CameraDevice::kFrameNumberKey    = "FrameNumber";
@@ -190,106 +192,6 @@
     return ACAMERA_OK;
 }
 
-camera_status_t
-CameraDevice::captureLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    return submitRequestsLocked(
-            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
-}
-
-camera_status_t
-CameraDevice::setRepeatingRequestsLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    return submitRequestsLocked(
-            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
-}
-
-camera_status_t
-CameraDevice::submitRequestsLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId,
-        bool isRepeating) {
-    camera_status_t ret = checkCameraClosedOrErrorLocked();
-    if (ret != ACAMERA_OK) {
-        ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
-        return ret;
-    }
-
-    // Form two vectors of capture request, one for internal tracking
-    std::vector<hardware::camera2::CaptureRequest> requestList;
-    Vector<sp<CaptureRequest> > requestsV;
-    requestsV.setCapacity(numRequests);
-    for (int i = 0; i < numRequests; i++) {
-        sp<CaptureRequest> req;
-        ret = allocateCaptureRequest(requests[i], req);
-        if (ret != ACAMERA_OK) {
-            ALOGE("Convert capture request to internal format failure! ret %d", ret);
-            return ret;
-        }
-        if (req->mSurfaceList.empty()) {
-            ALOGE("Capture request without output target cannot be submitted!");
-            return ACAMERA_ERROR_INVALID_PARAMETER;
-        }
-        requestList.push_back(*(req.get()));
-        requestsV.push_back(req);
-    }
-
-    if (isRepeating) {
-        ret = stopRepeatingLocked();
-        if (ret != ACAMERA_OK) {
-            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
-            return ret;
-        }
-    }
-
-    binder::Status remoteRet;
-    hardware::camera2::utils::SubmitInfo info;
-    remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info);
-    int sequenceId = info.mRequestId;
-    int64_t lastFrameNumber = info.mLastFrameNumber;
-    if (sequenceId < 0) {
-        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
-        return ACAMERA_ERROR_UNKNOWN;
-    }
-
-    CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
-    mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
-
-    if (isRepeating) {
-        // stopRepeating above should have cleanup repeating sequence id
-        if (mRepeatingSequenceId != REQUEST_ID_NONE) {
-            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
-            return ACAMERA_ERROR_CAMERA_DEVICE;
-        }
-        mRepeatingSequenceId = sequenceId;
-    } else {
-        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
-    }
-
-    if (mIdle) {
-        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
-        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
-        msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
-        postSessionMsgAndCleanup(msg);
-    }
-    mIdle = false;
-    mBusySession = session;
-
-    if (captureSequenceId) {
-        *captureSequenceId = sequenceId;
-    }
-    return ACAMERA_OK;
-}
-
 camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) {
     camera_status_t ret = checkCameraClosedOrErrorLocked();
     if (ret != ACAMERA_OK) {
@@ -325,8 +227,9 @@
         return ret;
     }
 
-    OutputConfiguration outConfig(iGBP, output->mRotation, OutputConfiguration::INVALID_SET_ID,
-            true);
+    String16 physicalId16(output->mPhysicalCameraId.c_str());
+    OutputConfiguration outConfig(iGBP, output->mRotation, physicalId16,
+            OutputConfiguration::INVALID_SET_ID, true);
 
     for (auto& anw : output->mSharedWindows) {
         ret = getIGBPfromAnw(anw, iGBP);
@@ -640,8 +543,9 @@
         if (ret != ACAMERA_OK) {
             return ret;
         }
+        String16 physicalId16(outConfig.mPhysicalCameraId.c_str());
         outputSet.insert(std::make_pair(
-                anw, OutputConfiguration(iGBP, outConfig.mRotation,
+                anw, OutputConfiguration(iGBP, outConfig.mRotation, physicalId16,
                         OutputConfiguration::INVALID_SET_ID, outConfig.mIsShared)));
     }
     auto addSet = outputSet;
@@ -829,7 +733,7 @@
     if (errorCode == hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER) {
         int32_t streamId = resultExtras.errorStreamId;
         ACameraCaptureSession_captureCallback_bufferLost onBufferLost =
-                cbh.mCallbacks.onCaptureBufferLost;
+                cbh.mOnCaptureBufferLost;
         auto outputPairIt = mConfiguredOutputs.find(streamId);
         if (outputPairIt == mConfiguredOutputs.end()) {
             ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId);
@@ -846,7 +750,7 @@
                             getId(), anw, frameNumber);
 
                     sp<AMessage> msg = new AMessage(kWhatCaptureBufferLost, mHandler);
-                    msg->setPointer(kContextKey, cbh.mCallbacks.context);
+                    msg->setPointer(kContextKey, cbh.mContext);
                     msg->setObject(kSessionSpKey, session);
                     msg->setPointer(kCallbackFpKey, (void*) onBufferLost);
                     msg->setObject(kCaptureRequestKey, request);
@@ -858,7 +762,7 @@
         }
     } else { // Handle other capture failures
         // Fire capture failure callback if there is one registered
-        ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed;
+        ACameraCaptureSession_captureCallback_failed onError = cbh.mOnCaptureFailed;
         sp<CameraCaptureFailure> failure(new CameraCaptureFailure());
         failure->frameNumber = frameNumber;
         // TODO: refine this when implementing flush
@@ -868,7 +772,7 @@
                 hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT);
 
         sp<AMessage> msg = new AMessage(kWhatCaptureFail, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
         msg->setPointer(kCallbackFpKey, (void*) onError);
         msg->setObject(kCaptureRequestKey, request);
@@ -890,6 +794,7 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
@@ -960,6 +865,7 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
@@ -977,6 +883,7 @@
             switch (msg->what()) {
                 case kWhatCaptureStart:
                 case kWhatCaptureResult:
+                case kWhatLogicalCaptureResult:
                 case kWhatCaptureFail:
                 case kWhatCaptureBufferLost:
                     found = msg->findObject(kCaptureRequestKey, &obj);
@@ -1048,6 +955,64 @@
                     freeACaptureRequest(request);
                     break;
                 }
+                case kWhatLogicalCaptureResult:
+                {
+                    ACameraCaptureSession_logicalCamera_captureCallback_result onResult;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onResult);
+                    if (!found) {
+                        ALOGE("%s: Cannot find logicalCamera capture result callback!",
+                                __FUNCTION__);
+                        return;
+                    }
+                    if (onResult == nullptr) {
+                        return;
+                    }
+
+                    found = msg->findObject(kCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraMetadata> result(static_cast<ACameraMetadata*>(obj.get()));
+
+                    found = msg->findObject(kPhysicalCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find physical capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                            static_cast<ACameraPhysicalCaptureResultInfo*>(obj.get()));
+                    std::vector<PhysicalCaptureResultInfo>& physicalResultInfo =
+                            physicalResult->mPhysicalResultInfo;
+
+                    std::vector<std::string> physicalCameraIds;
+                    std::vector<sp<ACameraMetadata>> physicalMetadataCopy;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        String8 physicalId8(physicalResultInfo[i].mPhysicalCameraId);
+                        physicalCameraIds.push_back(physicalId8.c_str());
+
+                        CameraMetadata clone = physicalResultInfo[i].mPhysicalCameraMetadata;
+                        clone.update(ANDROID_SYNC_FRAME_NUMBER,
+                                &physicalResult->mFrameNumber, /*data_count*/1);
+                        sp<ACameraMetadata> metadata =
+                                new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT);
+                        physicalMetadataCopy.push_back(metadata);
+                    }
+
+                    std::vector<const char*> physicalCameraIdPtrs;
+                    std::vector<const ACameraMetadata*> physicalMetadataCopyPtrs;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str());
+                        physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get());
+                    }
+
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onResult)(context, session.get(), request, result.get(),
+                            physicalResultInfo.size(), physicalCameraIdPtrs.data(),
+                            physicalMetadataCopyPtrs.data());
+                    freeACaptureRequest(request);
+                    break;
+                }
                 case kWhatCaptureFail:
                 {
                     ACameraCaptureSession_captureCallback_failed onFail;
@@ -1158,12 +1123,34 @@
 }
 
 CameraDevice::CallbackHolder::CallbackHolder(
-    sp<ACameraCaptureSession>          session,
-    const Vector<sp<CaptureRequest> >& requests,
-    bool                               isRepeating,
-    ACameraCaptureSession_captureCallbacks* cbs) :
-    mSession(session), mRequests(requests),
-    mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {}
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_captureCallbacks* cbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(false) {
+    initCaptureCallbacks(cbs);
+
+    if (cbs != nullptr) {
+        mOnCaptureCompleted = cbs->onCaptureCompleted;
+    }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(true) {
+    initCaptureCallbacks(lcbs);
+
+    if (lcbs != nullptr) {
+        mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted;
+    }
+}
 
 void
 CameraDevice::checkRepeatingSequenceCompleteLocked(
@@ -1180,9 +1167,9 @@
         mSequenceCallbackMap.erase(cbIt);
         // send seq aborted callback
         sp<AMessage> msg = new AMessage(kWhatCaptureSeqAbort, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, cbh.mSession);
-        msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted);
+        msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted);
         msg->setInt32(kSequenceIdKey, sequenceId);
         postSessionMsgAndCleanup(msg);
     } else {
@@ -1230,9 +1217,9 @@
             mSequenceCallbackMap.erase(cbIt);
             // send seq complete callback
             sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
-            msg->setPointer(kContextKey, cbh.mCallbacks.context);
+            msg->setPointer(kContextKey, cbh.mContext);
             msg->setObject(kSessionSpKey, cbh.mSession);
-            msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted);
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
             msg->setInt32(kSequenceIdKey, sequenceId);
             msg->setInt64(kFrameNumberKey, lastFrameNumber);
 
@@ -1389,7 +1376,7 @@
     auto it = dev->mSequenceCallbackMap.find(sequenceId);
     if (it != dev->mSequenceCallbackMap.end()) {
         CallbackHolder cbh = (*it).second;
-        ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted;
+        ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted;
         sp<ACameraCaptureSession> session = cbh.mSession;
         if ((size_t) burstId >= cbh.mRequests.size()) {
             ALOGE("%s: Error: request index %d out of bound (size %zu)",
@@ -1398,7 +1385,7 @@
         }
         sp<CaptureRequest> request = cbh.mRequests[burstId];
         sp<AMessage> msg = new AMessage(kWhatCaptureStart, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
         msg->setPointer(kCallbackFpKey, (void*) onStart);
         msg->setObject(kCaptureRequestKey, request);
@@ -1413,7 +1400,6 @@
         const CameraMetadata& metadata,
         const CaptureResultExtras& resultExtras,
         const std::vector<PhysicalCaptureResultInfo>& physicalResultInfos) {
-    (void) physicalResultInfos;
     binder::Status ret = binder::Status::ok();
 
     sp<CameraDevice> dev = mDevice.promote();
@@ -1449,9 +1435,6 @@
     auto it = dev->mSequenceCallbackMap.find(sequenceId);
     if (it != dev->mSequenceCallbackMap.end()) {
         CallbackHolder cbh = (*it).second;
-        ACameraCaptureSession_captureCallback_result onResult = isPartialResult ?
-                cbh.mCallbacks.onCaptureProgressed :
-                cbh.mCallbacks.onCaptureCompleted;
         sp<ACameraCaptureSession> session = cbh.mSession;
         if ((size_t) burstId >= cbh.mRequests.size()) {
             ALOGE("%s: Error: request index %d out of bound (size %zu)",
@@ -1461,13 +1444,27 @@
         sp<CaptureRequest> request = cbh.mRequests[burstId];
         sp<ACameraMetadata> result(new ACameraMetadata(
                 metadataCopy.release(), ACameraMetadata::ACM_RESULT));
+        sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                new ACameraPhysicalCaptureResultInfo(physicalResultInfos, frameNumber));
 
-        sp<AMessage> msg = new AMessage(kWhatCaptureResult, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        sp<AMessage> msg = new AMessage(
+                cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult,
+                dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) onResult);
         msg->setObject(kCaptureRequestKey, request);
         msg->setObject(kCaptureResultKey, result);
+        if (isPartialResult) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureProgressed);
+        } else if (cbh.mIsLogicalCameraCallback) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnLogicalCameraCaptureCompleted);
+            msg->setObject(kPhysicalCaptureResultKey, physicalResult);
+        } else {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureCompleted);
+        }
         dev->postSessionMsgAndCleanup(msg);
     }
 
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 8f56d3f..d0f363b 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -21,6 +21,7 @@
 #include <set>
 #include <atomic>
 #include <utility>
+#include <vector>
 #include <utils/StrongPointer.h>
 #include <utils/Mutex.h>
 #include <utils/String8.h>
@@ -46,6 +47,16 @@
 // Wrap ACameraCaptureFailure so it can be ref-counted
 struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {};
 
+// Wrap PhysicalCaptureResultInfo so that it can be ref-counted
+struct ACameraPhysicalCaptureResultInfo: public RefBase {
+    ACameraPhysicalCaptureResultInfo(const std::vector<PhysicalCaptureResultInfo>& info,
+            int64_t frameNumber) :
+        mPhysicalResultInfo(info), mFrameNumber(frameNumber) {}
+
+    std::vector<PhysicalCaptureResultInfo> mPhysicalResultInfo;
+    int64_t mFrameNumber;
+};
+
 class CameraDevice final : public RefBase {
   public:
     CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
@@ -109,19 +120,22 @@
     camera_status_t waitUntilIdleLocked();
 
 
+    template<class T>
     camera_status_t captureLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t submitRequestsLocked(
             sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*out*/int* captureSequenceId,
             bool isRepeating);
@@ -192,6 +206,7 @@
         // Capture callbacks
         kWhatCaptureStart,     // onCaptureStarted
         kWhatCaptureResult,    // onCaptureProgressed, onCaptureCompleted
+        kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted
         kWhatCaptureFail,      // onCaptureFailed
         kWhatCaptureSeqEnd,    // onCaptureSequenceCompleted
         kWhatCaptureSeqAbort,  // onCaptureSequenceAborted
@@ -207,6 +222,7 @@
     static const char* kCaptureRequestKey;
     static const char* kTimeStampKey;
     static const char* kCaptureResultKey;
+    static const char* kPhysicalCaptureResultKey;
     static const char* kCaptureFailureKey;
     static const char* kSequenceIdKey;
     static const char* kFrameNumberKey;
@@ -245,19 +261,46 @@
                        const Vector<sp<CaptureRequest> >& requests,
                        bool                               isRepeating,
                        ACameraCaptureSession_captureCallbacks* cbs);
+        CallbackHolder(sp<ACameraCaptureSession>          session,
+                       const Vector<sp<CaptureRequest> >& requests,
+                       bool                               isRepeating,
+                       ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs);
 
-        static ACameraCaptureSession_captureCallbacks fillCb(
-                ACameraCaptureSession_captureCallbacks* cbs) {
+        template <class T>
+        void initCaptureCallbacks(T* cbs) {
+            mContext = nullptr;
+            mOnCaptureStarted = nullptr;
+            mOnCaptureProgressed = nullptr;
+            mOnCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureCompleted = nullptr;
+            mOnCaptureFailed = nullptr;
+            mOnCaptureSequenceCompleted = nullptr;
+            mOnCaptureSequenceAborted = nullptr;
+            mOnCaptureBufferLost = nullptr;
             if (cbs != nullptr) {
-                return *cbs;
+                mContext = cbs->context;
+                mOnCaptureStarted = cbs->onCaptureStarted;
+                mOnCaptureProgressed = cbs->onCaptureProgressed;
+                mOnCaptureFailed = cbs->onCaptureFailed;
+                mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted;
+                mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted;
+                mOnCaptureBufferLost = cbs->onCaptureBufferLost;
             }
-            return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
         }
-
         sp<ACameraCaptureSession>   mSession;
         Vector<sp<CaptureRequest> > mRequests;
         const bool                  mIsRepeating;
-        ACameraCaptureSession_captureCallbacks mCallbacks;
+        const bool                  mIsLogicalCameraCallback;
+
+        void*                       mContext;
+        ACameraCaptureSession_captureCallback_start mOnCaptureStarted;
+        ACameraCaptureSession_captureCallback_result mOnCaptureProgressed;
+        ACameraCaptureSession_captureCallback_result mOnCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted;
+        ACameraCaptureSession_captureCallback_failed mOnCaptureFailed;
+        ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted;
+        ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted;
+        ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost;
     };
     // sequence id -> callbacks map
     std::map<int, CallbackHolder> mSequenceCallbackMap;
diff --git a/camera/ndk/impl/ACameraDevice.inc b/camera/ndk/impl/ACameraDevice.inc
new file mode 100644
index 0000000..1fc5352
--- /dev/null
+++ b/camera/ndk/impl/ACameraDevice.inc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <vector>
+#include <inttypes.h>
+#include "ACameraDevice.h"
+#include "ACameraMetadata.h"
+#include "ACaptureRequest.h"
+#include "ACameraCaptureSession.h"
+
+namespace android {
+namespace acam {
+
+template<class T>
+camera_status_t
+CameraDevice::captureLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+template<class T>
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+template<class T>
+camera_status_t CameraDevice::submitRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId,
+        bool isRepeating) {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
+        return ret;
+    }
+
+    // Form two vectors of capture request, one for internal tracking
+    std::vector<hardware::camera2::CaptureRequest> requestList;
+    Vector<sp<CaptureRequest> > requestsV;
+    requestsV.setCapacity(numRequests);
+    for (int i = 0; i < numRequests; i++) {
+        sp<CaptureRequest> req;
+        ret = allocateCaptureRequest(requests[i], req);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Convert capture request to internal format failure! ret %d", ret);
+            return ret;
+        }
+        if (req->mSurfaceList.empty()) {
+            ALOGE("Capture request without output target cannot be submitted!");
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        requestList.push_back(*(req.get()));
+        requestsV.push_back(req);
+    }
+
+    if (isRepeating) {
+        ret = stopRepeatingLocked();
+        if (ret != ACAMERA_OK) {
+            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+            return ret;
+        }
+    }
+
+    binder::Status remoteRet;
+    hardware::camera2::utils::SubmitInfo info;
+    remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info);
+    int sequenceId = info.mRequestId;
+    int64_t lastFrameNumber = info.mLastFrameNumber;
+    if (sequenceId < 0) {
+        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+
+    CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
+    mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
+
+    if (isRepeating) {
+        // stopRepeating above should have cleanup repeating sequence id
+        if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+            return ACAMERA_ERROR_CAMERA_DEVICE;
+        }
+        mRepeatingSequenceId = sequenceId;
+    } else {
+        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+    }
+
+    if (mIdle) {
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+        postSessionMsgAndCleanup(msg);
+    }
+    mIdle = false;
+    mBusySession = session;
+
+    if (captureSequenceId) {
+        *captureSequenceId = sequenceId;
+    }
+    return ACAMERA_OK;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 94b57132..c661233 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -50,6 +50,7 @@
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT:
+        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA:
             return true;
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
@@ -79,11 +80,41 @@
         uint8_t capability = entry.data.u8[i];
         if (isNdkSupportedCapability(capability)) {
             capabilities.push(capability);
+
+            if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+                derivePhysicalCameraIds();
+            }
         }
     }
     mData.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities);
 }
 
+void
+ACameraMetadata::derivePhysicalCameraIds() {
+    ACameraMetadata_const_entry entry;
+    auto ret = getConstEntry(ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, &entry);
+    if (ret != ACAMERA_OK) {
+        ALOGE("%s: Get ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS key failed. ret %d",
+                __FUNCTION__, ret);
+        return;
+    }
+
+    const uint8_t* ids = entry.data.u8;
+    size_t start = 0;
+    for (size_t i = 0; i < entry.count; ++i) {
+        if (ids[i] == '\0') {
+            if (start != i) {
+                mStaticPhysicalCameraIds.push_back((const char*)ids+start);
+            }
+            start = i+1;
+        }
+    }
+
+    if (mStaticPhysicalCameraIds.size() < 2) {
+        ALOGW("%s: Logical multi-camera device only has %zu physical cameras",
+                __FUNCTION__, mStaticPhysicalCameraIds.size());
+    }
+}
 
 void
 ACameraMetadata::filterDurations(uint32_t tag) {
@@ -309,6 +340,27 @@
     return mData;
 }
 
+bool
+ACameraMetadata::isLogicalMultiCamera(size_t* count, const char*const** physicalCameraIds) const {
+    if (mType != ACM_CHARACTERISTICS) {
+        ALOGE("%s must be called for a static metadata!", __FUNCTION__);
+        return false;
+    }
+    if (count == nullptr || physicalCameraIds == nullptr) {
+        ALOGE("%s: Invalid input count: %p, physicalCameraIds: %p", __FUNCTION__,
+                count, physicalCameraIds);
+        return false;
+    }
+
+    if (mStaticPhysicalCameraIds.size() >= 2) {
+        *count = mStaticPhysicalCameraIds.size();
+        *physicalCameraIds = mStaticPhysicalCameraIds.data();
+        return true;
+    }
+
+    return false;
+}
+
 // TODO: some of key below should be hidden from user
 // ex: ACAMERA_REQUEST_ID and ACAMERA_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
 /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index f21dbaf..7049c4b 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -17,6 +17,7 @@
 #define _ACAMERA_METADATA_H
 
 #include <unordered_set>
+#include <vector>
 
 #include <sys/types.h>
 #include <utils/Mutex.h>
@@ -65,6 +66,7 @@
                             /*out*/const uint32_t** tags) const;
 
     const CameraMetadata& getInternalData() const;
+    bool isLogicalMultiCamera(size_t* count, const char* const** physicalCameraIds) const;
 
   private:
 
@@ -74,6 +76,7 @@
     void filterUnsupportedFeatures(); // Hide features not yet supported by NDK
     void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats
     void filterDurations(uint32_t tag); // translate hal format to NDK formats
+    void derivePhysicalCameraIds(); // Derive array of physical ids.
 
     template<typename INTERNAL_T, typename NDK_T>
     camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) {
@@ -112,6 +115,8 @@
     const ACAMERA_METADATA_TYPE mType;
 
     static std::unordered_set<uint32_t> sSystemTags;
+
+    std::vector<const char*> mStaticPhysicalCameraIds;
 };
 
 #endif // _ACAMERA_METADATA_H
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 1244582..d13a818 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -643,6 +643,103 @@
         ACaptureSessionOutput* output) __INTRODUCED_IN(28);
 #endif /* __ANDROID_API__ >= 28 */
 
+#if __ANDROID_API__ >= 29
+/**
+ * The definition of final capture result callback with logical multi-camera support.
+ *
+ * This has the same functionality as final ACameraCaptureSession_captureCallback_result, with
+ * added ability to return physical camera result metadata within a logical multi-camera.
+ *
+ * For a logical multi-camera, this function will be called with the Id and result metadata
+ * of the underlying physical cameras, which the corresponding capture request contains targets for.
+ * If the capture request doesn't contain targets specific to any physical camera, or the current
+ * camera device isn't a logical multi-camera, physicalResultCount will be 0.
+ *
+ * @param context The optional application context provided by user in
+ *                {@link ACameraCaptureSession_captureCallbacks}.
+ * @param session The camera capture session of interest.
+ * @param request The capture request of interest. Note that this pointer points to a copy of
+ *                capture request sent by application, so the address is different to what
+ *                application sent but the content will match. This request will be freed by
+ *                framework immediately after this callback returns.
+ * @param result The capture result metadata reported by camera device. The memory is managed by
+ *                camera framework. Do not access this pointer after this callback returns.
+ * @param physicalResultCount The number of physical camera result metadata
+ * @param physicalCameraIds The array of physical camera IDs on which the
+ *                physical result metadata are reported.
+ * @param physicalResults The array of capture result metadata reported by the
+ *                physical camera devices.
+ */
+typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_result)(
+        void* context, ACameraCaptureSession* session,
+        ACaptureRequest* request, const ACameraMetadata* result,
+        size_t physicalResultCount, const char** physicalCameraIds,
+        const ACameraMetadata** physicalResults);
+
+/**
+ * This has the same functionality as ACameraCaptureSession_captureCallbacks,
+ * with the exception that an onLogicalCameraCaptureCompleted callback is
+ * used, instead of onCaptureCompleted, to support logical multi-camera.
+ */
+typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks {
+    /**
+     * Same as ACameraCaptureSession_captureCallbacks
+     */
+    void*                                               context;
+    ACameraCaptureSession_captureCallback_start         onCaptureStarted;
+    ACameraCaptureSession_captureCallback_result        onCaptureProgressed;
+
+    /**
+     * This callback is called when an image capture has fully completed and all the
+     * result metadata is available. For a logical multi-camera, this callback
+     * also returns the result metadata for all physical cameras being
+     * explicitly requested on.
+     *
+     * <p>This callback will always fire after the last {@link onCaptureProgressed};
+     * in other words, no more partial results will be delivered once the completed result
+     * is available.</p>
+     *
+     * <p>For performance-intensive use-cases where latency is a factor, consider
+     * using {@link onCaptureProgressed} instead.</p>
+     *
+     * <p>Note that the ACaptureRequest pointer in the callback will not match what application has
+     * submitted, but the contents the ACaptureRequest will match what application submitted.</p>
+     */
+    ACameraCaptureSession_logicalCamera_captureCallback_result onLogicalCameraCaptureCompleted;
+
+    /**
+     * Same as ACameraCaptureSession_captureCallbacks
+     */
+    ACameraCaptureSession_captureCallback_failed        onCaptureFailed;
+    ACameraCaptureSession_captureCallback_sequenceEnd   onCaptureSequenceCompleted;
+    ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted;
+    ACameraCaptureSession_captureCallback_bufferLost    onCaptureBufferLost;
+} ACameraCaptureSession_logicalCamera_captureCallbacks;
+
+/**
+ * This has the same functionality as ACameraCaptureSession_capture, with added
+ * support for logical multi-camera where the capture callbacks supports result metadata for
+ * physical cameras.
+ */
+camera_status_t ACameraCaptureSession_logicalCamera_capture(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(29);
+
+/**
+ * This has the same functionality as ACameraCaptureSession_setRepeatingRequest, with added
+ * support for logical multi-camera where the capture callbacks supports result metadata for
+ * physical cameras.
+ */
+camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_CAPTURE_SESSION_H */
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 4fe43d5..26af4f8 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -765,6 +765,36 @@
 
 #endif /* __ANDROID_API__ >= 28 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Create a ACaptureSessionOutput object used for streaming from a physical
+ * camera as part of a logical camera device.
+ *
+ * <p>The ACaptureSessionOutput is used in {@link ACaptureSessionOutputContainer_add} method to add
+ * an output {@link ANativeWindow} to ACaptureSessionOutputContainer. Use
+ * {@link ACaptureSessionOutput_free} to free the object and its memory after application no longer
+ * needs the {@link ACaptureSessionOutput}.</p>
+ *
+ * @param anw the {@link ANativeWindow} to be associated with the {@link ACaptureSessionOutput}
+ * @param physicalId the Id of the physical camera this output is associated
+ *                  with.
+ * @param output the output {@link ACaptureSessionOutput} will be stored here if the
+ *                  method call succeeds.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds. The created container will be
+ *                                filled in the output argument.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if anw, physicalId or output is NULL.</li></ul>
+ *
+ * @see ACaptureSessionOutputContainer_add
+ */
+camera_status_t ACaptureSessionPhysicalOutput_create(
+        ACameraWindowType* anw, const char* physicalId,
+        /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_DEVICE_H */
diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h
index 611e270..9bbfb83 100644
--- a/camera/ndk/include/camera/NdkCameraMetadata.h
+++ b/camera/ndk/include/camera/NdkCameraMetadata.h
@@ -233,6 +233,28 @@
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Helper function to check if a camera is logical multi-camera.
+ *
+ * <p> Check whether a camera device is a logical multi-camera based on its
+ * static metadata. If it is, also returns its physical sub camera Ids.</p>
+ *
+ * @param staticMetadata the static metadata of the camera being checked.
+ * @param numPhysicalCameras returns the number of physical cameras.
+ * @param physicalCameraIds returns the array of physical camera Ids backing this logical
+ *                          camera device. Note that this pointer is only valid
+ *                          during the lifetime of the staticMetadata object.
+ *
+ * @return true if this is a logical multi-camera, false otherwise.
+ */
+bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata,
+        /*out*/size_t* numPhysicalCameras, /*out*/const char* const** physicalCameraIds)
+        __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_METADATA_H */
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index cb474f4..4bb74cb 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -5552,6 +5552,25 @@
     ACAMERA_DEPTH_END,
 
     /**
+     * <p>String containing the ids of the underlying physical cameras.</p>
+     *
+     * <p>Type: byte[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
+     * The null terminator for physical camera id must be preserved so that the whole string
+     * can be tokenized using '\0' to generate list of physical camera ids.</p>
+     * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the
+     * value of this tag will be ['2', '\0', '3', '\0'].</p>
+     * <p>The number of physical camera ids must be no less than 2.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =                 // byte[n]
+            ACAMERA_LOGICAL_MULTI_CAMERA_START,
+    /**
      * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
      *
      * <p>Type: byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t)</p>
diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt
index a29e96d..5a00022 100644
--- a/camera/ndk/libcamera2ndk.map.txt
+++ b/camera/ndk/libcamera2ndk.map.txt
@@ -2,9 +2,11 @@
   global:
     ACameraCaptureSession_abortCaptures;
     ACameraCaptureSession_capture;
+    ACameraCaptureSession_logicalCamera_capture; # introduced=29
     ACameraCaptureSession_close;
     ACameraCaptureSession_getDevice;
     ACameraCaptureSession_setRepeatingRequest;
+    ACameraCaptureSession_logicalCamera_setRepeatingRequest; # introduced=29
     ACameraCaptureSession_stopRepeating;
     ACameraCaptureSession_updateSharedOutput; # introduced=28
     ACameraDevice_close;
@@ -24,6 +26,7 @@
     ACameraMetadata_free;
     ACameraMetadata_getAllTags;
     ACameraMetadata_getConstEntry;
+    ACameraMetadata_isLogicalMultiCamera; # introduced=29
     ACameraOutputTarget_create;
     ACameraOutputTarget_free;
     ACaptureRequest_addTarget;
@@ -48,6 +51,7 @@
     ACaptureSessionSharedOutput_create; # introduced=28
     ACaptureSessionSharedOutput_add; # introduced=28
     ACaptureSessionSharedOutput_remove; # introduced=28
+    ACaptureSessionPhysicalOutput_create; # introduced=29
     ACaptureSessionOutput_free;
   local:
     *;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
index 8d9e90ca..e1af8c1 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
+#include <string>
 #include "utils.h"
 
 struct ACaptureSessionOutput {
-    explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false) :
-            mWindow(window), mIsShared(isShared) {};
+    explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false,
+            const char* physicalCameraId = "") :
+            mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {};
 
     bool operator == (const ACaptureSessionOutput& other) const {
         return (mWindow == other.mWindow);
@@ -40,6 +42,7 @@
     std::set<android::acam::utils::native_handle_ptr_wrapper> mSharedWindows;
     bool           mIsShared;
     int            mRotation = CAMERA3_STREAM_ROTATION_0;
+    std::string mPhysicalCameraId;
 };
 
 
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index 26e6b3c..f7863a5 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -29,6 +29,8 @@
 #include "ACaptureRequest.h"
 #include "utils.h"
 
+#include "ACameraCaptureSession.inc"
+
 using namespace android;
 
 namespace android {
@@ -47,6 +49,7 @@
 const char* CameraDevice::kCaptureRequestKey = "CaptureRequest";
 const char* CameraDevice::kTimeStampKey      = "TimeStamp";
 const char* CameraDevice::kCaptureResultKey  = "CaptureResult";
+const char* CameraDevice::kPhysicalCaptureResultKey = "PhysicalCaptureResult";
 const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
 const char* CameraDevice::kSequenceIdKey     = "SequenceId";
 const char* CameraDevice::kFrameNumberKey    = "FrameNumber";
@@ -206,28 +209,8 @@
     return ACAMERA_OK;
 }
 
-camera_status_t
-CameraDevice::captureLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    return submitRequestsLocked(
-            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
-}
-
-camera_status_t
-CameraDevice::setRepeatingRequestsLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    return submitRequestsLocked(
-            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
-}
-
-void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
-                                sp<CaptureRequest> &req) {
+void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
+        sp<CaptureRequest> &req) {
     CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData();
     const camera_metadata_t *camera_metadata = metadataCopy.getAndLock();
     HCameraMetadata hCameraMetadata;
@@ -237,101 +220,6 @@
     req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata));
 }
 
-camera_status_t
-CameraDevice::submitRequestsLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId,
-        bool isRepeating) {
-    camera_status_t ret = checkCameraClosedOrErrorLocked();
-    if (ret != ACAMERA_OK) {
-        ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
-        return ret;
-    }
-
-    // Form two vectors of capture request, one for internal tracking
-    std::vector<frameworks::cameraservice::device::V2_0::CaptureRequest> requestList;
-    Vector<sp<CaptureRequest>> requestsV;
-    requestsV.setCapacity(numRequests);
-    for (int i = 0; i < numRequests; i++) {
-        sp<CaptureRequest> req;
-        ret = allocateCaptureRequest(requests[i], req);
-        // We need to call this method since after submitRequestList is called,
-        // the request metadata queue might have removed the capture request
-        // metadata. Therefore we simply add the metadata to its wrapper class,
-        // so that it can be retrived later.
-        addRequestSettingsMetadata(requests[i], req);
-        if (ret != ACAMERA_OK) {
-            ALOGE("Convert capture request to internal format failure! ret %d", ret);
-            return ret;
-        }
-        if (req->mCaptureRequest.streamAndWindowIds.size() == 0) {
-            ALOGE("Capture request without output target cannot be submitted!");
-            return ACAMERA_ERROR_INVALID_PARAMETER;
-        }
-        requestList.push_back(utils::convertToHidl(req.get()));
-        requestsV.push_back(req);
-    }
-    if (isRepeating) {
-        ret = stopRepeatingLocked();
-        if (ret != ACAMERA_OK) {
-            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
-            return ret;
-        }
-    }
-
-    SubmitInfo info;
-    Status status;
-    auto remoteRet = mRemote->submitRequestList(requestList, isRepeating,
-                                                [&status, &info](auto s, auto &submitInfo) {
-                                                    status = s;
-                                                    info = submitInfo;
-                                                });
-    if (!remoteRet.isOk()) {
-        ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__,
-              remoteRet.description().c_str());
-    }
-    if (status != Status::NO_ERROR) {
-        return utils::convertFromHidl(status);
-    }
-    int32_t sequenceId = info.requestId;
-    int64_t lastFrameNumber = info.lastFrameNumber;
-    if (sequenceId < 0) {
-        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
-        return ACAMERA_ERROR_UNKNOWN;
-    }
-
-    CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
-    mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
-
-    if (isRepeating) {
-        // stopRepeating above should have cleanup repeating sequence id
-        if (mRepeatingSequenceId != REQUEST_ID_NONE) {
-            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
-            return ACAMERA_ERROR_CAMERA_DEVICE;
-        }
-        mRepeatingSequenceId = sequenceId;
-    } else {
-        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
-    }
-
-    if (mIdle) {
-        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
-        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
-        msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
-        postSessionMsgAndCleanup(msg);
-    }
-    mIdle = false;
-    mBusySession = session;
-
-    if (captureSequenceId) {
-        *captureSequenceId = sequenceId;
-    }
-    return ACAMERA_OK;
-}
-
 camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) {
     camera_status_t ret = checkCameraClosedOrErrorLocked();
     if (ret != ACAMERA_OK) {
@@ -365,6 +253,7 @@
     outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing.
     outConfig.windowHandles.resize(output->mSharedWindows.size() + 1);
     outConfig.windowHandles[0] = output->mWindow;
+    outConfig.physicalCameraId = output->mPhysicalCameraId;
     int i = 1;
     for (auto& anw : output->mSharedWindows) {
         outConfig.windowHandles[i++] = anw;
@@ -668,6 +557,7 @@
         outConfigInsert.windowGroupId = -1;
         outConfigInsert.windowHandles.resize(outConfig.mSharedWindows.size() + 1);
         outConfigInsert.windowHandles[0] = anw;
+        outConfigInsert.physicalCameraId = outConfig.mPhysicalCameraId;
         native_handle_ptr_wrapper wrap(anw);
         outputSet.insert(std::make_pair(anw, outConfigInsertW));
     }
@@ -894,7 +784,7 @@
     if (errorCode == ErrorCode::CAMERA_BUFFER) {
         int32_t streamId = resultExtras.errorStreamId;
         ACameraCaptureSession_captureCallback_bufferLost onBufferLost =
-                cbh.mCallbacks.onCaptureBufferLost;
+                cbh.mOnCaptureBufferLost;
         auto outputPairIt = mConfiguredOutputs.find(streamId);
         if (outputPairIt == mConfiguredOutputs.end()) {
             ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId);
@@ -913,7 +803,7 @@
                             getId(), anw, frameNumber);
 
                     sp<AMessage> msg = new AMessage(kWhatCaptureBufferLost, mHandler);
-                    msg->setPointer(kContextKey, cbh.mCallbacks.context);
+                    msg->setPointer(kContextKey, cbh.mContext);
                     msg->setObject(kSessionSpKey, session);
                     msg->setPointer(kCallbackFpKey, (void*) onBufferLost);
                     msg->setObject(kCaptureRequestKey, request);
@@ -925,7 +815,7 @@
         }
     } else { // Handle other capture failures
         // Fire capture failure callback if there is one registered
-        ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed;
+        ACameraCaptureSession_captureCallback_failed onError = cbh.mOnCaptureFailed;
         sp<CameraCaptureFailure> failure(new CameraCaptureFailure());
         failure->frameNumber = frameNumber;
         // TODO: refine this when implementing flush
@@ -934,7 +824,7 @@
         failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT);
 
         sp<AMessage> msg = new AMessage(kWhatCaptureFail, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
         msg->setPointer(kCallbackFpKey, (void*) onError);
         msg->setObject(kCaptureRequestKey, request);
@@ -956,6 +846,7 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
@@ -1026,6 +917,7 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
@@ -1043,6 +935,7 @@
             switch (msg->what()) {
                 case kWhatCaptureStart:
                 case kWhatCaptureResult:
+                case kWhatLogicalCaptureResult:
                 case kWhatCaptureFail:
                 case kWhatCaptureBufferLost:
                     found = msg->findObject(kCaptureRequestKey, &obj);
@@ -1114,6 +1007,62 @@
                     freeACaptureRequest(request);
                     break;
                 }
+                case kWhatLogicalCaptureResult:
+                {
+                    ACameraCaptureSession_logicalCamera_captureCallback_result onResult;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onResult);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture result callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onResult == nullptr) {
+                        return;
+                    }
+
+                    found = msg->findObject(kCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraMetadata> result(static_cast<ACameraMetadata*>(obj.get()));
+
+                    found = msg->findObject(kPhysicalCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find physical capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                            static_cast<ACameraPhysicalCaptureResultInfo*>(obj.get()));
+                    std::vector<PhysicalCaptureResultInfoLocal>& physicalResultInfo =
+                            physicalResult->mPhysicalResultInfo;
+
+                    std::vector<std::string> physicalCameraIds;
+                    std::vector<sp<ACameraMetadata>> physicalMetadataCopy;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIds.push_back(physicalResultInfo[i].physicalCameraId);
+
+                        CameraMetadata clone = physicalResultInfo[i].physicalMetadata;
+                        clone.update(ANDROID_SYNC_FRAME_NUMBER,
+                                &physicalResult->mFrameNumber, /*data_count*/1);
+                        sp<ACameraMetadata> metadata =
+                                new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT);
+                        physicalMetadataCopy.push_back(metadata);
+                    }
+                    std::vector<const char*> physicalCameraIdPtrs;
+                    std::vector<const ACameraMetadata*> physicalMetadataCopyPtrs;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str());
+                        physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get());
+                    }
+
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onResult)(context, session.get(), request, result.get(),
+                            physicalResultInfo.size(), physicalCameraIdPtrs.data(),
+                            physicalMetadataCopyPtrs.data());
+                    freeACaptureRequest(request);
+                    break;
+                }
+
                 case kWhatCaptureFail:
                 {
                     ACameraCaptureSession_captureCallback_failed onFail;
@@ -1224,12 +1173,34 @@
 }
 
 CameraDevice::CallbackHolder::CallbackHolder(
-    sp<ACameraCaptureSession>          session,
-    const Vector<sp<CaptureRequest> >& requests,
-    bool                               isRepeating,
-    ACameraCaptureSession_captureCallbacks* cbs) :
-    mSession(session), mRequests(requests),
-    mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {}
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_captureCallbacks* cbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(false) {
+    initCaptureCallbacks(cbs);
+
+    if (cbs != nullptr) {
+        mOnCaptureCompleted = cbs->onCaptureCompleted;
+    }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(true) {
+    initCaptureCallbacks(lcbs);
+
+    if (lcbs != nullptr) {
+        mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted;
+    }
+}
 
 void
 CameraDevice::checkRepeatingSequenceCompleteLocked(
@@ -1246,9 +1217,9 @@
         mSequenceCallbackMap.erase(cbIt);
         // send seq aborted callback
         sp<AMessage> msg = new AMessage(kWhatCaptureSeqAbort, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, cbh.mSession);
-        msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted);
+        msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted);
         msg->setInt32(kSequenceIdKey, sequenceId);
         postSessionMsgAndCleanup(msg);
     } else {
@@ -1295,9 +1266,9 @@
             mSequenceCallbackMap.erase(cbIt);
             // send seq complete callback
             sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
-            msg->setPointer(kContextKey, cbh.mCallbacks.context);
+            msg->setPointer(kContextKey, cbh.mContext);
             msg->setObject(kSessionSpKey, cbh.mSession);
-            msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted);
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
             msg->setInt32(kSequenceIdKey, sequenceId);
             msg->setInt64(kFrameNumberKey, lastFrameNumber);
 
@@ -1454,7 +1425,7 @@
     auto it = dev->mSequenceCallbackMap.find(sequenceId);
     if (it != dev->mSequenceCallbackMap.end()) {
         CallbackHolder cbh = (*it).second;
-        ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted;
+        ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted;
         sp<ACameraCaptureSession> session = cbh.mSession;
         if ((size_t) burstId >= cbh.mRequests.size()) {
             ALOGE("%s: Error: request index %d out of bound (size %zu)",
@@ -1463,7 +1434,7 @@
         }
         sp<CaptureRequest> request = cbh.mRequests[burstId];
         sp<AMessage> msg = new AMessage(kWhatCaptureStart, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
         msg->setPointer(kCallbackFpKey, (void*) onStart);
         msg->setObject(kCaptureRequestKey, request);
@@ -1478,7 +1449,6 @@
         const FmqSizeOrMetadata& resultMetadata,
         const CaptureResultExtras& resultExtras,
         const hidl_vec<PhysicalCaptureResultInfo>& physicalResultInfos) {
-    (void) physicalResultInfos;
     auto ret = Void();
 
     sp<CameraDevice> dev = mDevice.promote();
@@ -1508,27 +1478,10 @@
     }
 
     CameraMetadata metadataCopy;
-    HCameraMetadata hCameraMetadata;
-    bool converted = false;
-    if (resultMetadata.getDiscriminator() ==
-        FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
-        hCameraMetadata.resize(resultMetadata.fmqMetadataSize());
-        bool read = dev->mCaptureResultMetadataQueue->read(hCameraMetadata.data(),
-                                                           resultMetadata.fmqMetadataSize());
-            if (!read) {
-                ALOGE("%s capture request settings could't be read from fmq",
-                      __FUNCTION__);
-                return ret;
-            }
-            // TODO: Do we actually need to clone here ?
-            converted = utils::convertFromHidlCloned(hCameraMetadata, &metadataCopy);
-
-    } else {
-          converted = utils::convertFromHidlCloned(resultMetadata.metadata(), &metadataCopy);
-    }
-
-    if (!converted) {
-        ALOGE("%s result metadata couldn't be converted", __FUNCTION__);
+    camera_status_t status = readOneResultMetadata(resultMetadata,
+            dev->mCaptureResultMetadataQueue.get(), &metadataCopy);
+    if (status != ACAMERA_OK) {
+        ALOGE("%s: result metadata couldn't be converted", __FUNCTION__);
         return ret;
     }
 
@@ -1538,9 +1491,6 @@
     auto it = dev->mSequenceCallbackMap.find(sequenceId);
     if (it != dev->mSequenceCallbackMap.end()) {
         CallbackHolder cbh = (*it).second;
-        ACameraCaptureSession_captureCallback_result onResult = isPartialResult ?
-                cbh.mCallbacks.onCaptureProgressed :
-                cbh.mCallbacks.onCaptureCompleted;
         sp<ACameraCaptureSession> session = cbh.mSession;
         if ((size_t) burstId >= cbh.mRequests.size()) {
             ALOGE("%s: Error: request index %d out of bound (size %zu)",
@@ -1551,12 +1501,39 @@
         sp<ACameraMetadata> result(new ACameraMetadata(
                 metadataCopy.release(), ACameraMetadata::ACM_RESULT));
 
-        sp<AMessage> msg = new AMessage(kWhatCaptureResult, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        std::vector<PhysicalCaptureResultInfoLocal> localPhysicalResult;
+        localPhysicalResult.resize(physicalResultInfos.size());
+        for (size_t i = 0; i < physicalResultInfos.size(); i++) {
+            localPhysicalResult[i].physicalCameraId = physicalResultInfos[i].physicalCameraId;
+            status = readOneResultMetadata(physicalResultInfos[i].physicalCameraMetadata,
+                    dev->mCaptureResultMetadataQueue.get(),
+                    &localPhysicalResult[i].physicalMetadata);
+            if (status != ACAMERA_OK) {
+                ALOGE("%s: physical camera result metadata couldn't be converted", __FUNCTION__);
+                return ret;
+            }
+        }
+        sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                new ACameraPhysicalCaptureResultInfo(localPhysicalResult, frameNumber));
+
+        sp<AMessage> msg = new AMessage(
+                cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult,
+                dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) onResult);
         msg->setObject(kCaptureRequestKey, request);
         msg->setObject(kCaptureResultKey, result);
+        if (isPartialResult) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureProgressed);
+        } else if (cbh.mIsLogicalCameraCallback) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnLogicalCameraCaptureCompleted);
+            msg->setObject(kPhysicalCaptureResultKey, physicalResult);
+        } else {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureCompleted);
+        }
         dev->postSessionMsgAndCleanup(msg);
     }
 
@@ -1590,5 +1567,31 @@
     return ret;
 }
 
+camera_status_t CameraDevice::ServiceCallback::readOneResultMetadata(
+        const FmqSizeOrMetadata& fmqSizeOrMetadata, ResultMetadataQueue* metadataQueue,
+        CameraMetadata* metadata) {
+    if (metadataQueue == nullptr || metadata == nullptr) {
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    bool converted;
+    HCameraMetadata hCameraMetadata;
+    if (fmqSizeOrMetadata.getDiscriminator() ==
+            FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+        hCameraMetadata.resize(fmqSizeOrMetadata.fmqMetadataSize());
+        bool read = metadataQueue->read(
+                hCameraMetadata.data(), fmqSizeOrMetadata.fmqMetadataSize());
+        if (!read) {
+            ALOGE("%s capture request settings could't be read from fmq", __FUNCTION__);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        // TODO: Do we actually need to clone here ?
+        converted = utils::convertFromHidlCloned(hCameraMetadata, metadata);
+    } else {
+        converted = utils::convertFromHidlCloned(fmqSizeOrMetadata.metadata(), metadata);
+    }
+
+    return converted ? ACAMERA_OK : ACAMERA_ERROR_UNKNOWN;
+}
+
 } // namespace acam
 } // namespace android
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index 01a219f..c63b97f 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -21,6 +21,7 @@
 #include <set>
 #include <atomic>
 #include <utility>
+#include <vector>
 #include <utils/StrongPointer.h>
 #include <utils/Mutex.h>
 #include <utils/List.h>
@@ -65,6 +66,21 @@
 // Wrap ACameraCaptureFailure so it can be ref-counted
 struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure { };
 
+// Wrap PhysicalCaptureResultInfo so that it can be ref-counted
+struct PhysicalCaptureResultInfoLocal {
+    std::string physicalCameraId;
+    CameraMetadata physicalMetadata;
+};
+
+struct ACameraPhysicalCaptureResultInfo: public RefBase {
+    ACameraPhysicalCaptureResultInfo(const std::vector<PhysicalCaptureResultInfoLocal>& info,
+            int64_t frameNumber) :
+        mPhysicalResultInfo(info), mFrameNumber(frameNumber) {}
+
+    std::vector<PhysicalCaptureResultInfoLocal> mPhysicalResultInfo;
+    int64_t mFrameNumber;
+};
+
 class CameraDevice final : public RefBase {
   public:
     CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
@@ -99,6 +115,8 @@
         android::hardware::Return<void> onRepeatingRequestError(uint64_t lastFrameNumber,
                 int32_t stoppedSequenceId) override;
       private:
+        camera_status_t readOneResultMetadata(const FmqSizeOrMetadata& fmqSizeOrMetadata,
+                ResultMetadataQueue* metadataQueue, CameraMetadata* metadata);
         const wp<CameraDevice> mDevice;
     };
     inline sp<ICameraDeviceCallback> getServiceCallback() {
@@ -127,24 +145,28 @@
 
     camera_status_t waitUntilIdleLocked();
 
-
+    template<class T>
     camera_status_t captureLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t submitRequestsLocked(
             sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*out*/int* captureSequenceId,
             bool isRepeating);
 
+    void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, sp<CaptureRequest> &req);
+
     camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
 
     camera_status_t allocateCaptureRequest(
@@ -206,6 +228,7 @@
         // Capture callbacks
         kWhatCaptureStart,     // onCaptureStarted
         kWhatCaptureResult,    // onCaptureProgressed, onCaptureCompleted
+        kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted
         kWhatCaptureFail,      // onCaptureFailed
         kWhatCaptureSeqEnd,    // onCaptureSequenceCompleted
         kWhatCaptureSeqAbort,  // onCaptureSequenceAborted
@@ -221,6 +244,7 @@
     static const char* kCaptureRequestKey;
     static const char* kTimeStampKey;
     static const char* kCaptureResultKey;
+    static const char* kPhysicalCaptureResultKey;
     static const char* kCaptureFailureKey;
     static const char* kSequenceIdKey;
     static const char* kFrameNumberKey;
@@ -259,19 +283,47 @@
                        const Vector<sp<CaptureRequest>>&  requests,
                        bool                               isRepeating,
                        ACameraCaptureSession_captureCallbacks* cbs);
+        CallbackHolder(sp<ACameraCaptureSession>          session,
+                       const Vector<sp<CaptureRequest>>&  requests,
+                       bool                               isRepeating,
+                       ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs);
 
-        static ACameraCaptureSession_captureCallbacks fillCb(
-                ACameraCaptureSession_captureCallbacks* cbs) {
+        template <class T>
+        void initCaptureCallbacks(T* cbs) {
+            mContext = nullptr;
+            mOnCaptureStarted = nullptr;
+            mOnCaptureProgressed = nullptr;
+            mOnCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureCompleted = nullptr;
+            mOnCaptureFailed = nullptr;
+            mOnCaptureSequenceCompleted = nullptr;
+            mOnCaptureSequenceAborted = nullptr;
+            mOnCaptureBufferLost = nullptr;
             if (cbs != nullptr) {
-                return *cbs;
+                mContext = cbs->context;
+                mOnCaptureStarted = cbs->onCaptureStarted;
+                mOnCaptureProgressed = cbs->onCaptureProgressed;
+                mOnCaptureFailed = cbs->onCaptureFailed;
+                mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted;
+                mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted;
+                mOnCaptureBufferLost = cbs->onCaptureBufferLost;
             }
-            return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
         }
 
         sp<ACameraCaptureSession>   mSession;
-        Vector<sp<CaptureRequest>> mRequests;
+        Vector<sp<CaptureRequest>>  mRequests;
         const bool                  mIsRepeating;
-        ACameraCaptureSession_captureCallbacks mCallbacks;
+        const bool                  mIsLogicalCameraCallback;
+
+        void*                       mContext;
+        ACameraCaptureSession_captureCallback_start mOnCaptureStarted;
+        ACameraCaptureSession_captureCallback_result mOnCaptureProgressed;
+        ACameraCaptureSession_captureCallback_result mOnCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted;
+        ACameraCaptureSession_captureCallback_failed mOnCaptureFailed;
+        ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted;
+        ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted;
+        ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost;
     };
     // sequence id -> callbacks map
     std::map<int, CallbackHolder> mSequenceCallbackMap;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc
new file mode 100644
index 0000000..7d2304e
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <vector>
+#include <inttypes.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <CameraMetadata.h>
+
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "ACameraCaptureSession.h"
+#include "ACameraMetadata.h"
+#include "ACaptureRequest.h"
+#include "utils.h"
+
+using namespace android;
+
+namespace android {
+namespace acam {
+
+template<class T>
+camera_status_t
+CameraDevice::captureLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+template<class T>
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+template<class T>
+camera_status_t CameraDevice::submitRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*out*/int* captureSequenceId,
+        bool isRepeating)
+{
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
+        return ret;
+    }
+
+    // Form two vectors of capture request, one for internal tracking
+    std::vector<frameworks::cameraservice::device::V2_0::CaptureRequest> requestList;
+    Vector<sp<CaptureRequest>> requestsV;
+    requestsV.setCapacity(numRequests);
+    for (int i = 0; i < numRequests; i++) {
+        sp<CaptureRequest> req;
+        ret = allocateCaptureRequest(requests[i], req);
+        // We need to call this method since after submitRequestList is called,
+        // the request metadata queue might have removed the capture request
+        // metadata. Therefore we simply add the metadata to its wrapper class,
+        // so that it can be retrieved later.
+        addRequestSettingsMetadata(requests[i], req);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Convert capture request to internal format failure! ret %d", ret);
+            return ret;
+        }
+        if (req->mCaptureRequest.streamAndWindowIds.size() == 0) {
+            ALOGE("Capture request without output target cannot be submitted!");
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        requestList.push_back(utils::convertToHidl(req.get()));
+        requestsV.push_back(req);
+    }
+    if (isRepeating) {
+        ret = stopRepeatingLocked();
+        if (ret != ACAMERA_OK) {
+            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+            return ret;
+        }
+    }
+
+    SubmitInfo info;
+    Status status;
+    auto remoteRet = mRemote->submitRequestList(requestList, isRepeating,
+                                                [&status, &info](auto s, auto &submitInfo) {
+                                                    status = s;
+                                                    info = submitInfo;
+                                                });
+    if (!remoteRet.isOk()) {
+        ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__,
+              remoteRet.description().c_str());
+    }
+    if (status != Status::NO_ERROR) {
+        return utils::convertFromHidl(status);
+    }
+    int32_t sequenceId = info.requestId;
+    int64_t lastFrameNumber = info.lastFrameNumber;
+    if (sequenceId < 0) {
+        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+
+    CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
+    mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
+    if (isRepeating) {
+        // stopRepeating above should have cleanup repeating sequence id
+        if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+            return ACAMERA_ERROR_CAMERA_DEVICE;
+        }
+        mRepeatingSequenceId = sequenceId;
+    } else {
+        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+    }
+
+    if (mIdle) {
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+        postSessionMsgAndCleanup(msg);
+    }
+    mIdle = false;
+    mBusySession = session;
+
+    if (captureSequenceId) {
+        *captureSequenceId = sequenceId;
+    }
+    return ACAMERA_OK;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index fa8a7a3..8534b28 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -480,7 +480,8 @@
 
         sp<Surface> surface(new Surface(gbProducer, /*controlledByApp*/false));
 
-        OutputConfiguration output(gbProducer, /*rotation*/0);
+        String16 noPhysicalId;
+        OutputConfiguration output(gbProducer, /*rotation*/0, noPhysicalId);
 
         // Can we configure?
         res = device->beginConfigure();
diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
index e5e5024..f063506 100644
--- a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
+++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
@@ -139,18 +139,18 @@
     }
     CameraMetadataNative &result = resultWrapper->mResult;
     auto resultExtras = resultWrapper->mResultExtras;
-    auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos;
     HCaptureResultExtras hResultExtras =
             hardware::cameraservice::utils::conversion::convertToHidl(resultExtras);
-    hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos =
-            hardware::cameraservice::utils::conversion::convertToHidl(
-                    physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue);
 
     // Convert Metadata into HCameraMetadata;
     FmqSizeOrMetadata hResult;
     const camera_metadata_t *rawMetadata = result.getAndLock();
     converter->convertResultMetadataToHidl(rawMetadata, &hResult);
     result.unlock(rawMetadata);
+    auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos;
+    hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos =
+            hardware::cameraservice::utils::conversion::convertToHidl(
+                    physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue);
     auto ret = converter->mBase->onResultReceived(hResult, hResultExtras,
                                                   hPhysicalCaptureResultInfos);
     if (!ret.isOk()) {
diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp
index 582ce34..a87812b 100644
--- a/services/camera/libcameraservice/hidl/Convert.cpp
+++ b/services/camera/libcameraservice/hidl/Convert.cpp
@@ -89,8 +89,9 @@
     for (auto &handle : windowHandles) {
         iGBPs.push_back(new H2BGraphicBufferProducer(AImageReader_getHGBPFromHandle(handle)));
     }
+    String16 physicalCameraId16(hOutputConfiguration.physicalCameraId.c_str());
     hardware::camera2::params::OutputConfiguration outputConfiguration(
-        iGBPs, convertFromHidl(hOutputConfiguration.rotation),
+        iGBPs, convertFromHidl(hOutputConfiguration.rotation), physicalCameraId16,
         hOutputConfiguration.windowGroupId, OutputConfiguration::SURFACE_TYPE_UNKNOWN, 0, 0,
         (windowHandles.size() > 1));
     return outputConfiguration;