Merge "Audio V4: Split system and vendor Audio.h"
diff --git a/audio/2.0/default/Android.mk b/audio/2.0/default/Android.mk
index c1f1a25..12713d3 100644
--- a/audio/2.0/default/Android.mk
+++ b/audio/2.0/default/Android.mk
@@ -39,9 +39,8 @@
     android.hardware.audio@2.0 \
     android.hardware.audio.common@2.0 \
     android.hardware.audio.effect@2.0 \
-    android.hardware.soundtrigger@2.1 \
-    android.hardware.broadcastradio@1.0 \
-    android.hardware.broadcastradio@1.1
+    android.hardware.soundtrigger@2.0 \
+    android.hardware.soundtrigger@2.1
 
 # Can not switch to Android.bp until AUDIOSERVER_MULTILIB
 # is deprecated as build config variable are not supported
diff --git a/audio/2.0/default/service.cpp b/audio/2.0/default/service.cpp
index 4763c70..3cf7134 100644
--- a/audio/2.0/default/service.cpp
+++ b/audio/2.0/default/service.cpp
@@ -18,6 +18,7 @@
 
 #include <android/hardware/audio/2.0/IDevicesFactory.h>
 #include <android/hardware/audio/effect/2.0/IEffectsFactory.h>
+#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
 #include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
 #include <hidl/HidlTransportSupport.h>
 #include <hidl/LegacySupport.h>
@@ -28,7 +29,8 @@
 
 using android::hardware::audio::effect::V2_0::IEffectsFactory;
 using android::hardware::audio::V2_0::IDevicesFactory;
-using android::hardware::soundtrigger::V2_1::ISoundTriggerHw;
+using V2_0_ISoundTriggerHw = android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
+using V2_1_ISoundTriggerHw = android::hardware::soundtrigger::V2_1::ISoundTriggerHw;
 using android::hardware::registerPassthroughServiceImplementation;
 
 using android::OK;
@@ -41,8 +43,10 @@
     status = registerPassthroughServiceImplementation<IEffectsFactory>();
     LOG_ALWAYS_FATAL_IF(status != OK, "Error while registering audio effects service: %d", status);
     // Soundtrigger might be not present.
-    status = registerPassthroughServiceImplementation<ISoundTriggerHw>();
-    ALOGE_IF(status != OK, "Error while registering soundtrigger service: %d", status);
+    status = registerPassthroughServiceImplementation<V2_1_ISoundTriggerHw>();
+    ALOGW_IF(status != OK, "Registering soundtrigger V2.1 service was unsuccessful: %d", status);
+    status = registerPassthroughServiceImplementation<V2_0_ISoundTriggerHw>();
+    ALOGW_IF(status != OK, "Registering soundtrigger V2.0 service was unsuccessful: %d", status);
     joinRpcThreadpool();
     return status;
 }
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 6a254a5..774bc4f 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -35,7 +35,7 @@
 }
 
 // Vehicle reference implementation lib
-cc_library_static {
+cc_library {
     name: "android.hardware.automotive.vehicle@2.0-manager-lib",
     vendor: true,
     defaults: ["vhal_v2_0_defaults"],
@@ -52,13 +52,6 @@
     export_include_dirs: ["common/include"],
 }
 
-cc_library_shared {
-    name: "android.hardware.automotive.vehicle@2.0-manager-lib-shared",
-    vendor: true,
-    static_libs: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
-    export_static_lib_headers: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
-}
-
 // Vehicle default VehicleHAL implementation
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-default-impl-lib",
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 3a4bc9c..9217a82 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -425,6 +425,13 @@
     return -ENODEV;
 }
 
+void CameraModule::removeCamera(int cameraId) {
+    free_camera_metadata(
+        const_cast<camera_metadata_t*>(mCameraInfoMap[cameraId].static_camera_characteristics));
+    mCameraInfoMap.removeItem(cameraId);
+    mDeviceVersionMap.removeItem(cameraId);
+}
+
 uint16_t CameraModule::getModuleApiVersion() const {
     return mModule->common.module_api_version;
 }
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index fd8b943..e9741ef 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -134,6 +134,65 @@
     }
 }
 
+YCbCrLayout HandleImporter::lockYCbCr(
+        buffer_handle_t& buf, uint64_t cpuUsage,
+        const IMapper::Rect& accessRegion) {
+    Mutex::Autolock lock(mLock);
+    YCbCrLayout layout = {};
+
+    if (!mInitialized) {
+        initializeLocked();
+    }
+
+    if (mMapper == nullptr) {
+        ALOGE("%s: mMapper is null!", __FUNCTION__);
+        return layout;
+    }
+
+    hidl_handle acquireFenceHandle;
+    auto buffer = const_cast<native_handle_t*>(buf);
+    mMapper->lockYCbCr(buffer, cpuUsage, accessRegion, acquireFenceHandle,
+            [&](const auto& tmpError, const auto& tmpLayout) {
+                if (tmpError == MapperError::NONE) {
+                    layout = tmpLayout;
+                } else {
+                    ALOGE("%s: failed to lockYCbCr error %d!", __FUNCTION__, tmpError);
+                }
+           });
+
+    ALOGV("%s: layout y %p cb %p cr %p y_str %d c_str %d c_step %d",
+            __FUNCTION__, layout.y, layout.cb, layout.cr,
+            layout.yStride, layout.cStride, layout.chromaStep);
+    return layout;
+}
+
+int HandleImporter::unlock(buffer_handle_t& buf) {
+    int releaseFence = -1;
+    auto buffer = const_cast<native_handle_t*>(buf);
+    mMapper->unlock(
+        buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+            if (tmpError == MapperError::NONE) {
+                auto fenceHandle = tmpReleaseFence.getNativeHandle();
+                if (fenceHandle) {
+                    if (fenceHandle->numInts != 0 || fenceHandle->numFds != 1) {
+                        ALOGE("%s: bad release fence numInts %d numFds %d",
+                                __FUNCTION__, fenceHandle->numInts, fenceHandle->numFds);
+                        return;
+                    }
+                    releaseFence = dup(fenceHandle->data[0]);
+                    if (releaseFence <= 0) {
+                        ALOGE("%s: bad release fence FD %d",
+                                __FUNCTION__, releaseFence);
+                    }
+                }
+            } else {
+                ALOGE("%s: failed to unlock error %d!", __FUNCTION__, tmpError);
+            }
+        });
+
+    return releaseFence;
+}
+
 } // namespace helper
 } // namespace V1_0
 } // namespace common
diff --git a/camera/common/1.0/default/OWNERS b/camera/common/1.0/default/OWNERS
new file mode 100644
index 0000000..18acfee
--- /dev/null
+++ b/camera/common/1.0/default/OWNERS
@@ -0,0 +1,6 @@
+cychen@google.com
+epeev@google.com
+etalvala@google.com
+shuzhenwang@google.com
+yinchiayeh@google.com
+zhijunhe@google.com
diff --git a/camera/common/1.0/default/include/CameraModule.h b/camera/common/1.0/default/include/CameraModule.h
index 9fbfbd5..deebd09 100644
--- a/camera/common/1.0/default/include/CameraModule.h
+++ b/camera/common/1.0/default/include/CameraModule.h
@@ -63,6 +63,8 @@
     const char* getModuleAuthor() const;
     // Only used by CameraModuleFixture native test. Do NOT use elsewhere.
     void *getDso();
+    // Only used by CameraProvider
+    void removeCamera(int cameraId);
 
 private:
     // Derive camera characteristics keys defined after HAL device version
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
index e47397c..443362d 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -22,6 +22,7 @@
 #include <cutils/native_handle.h>
 
 using android::hardware::graphics::mapper::V2_0::IMapper;
+using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
 
 namespace android {
 namespace hardware {
@@ -43,6 +44,12 @@
     bool importFence(const native_handle_t* handle, int& fd) const;
     void closeFence(int fd) const;
 
+    // Assume caller has done waiting for acquire fences
+    YCbCrLayout lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
+                          const IMapper::Rect& accessRegion);
+
+    int unlock(buffer_handle_t& buf); // returns release fence
+
 private:
     void initializeLocked();
     void cleanup();
@@ -60,4 +67,4 @@
 } // namespace hardware
 } // namespace android
 
-#endif // CAMERA_COMMON_1_0_HANDLEIMPORTED_H
\ No newline at end of file
+#endif // CAMERA_COMMON_1_0_HANDLEIMPORTED_H
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index 631404e..31b4739 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -333,11 +333,10 @@
     mResultMetadataQueue = q;
 }
 
-void CameraDeviceSession::ResultBatcher::registerBatch(
-        const hidl_vec<CaptureRequest>& requests) {
+void CameraDeviceSession::ResultBatcher::registerBatch(uint32_t frameNumber, uint32_t batchSize) {
     auto batch = std::make_shared<InflightBatch>();
-    batch->mFirstFrame = requests[0].frameNumber;
-    batch->mBatchSize = requests.size();
+    batch->mFirstFrame = frameNumber;
+    batch->mBatchSize = batchSize;
     batch->mLastFrame = batch->mFirstFrame + batch->mBatchSize - 1;
     batch->mNumPartialResults = mNumPartialResults;
     for (int id : mStreamsToBatch) {
@@ -1010,7 +1009,7 @@
     }
 
     if (s == Status::OK && requests.size() > 1) {
-        mResultBatcher.registerBatch(requests);
+        mResultBatcher.registerBatch(requests[0].frameNumber, requests.size());
     }
 
     _hidl_cb(s, numRequestProcessed);
@@ -1111,6 +1110,7 @@
             halRequest.settings = settingsOverride.getAndLock();
         }
     }
+    halRequest.num_physcam_settings = 0;
 
     ATRACE_ASYNC_BEGIN("frame capture", request.frameNumber);
     ATRACE_BEGIN("camera3->process_capture_request");
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index c5a63c8..0048ef4 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -184,7 +184,7 @@
         void setBatchedStreams(const std::vector<int>& streamsToBatch);
         void setResultMetadataQueue(std::shared_ptr<ResultMetadataQueue> q);
 
-        void registerBatch(const hidl_vec<CaptureRequest>& requests);
+        void registerBatch(uint32_t frameNumber, uint32_t batchSize);
         void notify(NotifyMsg& msg);
         void processCaptureResult(CaptureResult& result);
 
diff --git a/camera/device/3.4/Android.bp b/camera/device/3.4/Android.bp
index 2523fa8..822cf69 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/Android.bp
@@ -18,6 +18,11 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "CaptureRequest",
+        "HalStream",
+        "HalStreamConfiguration",
+        "PhysicalCameraSetting",
+        "Stream",
         "StreamConfiguration",
     ],
     gen_java: false,
diff --git a/camera/device/3.4/ICameraDeviceSession.hal b/camera/device/3.4/ICameraDeviceSession.hal
index e5693b2..4ce749d 100644
--- a/camera/device/3.4/ICameraDeviceSession.hal
+++ b/camera/device/3.4/ICameraDeviceSession.hal
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017-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.
@@ -19,6 +19,7 @@
 import android.hardware.camera.common@1.0::Status;
 import @3.3::ICameraDeviceSession;
 import @3.3::HalStreamConfiguration;
+import @3.2::BufferCache;
 
 /**
  * Camera device active session interface.
@@ -69,6 +70,39 @@
      */
     configureStreams_3_4(@3.4::StreamConfiguration requestedConfiguration)
             generates (Status status,
-                       @3.3::HalStreamConfiguration halConfiguration);
+                       @3.4::HalStreamConfiguration halConfiguration);
 
+    /**
+     * processCaptureRequest_3_4:
+     *
+     * Identical to @3.2::ICameraDeviceSession.processCaptureRequest, except that:
+     *
+     * - The capture request can include individual settings for physical camera devices
+     *   backing a logical multi-camera.
+     *
+     * @return status Status code for the operation, one of:
+     *     OK:
+     *         On a successful start to processing the capture request
+     *     ILLEGAL_ARGUMENT:
+     *         If the input is malformed (the settings are empty when not
+     *         allowed, the physical camera settings are invalid, there are 0
+     *         output buffers, etc) and capture processing
+     *         cannot start. Failures during request processing must be
+     *         handled by calling ICameraDeviceCallback::notify(). In case of
+     *         this error, the framework retains responsibility for the
+     *         stream buffers' fences and the buffer handles; the HAL must not
+     *         close the fences or return these buffers with
+     *         ICameraDeviceCallback::processCaptureResult().
+     *     INTERNAL_ERROR:
+     *         If the camera device has encountered a serious error. After this
+     *         error is returned, only the close() method can be successfully
+     *         called by the framework.
+     * @return numRequestProcessed Number of requests successfully processed by
+     *     camera HAL. When status is OK, this must be equal to the size of
+     *     requests. When the call fails, this number is the number of requests
+     *     that HAL processed successfully before HAL runs into an error.
+     *
+     */
+    processCaptureRequest_3_4(vec<CaptureRequest> requests, vec<BufferCache> cachesToRemove)
+            generates (Status status, uint32_t numRequestProcessed);
 };
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index c0ce838..61ac244 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -17,7 +17,13 @@
 cc_library_headers {
     name: "camera.device@3.4-impl_headers",
     vendor: true,
-    export_include_dirs: ["include/device_v3_4_impl"],
+    export_include_dirs: ["include/device_v3_4_impl"]
+}
+
+cc_library_headers {
+    name: "camera.device@3.4-external-impl_headers",
+    vendor: true,
+    export_include_dirs: ["include/ext_device_v3_4_impl"]
 }
 
 cc_library_shared {
@@ -28,6 +34,7 @@
     srcs: [
         "CameraDevice.cpp",
         "CameraDeviceSession.cpp",
+        "convert.cpp",
     ],
     shared_libs: [
         "libhidlbase",
@@ -54,3 +61,40 @@
         "libfmq",
     ],
 }
+
+cc_library_shared {
+    name: "camera.device@3.4-external-impl",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    vendor: true,
+    srcs: [
+        "ExternalCameraDevice.cpp",
+        "ExternalCameraDeviceSession.cpp"
+    ],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+        "libcutils",
+        "camera.device@3.2-impl",
+        "camera.device@3.3-impl",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.graphics.mapper@2.0",
+        "liblog",
+        "libhardware",
+        "libcamera_metadata",
+        "libfmq",
+        "libsync",
+        "libyuv",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    local_include_dirs: ["include/ext_device_v3_4_impl"],
+    export_shared_lib_headers: [
+        "libfmq",
+    ],
+}
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index 0ae470f..c8d33eb 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017-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.
@@ -41,8 +41,8 @@
 }
 
 Return<void> CameraDeviceSession::configureStreams_3_4(
-        const V3_4::StreamConfiguration& requestedConfiguration,
-        ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb)  {
+        const StreamConfiguration& requestedConfiguration,
+        ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb)  {
     Status status = initStatus();
     HalStreamConfiguration outStreams;
 
@@ -86,7 +86,7 @@
     camera3_stream_configuration_t stream_list{};
     hidl_vec<camera3_stream_t*> streams;
     stream_list.session_parameters = paramBuffer;
-    if (!preProcessConfigurationLocked(requestedConfiguration.v3_2, &stream_list, &streams)) {
+    if (!preProcessConfigurationLocked_3_4(requestedConfiguration, &stream_list, &streams)) {
         _hidl_cb(Status::INTERNAL_ERROR, outStreams);
         return Void();
     }
@@ -98,7 +98,7 @@
     // In case Hal returns error most likely it was not able to release
     // the corresponding resources of the deleted streams.
     if (ret == OK) {
-        postProcessConfigurationLocked(requestedConfiguration.v3_2);
+        postProcessConfigurationLocked_3_4(requestedConfiguration);
     }
 
     if (ret == -EINVAL) {
@@ -106,7 +106,7 @@
     } else if (ret != OK) {
         status = Status::INTERNAL_ERROR;
     } else {
-        V3_3::implementation::convertToHidl(stream_list, &outStreams);
+        V3_4::implementation::convertToHidl(stream_list, &outStreams);
         mFirstRequest = true;
     }
 
@@ -114,6 +114,291 @@
     return Void();
 }
 
+bool CameraDeviceSession::preProcessConfigurationLocked_3_4(
+        const StreamConfiguration& requestedConfiguration,
+        camera3_stream_configuration_t *stream_list /*out*/,
+        hidl_vec<camera3_stream_t*> *streams /*out*/) {
+
+    if ((stream_list == nullptr) || (streams == nullptr)) {
+        return false;
+    }
+
+    stream_list->operation_mode = (uint32_t) requestedConfiguration.operationMode;
+    stream_list->num_streams = requestedConfiguration.streams.size();
+    streams->resize(stream_list->num_streams);
+    stream_list->streams = streams->data();
+
+    for (uint32_t i = 0; i < stream_list->num_streams; i++) {
+        int id = requestedConfiguration.streams[i].v3_2.id;
+
+        if (mStreamMap.count(id) == 0) {
+            Camera3Stream stream;
+            convertFromHidl(requestedConfiguration.streams[i], &stream);
+            mStreamMap[id] = stream;
+            mPhysicalCameraIdMap[id] = requestedConfiguration.streams[i].physicalCameraId;
+            mStreamMap[id].data_space = mapToLegacyDataspace(
+                    mStreamMap[id].data_space);
+            mStreamMap[id].physical_camera_id = mPhysicalCameraIdMap[id].c_str();
+            mCirculatingBuffers.emplace(stream.mId, CirculatingBuffers{});
+        } else {
+            // width/height/format must not change, but usage/rotation might need to change
+            if (mStreamMap[id].stream_type !=
+                    (int) requestedConfiguration.streams[i].v3_2.streamType ||
+                    mStreamMap[id].width != requestedConfiguration.streams[i].v3_2.width ||
+                    mStreamMap[id].height != requestedConfiguration.streams[i].v3_2.height ||
+                    mStreamMap[id].format != (int) requestedConfiguration.streams[i].v3_2.format ||
+                    mStreamMap[id].data_space !=
+                            mapToLegacyDataspace( static_cast<android_dataspace_t> (
+                                    requestedConfiguration.streams[i].v3_2.dataSpace)) ||
+                    mPhysicalCameraIdMap[id] != requestedConfiguration.streams[i].physicalCameraId) {
+                ALOGE("%s: stream %d configuration changed!", __FUNCTION__, id);
+                return false;
+            }
+            mStreamMap[id].rotation = (int) requestedConfiguration.streams[i].v3_2.rotation;
+            mStreamMap[id].usage = (uint32_t) requestedConfiguration.streams[i].v3_2.usage;
+        }
+        (*streams)[i] = &mStreamMap[id];
+    }
+
+    return true;
+}
+
+void CameraDeviceSession::postProcessConfigurationLocked_3_4(
+        const StreamConfiguration& requestedConfiguration) {
+    // delete unused streams, note we do this after adding new streams to ensure new stream
+    // will not have the same address as deleted stream, and HAL has a chance to reference
+    // the to be deleted stream in configure_streams call
+    for(auto it = mStreamMap.begin(); it != mStreamMap.end();) {
+        int id = it->first;
+        bool found = false;
+        for (const auto& stream : requestedConfiguration.streams) {
+            if (id == stream.v3_2.id) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            // Unmap all buffers of deleted stream
+            // in case the configuration call succeeds and HAL
+            // is able to release the corresponding resources too.
+            cleanupBuffersLocked(id);
+            it = mStreamMap.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Track video streams
+    mVideoStreamIds.clear();
+    for (const auto& stream : requestedConfiguration.streams) {
+        if (stream.v3_2.streamType == StreamType::OUTPUT &&
+            stream.v3_2.usage &
+                graphics::common::V1_0::BufferUsage::VIDEO_ENCODER) {
+            mVideoStreamIds.push_back(stream.v3_2.id);
+        }
+    }
+    mResultBatcher.setBatchedStreams(mVideoStreamIds);
+}
+
+Return<void> CameraDeviceSession::processCaptureRequest_3_4(
+        const hidl_vec<V3_4::CaptureRequest>& requests,
+        const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+        ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb)  {
+    updateBufferCaches(cachesToRemove);
+
+    uint32_t numRequestProcessed = 0;
+    Status s = Status::OK;
+    for (size_t i = 0; i < requests.size(); i++, numRequestProcessed++) {
+        s = processOneCaptureRequest_3_4(requests[i]);
+        if (s != Status::OK) {
+            break;
+        }
+    }
+
+    if (s == Status::OK && requests.size() > 1) {
+        mResultBatcher.registerBatch(requests[0].v3_2.frameNumber, requests.size());
+    }
+
+    _hidl_cb(s, numRequestProcessed);
+    return Void();
+}
+
+Status CameraDeviceSession::processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request)  {
+    Status status = initStatus();
+    if (status != Status::OK) {
+        ALOGE("%s: camera init failed or disconnected", __FUNCTION__);
+        return status;
+    }
+
+    camera3_capture_request_t halRequest;
+    halRequest.frame_number = request.v3_2.frameNumber;
+
+    bool converted = true;
+    V3_2::CameraMetadata settingsFmq;  // settings from FMQ
+    if (request.v3_2.fmqSettingsSize > 0) {
+        // non-blocking read; client must write metadata before calling
+        // processOneCaptureRequest
+        settingsFmq.resize(request.v3_2.fmqSettingsSize);
+        bool read = mRequestMetadataQueue->read(settingsFmq.data(), request.v3_2.fmqSettingsSize);
+        if (read) {
+            converted = V3_2::implementation::convertFromHidl(settingsFmq, &halRequest.settings);
+        } else {
+            ALOGE("%s: capture request settings metadata couldn't be read from fmq!", __FUNCTION__);
+            converted = false;
+        }
+    } else {
+        converted = V3_2::implementation::convertFromHidl(request.v3_2.settings,
+                &halRequest.settings);
+    }
+
+    if (!converted) {
+        ALOGE("%s: capture request settings metadata is corrupt!", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (mFirstRequest && halRequest.settings == nullptr) {
+        ALOGE("%s: capture request settings must not be null for first request!",
+                __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    hidl_vec<buffer_handle_t*> allBufPtrs;
+    hidl_vec<int> allFences;
+    bool hasInputBuf = (request.v3_2.inputBuffer.streamId != -1 &&
+            request.v3_2.inputBuffer.bufferId != 0);
+    size_t numOutputBufs = request.v3_2.outputBuffers.size();
+    size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
+
+    if (numOutputBufs == 0) {
+        ALOGE("%s: capture request must have at least one output buffer!", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    status = importRequest(request.v3_2, allBufPtrs, allFences);
+    if (status != Status::OK) {
+        return status;
+    }
+
+    hidl_vec<camera3_stream_buffer_t> outHalBufs;
+    outHalBufs.resize(numOutputBufs);
+    bool aeCancelTriggerNeeded = false;
+    ::android::hardware::camera::common::V1_0::helper::CameraMetadata settingsOverride;
+    {
+        Mutex::Autolock _l(mInflightLock);
+        if (hasInputBuf) {
+            auto streamId = request.v3_2.inputBuffer.streamId;
+            auto key = std::make_pair(request.v3_2.inputBuffer.streamId, request.v3_2.frameNumber);
+            auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{};
+            convertFromHidl(
+                    allBufPtrs[numOutputBufs], request.v3_2.inputBuffer.status,
+                    &mStreamMap[request.v3_2.inputBuffer.streamId], allFences[numOutputBufs],
+                    &bufCache);
+            bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str();
+            halRequest.input_buffer = &bufCache;
+        } else {
+            halRequest.input_buffer = nullptr;
+        }
+
+        halRequest.num_output_buffers = numOutputBufs;
+        for (size_t i = 0; i < numOutputBufs; i++) {
+            auto streamId = request.v3_2.outputBuffers[i].streamId;
+            auto key = std::make_pair(streamId, request.v3_2.frameNumber);
+            auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{};
+            convertFromHidl(
+                    allBufPtrs[i], request.v3_2.outputBuffers[i].status,
+                    &mStreamMap[streamId], allFences[i],
+                    &bufCache);
+            bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str();
+            outHalBufs[i] = bufCache;
+        }
+        halRequest.output_buffers = outHalBufs.data();
+
+        AETriggerCancelOverride triggerOverride;
+        aeCancelTriggerNeeded = handleAePrecaptureCancelRequestLocked(
+                halRequest, &settingsOverride /*out*/, &triggerOverride/*out*/);
+        if (aeCancelTriggerNeeded) {
+            mInflightAETriggerOverrides[halRequest.frame_number] =
+                    triggerOverride;
+            halRequest.settings = settingsOverride.getAndLock();
+        }
+    }
+
+    std::vector<const char *> physicalCameraIds;
+    std::vector<const camera_metadata_t *> physicalCameraSettings;
+    std::vector<V3_2::CameraMetadata> physicalFmq;
+    size_t settingsCount = request.physicalCameraSettings.size();
+    if (settingsCount > 0) {
+        physicalCameraIds.reserve(settingsCount);
+        physicalCameraSettings.reserve(settingsCount);
+        physicalFmq.reserve(settingsCount);
+
+        for (size_t i = 0; i < settingsCount; i++) {
+            uint64_t settingsSize = request.physicalCameraSettings[i].fmqSettingsSize;
+            const camera_metadata_t *settings;
+            if (settingsSize > 0) {
+                physicalFmq.push_back(V3_2::CameraMetadata(settingsSize));
+                bool read = mRequestMetadataQueue->read(physicalFmq[i].data(), settingsSize);
+                if (read) {
+                    converted = V3_2::implementation::convertFromHidl(physicalFmq[i], &settings);
+                    physicalCameraSettings.push_back(settings);
+                } else {
+                    ALOGE("%s: physical camera settings metadata couldn't be read from fmq!",
+                            __FUNCTION__);
+                    converted = false;
+                }
+            } else {
+                converted = V3_2::implementation::convertFromHidl(
+                        request.physicalCameraSettings[i].settings, &settings);
+                physicalCameraSettings.push_back(settings);
+            }
+
+            if (!converted) {
+                ALOGE("%s: physical camera settings metadata is corrupt!", __FUNCTION__);
+                return Status::ILLEGAL_ARGUMENT;
+            }
+            physicalCameraIds.push_back(request.physicalCameraSettings[i].physicalCameraId.c_str());
+        }
+    }
+    halRequest.num_physcam_settings = settingsCount;
+    halRequest.physcam_id = physicalCameraIds.data();
+    halRequest.physcam_settings = physicalCameraSettings.data();
+
+    ATRACE_ASYNC_BEGIN("frame capture", request.v3_2.frameNumber);
+    ATRACE_BEGIN("camera3->process_capture_request");
+    status_t ret = mDevice->ops->process_capture_request(mDevice, &halRequest);
+    ATRACE_END();
+    if (aeCancelTriggerNeeded) {
+        settingsOverride.unlock(halRequest.settings);
+    }
+    if (ret != OK) {
+        Mutex::Autolock _l(mInflightLock);
+        ALOGE("%s: HAL process_capture_request call failed!", __FUNCTION__);
+
+        cleanupInflightFences(allFences, numBufs);
+        if (hasInputBuf) {
+            auto key = std::make_pair(request.v3_2.inputBuffer.streamId, request.v3_2.frameNumber);
+            mInflightBuffers.erase(key);
+        }
+        for (size_t i = 0; i < numOutputBufs; i++) {
+            auto key = std::make_pair(request.v3_2.outputBuffers[i].streamId,
+                    request.v3_2.frameNumber);
+            mInflightBuffers.erase(key);
+        }
+        if (aeCancelTriggerNeeded) {
+            mInflightAETriggerOverrides.erase(request.v3_2.frameNumber);
+        }
+
+        if (ret == BAD_VALUE) {
+            return Status::ILLEGAL_ARGUMENT;
+        } else {
+            return Status::INTERNAL_ERROR;
+        }
+    }
+
+    mFirstRequest = false;
+    return Status::OK;
+}
+
 } // namespace implementation
 }  // namespace V3_4
 }  // namespace device
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
new file mode 100644
index 0000000..4ad1768
--- /dev/null
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -0,0 +1,793 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ExtCamDev@3.4"
+#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <array>
+#include <linux/videodev2.h>
+#include "android-base/macros.h"
+#include "CameraMetadata.h"
+#include "../../3.2/default/include/convert.h"
+#include "ExternalCameraDevice_3_4.h"
+
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+namespace {
+// Only support MJPEG for now as it seems to be the one supports higher fps
+// Other formats to consider in the future:
+// * V4L2_PIX_FMT_YVU420 (== YV12)
+// * V4L2_PIX_FMT_YVYU (YVYU: can be converted to YV12 or other YUV420_888 formats)
+const std::array<uint32_t, /*size*/1> kSupportedFourCCs {{
+    V4L2_PIX_FMT_MJPEG
+}}; // double braces required in C++11
+
+// TODO: b/72261897
+//       Define max size/fps this Android device can advertise (and streaming at reasonable speed)
+//       Also make sure that can be done without editing source code
+
+// TODO: b/72261675: make it dynamic since this affects memory usage
+const int kMaxJpegSize = {13 * 1024 * 1024};  // 13MB
+} // anonymous namespace
+
+ExternalCameraDevice::ExternalCameraDevice(const std::string& cameraId) :
+        mCameraId(cameraId) {
+    status_t ret = initCameraCharacteristics();
+    if (ret != OK) {
+        ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
+        mInitFailed = true;
+    }
+}
+
+ExternalCameraDevice::~ExternalCameraDevice() {}
+
+bool ExternalCameraDevice::isInitFailed() {
+    return mInitFailed;
+}
+
+Return<void> ExternalCameraDevice::getResourceCost(getResourceCost_cb _hidl_cb) {
+    CameraResourceCost resCost;
+    resCost.resourceCost = 100;
+    _hidl_cb(Status::OK, resCost);
+    return Void();
+}
+
+Return<void> ExternalCameraDevice::getCameraCharacteristics(
+        getCameraCharacteristics_cb _hidl_cb) {
+    Mutex::Autolock _l(mLock);
+    V3_2::CameraMetadata hidlChars;
+
+    if (isInitFailed()) {
+        _hidl_cb(Status::INTERNAL_ERROR, hidlChars);
+        return Void();
+    }
+
+    const camera_metadata_t* rawMetadata = mCameraCharacteristics.getAndLock();
+    V3_2::implementation::convertToHidl(rawMetadata, &hidlChars);
+    _hidl_cb(Status::OK, hidlChars);
+    mCameraCharacteristics.unlock(rawMetadata);
+    return Void();
+}
+
+Return<Status> ExternalCameraDevice::setTorchMode(TorchMode) {
+    return Status::METHOD_NOT_SUPPORTED;
+}
+
+Return<void> ExternalCameraDevice::open(
+        const sp<ICameraDeviceCallback>& callback, open_cb _hidl_cb) {
+    Status status = Status::OK;
+    sp<ExternalCameraDeviceSession> session = nullptr;
+
+    if (callback == nullptr) {
+        ALOGE("%s: cannot open camera %s. callback is null!",
+                __FUNCTION__, mCameraId.c_str());
+        _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+        return Void();
+    }
+
+    if (isInitFailed()) {
+        ALOGE("%s: cannot open camera %s. camera init failed!",
+                __FUNCTION__, mCameraId.c_str());
+        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+        return Void();
+    }
+
+    mLock.lock();
+
+    ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mCameraId.c_str());
+    session = mSession.promote();
+    if (session != nullptr && !session->isClosed()) {
+        ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
+        mLock.unlock();
+        _hidl_cb(Status::CAMERA_IN_USE, nullptr);
+        return Void();
+    }
+
+    unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+    if (fd.get() < 0) {
+        ALOGE("%s: v4l2 device open %s failed: %s",
+                __FUNCTION__, mCameraId.c_str(), strerror(errno));
+        mLock.unlock();
+        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+        return Void();
+    }
+
+    session = new ExternalCameraDeviceSession(
+            callback, mSupportedFormats, mCameraCharacteristics, std::move(fd));
+    if (session == nullptr) {
+        ALOGE("%s: camera device session allocation failed", __FUNCTION__);
+        mLock.unlock();
+        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+        return Void();
+    }
+    if (session->isInitFailed()) {
+        ALOGE("%s: camera device session init failed", __FUNCTION__);
+        session = nullptr;
+        mLock.unlock();
+        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+        return Void();
+    }
+    mSession = session;
+
+    mLock.unlock();
+
+    _hidl_cb(status, session->getInterface());
+    return Void();
+}
+
+Return<void> ExternalCameraDevice::dumpState(const ::android::hardware::hidl_handle& handle) {
+    Mutex::Autolock _l(mLock);
+    if (handle.getNativeHandle() == nullptr) {
+        ALOGE("%s: handle must not be null", __FUNCTION__);
+        return Void();
+    }
+    if (handle->numFds != 1 || handle->numInts != 0) {
+        ALOGE("%s: handle must contain 1 FD and 0 integers! Got %d FDs and %d ints",
+                __FUNCTION__, handle->numFds, handle->numInts);
+        return Void();
+    }
+    int fd = handle->data[0];
+    if (mSession == nullptr) {
+        dprintf(fd, "No active camera device session instance\n");
+        return Void();
+    }
+    auto session = mSession.promote();
+    if (session == nullptr) {
+        dprintf(fd, "No active camera device session instance\n");
+        return Void();
+    }
+    // Call into active session to dump states
+    session->dumpState(handle);
+    return Void();
+}
+
+
+status_t ExternalCameraDevice::initCameraCharacteristics() {
+    if (mCameraCharacteristics.isEmpty()) {
+        // init camera characteristics
+        unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+        if (fd.get() < 0) {
+            ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mCameraId.c_str());
+            return DEAD_OBJECT;
+        }
+
+        status_t ret;
+        ret = initDefaultCharsKeys(&mCameraCharacteristics);
+        if (ret != OK) {
+            ALOGE("%s: init default characteristics key failed: errorno %d", __FUNCTION__, ret);
+            mCameraCharacteristics.clear();
+            return ret;
+        }
+
+        ret = initCameraControlsCharsKeys(fd.get(), &mCameraCharacteristics);
+        if (ret != OK) {
+            ALOGE("%s: init camera control characteristics key failed: errorno %d", __FUNCTION__, ret);
+            mCameraCharacteristics.clear();
+            return ret;
+        }
+
+        ret = initOutputCharsKeys(fd.get(), &mCameraCharacteristics);
+        if (ret != OK) {
+            ALOGE("%s: init output characteristics key failed: errorno %d", __FUNCTION__, ret);
+            mCameraCharacteristics.clear();
+            return ret;
+        }
+    }
+    return OK;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define UPDATE(tag, data, size)                    \
+do {                                               \
+  if (metadata->update((tag), (data), (size))) {   \
+    ALOGE("Update " #tag " failed!");              \
+    return -EINVAL;                                \
+  }                                                \
+} while (0)
+
+status_t ExternalCameraDevice::initDefaultCharsKeys(
+        ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+    // TODO: changed to HARDWARELEVEL_EXTERNAL later
+    const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    UPDATE(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &hardware_level, 1);
+
+    // android.colorCorrection
+    const uint8_t availableAberrationModes[] = {
+        ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF};
+    UPDATE(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+           availableAberrationModes, ARRAY_SIZE(availableAberrationModes));
+
+    // android.control
+    const uint8_t antibandingMode =
+        ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+    UPDATE(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+           &antibandingMode, 1);
+
+    const int32_t controlMaxRegions[] = {/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0};
+    UPDATE(ANDROID_CONTROL_MAX_REGIONS, controlMaxRegions,
+           ARRAY_SIZE(controlMaxRegions));
+
+    const uint8_t videoStabilizationMode =
+        ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+    UPDATE(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+           &videoStabilizationMode, 1);
+
+    const uint8_t awbAvailableMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+    UPDATE(ANDROID_CONTROL_AWB_AVAILABLE_MODES, &awbAvailableMode, 1);
+
+    const uint8_t aeAvailableMode = ANDROID_CONTROL_AE_MODE_ON;
+    UPDATE(ANDROID_CONTROL_AE_AVAILABLE_MODES, &aeAvailableMode, 1);
+
+    const uint8_t availableFffect = ANDROID_CONTROL_EFFECT_MODE_OFF;
+    UPDATE(ANDROID_CONTROL_AVAILABLE_EFFECTS, &availableFffect, 1);
+
+    const uint8_t controlAvailableModes[] = {ANDROID_CONTROL_MODE_OFF,
+                                             ANDROID_CONTROL_MODE_AUTO};
+    UPDATE(ANDROID_CONTROL_AVAILABLE_MODES, controlAvailableModes,
+           ARRAY_SIZE(controlAvailableModes));
+
+    // android.edge
+    const uint8_t edgeMode = ANDROID_EDGE_MODE_OFF;
+    UPDATE(ANDROID_EDGE_AVAILABLE_EDGE_MODES, &edgeMode, 1);
+
+    // android.flash
+    const uint8_t flashInfo = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+    UPDATE(ANDROID_FLASH_INFO_AVAILABLE, &flashInfo, 1);
+
+    // android.hotPixel
+    const uint8_t hotPixelMode = ANDROID_HOT_PIXEL_MODE_OFF;
+    UPDATE(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &hotPixelMode, 1);
+
+    // android.jpeg
+    // TODO: b/72261675 See if we can provide thumbnail size for all jpeg aspect ratios
+    const int32_t jpegAvailableThumbnailSizes[] = {0, 0, 240, 180};
+    UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes,
+           ARRAY_SIZE(jpegAvailableThumbnailSizes));
+
+    const int32_t jpegMaxSize = kMaxJpegSize;
+    UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
+
+    const uint8_t jpegQuality = 90;
+    UPDATE(ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+    UPDATE(ANDROID_JPEG_THUMBNAIL_QUALITY, &jpegQuality, 1);
+
+    const int32_t jpegOrientation = 0;
+    UPDATE(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
+
+    // android.lens
+    const uint8_t focusDistanceCalibration =
+            ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+    UPDATE(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, &focusDistanceCalibration, 1);
+
+    const uint8_t opticalStabilizationMode =
+        ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+    UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+           &opticalStabilizationMode, 1);
+
+    const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+    UPDATE(ANDROID_LENS_FACING, &facing, 1);
+
+    // android.noiseReduction
+    const uint8_t noiseReductionMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+    UPDATE(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+           &noiseReductionMode, 1);
+    UPDATE(ANDROID_NOISE_REDUCTION_MODE, &noiseReductionMode, 1);
+
+    // android.request
+    const uint8_t availableCapabilities[] = {
+        ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE};
+    UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, availableCapabilities,
+           ARRAY_SIZE(availableCapabilities));
+
+    const int32_t partialResultCount = 1;
+    UPDATE(ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &partialResultCount, 1);
+
+    // This means pipeline latency of X frame intervals. The maximum number is 4.
+    const uint8_t requestPipelineMaxDepth = 4;
+    UPDATE(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &requestPipelineMaxDepth, 1);
+    UPDATE(ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+    // Three numbers represent the maximum numbers of different types of output
+    // streams simultaneously. The types are raw sensor, processed (but not
+    // stalling), and processed (but stalling). For usb limited mode, raw sensor
+    // is not supported. Stalling stream is JPEG. Non-stalling streams are
+    // YUV_420_888 or YV12.
+    const int32_t requestMaxNumOutputStreams[] = {
+            /*RAW*/0,
+            /*Processed*/ExternalCameraDeviceSession::kMaxProcessedStream,
+            /*Stall*/ExternalCameraDeviceSession::kMaxStallStream};
+    UPDATE(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, requestMaxNumOutputStreams,
+           ARRAY_SIZE(requestMaxNumOutputStreams));
+
+    // Limited mode doesn't support reprocessing.
+    const int32_t requestMaxNumInputStreams = 0;
+    UPDATE(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &requestMaxNumInputStreams,
+           1);
+
+    // android.scaler
+    // TODO: b/72263447 V4L2_CID_ZOOM_*
+    const float scalerAvailableMaxDigitalZoom[] = {1};
+    UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+           scalerAvailableMaxDigitalZoom,
+           ARRAY_SIZE(scalerAvailableMaxDigitalZoom));
+
+    const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
+    UPDATE(ANDROID_SCALER_CROPPING_TYPE, &croppingType, 1);
+
+    const int32_t testPatternModes[] = {
+        ANDROID_SENSOR_TEST_PATTERN_MODE_OFF};
+    UPDATE(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, testPatternModes,
+           ARRAY_SIZE(testPatternModes));
+    UPDATE(ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternModes[0], 1);
+
+    const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+    UPDATE(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, &timestampSource, 1);
+
+    // Orientation probably isn't useful for external facing camera?
+    const int32_t orientation = 0;
+    UPDATE(ANDROID_SENSOR_ORIENTATION, &orientation, 1);
+
+    // android.shading
+    const uint8_t availabeMode = ANDROID_SHADING_MODE_OFF;
+    UPDATE(ANDROID_SHADING_AVAILABLE_MODES, &availabeMode, 1);
+
+    // android.statistics
+    const uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+    UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, &faceDetectMode,
+           1);
+
+    const int32_t maxFaceCount = 0;
+    UPDATE(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, &maxFaceCount, 1);
+
+    const uint8_t availableHotpixelMode =
+        ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+    UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+           &availableHotpixelMode, 1);
+
+    const uint8_t lensShadingMapMode =
+        ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+    UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+           &lensShadingMapMode, 1);
+
+    // android.sync
+    const int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
+    UPDATE(ANDROID_SYNC_MAX_LATENCY, &maxLatency, 1);
+
+    /* Other sensor/RAW realted keys:
+     * android.sensor.info.colorFilterArrangement -> no need if we don't do RAW
+     * android.sensor.info.physicalSize           -> not available
+     * android.sensor.info.whiteLevel             -> not available/not needed
+     * android.sensor.info.lensShadingApplied     -> not needed
+     * android.sensor.info.preCorrectionActiveArraySize -> not available/not needed
+     * android.sensor.blackLevelPattern           -> not available/not needed
+     */
+
+    const int32_t availableRequestKeys[] = {
+        ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+        ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+        ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+        ANDROID_CONTROL_AE_LOCK,
+        ANDROID_CONTROL_AE_MODE,
+        ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+        ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+        ANDROID_CONTROL_AF_MODE,
+        ANDROID_CONTROL_AF_TRIGGER,
+        ANDROID_CONTROL_AWB_LOCK,
+        ANDROID_CONTROL_AWB_MODE,
+        ANDROID_CONTROL_CAPTURE_INTENT,
+        ANDROID_CONTROL_EFFECT_MODE,
+        ANDROID_CONTROL_MODE,
+        ANDROID_CONTROL_SCENE_MODE,
+        ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+        ANDROID_FLASH_MODE,
+        ANDROID_JPEG_ORIENTATION,
+        ANDROID_JPEG_QUALITY,
+        ANDROID_JPEG_THUMBNAIL_QUALITY,
+        ANDROID_JPEG_THUMBNAIL_SIZE,
+        ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+        ANDROID_NOISE_REDUCTION_MODE,
+        ANDROID_SCALER_CROP_REGION,
+        ANDROID_SENSOR_TEST_PATTERN_MODE,
+        ANDROID_STATISTICS_FACE_DETECT_MODE,
+        ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE};
+    UPDATE(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, availableRequestKeys,
+           ARRAY_SIZE(availableRequestKeys));
+
+    const int32_t availableResultKeys[] = {
+        ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+        ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+        ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+        ANDROID_CONTROL_AE_LOCK,
+        ANDROID_CONTROL_AE_MODE,
+        ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+        ANDROID_CONTROL_AE_STATE,
+        ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+        ANDROID_CONTROL_AF_MODE,
+        ANDROID_CONTROL_AF_STATE,
+        ANDROID_CONTROL_AF_TRIGGER,
+        ANDROID_CONTROL_AWB_LOCK,
+        ANDROID_CONTROL_AWB_MODE,
+        ANDROID_CONTROL_AWB_STATE,
+        ANDROID_CONTROL_CAPTURE_INTENT,
+        ANDROID_CONTROL_EFFECT_MODE,
+        ANDROID_CONTROL_MODE,
+        ANDROID_CONTROL_SCENE_MODE,
+        ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+        ANDROID_FLASH_MODE,
+        ANDROID_FLASH_STATE,
+        ANDROID_JPEG_ORIENTATION,
+        ANDROID_JPEG_QUALITY,
+        ANDROID_JPEG_THUMBNAIL_QUALITY,
+        ANDROID_JPEG_THUMBNAIL_SIZE,
+        ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+        ANDROID_NOISE_REDUCTION_MODE,
+        ANDROID_REQUEST_PIPELINE_DEPTH,
+        ANDROID_SCALER_CROP_REGION,
+        ANDROID_SENSOR_TIMESTAMP,
+        ANDROID_STATISTICS_FACE_DETECT_MODE,
+        ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+        ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+        ANDROID_STATISTICS_SCENE_FLICKER};
+    UPDATE(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys,
+           ARRAY_SIZE(availableResultKeys));
+
+    const int32_t availableCharacteristicsKeys[] = {
+        ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+        ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+        ANDROID_CONTROL_AE_COMPENSATION_STEP,
+        ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+        ANDROID_CONTROL_AF_AVAILABLE_MODES,
+        ANDROID_CONTROL_AVAILABLE_EFFECTS,
+        ANDROID_CONTROL_AVAILABLE_MODES,
+        ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+        ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+        ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+        ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+        ANDROID_CONTROL_MAX_REGIONS,
+        ANDROID_FLASH_INFO_AVAILABLE,
+        ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+        ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+        ANDROID_LENS_FACING,
+        ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+        ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+        ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+        ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+        ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+        ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+        ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+        ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+        ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+        ANDROID_SCALER_CROPPING_TYPE,
+        ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+        ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+        ANDROID_SENSOR_ORIENTATION,
+        ANDROID_SHADING_AVAILABLE_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+        ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+        ANDROID_SYNC_MAX_LATENCY};
+    UPDATE(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+           availableCharacteristicsKeys,
+           ARRAY_SIZE(availableCharacteristicsKeys));
+
+    return OK;
+}
+
+status_t ExternalCameraDevice::initCameraControlsCharsKeys(int,
+        ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+    /**
+     * android.sensor.info.sensitivityRange   -> V4L2_CID_ISO_SENSITIVITY
+     * android.sensor.info.exposureTimeRange  -> V4L2_CID_EXPOSURE_ABSOLUTE
+     * android.sensor.info.maxFrameDuration   -> TBD
+     * android.lens.info.minimumFocusDistance -> V4L2_CID_FOCUS_ABSOLUTE
+     * android.lens.info.hyperfocalDistance
+     * android.lens.info.availableFocalLengths -> not available?
+     */
+
+    // android.control
+    // No AE compensation support for now.
+    // TODO: V4L2_CID_EXPOSURE_BIAS
+    const int32_t controlAeCompensationRange[] = {0, 0};
+    UPDATE(ANDROID_CONTROL_AE_COMPENSATION_RANGE, controlAeCompensationRange,
+           ARRAY_SIZE(controlAeCompensationRange));
+    const camera_metadata_rational_t controlAeCompensationStep[] = {{0, 1}};
+    UPDATE(ANDROID_CONTROL_AE_COMPENSATION_STEP, controlAeCompensationStep,
+           ARRAY_SIZE(controlAeCompensationStep));
+
+
+    // TODO: Check V4L2_CID_AUTO_FOCUS_*.
+    const uint8_t afAvailableModes[] = {ANDROID_CONTROL_AF_MODE_AUTO,
+                                        ANDROID_CONTROL_AF_MODE_OFF};
+    UPDATE(ANDROID_CONTROL_AF_AVAILABLE_MODES, afAvailableModes,
+           ARRAY_SIZE(afAvailableModes));
+
+    // TODO: V4L2_CID_SCENE_MODE
+    const uint8_t availableSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+    UPDATE(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, &availableSceneMode, 1);
+
+    // TODO: V4L2_CID_3A_LOCK
+    const uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+    UPDATE(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &aeLockAvailable, 1);
+    const uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+    UPDATE(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &awbLockAvailable, 1);
+
+    // TODO: V4L2_CID_ZOOM_*
+    const float scalerAvailableMaxDigitalZoom[] = {1};
+    UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+           scalerAvailableMaxDigitalZoom,
+           ARRAY_SIZE(scalerAvailableMaxDigitalZoom));
+
+    return OK;
+}
+
+status_t ExternalCameraDevice::initOutputCharsKeys(int fd,
+        ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+    initSupportedFormatsLocked(fd);
+    if (mSupportedFormats.empty()) {
+        ALOGE("%s: Init supported format list failed", __FUNCTION__);
+        return UNKNOWN_ERROR;
+    }
+
+    std::vector<int32_t> streamConfigurations;
+    std::vector<int64_t> minFrameDurations;
+    std::vector<int64_t> stallDurations;
+    int64_t maxFrameDuration = 0;
+    int32_t maxFps = std::numeric_limits<int32_t>::min();
+    int32_t minFps = std::numeric_limits<int32_t>::max();
+    std::set<int32_t> framerates;
+
+    std::array<int, /*size*/3> halFormats{{
+        HAL_PIXEL_FORMAT_BLOB,
+        HAL_PIXEL_FORMAT_YCbCr_420_888,
+        HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}};
+
+    for (const auto& supportedFormat : mSupportedFormats) {
+        for (const auto& format : halFormats) {
+            streamConfigurations.push_back(format);
+            streamConfigurations.push_back(supportedFormat.width);
+            streamConfigurations.push_back(supportedFormat.height);
+            streamConfigurations.push_back(
+                    ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+        }
+
+        int64_t min_frame_duration = std::numeric_limits<int64_t>::max();
+        for (const auto& frameRate : supportedFormat.frameRates) {
+            int64_t frame_duration = 1000000000LL / frameRate;
+            if (frame_duration < min_frame_duration) {
+                min_frame_duration = frame_duration;
+            }
+            if (frame_duration > maxFrameDuration) {
+                maxFrameDuration = frame_duration;
+            }
+            int32_t frameRateInt = static_cast<int32_t>(frameRate);
+            if (minFps > frameRateInt) {
+                minFps = frameRateInt;
+            }
+            if (maxFps < frameRateInt) {
+                maxFps = frameRateInt;
+            }
+            framerates.insert(frameRateInt);
+        }
+
+        for (const auto& format : halFormats) {
+            minFrameDurations.push_back(format);
+            minFrameDurations.push_back(supportedFormat.width);
+            minFrameDurations.push_back(supportedFormat.height);
+            minFrameDurations.push_back(min_frame_duration);
+        }
+
+        // The stall duration is 0 for non-jpeg formats. For JPEG format, stall
+        // duration can be 0 if JPEG is small. Here we choose 1 sec for JPEG.
+        // TODO: b/72261675. Maybe set this dynamically
+        for (const auto& format : halFormats) {
+            const int64_t NS_TO_SECOND = 1000000000;
+            int64_t stall_duration =
+                    (format == HAL_PIXEL_FORMAT_BLOB) ? NS_TO_SECOND : 0;
+            stallDurations.push_back(format);
+            stallDurations.push_back(supportedFormat.width);
+            stallDurations.push_back(supportedFormat.height);
+            stallDurations.push_back(stall_duration);
+        }
+    }
+
+    // The document in aeAvailableTargetFpsRanges section says the minFps should
+    // not be larger than 15.
+    // We cannot support fixed 30fps but Android requires (min, max) and
+    // (max, max) ranges.
+    // TODO: populate more, right now this does not support 30,30 if the device
+    //       has higher than 30 fps modes
+    std::vector<int32_t> fpsRanges;
+    // Variable range
+    fpsRanges.push_back(minFps);
+    fpsRanges.push_back(maxFps);
+    // Fixed ranges
+    for (const auto& framerate : framerates) {
+        fpsRanges.push_back(framerate);
+        fpsRanges.push_back(framerate);
+    }
+    UPDATE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fpsRanges.data(),
+           fpsRanges.size());
+
+    UPDATE(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+           streamConfigurations.data(), streamConfigurations.size());
+
+    UPDATE(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+           minFrameDurations.data(), minFrameDurations.size());
+
+    UPDATE(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, stallDurations.data(),
+           stallDurations.size());
+
+    UPDATE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &maxFrameDuration, 1);
+
+    SupportedV4L2Format maximumFormat {.width = 0, .height = 0};
+    for (const auto& supportedFormat : mSupportedFormats) {
+        if (supportedFormat.width >= maximumFormat.width &&
+            supportedFormat.height >= maximumFormat.height) {
+            maximumFormat = supportedFormat;
+        }
+    }
+    int32_t activeArraySize[] = {0, 0,
+                                 static_cast<int32_t>(maximumFormat.width),
+                                 static_cast<int32_t>(maximumFormat.height)};
+    UPDATE(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+           activeArraySize, ARRAY_SIZE(activeArraySize));
+    UPDATE(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArraySize,
+           ARRAY_SIZE(activeArraySize));
+
+    int32_t pixelArraySize[] = {static_cast<int32_t>(maximumFormat.width),
+                                static_cast<int32_t>(maximumFormat.height)};
+    UPDATE(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize,
+           ARRAY_SIZE(pixelArraySize));
+    return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
+void ExternalCameraDevice::getFrameRateList(
+        int fd, SupportedV4L2Format* format) {
+    format->frameRates.clear();
+
+    v4l2_frmivalenum frameInterval {
+        .pixel_format = format->fourcc,
+        .width = format->width,
+        .height = format->height,
+        .index = 0
+    };
+
+    for (frameInterval.index = 0;
+            TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) == 0;
+            ++frameInterval.index) {
+        if (frameInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
+            if (frameInterval.discrete.numerator != 0) {
+                float framerate = frameInterval.discrete.denominator /
+                        static_cast<float>(frameInterval.discrete.numerator);
+                ALOGV("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f",
+                    frameInterval.index,
+                    frameInterval.pixel_format & 0xFF,
+                    (frameInterval.pixel_format >> 8) & 0xFF,
+                    (frameInterval.pixel_format >> 16) & 0xFF,
+                    (frameInterval.pixel_format >> 24) & 0xFF,
+                    frameInterval.width, frameInterval.height, framerate);
+                format->frameRates.push_back(framerate);
+            }
+        }
+    }
+
+    if (format->frameRates.empty()) {
+        ALOGE("%s: failed to get supported frame rates for format:%c%c%c%c w %d h %d",
+                __FUNCTION__,
+                frameInterval.pixel_format & 0xFF,
+                (frameInterval.pixel_format >> 8) & 0xFF,
+                (frameInterval.pixel_format >> 16) & 0xFF,
+                (frameInterval.pixel_format >> 24) & 0xFF,
+                frameInterval.width, frameInterval.height);
+    }
+}
+
+void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
+    struct v4l2_fmtdesc fmtdesc {
+        .index = 0,
+        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
+    int ret = 0;
+    while (ret == 0) {
+        ret = TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc));
+        ALOGD("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret,
+                fmtdesc.pixelformat & 0xFF,
+                (fmtdesc.pixelformat >> 8) & 0xFF,
+                (fmtdesc.pixelformat >> 16) & 0xFF,
+                (fmtdesc.pixelformat >> 24) & 0xFF);
+        if (ret == 0 && !(fmtdesc.flags & V4L2_FMT_FLAG_EMULATED)) {
+            auto it = std::find (
+                    kSupportedFourCCs.begin(), kSupportedFourCCs.end(), fmtdesc.pixelformat);
+            if (it != kSupportedFourCCs.end()) {
+                // Found supported format
+                v4l2_frmsizeenum frameSize {
+                        .index = 0,
+                        .pixel_format = fmtdesc.pixelformat};
+                for (; TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) == 0;
+                        ++frameSize.index) {
+                    if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+                        ALOGD("index:%d, format:%c%c%c%c, w %d, h %d", frameSize.index,
+                            fmtdesc.pixelformat & 0xFF,
+                            (fmtdesc.pixelformat >> 8) & 0xFF,
+                            (fmtdesc.pixelformat >> 16) & 0xFF,
+                            (fmtdesc.pixelformat >> 24) & 0xFF,
+                            frameSize.discrete.width, frameSize.discrete.height);
+                        // Disregard h > w formats so all aspect ratio (h/w) <= 1.0
+                        // This will simplify the crop/scaling logic down the road
+                        if (frameSize.discrete.height > frameSize.discrete.width) {
+                            continue;
+                        }
+                        SupportedV4L2Format format {
+                            .width = frameSize.discrete.width,
+                            .height = frameSize.discrete.height,
+                            .fourcc = fmtdesc.pixelformat
+                        };
+                        getFrameRateList(fd, &format);
+                        if (!format.frameRates.empty()) {
+                            mSupportedFormats.push_back(format);
+                        }
+                    }
+                }
+            }
+        }
+        fmtdesc.index++;
+    }
+}
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
new file mode 100644
index 0000000..9589782
--- /dev/null
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -0,0 +1,1990 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "ExtCamDevSsn@3.4"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <inttypes.h>
+#include "ExternalCameraDeviceSession.h"
+
+#include "android-base/macros.h"
+#include "algorithm"
+#include <utils/Timers.h>
+#include <cmath>
+#include <linux/videodev2.h>
+#include <sync/sync.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+// Size of request/result metadata fast message queue. Change to 0 to always use hwbinder buffer.
+static constexpr size_t kMetadataMsgQueueSize = 1 << 20 /* 1MB */;
+const int ExternalCameraDeviceSession::kMaxProcessedStream;
+const int ExternalCameraDeviceSession::kMaxStallStream;
+const Size kMaxVideoSize = {1920, 1088}; // Maybe this should be programmable
+const int kNumVideoBuffers = 4; // number of v4l2 buffers when streaming <= kMaxVideoSize
+const int kNumStillBuffers = 2; // number of v4l2 buffers when streaming > kMaxVideoSize
+const int kBadFramesAfterStreamOn = 1; // drop x frames after streamOn to get rid of some initial
+                                       // bad frames. TODO: develop a better bad frame detection
+                                       // method
+
+// Aspect ratio is defined as width/height here and ExternalCameraDevice
+// will guarantee all supported sizes has width >= height (so aspect ratio >= 1.0)
+#define ASPECT_RATIO(sz) (static_cast<float>((sz).width) / (sz).height)
+const float kMaxAspectRatio = std::numeric_limits<float>::max();
+const float kMinAspectRatio = 1.f;
+
+HandleImporter ExternalCameraDeviceSession::sHandleImporter;
+
+bool isAspectRatioClose(float ar1, float ar2) {
+    const float kAspectRatioMatchThres = 0.01f; // This threshold is good enough to distinguish
+                                                // 4:3/16:9/20:9
+    return (std::abs(ar1 - ar2) < kAspectRatioMatchThres);
+}
+
+ExternalCameraDeviceSession::ExternalCameraDeviceSession(
+        const sp<ICameraDeviceCallback>& callback,
+        const std::vector<SupportedV4L2Format>& supportedFormats,
+        const common::V1_0::helper::CameraMetadata& chars,
+        unique_fd v4l2Fd) :
+        mCallback(callback),
+        mCameraCharacteristics(chars),
+        mV4l2Fd(std::move(v4l2Fd)),
+        mSupportedFormats(sortFormats(supportedFormats)),
+        mCroppingType(initCroppingType(mSupportedFormats)),
+        mOutputThread(new OutputThread(this, mCroppingType)) {
+    mInitFail = initialize();
+}
+
+std::vector<SupportedV4L2Format> ExternalCameraDeviceSession::sortFormats(
+            const std::vector<SupportedV4L2Format>& inFmts) {
+    std::vector<SupportedV4L2Format> fmts = inFmts;
+    std::sort(fmts.begin(), fmts.end(),
+            [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
+                if (a.width == b.width) {
+                    return a.height < b.height;
+                }
+                return a.width < b.width;
+            });
+    return fmts;
+}
+
+CroppingType ExternalCameraDeviceSession::initCroppingType(
+        const std::vector<SupportedV4L2Format>& sortedFmts) {
+    const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
+    float maxSizeAr = ASPECT_RATIO(maxSize);
+    float minAr = kMinAspectRatio;
+    float maxAr = kMaxAspectRatio;
+    for (const auto& fmt : sortedFmts) {
+        float ar = ASPECT_RATIO(fmt);
+        if (ar < minAr) {
+            minAr = ar;
+        }
+        if (ar > maxAr) {
+            maxAr = ar;
+        }
+    }
+
+    CroppingType ct = VERTICAL;
+    if (isAspectRatioClose(maxSizeAr, maxAr)) {
+        // Ex: 16:9 sensor, cropping horizontally to get to 4:3
+        ct = HORIZONTAL;
+    } else if (isAspectRatioClose(maxSizeAr, minAr)) {
+        // Ex: 4:3 sensor, cropping vertically to get to 16:9
+        ct = VERTICAL;
+    } else {
+        ALOGI("%s: camera maxSizeAr %f is not close to minAr %f or maxAr %f",
+                __FUNCTION__, maxSizeAr, minAr, maxAr);
+        if ((maxSizeAr - minAr) < (maxAr - maxSizeAr)) {
+            ct = VERTICAL;
+        } else {
+            ct = HORIZONTAL;
+        }
+    }
+    ALOGI("%s: camera croppingType is %d", __FUNCTION__, ct);
+    return ct;
+}
+
+
+bool ExternalCameraDeviceSession::initialize() {
+    if (mV4l2Fd.get() < 0) {
+        ALOGE("%s: invalid v4l2 device fd %d!", __FUNCTION__, mV4l2Fd.get());
+        return true;
+    }
+
+    status_t status = initDefaultRequests();
+    if (status != OK) {
+        ALOGE("%s: init default requests failed!", __FUNCTION__);
+        return true;
+    }
+
+    mRequestMetadataQueue = std::make_unique<RequestMetadataQueue>(
+            kMetadataMsgQueueSize, false /* non blocking */);
+    if (!mRequestMetadataQueue->isValid()) {
+        ALOGE("%s: invalid request fmq", __FUNCTION__);
+        return true;
+    }
+    mResultMetadataQueue = std::make_shared<RequestMetadataQueue>(
+            kMetadataMsgQueueSize, false /* non blocking */);
+    if (!mResultMetadataQueue->isValid()) {
+        ALOGE("%s: invalid result fmq", __FUNCTION__);
+        return true;
+    }
+
+    // TODO: check is PRIORITY_DISPLAY enough?
+    mOutputThread->run("ExtCamOut", PRIORITY_DISPLAY);
+    return false;
+}
+
+Status ExternalCameraDeviceSession::initStatus() const {
+    Mutex::Autolock _l(mLock);
+    Status status = Status::OK;
+    if (mInitFail || mClosed) {
+        ALOGI("%s: sesssion initFailed %d closed %d", __FUNCTION__, mInitFail, mClosed);
+        status = Status::INTERNAL_ERROR;
+    }
+    return status;
+}
+
+ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {
+    if (!isClosed()) {
+        ALOGE("ExternalCameraDeviceSession deleted before close!");
+        close();
+    }
+}
+
+void ExternalCameraDeviceSession::dumpState(const native_handle_t*) {
+    // TODO: b/72261676 dump more runtime information
+}
+
+Return<void> ExternalCameraDeviceSession::constructDefaultRequestSettings(
+        RequestTemplate type,
+        ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+    CameraMetadata emptyMd;
+    Status status = initStatus();
+    if (status != Status::OK) {
+        _hidl_cb(status, emptyMd);
+        return Void();
+    }
+
+    switch (type) {
+        case RequestTemplate::PREVIEW:
+        case RequestTemplate::STILL_CAPTURE:
+        case RequestTemplate::VIDEO_RECORD:
+        case RequestTemplate::VIDEO_SNAPSHOT:
+            _hidl_cb(Status::OK, mDefaultRequests[static_cast<int>(type)]);
+            break;
+        case RequestTemplate::MANUAL:
+        case RequestTemplate::ZERO_SHUTTER_LAG:
+            // Don't support MANUAL or ZSL template
+            _hidl_cb(Status::ILLEGAL_ARGUMENT, emptyMd);
+            break;
+        default:
+            ALOGE("%s: unknown request template type %d", __FUNCTION__, static_cast<int>(type));
+            _hidl_cb(Status::ILLEGAL_ARGUMENT, emptyMd);
+            break;
+    }
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::configureStreams(
+        const V3_2::StreamConfiguration& streams,
+        ICameraDeviceSession::configureStreams_cb _hidl_cb) {
+    V3_2::HalStreamConfiguration outStreams;
+    V3_3::HalStreamConfiguration outStreams_v33;
+    Mutex::Autolock _il(mInterfaceLock);
+
+    Status status = configureStreams(streams, &outStreams_v33);
+    size_t size = outStreams_v33.streams.size();
+    outStreams.streams.resize(size);
+    for (size_t i = 0; i < size; i++) {
+        outStreams.streams[i] = outStreams_v33.streams[i].v3_2;
+    }
+    _hidl_cb(status, outStreams);
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::configureStreams_3_3(
+        const V3_2::StreamConfiguration& streams,
+        ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb) {
+    V3_3::HalStreamConfiguration outStreams;
+    Mutex::Autolock _il(mInterfaceLock);
+
+    Status status = configureStreams(streams, &outStreams);
+    _hidl_cb(status, outStreams);
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::configureStreams_3_4(
+        const V3_4::StreamConfiguration& requestedConfiguration,
+        ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb)  {
+    V3_2::StreamConfiguration config_v32;
+    V3_3::HalStreamConfiguration outStreams_v33;
+    Mutex::Autolock _il(mInterfaceLock);
+
+    config_v32.operationMode = requestedConfiguration.operationMode;
+    config_v32.streams.resize(requestedConfiguration.streams.size());
+    for (size_t i = 0; i < config_v32.streams.size(); i++) {
+        config_v32.streams[i] = requestedConfiguration.streams[i].v3_2;
+    }
+
+    // Ignore requestedConfiguration.sessionParams. External camera does not support it
+    Status status = configureStreams(config_v32, &outStreams_v33);
+
+    V3_4::HalStreamConfiguration outStreams;
+    outStreams.streams.resize(outStreams_v33.streams.size());
+    for (size_t i = 0; i < outStreams.streams.size(); i++) {
+        outStreams.streams[i].v3_3 = outStreams_v33.streams[i];
+    }
+    _hidl_cb(status, outStreams);
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::getCaptureRequestMetadataQueue(
+    ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    _hidl_cb(*mRequestMetadataQueue->getDesc());
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::getCaptureResultMetadataQueue(
+    ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    _hidl_cb(*mResultMetadataQueue->getDesc());
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::processCaptureRequest(
+        const hidl_vec<CaptureRequest>& requests,
+        const hidl_vec<BufferCache>& cachesToRemove,
+        ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    updateBufferCaches(cachesToRemove);
+
+    uint32_t numRequestProcessed = 0;
+    Status s = Status::OK;
+    for (size_t i = 0; i < requests.size(); i++, numRequestProcessed++) {
+        s = processOneCaptureRequest(requests[i]);
+        if (s != Status::OK) {
+            break;
+        }
+    }
+
+    _hidl_cb(s, numRequestProcessed);
+    return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::processCaptureRequest_3_4(
+        const hidl_vec<V3_4::CaptureRequest>& requests,
+        const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+        ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) {
+    Mutex::Autolock _il(mInterfaceLock);
+    updateBufferCaches(cachesToRemove);
+
+    uint32_t numRequestProcessed = 0;
+    Status s = Status::OK;
+    for (size_t i = 0; i < requests.size(); i++, numRequestProcessed++) {
+        s = processOneCaptureRequest(requests[i].v3_2);
+        if (s != Status::OK) {
+            break;
+        }
+    }
+
+    _hidl_cb(s, numRequestProcessed);
+    return Void();
+}
+
+Return<Status> ExternalCameraDeviceSession::flush() {
+    return Status::OK;
+}
+
+Return<void> ExternalCameraDeviceSession::close() {
+    Mutex::Autolock _il(mInterfaceLock);
+    Mutex::Autolock _l(mLock);
+    if (!mClosed) {
+        // TODO: b/72261676 Cleanup inflight buffers/V4L2 buffer queue
+        ALOGV("%s: closing V4L2 camera FD %d", __FUNCTION__, mV4l2Fd.get());
+        mV4l2Fd.reset();
+        mOutputThread->requestExit(); // TODO: join?
+
+        // free all imported buffers
+        for(auto& pair : mCirculatingBuffers) {
+            CirculatingBuffers& buffers = pair.second;
+            for (auto& p2 : buffers) {
+                sHandleImporter.freeBuffer(p2.second);
+            }
+        }
+
+        mClosed = true;
+    }
+    return Void();
+}
+
+Status ExternalCameraDeviceSession::importRequest(
+        const CaptureRequest& request,
+        hidl_vec<buffer_handle_t*>& allBufPtrs,
+        hidl_vec<int>& allFences) {
+    size_t numOutputBufs = request.outputBuffers.size();
+    size_t numBufs = numOutputBufs;
+    // Validate all I/O buffers
+    hidl_vec<buffer_handle_t> allBufs;
+    hidl_vec<uint64_t> allBufIds;
+    allBufs.resize(numBufs);
+    allBufIds.resize(numBufs);
+    allBufPtrs.resize(numBufs);
+    allFences.resize(numBufs);
+    std::vector<int32_t> streamIds(numBufs);
+
+    for (size_t i = 0; i < numOutputBufs; i++) {
+        allBufs[i] = request.outputBuffers[i].buffer.getNativeHandle();
+        allBufIds[i] = request.outputBuffers[i].bufferId;
+        allBufPtrs[i] = &allBufs[i];
+        streamIds[i] = request.outputBuffers[i].streamId;
+    }
+
+    for (size_t i = 0; i < numBufs; i++) {
+        buffer_handle_t buf = allBufs[i];
+        uint64_t bufId = allBufIds[i];
+        CirculatingBuffers& cbs = mCirculatingBuffers[streamIds[i]];
+        if (cbs.count(bufId) == 0) {
+            if (buf == nullptr) {
+                ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+                return Status::ILLEGAL_ARGUMENT;
+            }
+            // Register a newly seen buffer
+            buffer_handle_t importedBuf = buf;
+            sHandleImporter.importBuffer(importedBuf);
+            if (importedBuf == nullptr) {
+                ALOGE("%s: output buffer %zu is invalid!", __FUNCTION__, i);
+                return Status::INTERNAL_ERROR;
+            } else {
+                cbs[bufId] = importedBuf;
+            }
+        }
+        allBufPtrs[i] = &cbs[bufId];
+    }
+
+    // All buffers are imported. Now validate output buffer acquire fences
+    for (size_t i = 0; i < numOutputBufs; i++) {
+        if (!sHandleImporter.importFence(
+                request.outputBuffers[i].acquireFence, allFences[i])) {
+            ALOGE("%s: output buffer %zu acquire fence is invalid", __FUNCTION__, i);
+            cleanupInflightFences(allFences, i);
+            return Status::INTERNAL_ERROR;
+        }
+    }
+    return Status::OK;
+}
+
+void ExternalCameraDeviceSession::cleanupInflightFences(
+        hidl_vec<int>& allFences, size_t numFences) {
+    for (size_t j = 0; j < numFences; j++) {
+        sHandleImporter.closeFence(allFences[j]);
+    }
+}
+
+Status ExternalCameraDeviceSession::processOneCaptureRequest(const CaptureRequest& request)  {
+    Status status = initStatus();
+    if (status != Status::OK) {
+        return status;
+    }
+
+    if (request.inputBuffer.streamId != -1) {
+        ALOGE("%s: external camera does not support reprocessing!", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    Mutex::Autolock _l(mLock);
+    if (!mV4l2Streaming) {
+        ALOGE("%s: cannot process request in streamOff state!", __FUNCTION__);
+        return Status::INTERNAL_ERROR;
+    }
+
+    const camera_metadata_t *rawSettings = nullptr;
+    bool converted = true;
+    CameraMetadata settingsFmq;  // settings from FMQ
+    if (request.fmqSettingsSize > 0) {
+        // non-blocking read; client must write metadata before calling
+        // processOneCaptureRequest
+        settingsFmq.resize(request.fmqSettingsSize);
+        bool read = mRequestMetadataQueue->read(settingsFmq.data(), request.fmqSettingsSize);
+        if (read) {
+            converted = V3_2::implementation::convertFromHidl(settingsFmq, &rawSettings);
+        } else {
+            ALOGE("%s: capture request settings metadata couldn't be read from fmq!", __FUNCTION__);
+            converted = false;
+        }
+    } else {
+        converted = V3_2::implementation::convertFromHidl(request.settings, &rawSettings);
+    }
+
+    if (converted && rawSettings != nullptr) {
+        mLatestReqSetting = rawSettings;
+    }
+
+    if (!converted) {
+        ALOGE("%s: capture request settings metadata is corrupt!", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (mFirstRequest && rawSettings == nullptr) {
+        ALOGE("%s: capture request settings must not be null for first request!",
+                __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    hidl_vec<buffer_handle_t*> allBufPtrs;
+    hidl_vec<int> allFences;
+    size_t numOutputBufs = request.outputBuffers.size();
+
+    if (numOutputBufs == 0) {
+        ALOGE("%s: capture request must have at least one output buffer!", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    status = importRequest(request, allBufPtrs, allFences);
+    if (status != Status::OK) {
+        return status;
+    }
+
+    // TODO: program fps range per capture request here
+    //       or limit the set of availableFpsRange
+
+    sp<V4L2Frame> frameIn = dequeueV4l2FrameLocked();
+    if ( frameIn == nullptr) {
+        ALOGE("%s: V4L2 deque frame failed!", __FUNCTION__);
+        return Status::INTERNAL_ERROR;
+    }
+    // TODO: This can probably be replaced by use v4lbuffer timestamp
+    //       if the device supports it
+    nsecs_t shutterTs = systemTime(SYSTEM_TIME_MONOTONIC);
+
+
+    // TODO: reduce object copy in this path
+    HalRequest halReq = {
+            .frameNumber = request.frameNumber,
+            .setting = mLatestReqSetting,
+            .frameIn = frameIn,
+            .shutterTs = shutterTs};
+    halReq.buffers.resize(numOutputBufs);
+    for (size_t i = 0; i < numOutputBufs; i++) {
+        HalStreamBuffer& halBuf = halReq.buffers[i];
+        int streamId = halBuf.streamId = request.outputBuffers[i].streamId;
+        halBuf.bufferId = request.outputBuffers[i].bufferId;
+        const Stream& stream = mStreamMap[streamId];
+        halBuf.width = stream.width;
+        halBuf.height = stream.height;
+        halBuf.format = stream.format;
+        halBuf.usage = stream.usage;
+        halBuf.bufPtr = allBufPtrs[i];
+        halBuf.acquireFence = allFences[i];
+        halBuf.fenceTimeout = false;
+    }
+    mInflightFrames.insert(halReq.frameNumber);
+    // Send request to OutputThread for the rest of processing
+    mOutputThread->submitRequest(halReq);
+    mFirstRequest = false;
+    return Status::OK;
+}
+
+void ExternalCameraDeviceSession::notifyShutter(uint32_t frameNumber, nsecs_t shutterTs) {
+    NotifyMsg msg;
+    msg.type = MsgType::SHUTTER;
+    msg.msg.shutter.frameNumber = frameNumber;
+    msg.msg.shutter.timestamp = shutterTs;
+    mCallback->notify({msg});
+}
+
+void ExternalCameraDeviceSession::notifyError(
+        uint32_t frameNumber, int32_t streamId, ErrorCode ec) {
+    NotifyMsg msg;
+    msg.type = MsgType::ERROR;
+    msg.msg.error.frameNumber = frameNumber;
+    msg.msg.error.errorStreamId = streamId;
+    msg.msg.error.errorCode = ec;
+    mCallback->notify({msg});
+}
+
+//TODO: refactor with processCaptureResult
+Status ExternalCameraDeviceSession::processCaptureRequestError(HalRequest& req) {
+    // Return V4L2 buffer to V4L2 buffer queue
+    enqueueV4l2Frame(req.frameIn);
+
+    // NotifyShutter
+    notifyShutter(req.frameNumber, req.shutterTs);
+
+    notifyError(/*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+
+    // Fill output buffers
+    hidl_vec<CaptureResult> results;
+    results.resize(1);
+    CaptureResult& result = results[0];
+    result.frameNumber = req.frameNumber;
+    result.partialResult = 1;
+    result.inputBuffer.streamId = -1;
+    result.outputBuffers.resize(req.buffers.size());
+    for (size_t i = 0; i < req.buffers.size(); i++) {
+        result.outputBuffers[i].streamId = req.buffers[i].streamId;
+        result.outputBuffers[i].bufferId = req.buffers[i].bufferId;
+        result.outputBuffers[i].status = BufferStatus::ERROR;
+        if (req.buffers[i].acquireFence >= 0) {
+            native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+            handle->data[0] = req.buffers[i].acquireFence;
+            result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/true);
+        }
+    }
+
+    // update inflight records
+    {
+        Mutex::Autolock _l(mLock);
+        mInflightFrames.erase(req.frameNumber);
+    }
+
+    // Callback into framework
+    invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+    freeReleaseFences(results);
+    return Status::OK;
+}
+
+Status ExternalCameraDeviceSession::processCaptureResult(HalRequest& req) {
+    // Return V4L2 buffer to V4L2 buffer queue
+    enqueueV4l2Frame(req.frameIn);
+
+    // NotifyShutter
+    notifyShutter(req.frameNumber, req.shutterTs);
+
+    // Fill output buffers
+    hidl_vec<CaptureResult> results;
+    results.resize(1);
+    CaptureResult& result = results[0];
+    result.frameNumber = req.frameNumber;
+    result.partialResult = 1;
+    result.inputBuffer.streamId = -1;
+    result.outputBuffers.resize(req.buffers.size());
+    for (size_t i = 0; i < req.buffers.size(); i++) {
+        result.outputBuffers[i].streamId = req.buffers[i].streamId;
+        result.outputBuffers[i].bufferId = req.buffers[i].bufferId;
+        if (req.buffers[i].fenceTimeout) {
+            result.outputBuffers[i].status = BufferStatus::ERROR;
+            native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+            handle->data[0] = req.buffers[i].acquireFence;
+            result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/true);
+            notifyError(req.frameNumber, req.buffers[i].streamId, ErrorCode::ERROR_BUFFER);
+        } else {
+            result.outputBuffers[i].status = BufferStatus::OK;
+            // TODO: refactor
+            if (req.buffers[i].acquireFence > 0) {
+                native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+                handle->data[0] = req.buffers[i].acquireFence;
+                result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/true);
+            }
+        }
+    }
+
+    // Fill capture result metadata
+    fillCaptureResult(req.setting, req.shutterTs);
+    const camera_metadata_t *rawResult = req.setting.getAndLock();
+    V3_2::implementation::convertToHidl(rawResult, &result.result);
+    req.setting.unlock(rawResult);
+
+    // update inflight records
+    {
+        Mutex::Autolock _l(mLock);
+        mInflightFrames.erase(req.frameNumber);
+    }
+
+    // Callback into framework
+    invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+    freeReleaseFences(results);
+    return Status::OK;
+}
+
+void ExternalCameraDeviceSession::invokeProcessCaptureResultCallback(
+        hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+    if (mProcessCaptureResultLock.tryLock() != OK) {
+        const nsecs_t NS_TO_SECOND = 1000000000;
+        ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+        if (mProcessCaptureResultLock.timedLock(/* 1s */NS_TO_SECOND) != OK) {
+            ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+                    __FUNCTION__);
+            return;
+        }
+    }
+    if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+        for (CaptureResult &result : results) {
+            if (result.result.size() > 0) {
+                if (mResultMetadataQueue->write(result.result.data(), result.result.size())) {
+                    result.fmqResultSize = result.result.size();
+                    result.result.resize(0);
+                } else {
+                    ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+                    result.fmqResultSize = 0;
+                }
+            } else {
+                result.fmqResultSize = 0;
+            }
+        }
+    }
+    mCallback->processCaptureResult(results);
+    mProcessCaptureResultLock.unlock();
+}
+
+void ExternalCameraDeviceSession::freeReleaseFences(hidl_vec<CaptureResult>& results) {
+    for (auto& result : results) {
+        if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+            native_handle_t* handle = const_cast<native_handle_t*>(
+                    result.inputBuffer.releaseFence.getNativeHandle());
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+        for (auto& buf : result.outputBuffers) {
+            if (buf.releaseFence.getNativeHandle() != nullptr) {
+                native_handle_t* handle = const_cast<native_handle_t*>(
+                        buf.releaseFence.getNativeHandle());
+                native_handle_close(handle);
+                native_handle_delete(handle);
+            }
+        }
+    }
+    return;
+}
+
+ExternalCameraDeviceSession::OutputThread::OutputThread(
+        wp<ExternalCameraDeviceSession> parent,
+        CroppingType ct) : mParent(parent), mCroppingType(ct) {}
+
+ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
+
+uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout(
+        const YCbCrLayout& layout) {
+    intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
+    intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
+    if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
+        // Interleaved format
+        if (layout.cb > layout.cr) {
+            return V4L2_PIX_FMT_NV21;
+        } else {
+            return V4L2_PIX_FMT_NV12;
+        }
+    } else if (layout.chromaStep == 1) {
+        // Planar format
+        if (layout.cb > layout.cr) {
+            return V4L2_PIX_FMT_YVU420; // YV12
+        } else {
+            return V4L2_PIX_FMT_YUV420; // YU12
+        }
+    } else {
+        return FLEX_YUV_GENERIC;
+    }
+}
+
+int ExternalCameraDeviceSession::OutputThread::getCropRect(
+        CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
+    if (out == nullptr) {
+        ALOGE("%s: out is null", __FUNCTION__);
+        return -1;
+    }
+    uint32_t inW = inSize.width;
+    uint32_t inH = inSize.height;
+    uint32_t outW = outSize.width;
+    uint32_t outH = outSize.height;
+
+    if (ct == VERTICAL) {
+        uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
+        if (scaledOutH > inH) {
+            ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
+                    __FUNCTION__, outW, outH, inW, inH);
+            return -1;
+        }
+        scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
+
+        out->left = 0;
+        out->top = ((inH - scaledOutH) / 2) & ~0x1;
+        out->width = inW;
+        out->height = static_cast<int32_t>(scaledOutH);
+        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
+                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
+    } else {
+        uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
+        if (scaledOutW > inW) {
+            ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
+                    __FUNCTION__, outW, outH, inW, inH);
+            return -1;
+        }
+        scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
+
+        out->left = ((inW - scaledOutW) / 2) & ~0x1;
+        out->top = 0;
+        out->width = static_cast<int32_t>(scaledOutW);
+        out->height = inH;
+        ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
+                __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
+    }
+
+    return 0;
+}
+
+int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
+        sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf, YCbCrLayout* out) {
+    Size inSz = {in->mWidth, in->mHeight};
+    Size outSz = {halBuf.width, halBuf.height};
+    int ret;
+    if (inSz == outSz) {
+        ret = in->getLayout(out);
+        if (ret != 0) {
+            ALOGE("%s: failed to get input image layout", __FUNCTION__);
+            return ret;
+        }
+        return ret;
+    }
+
+    // Cropping to output aspect ratio
+    IMapper::Rect inputCrop;
+    ret = getCropRect(mCroppingType, inSz, outSz, &inputCrop);
+    if (ret != 0) {
+        ALOGE("%s: failed to compute crop rect for output size %dx%d",
+                __FUNCTION__, outSz.width, outSz.height);
+        return ret;
+    }
+
+    YCbCrLayout croppedLayout;
+    ret = in->getCroppedLayout(inputCrop, &croppedLayout);
+    if (ret != 0) {
+        ALOGE("%s: failed to crop input image %dx%d to output size %dx%d",
+                __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+        return ret;
+    }
+
+    if ((mCroppingType == VERTICAL && inSz.width == outSz.width) ||
+            (mCroppingType == HORIZONTAL && inSz.height == outSz.height)) {
+        // No scale is needed
+        *out = croppedLayout;
+        return 0;
+    }
+
+    auto it = mScaledYu12Frames.find(outSz);
+    sp<AllocatedFrame> scaledYu12Buf;
+    if (it != mScaledYu12Frames.end()) {
+        scaledYu12Buf = it->second;
+    } else {
+        it = mIntermediateBuffers.find(outSz);
+        if (it == mIntermediateBuffers.end()) {
+            ALOGE("%s: failed to find intermediate buffer size %dx%d",
+                    __FUNCTION__, outSz.width, outSz.height);
+            return -1;
+        }
+        scaledYu12Buf = it->second;
+    }
+    // Scale
+    YCbCrLayout outLayout;
+    ret = scaledYu12Buf->getLayout(&outLayout);
+    if (ret != 0) {
+        ALOGE("%s: failed to get output buffer layout", __FUNCTION__);
+        return ret;
+    }
+
+    ret = libyuv::I420Scale(
+            static_cast<uint8_t*>(croppedLayout.y),
+            croppedLayout.yStride,
+            static_cast<uint8_t*>(croppedLayout.cb),
+            croppedLayout.cStride,
+            static_cast<uint8_t*>(croppedLayout.cr),
+            croppedLayout.cStride,
+            inputCrop.width,
+            inputCrop.height,
+            static_cast<uint8_t*>(outLayout.y),
+            outLayout.yStride,
+            static_cast<uint8_t*>(outLayout.cb),
+            outLayout.cStride,
+            static_cast<uint8_t*>(outLayout.cr),
+            outLayout.cStride,
+            outSz.width,
+            outSz.height,
+            // TODO: b/72261744 see if we can use better filter without losing too much perf
+            libyuv::FilterMode::kFilterNone);
+
+    if (ret != 0) {
+        ALOGE("%s: failed to scale buffer from %dx%d to %dx%d. Ret %d",
+                __FUNCTION__, inputCrop.width, inputCrop.height,
+                outSz.width, outSz.height, ret);
+        return ret;
+    }
+
+    *out = outLayout;
+    mScaledYu12Frames.insert({outSz, scaledYu12Buf});
+    return 0;
+}
+
+int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
+        const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
+    int ret = 0;
+    switch (format) {
+        case V4L2_PIX_FMT_NV21:
+            ret = libyuv::I420ToNV21(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cr),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: convert to NV21 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case V4L2_PIX_FMT_NV12:
+            ret = libyuv::I420ToNV12(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cb),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: convert to NV12 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case V4L2_PIX_FMT_YVU420: // YV12
+        case V4L2_PIX_FMT_YUV420: // YU12
+            // TODO: maybe we can speed up here by somehow save this copy?
+            ret = libyuv::I420Copy(
+                    static_cast<uint8_t*>(in.y),
+                    in.yStride,
+                    static_cast<uint8_t*>(in.cb),
+                    in.cStride,
+                    static_cast<uint8_t*>(in.cr),
+                    in.cStride,
+                    static_cast<uint8_t*>(out.y),
+                    out.yStride,
+                    static_cast<uint8_t*>(out.cb),
+                    out.cStride,
+                    static_cast<uint8_t*>(out.cr),
+                    out.cStride,
+                    sz.width,
+                    sz.height);
+            if (ret != 0) {
+                ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
+                            __FUNCTION__, ret);
+                return ret;
+            }
+            break;
+        case FLEX_YUV_GENERIC:
+            // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
+            ALOGE("%s: unsupported flexible yuv layout"
+                    " y %p cb %p cr %p y_str %d c_str %d c_step %d",
+                    __FUNCTION__, out.y, out.cb, out.cr,
+                    out.yStride, out.cStride, out.chromaStep);
+            return -1;
+        default:
+            ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
+            return -1;
+    }
+    return 0;
+}
+
+bool ExternalCameraDeviceSession::OutputThread::threadLoop() {
+    HalRequest req;
+    auto parent = mParent.promote();
+    if (parent == nullptr) {
+       ALOGE("%s: session has been disconnected!", __FUNCTION__);
+       return false;
+    }
+
+    // TODO: maybe we need to setup a sensor thread to dq/enq v4l frames
+    //       regularly to prevent v4l buffer queue filled with stale buffers
+    //       when app doesn't program a preveiw request
+    waitForNextRequest(&req);
+    if (req.frameIn == nullptr) {
+        // No new request, wait again
+        return true;
+    }
+
+    if (req.frameIn->mFourcc != V4L2_PIX_FMT_MJPEG) {
+        ALOGE("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
+                req.frameIn->mFourcc & 0xFF,
+                (req.frameIn->mFourcc >> 8) & 0xFF,
+                (req.frameIn->mFourcc >> 16) & 0xFF,
+                (req.frameIn->mFourcc >> 24) & 0xFF);
+        parent->notifyError(
+                /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lk(mLock);
+
+    // Convert input V4L2 frame to YU12 of the same size
+    // TODO: see if we can save some computation by converting to YV12 here
+    uint8_t* inData;
+    size_t inDataSize;
+    req.frameIn->map(&inData, &inDataSize);
+    // TODO: profile
+    // TODO: in some special case maybe we can decode jpg directly to gralloc output?
+    int res = libyuv::MJPGToI420(
+            inData, inDataSize,
+            static_cast<uint8_t*>(mYu12FrameLayout.y),
+            mYu12FrameLayout.yStride,
+            static_cast<uint8_t*>(mYu12FrameLayout.cb),
+            mYu12FrameLayout.cStride,
+            static_cast<uint8_t*>(mYu12FrameLayout.cr),
+            mYu12FrameLayout.cStride,
+            mYu12Frame->mWidth, mYu12Frame->mHeight,
+            mYu12Frame->mWidth, mYu12Frame->mHeight);
+
+    if (res != 0) {
+        // For some webcam, the first few V4L2 frames might be malformed...
+        ALOGE("%s: Convert V4L2 frame to YU12 failed! res %d", __FUNCTION__, res);
+        lk.unlock();
+        Status st = parent->processCaptureRequestError(req);
+        if (st != Status::OK) {
+            ALOGE("%s: failed to process capture request error!", __FUNCTION__);
+            parent->notifyError(
+                    /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+            return false;
+        }
+        return true;
+    }
+
+    ALOGV("%s processing new request", __FUNCTION__);
+    const int kSyncWaitTimeoutMs = 500;
+    for (auto& halBuf : req.buffers) {
+        if (halBuf.acquireFence != -1) {
+            int ret = sync_wait(halBuf.acquireFence, kSyncWaitTimeoutMs);
+            if (ret) {
+                halBuf.fenceTimeout = true;
+            } else {
+                ::close(halBuf.acquireFence);
+            }
+        }
+
+        if (halBuf.fenceTimeout) {
+            continue;
+        }
+
+        // Gralloc lockYCbCr the buffer
+        switch (halBuf.format) {
+            case PixelFormat::BLOB:
+                // TODO: b/72261675 implement JPEG output path
+                break;
+            case PixelFormat::YCBCR_420_888:
+            case PixelFormat::YV12: {
+                IMapper::Rect outRect {0, 0,
+                        static_cast<int32_t>(halBuf.width),
+                        static_cast<int32_t>(halBuf.height)};
+                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
+                        *(halBuf.bufPtr), halBuf.usage, outRect);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d",
+                        __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr,
+                        outLayout.yStride, outLayout.cStride, outLayout.chromaStep);
+
+                // Convert to output buffer size/format
+                uint32_t outputFourcc = getFourCcFromLayout(outLayout);
+                ALOGV("%s: converting to format %c%c%c%c", __FUNCTION__,
+                        outputFourcc & 0xFF,
+                        (outputFourcc >> 8) & 0xFF,
+                        (outputFourcc >> 16) & 0xFF,
+                        (outputFourcc >> 24) & 0xFF);
+
+                YCbCrLayout cropAndScaled;
+                int ret = cropAndScaleLocked(
+                        mYu12Frame, halBuf, &cropAndScaled);
+                if (ret != 0) {
+                    ALOGE("%s: crop and scale failed!", __FUNCTION__);
+                    lk.unlock();
+                    parent->notifyError(
+                            /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+                    return false;
+                }
+
+                Size sz {halBuf.width, halBuf.height};
+                ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc);
+                if (ret != 0) {
+                    ALOGE("%s: format coversion failed!", __FUNCTION__);
+                    lk.unlock();
+                    parent->notifyError(
+                            /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+                    return false;
+                }
+                int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+                if (relFence > 0) {
+                    halBuf.acquireFence = relFence;
+                }
+            } break;
+            default:
+                ALOGE("%s: unknown output format %x", __FUNCTION__, halBuf.format);
+                lk.unlock();
+                parent->notifyError(
+                        /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+                return false;
+        }
+    } // for each buffer
+    mScaledYu12Frames.clear();
+
+    // Don't hold the lock while calling back to parent
+    lk.unlock();
+    Status st = parent->processCaptureResult(req);
+    if (st != Status::OK) {
+        ALOGE("%s: failed to process capture result!", __FUNCTION__);
+        parent->notifyError(
+                /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+        return false;
+    }
+    return true;
+}
+
+Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers(
+        const Size& v4lSize, const hidl_vec<Stream>& streams) {
+    std::lock_guard<std::mutex> lk(mLock);
+    if (mScaledYu12Frames.size() != 0) {
+        ALOGE("%s: intermediate buffer pool has %zu inflight buffers! (expect 0)",
+                __FUNCTION__, mScaledYu12Frames.size());
+        return Status::INTERNAL_ERROR;
+    }
+
+    // Allocating intermediate YU12 frame
+    if (mYu12Frame == nullptr || mYu12Frame->mWidth != v4lSize.width ||
+            mYu12Frame->mHeight != v4lSize.height) {
+        mYu12Frame.clear();
+        mYu12Frame = new AllocatedFrame(v4lSize.width, v4lSize.height);
+        int ret = mYu12Frame->allocate(&mYu12FrameLayout);
+        if (ret != 0) {
+            ALOGE("%s: allocating YU12 frame failed!", __FUNCTION__);
+            return Status::INTERNAL_ERROR;
+        }
+    }
+
+    // Allocating scaled buffers
+    for (const auto& stream : streams) {
+        Size sz = {stream.width, stream.height};
+        if (sz == v4lSize) {
+            continue; // Don't need an intermediate buffer same size as v4lBuffer
+        }
+        if (mIntermediateBuffers.count(sz) == 0) {
+            // Create new intermediate buffer
+            sp<AllocatedFrame> buf = new AllocatedFrame(stream.width, stream.height);
+            int ret = buf->allocate();
+            if (ret != 0) {
+                ALOGE("%s: allocating intermediate YU12 frame %dx%d failed!",
+                            __FUNCTION__, stream.width, stream.height);
+                return Status::INTERNAL_ERROR;
+            }
+            mIntermediateBuffers[sz] = buf;
+        }
+    }
+
+    // Remove unconfigured buffers
+    auto it = mIntermediateBuffers.begin();
+    while (it != mIntermediateBuffers.end()) {
+        bool configured = false;
+        auto sz = it->first;
+        for (const auto& stream : streams) {
+            if (stream.width == sz.width && stream.height == sz.height) {
+                configured = true;
+                break;
+            }
+        }
+        if (configured) {
+            it++;
+        } else {
+            it = mIntermediateBuffers.erase(it);
+        }
+    }
+    return Status::OK;
+}
+
+Status ExternalCameraDeviceSession::OutputThread::submitRequest(const HalRequest& req) {
+    std::lock_guard<std::mutex> lk(mLock);
+    // TODO: reduce object copy in this path
+    mRequestList.push_back(req);
+    mRequestCond.notify_one();
+    return Status::OK;
+}
+
+void ExternalCameraDeviceSession::OutputThread::flush() {
+    std::lock_guard<std::mutex> lk(mLock);
+    // TODO: send buffer/request errors back to framework
+    mRequestList.clear();
+}
+
+void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(HalRequest* out) {
+    if (out == nullptr) {
+        ALOGE("%s: out is null", __FUNCTION__);
+        return;
+    }
+
+    std::unique_lock<std::mutex> lk(mLock);
+    while (mRequestList.empty()) {
+        std::chrono::seconds timeout = std::chrono::seconds(kReqWaitTimeoutSec);
+        auto st = mRequestCond.wait_for(lk, timeout);
+        if (st == std::cv_status::timeout) {
+            // no new request, return
+            return;
+        }
+    }
+    *out = mRequestList.front();
+    mRequestList.pop_front();
+}
+
+void ExternalCameraDeviceSession::cleanupBuffersLocked(int id) {
+    for (auto& pair : mCirculatingBuffers.at(id)) {
+        sHandleImporter.freeBuffer(pair.second);
+    }
+    mCirculatingBuffers[id].clear();
+    mCirculatingBuffers.erase(id);
+}
+
+void ExternalCameraDeviceSession::updateBufferCaches(const hidl_vec<BufferCache>& cachesToRemove) {
+    Mutex::Autolock _l(mLock);
+    for (auto& cache : cachesToRemove) {
+        auto cbsIt = mCirculatingBuffers.find(cache.streamId);
+        if (cbsIt == mCirculatingBuffers.end()) {
+            // The stream could have been removed
+            continue;
+        }
+        CirculatingBuffers& cbs = cbsIt->second;
+        auto it = cbs.find(cache.bufferId);
+        if (it != cbs.end()) {
+            sHandleImporter.freeBuffer(it->second);
+            cbs.erase(it);
+        } else {
+            ALOGE("%s: stream %d buffer %" PRIu64 " is not cached",
+                    __FUNCTION__, cache.streamId, cache.bufferId);
+        }
+    }
+}
+
+bool ExternalCameraDeviceSession::isSupported(const Stream& stream) {
+    int32_t ds = static_cast<int32_t>(stream.dataSpace);
+    PixelFormat fmt = stream.format;
+    uint32_t width = stream.width;
+    uint32_t height = stream.height;
+    // TODO: check usage flags
+
+    if (stream.streamType != StreamType::OUTPUT) {
+        ALOGE("%s: does not support non-output stream type", __FUNCTION__);
+        return false;
+    }
+
+    if (stream.rotation != StreamRotation::ROTATION_0) {
+        ALOGE("%s: does not support stream rotation", __FUNCTION__);
+        return false;
+    }
+
+    if (ds & Dataspace::DEPTH) {
+        ALOGI("%s: does not support depth output", __FUNCTION__);
+        return false;
+    }
+
+    switch (fmt) {
+        case PixelFormat::BLOB:
+            if (ds != static_cast<int32_t>(Dataspace::V0_JFIF)) {
+                ALOGI("%s: BLOB format does not support dataSpace %x", __FUNCTION__, ds);
+                return false;
+            }
+        case PixelFormat::IMPLEMENTATION_DEFINED:
+        case PixelFormat::YCBCR_420_888:
+        case PixelFormat::YV12:
+            // TODO: check what dataspace we can support here.
+            // intentional no-ops.
+            break;
+        default:
+            ALOGI("%s: does not support format %x", __FUNCTION__, fmt);
+            return false;
+    }
+
+    // Assume we can convert any V4L2 format to any of supported output format for now, i.e,
+    // ignoring v4l2Fmt.fourcc for now. Might need more subtle check if we support more v4l format
+    // in the futrue.
+    for (const auto& v4l2Fmt : mSupportedFormats) {
+        if (width == v4l2Fmt.width && height == v4l2Fmt.height) {
+            return true;
+        }
+    }
+    ALOGI("%s: resolution %dx%d is not supported", __FUNCTION__, width, height);
+    return false;
+}
+
+int ExternalCameraDeviceSession::v4l2StreamOffLocked() {
+    if (!mV4l2Streaming) {
+        return OK;
+    }
+
+    {
+        std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+        if (mNumDequeuedV4l2Buffers != 0)  {
+            ALOGE("%s: there are %zu inflight V4L buffers",
+                __FUNCTION__, mNumDequeuedV4l2Buffers);
+            return -1;
+        }
+    }
+    mV4l2Buffers.clear(); // VIDIOC_REQBUFS will fail if FDs are not clear first
+
+    // VIDIOC_STREAMOFF
+    v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMOFF, &capture_type)) < 0) {
+        ALOGE("%s: STREAMOFF failed: %s", __FUNCTION__, strerror(errno));
+        return -errno;
+    }
+
+    // VIDIOC_REQBUFS: clear buffers
+    v4l2_requestbuffers req_buffers{};
+    req_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    req_buffers.memory = V4L2_MEMORY_MMAP;
+    req_buffers.count = 0;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_REQBUFS, &req_buffers)) < 0) {
+        ALOGE("%s: REQBUFS failed: %s", __FUNCTION__, strerror(errno));
+        return -errno;
+    }
+
+    mV4l2Streaming = false;
+    return OK;
+}
+
+int ExternalCameraDeviceSession::configureV4l2StreamLocked(const SupportedV4L2Format& v4l2Fmt) {
+    int ret = v4l2StreamOffLocked();
+    if (ret != OK) {
+        ALOGE("%s: stop v4l2 streaming failed: ret %d", __FUNCTION__, ret);
+        return ret;
+    }
+
+    // VIDIOC_S_FMT w/h/fmt
+    v4l2_format fmt;
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    fmt.fmt.pix.width = v4l2Fmt.width;
+    fmt.fmt.pix.height = v4l2Fmt.height;
+    fmt.fmt.pix.pixelformat = v4l2Fmt.fourcc;
+    ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_FMT, &fmt));
+    if (ret < 0) {
+        ALOGE("%s: S_FMT ioctl failed: %s", __FUNCTION__, strerror(errno));
+        return -errno;
+    }
+
+    if (v4l2Fmt.width != fmt.fmt.pix.width || v4l2Fmt.height != fmt.fmt.pix.height ||
+            v4l2Fmt.fourcc != fmt.fmt.pix.pixelformat) {
+        ALOGE("%s: S_FMT expect %c%c%c%c %dx%d, got %c%c%c%c %dx%d instead!", __FUNCTION__,
+                v4l2Fmt.fourcc & 0xFF,
+                (v4l2Fmt.fourcc >> 8) & 0xFF,
+                (v4l2Fmt.fourcc >> 16) & 0xFF,
+                (v4l2Fmt.fourcc >> 24) & 0xFF,
+                v4l2Fmt.width, v4l2Fmt.height,
+                fmt.fmt.pix.pixelformat & 0xFF,
+                (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
+                (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
+                (fmt.fmt.pix.pixelformat >> 24) & 0xFF,
+                fmt.fmt.pix.width, fmt.fmt.pix.height);
+        return -EINVAL;
+    }
+    uint32_t bufferSize = fmt.fmt.pix.sizeimage;
+    ALOGI("%s: V4L2 buffer size is %d", __FUNCTION__, bufferSize);
+
+    float maxFps = -1.f;
+    float fps = 1000.f;
+    const float kDefaultFps = 30.f;
+    // Try to pick the slowest fps that is at least 30
+    for (const auto& f : v4l2Fmt.frameRates) {
+        if (maxFps < f) {
+            maxFps = f;
+        }
+        if (f >= kDefaultFps && f < fps) {
+            fps = f;
+        }
+    }
+    if (fps == 1000.f) {
+        fps = maxFps;
+    }
+
+    // VIDIOC_G_PARM/VIDIOC_S_PARM: set fps
+    v4l2_streamparm streamparm = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
+    // The following line checks that the driver knows about framerate get/set.
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
+        // Now check if the device is able to accept a capture framerate set.
+        if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
+            // |frame_rate| is float, approximate by a fraction.
+            const int kFrameRatePrecision = 10000;
+            streamparm.parm.capture.timeperframe.numerator = kFrameRatePrecision;
+            streamparm.parm.capture.timeperframe.denominator =
+                (fps * kFrameRatePrecision);
+
+            if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_PARM, &streamparm)) < 0) {
+                ALOGE("%s: failed to set framerate to %f", __FUNCTION__, fps);
+                return UNKNOWN_ERROR;
+            }
+        }
+    }
+    float retFps = streamparm.parm.capture.timeperframe.denominator /
+                streamparm.parm.capture.timeperframe.numerator;
+    if (std::fabs(fps - retFps) > std::numeric_limits<float>::epsilon()) {
+        ALOGE("%s: expect fps %f, got %f instead", __FUNCTION__, fps, retFps);
+        return BAD_VALUE;
+    }
+
+    uint32_t v4lBufferCount = (v4l2Fmt.width <= kMaxVideoSize.width &&
+            v4l2Fmt.height <= kMaxVideoSize.height) ? kNumVideoBuffers : kNumStillBuffers;
+    // VIDIOC_REQBUFS: create buffers
+    v4l2_requestbuffers req_buffers{};
+    req_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    req_buffers.memory = V4L2_MEMORY_MMAP;
+    req_buffers.count = v4lBufferCount;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_REQBUFS, &req_buffers)) < 0) {
+        ALOGE("%s: VIDIOC_REQBUFS failed: %s", __FUNCTION__, strerror(errno));
+        return -errno;
+    }
+
+    // Driver can indeed return more buffer if it needs more to operate
+    if (req_buffers.count < v4lBufferCount) {
+        ALOGE("%s: VIDIOC_REQBUFS expected %d buffers, got %d instead",
+                __FUNCTION__, v4lBufferCount, req_buffers.count);
+        return NO_MEMORY;
+    }
+
+    // VIDIOC_EXPBUF:  export buffers as FD
+    // VIDIOC_QBUF: send buffer to driver
+    mV4l2Buffers.resize(req_buffers.count);
+    for (uint32_t i = 0; i < req_buffers.count; i++) {
+        v4l2_exportbuffer expbuf {};
+        expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        expbuf.index = i;
+        if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_EXPBUF, &expbuf)) < 0) {
+            ALOGE("%s: EXPBUF %d failed: %s", __FUNCTION__, i,  strerror(errno));
+            return -errno;
+        }
+        mV4l2Buffers[i].reset(expbuf.fd);
+
+        v4l2_buffer buffer = {
+            .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+            .index = i,
+            .memory = V4L2_MEMORY_MMAP};
+
+        if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+            ALOGE("%s: QBUF %d failed: %s", __FUNCTION__, i,  strerror(errno));
+            return -errno;
+        }
+    }
+
+    // VIDIOC_STREAMON: start streaming
+    v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMON, &capture_type)) < 0) {
+        ALOGE("%s: VIDIOC_STREAMON failed: %s", __FUNCTION__, strerror(errno));
+        return -errno;
+    }
+
+    // Swallow first few frames after streamOn to account for bad frames from some devices
+    for (int i = 0; i < kBadFramesAfterStreamOn; i++) {
+        v4l2_buffer buffer{};
+        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        buffer.memory = V4L2_MEMORY_MMAP;
+        if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+            ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
+            return -errno;
+        }
+
+        if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+            ALOGE("%s: QBUF index %d fails: %s", __FUNCTION__, buffer.index, strerror(errno));
+            return -errno;
+        }
+    }
+
+    mV4l2StreamingFmt = v4l2Fmt;
+    mV4l2Streaming = true;
+    return OK;
+}
+
+sp<V4L2Frame> ExternalCameraDeviceSession::dequeueV4l2FrameLocked() {
+    sp<V4L2Frame> ret = nullptr;
+
+    {
+        std::unique_lock<std::mutex> lk(mV4l2BufferLock);
+        if (mNumDequeuedV4l2Buffers == mV4l2Buffers.size()) {
+            std::chrono::seconds timeout = std::chrono::seconds(kBufferWaitTimeoutSec);
+            mLock.unlock();
+            auto st = mV4L2BufferReturned.wait_for(lk, timeout);
+            mLock.lock();
+            if (st == std::cv_status::timeout) {
+                ALOGE("%s: wait for V4L2 buffer return timeout!", __FUNCTION__);
+                return ret;
+            }
+        }
+    }
+
+    v4l2_buffer buffer{};
+    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buffer.memory = V4L2_MEMORY_MMAP;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+        ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
+        return ret;
+    }
+
+    if (buffer.index >= mV4l2Buffers.size()) {
+        ALOGE("%s: Invalid buffer id: %d", __FUNCTION__, buffer.index);
+        return ret;
+    }
+
+    if (buffer.flags & V4L2_BUF_FLAG_ERROR) {
+        ALOGE("%s: v4l2 buf error! buf flag 0x%x", __FUNCTION__, buffer.flags);
+        // TODO: try to dequeue again
+    }
+
+    {
+        std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+        mNumDequeuedV4l2Buffers++;
+    }
+    return new V4L2Frame(
+            mV4l2StreamingFmt.width, mV4l2StreamingFmt.height, mV4l2StreamingFmt.fourcc,
+            buffer.index, mV4l2Buffers[buffer.index].get(), buffer.bytesused);
+}
+
+void ExternalCameraDeviceSession::enqueueV4l2Frame(const sp<V4L2Frame>& frame) {
+    Mutex::Autolock _l(mLock);
+    frame->unmap();
+    v4l2_buffer buffer{};
+    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buffer.memory = V4L2_MEMORY_MMAP;
+    buffer.index = frame->mBufferIndex;
+    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+        ALOGE("%s: QBUF index %d fails: %s", __FUNCTION__, frame->mBufferIndex, strerror(errno));
+        return;
+    }
+
+    {
+        std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+        mNumDequeuedV4l2Buffers--;
+        mV4L2BufferReturned.notify_one();
+    }
+}
+
+Status ExternalCameraDeviceSession::configureStreams(
+        const V3_2::StreamConfiguration& config, V3_3::HalStreamConfiguration* out) {
+    if (config.operationMode != StreamConfigurationMode::NORMAL_MODE) {
+        ALOGE("%s: unsupported operation mode: %d", __FUNCTION__, config.operationMode);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (config.streams.size() == 0) {
+        ALOGE("%s: cannot configure zero stream", __FUNCTION__);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    int numProcessedStream = 0;
+    int numStallStream = 0;
+    for (const auto& stream : config.streams) {
+        // Check if the format/width/height combo is supported
+        if (!isSupported(stream)) {
+            return Status::ILLEGAL_ARGUMENT;
+        }
+        if (stream.format == PixelFormat::BLOB) {
+            numStallStream++;
+        } else {
+            numProcessedStream++;
+        }
+    }
+
+    if (numProcessedStream > kMaxProcessedStream) {
+        ALOGE("%s: too many processed streams (expect <= %d, got %d)", __FUNCTION__,
+                kMaxProcessedStream, numProcessedStream);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (numStallStream > kMaxStallStream) {
+        ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__,
+                kMaxStallStream, numStallStream);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    Status status = initStatus();
+    if (status != Status::OK) {
+        return status;
+    }
+
+    Mutex::Autolock _l(mLock);
+    if (!mInflightFrames.empty()) {
+        ALOGE("%s: trying to configureStreams while there are still %zu inflight frames!",
+                __FUNCTION__, mInflightFrames.size());
+        return Status::INTERNAL_ERROR;
+    }
+
+    // Add new streams
+    for (const auto& stream : config.streams) {
+        if (mStreamMap.count(stream.id) == 0) {
+            mStreamMap[stream.id] = stream;
+            mCirculatingBuffers.emplace(stream.id, CirculatingBuffers{});
+        }
+    }
+
+    // Cleanup removed streams
+    for(auto it = mStreamMap.begin(); it != mStreamMap.end();) {
+        int id = it->first;
+        bool found = false;
+        for (const auto& stream : config.streams) {
+            if (id == stream.id) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            // Unmap all buffers of deleted stream
+            cleanupBuffersLocked(id);
+            it = mStreamMap.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Now select a V4L2 format to produce all output streams
+    float desiredAr = (mCroppingType == VERTICAL) ? kMaxAspectRatio : kMinAspectRatio;
+    uint32_t maxDim = 0;
+    for (const auto& stream : config.streams) {
+        float aspectRatio = ASPECT_RATIO(stream);
+        if ((mCroppingType == VERTICAL && aspectRatio < desiredAr) ||
+                (mCroppingType == HORIZONTAL && aspectRatio > desiredAr)) {
+            desiredAr = aspectRatio;
+        }
+
+        // The dimension that's not cropped
+        uint32_t dim = (mCroppingType == VERTICAL) ? stream.width : stream.height;
+        if (dim > maxDim) {
+            maxDim = dim;
+        }
+    }
+    // Find the smallest format that matches the desired aspect ratio and is wide/high enough
+    SupportedV4L2Format v4l2Fmt {.width = 0, .height = 0};
+    for (const auto& fmt : mSupportedFormats) {
+        uint32_t dim = (mCroppingType == VERTICAL) ? fmt.width : fmt.height;
+        if (dim >= maxDim) {
+            float aspectRatio = ASPECT_RATIO(fmt);
+            if (isAspectRatioClose(aspectRatio, desiredAr)) {
+                v4l2Fmt = fmt;
+                // since mSupportedFormats is sorted by width then height, the first matching fmt
+                // will be the smallest one with matching aspect ratio
+                break;
+            }
+        }
+    }
+    if (v4l2Fmt.width == 0) {
+        // Cannot find exact good aspect ratio candidate, try to find a close one
+        for (const auto& fmt : mSupportedFormats) {
+            uint32_t dim = (mCroppingType == VERTICAL) ? fmt.width : fmt.height;
+            if (dim >= maxDim) {
+                float aspectRatio = ASPECT_RATIO(fmt);
+                if ((mCroppingType == VERTICAL && aspectRatio < desiredAr) ||
+                        (mCroppingType == HORIZONTAL && aspectRatio > desiredAr)) {
+                    v4l2Fmt = fmt;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (v4l2Fmt.width == 0) {
+        ALOGE("%s: unable to find a resolution matching (%s at least %d, aspect ratio %f)"
+                , __FUNCTION__, (mCroppingType == VERTICAL) ? "width" : "height",
+                maxDim, desiredAr);
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    if (configureV4l2StreamLocked(v4l2Fmt) != 0) {
+        ALOGE("V4L configuration failed!, format:%c%c%c%c, w %d, h %d",
+            v4l2Fmt.fourcc & 0xFF,
+            (v4l2Fmt.fourcc >> 8) & 0xFF,
+            (v4l2Fmt.fourcc >> 16) & 0xFF,
+            (v4l2Fmt.fourcc >> 24) & 0xFF,
+            v4l2Fmt.width, v4l2Fmt.height);
+        return Status::INTERNAL_ERROR;
+    }
+
+    Size v4lSize = {v4l2Fmt.width, v4l2Fmt.height};
+    status = mOutputThread->allocateIntermediateBuffers(v4lSize, config.streams);
+    if (status != Status::OK) {
+        ALOGE("%s: allocating intermediate buffers failed!", __FUNCTION__);
+        return status;
+    }
+
+    out->streams.resize(config.streams.size());
+    for (size_t i = 0; i < config.streams.size(); i++) {
+        out->streams[i].overrideDataSpace = config.streams[i].dataSpace;
+        out->streams[i].v3_2.id = config.streams[i].id;
+        // TODO: double check should we add those CAMERA flags
+        mStreamMap[config.streams[i].id].usage =
+                out->streams[i].v3_2.producerUsage = config.streams[i].usage |
+                BufferUsage::CPU_WRITE_OFTEN |
+                BufferUsage::CAMERA_OUTPUT;
+        out->streams[i].v3_2.consumerUsage = 0;
+        out->streams[i].v3_2.maxBuffers  = mV4l2Buffers.size();
+
+        switch (config.streams[i].format) {
+            case PixelFormat::BLOB:
+            case PixelFormat::YCBCR_420_888:
+                // No override
+                out->streams[i].v3_2.overrideFormat = config.streams[i].format;
+                break;
+            case PixelFormat::IMPLEMENTATION_DEFINED:
+                // Override based on VIDEO or not
+                out->streams[i].v3_2.overrideFormat =
+                        (config.streams[i].usage & BufferUsage::VIDEO_ENCODER) ?
+                        PixelFormat::YCBCR_420_888 : PixelFormat::YV12;
+                // Save overridden formt in mStreamMap
+                mStreamMap[config.streams[i].id].format = out->streams[i].v3_2.overrideFormat;
+                break;
+            default:
+                ALOGE("%s: unsupported format %x", __FUNCTION__, config.streams[i].format);
+                return Status::ILLEGAL_ARGUMENT;
+        }
+    }
+
+    mFirstRequest = true;
+    return Status::OK;
+}
+
+bool ExternalCameraDeviceSession::isClosed() {
+    Mutex::Autolock _l(mLock);
+    return mClosed;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define UPDATE(md, tag, data, size)               \
+do {                                              \
+    if ((md).update((tag), (data), (size))) {     \
+        ALOGE("Update " #tag " failed!");         \
+        return BAD_VALUE;                         \
+    }                                             \
+} while (0)
+
+status_t ExternalCameraDeviceSession::initDefaultRequests() {
+    ::android::hardware::camera::common::V1_0::helper::CameraMetadata md;
+
+    const uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
+    UPDATE(md, ANDROID_COLOR_CORRECTION_ABERRATION_MODE, &aberrationMode, 1);
+
+    const int32_t exposureCompensation = 0;
+    UPDATE(md, ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &exposureCompensation, 1);
+
+    const uint8_t videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+    UPDATE(md, ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &videoStabilizationMode, 1);
+
+    const uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+    UPDATE(md, ANDROID_CONTROL_AWB_MODE, &awbMode, 1);
+
+    const uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
+    UPDATE(md, ANDROID_CONTROL_AE_MODE, &aeMode, 1);
+
+    const uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+    UPDATE(md, ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1);
+
+    const uint8_t afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+    UPDATE(md, ANDROID_CONTROL_AF_MODE, &afMode, 1);
+
+    const uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+    UPDATE(md, ANDROID_CONTROL_AF_TRIGGER, &afTrigger, 1);
+
+    const uint8_t sceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+    UPDATE(md, ANDROID_CONTROL_SCENE_MODE, &sceneMode, 1);
+
+    const uint8_t effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+    UPDATE(md, ANDROID_CONTROL_EFFECT_MODE, &effectMode, 1);
+
+    const uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+    UPDATE(md, ANDROID_FLASH_MODE, &flashMode, 1);
+
+    const int32_t thumbnailSize[] = {240, 180};
+    UPDATE(md, ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2);
+
+    const uint8_t jpegQuality = 90;
+    UPDATE(md, ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+    UPDATE(md, ANDROID_JPEG_THUMBNAIL_QUALITY, &jpegQuality, 1);
+
+    const int32_t jpegOrientation = 0;
+    UPDATE(md, ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
+
+    const uint8_t oisMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+    UPDATE(md, ANDROID_LENS_OPTICAL_STABILIZATION_MODE, &oisMode, 1);
+
+    const uint8_t nrMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+    UPDATE(md, ANDROID_NOISE_REDUCTION_MODE, &nrMode, 1);
+
+    const uint8_t fdMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+    UPDATE(md, ANDROID_STATISTICS_FACE_DETECT_MODE, &fdMode, 1);
+
+    const uint8_t hotpixelMode = ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+    UPDATE(md, ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, &hotpixelMode, 1);
+
+    bool support30Fps = false;
+    int32_t maxFps = std::numeric_limits<int32_t>::min();
+    for (const auto& supportedFormat : mSupportedFormats) {
+        for (const auto& frameRate : supportedFormat.frameRates) {
+            int32_t framerateInt = static_cast<int32_t>(frameRate);
+            if (maxFps < framerateInt) {
+                maxFps = framerateInt;
+            }
+            if (framerateInt == 30) {
+                support30Fps = true;
+                break;
+            }
+        }
+        if (support30Fps) {
+            break;
+        }
+    }
+    int32_t defaultFramerate = support30Fps ? 30 : maxFps;
+    int32_t defaultFpsRange[] = {defaultFramerate, defaultFramerate};
+    UPDATE(md, ANDROID_CONTROL_AE_TARGET_FPS_RANGE, defaultFpsRange, ARRAY_SIZE(defaultFpsRange));
+
+    uint8_t antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+    UPDATE(md, ANDROID_CONTROL_AE_ANTIBANDING_MODE, &antibandingMode, 1);
+
+    const uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
+    UPDATE(md, ANDROID_CONTROL_MODE, &controlMode, 1);
+
+    for (int type = static_cast<int>(RequestTemplate::PREVIEW);
+            type <= static_cast<int>(RequestTemplate::VIDEO_SNAPSHOT); type++) {
+        ::android::hardware::camera::common::V1_0::helper::CameraMetadata mdCopy = md;
+        uint8_t intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+        switch (type) {
+            case static_cast<int>(RequestTemplate::PREVIEW):
+                intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+                break;
+            case static_cast<int>(RequestTemplate::STILL_CAPTURE):
+                intent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
+                break;
+            case static_cast<int>(RequestTemplate::VIDEO_RECORD):
+                intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+                break;
+            case static_cast<int>(RequestTemplate::VIDEO_SNAPSHOT):
+                intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
+                break;
+            default:
+                ALOGE("%s: unknown template type %d", __FUNCTION__, type);
+                return BAD_VALUE;
+        }
+        UPDATE(mdCopy, ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
+
+        camera_metadata_t* rawMd = mdCopy.release();
+        CameraMetadata hidlMd;
+        hidlMd.setToExternal(
+                (uint8_t*) rawMd, get_camera_metadata_size(rawMd));
+        mDefaultRequests[type] = hidlMd;
+        free_camera_metadata(rawMd);
+    }
+
+    return OK;
+}
+
+status_t ExternalCameraDeviceSession::fillCaptureResult(
+        common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
+    // android.control
+    // For USB camera, we don't know the AE state. Set the state to converged to
+    // indicate the frame should be good to use. Then apps don't have to wait the
+    // AE state.
+    const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+    UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+    const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+    UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+
+    // TODO: b/72261912 AF should stay LOCKED until cancel is seen
+    bool afTrigger = false;
+    if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) {
+        camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER);
+        if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) {
+            afTrigger = true;
+        } else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) {
+            afTrigger = false;
+        }
+    }
+
+    // For USB camera, the USB camera handles everything and we don't have control
+    // over AF. We only simply fake the AF metadata based on the request
+    // received here.
+    uint8_t afState;
+    if (afTrigger) {
+        afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+    } else {
+        afState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+    }
+    UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
+
+    // Set AWB state to converged to indicate the frame should be good to use.
+    const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+    UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+    const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+    UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+    camera_metadata_ro_entry active_array_size =
+        mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+    if (active_array_size.count == 0) {
+        ALOGE("%s: cannot find active array size!", __FUNCTION__);
+        return -EINVAL;
+    }
+
+    // android.scaler
+    const int32_t crop_region[] = {
+          active_array_size.data.i32[0], active_array_size.data.i32[1],
+          active_array_size.data.i32[2], active_array_size.data.i32[3],
+    };
+    UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
+
+    // android.sensor
+    UPDATE(md, ANDROID_SENSOR_TIMESTAMP, &timestamp, 1);
+
+    // android.statistics
+    const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+    UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+    const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+    UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+
+    return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
+V4L2Frame::V4L2Frame(
+        uint32_t w, uint32_t h, uint32_t fourcc,
+        int bufIdx, int fd, uint32_t dataSize) :
+        mWidth(w), mHeight(h), mFourcc(fourcc),
+        mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize) {}
+
+int V4L2Frame::map(uint8_t** data, size_t* dataSize) {
+    if (data == nullptr || dataSize == nullptr) {
+        ALOGI("%s: V4L2 buffer map bad argument: data %p, dataSize %p",
+                __FUNCTION__, data, dataSize);
+        return -EINVAL;
+    }
+
+    Mutex::Autolock _l(mLock);
+    if (!mMapped) {
+        void* addr = mmap(NULL, mDataSize, PROT_READ, MAP_SHARED, mFd, 0);
+        if (addr == MAP_FAILED) {
+            ALOGE("%s: V4L2 buffer map failed: %s", __FUNCTION__, strerror(errno));
+            return -EINVAL;
+        }
+        mData = static_cast<uint8_t*>(addr);
+        mMapped = true;
+    }
+    *data = mData;
+    *dataSize = mDataSize;
+    ALOGV("%s: V4L map FD %d, data %p size %zu", __FUNCTION__, mFd, mData, mDataSize);
+    return 0;
+}
+
+int V4L2Frame::unmap() {
+    Mutex::Autolock _l(mLock);
+    if (mMapped) {
+        ALOGV("%s: V4L unmap data %p size %zu", __FUNCTION__, mData, mDataSize);
+        if (munmap(mData, mDataSize) != 0) {
+            ALOGE("%s: V4L2 buffer unmap failed: %s", __FUNCTION__, strerror(errno));
+            return -EINVAL;
+        }
+        mMapped = false;
+    }
+    return 0;
+}
+
+V4L2Frame::~V4L2Frame() {
+    unmap();
+}
+
+AllocatedFrame::AllocatedFrame(
+        uint32_t w, uint32_t h) :
+        mWidth(w), mHeight(h), mFourcc(V4L2_PIX_FMT_YUV420) {};
+
+AllocatedFrame::~AllocatedFrame() {}
+
+int AllocatedFrame::allocate(YCbCrLayout* out) {
+    if ((mWidth % 2) || (mHeight % 2)) {
+        ALOGE("%s: bad dimension %dx%d (not multiple of 2)", __FUNCTION__, mWidth, mHeight);
+        return -EINVAL;
+    }
+
+    uint32_t dataSize = mWidth * mHeight * 3 / 2; // YUV420
+    if (mData.size() != dataSize) {
+        mData.resize(dataSize);
+    }
+
+    if (out != nullptr) {
+        out->y = mData.data();
+        out->yStride = mWidth;
+        uint8_t* cbStart = mData.data() + mWidth * mHeight;
+        uint8_t* crStart = cbStart + mWidth * mHeight / 4;
+        out->cb = cbStart;
+        out->cr = crStart;
+        out->cStride = mWidth / 2;
+        out->chromaStep = 1;
+    }
+    return 0;
+}
+
+int AllocatedFrame::getLayout(YCbCrLayout* out) {
+    IMapper::Rect noCrop = {0, 0,
+            static_cast<int32_t>(mWidth),
+            static_cast<int32_t>(mHeight)};
+    return getCroppedLayout(noCrop, out);
+}
+
+int AllocatedFrame::getCroppedLayout(const IMapper::Rect& rect, YCbCrLayout* out) {
+    if (out == nullptr) {
+        ALOGE("%s: null out", __FUNCTION__);
+        return -1;
+    }
+    if ((rect.left + rect.width) > static_cast<int>(mWidth) ||
+        (rect.top + rect.height) > static_cast<int>(mHeight) ||
+            (rect.left % 2) || (rect.top % 2) || (rect.width % 2) || (rect.height % 2)) {
+        ALOGE("%s: bad rect left %d top %d w %d h %d", __FUNCTION__,
+                rect.left, rect.top, rect.width, rect.height);
+        return -1;
+    }
+
+    out->y = mData.data() + mWidth * rect.top + rect.left;
+    out->yStride = mWidth;
+    uint8_t* cbStart = mData.data() + mWidth * mHeight;
+    uint8_t* crStart = cbStart + mWidth * mHeight / 4;
+    out->cb = cbStart + mWidth * rect.top / 4 + rect.left / 2;
+    out->cr = crStart + mWidth * rect.top / 4 + rect.left / 2;
+    out->cStride = mWidth / 2;
+    out->chromaStep = 1;
+    return 0;
+}
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
diff --git a/camera/device/3.4/default/convert.cpp b/camera/device/3.4/default/convert.cpp
new file mode 100644
index 0000000..f12230c
--- /dev/null
+++ b/camera/device/3.4/default/convert.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.camera.device@3.4-convert-impl"
+#include <log/log.h>
+
+#include <cstring>
+#include "include/convert.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::camera::device::V3_2::BufferUsageFlags;
+
+void convertToHidl(const Camera3Stream* src, HalStream* dst) {
+    V3_3::implementation::convertToHidl(src, &dst->v3_3);
+    dst->physicalCameraId = src->physical_camera_id;
+}
+
+void convertToHidl(const camera3_stream_configuration_t& src, HalStreamConfiguration* dst) {
+    dst->streams.resize(src.num_streams);
+    for (uint32_t i = 0; i < src.num_streams; i++) {
+        convertToHidl(static_cast<Camera3Stream*>(src.streams[i]), &dst->streams[i]);
+    }
+    return;
+}
+
+void convertFromHidl(const Stream &src, Camera3Stream* dst) {
+    V3_2::implementation::convertFromHidl(src.v3_2, dst);
+    // Initialize physical_camera_id
+    dst->physical_camera_id = nullptr;
+    return;
+}
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/device/3.4/default/include/convert.h b/camera/device/3.4/default/include/convert.h
new file mode 100644
index 0000000..e8e3951
--- /dev/null
+++ b/camera/device/3.4/default/include/convert.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_V3_4_DEFAULT_INCLUDE_CONVERT_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_V3_4_DEFAULT_INCLUDE_CONVERT_H_
+
+#include <android/hardware/camera/device/3.4/types.h>
+#include "hardware/camera3.h"
+#include "../../3.3/default/include/convert.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+using ::android::hardware::camera::device::V3_2::implementation::Camera3Stream;
+
+void convertToHidl(const Camera3Stream* src, HalStream* dst);
+
+void convertToHidl(const camera3_stream_configuration_t& src, HalStreamConfiguration* dst);
+
+void convertFromHidl(const Stream &src, Camera3Stream* dst);
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // HARDWARE_INTERFACES_CAMERA_DEVICE_V3_4_DEFAULT_INCLUDE_CONVERT_H_
diff --git a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
index bff1734..fbde083 100644
--- a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
+++ b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017-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.
@@ -18,7 +18,6 @@
 #define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_CAMERADEVICE3SESSION_H
 
 #include <android/hardware/camera/device/3.2/ICameraDevice.h>
-#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
 #include <../../3.3/default/CameraDeviceSession.h>
 #include <../../3.3/default/include/convert.h>
@@ -43,8 +42,9 @@
 
 using namespace ::android::hardware::camera::device;
 using ::android::hardware::camera::device::V3_2::CaptureRequest;
-using ::android::hardware::camera::device::V3_2::StreamConfiguration;
-using ::android::hardware::camera::device::V3_3::HalStreamConfiguration;
+using ::android::hardware::camera::device::V3_2::StreamType;
+using ::android::hardware::camera::device::V3_4::StreamConfiguration;
+using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
 using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
 using ::android::hardware::camera::common::V1_0::Status;
 using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
@@ -75,8 +75,22 @@
     // New methods for v3.4
 
     Return<void> configureStreams_3_4(
-            const V3_4::StreamConfiguration& requestedConfiguration,
-            ICameraDeviceSession::configureStreams_3_3_cb _hidl_cb);
+            const StreamConfiguration& requestedConfiguration,
+            ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb);
+
+    bool preProcessConfigurationLocked_3_4(
+            const StreamConfiguration& requestedConfiguration,
+            camera3_stream_configuration_t *stream_list /*out*/,
+            hidl_vec<camera3_stream_t*> *streams /*out*/);
+    void postProcessConfigurationLocked_3_4(const StreamConfiguration& requestedConfiguration);
+
+    Return<void> processCaptureRequest_3_4(
+            const hidl_vec<V3_4::CaptureRequest>& requests,
+            const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+            ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
+    Status processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request);
+
+    std::map<int, std::string> mPhysicalCameraIdMap;
 private:
 
     struct TrampolineSessionInterface_3_4 : public ICameraDeviceSession {
@@ -90,11 +104,17 @@
         }
 
         virtual Return<void> configureStreams(
-                const StreamConfiguration& requestedConfiguration,
+                const V3_2::StreamConfiguration& requestedConfiguration,
                 V3_3::ICameraDeviceSession::configureStreams_cb _hidl_cb) override {
             return mParent->configureStreams(requestedConfiguration, _hidl_cb);
         }
 
+        virtual Return<void> processCaptureRequest_3_4(const hidl_vec<V3_4::CaptureRequest>& requests,
+                const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+                ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override {
+            return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb);
+        }
+
         virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests,
                 const hidl_vec<V3_2::BufferCache>& cachesToRemove,
                 V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override {
@@ -120,14 +140,14 @@
         }
 
         virtual Return<void> configureStreams_3_3(
-                const StreamConfiguration& requestedConfiguration,
+                const V3_2::StreamConfiguration& requestedConfiguration,
                 configureStreams_3_3_cb _hidl_cb) override {
             return mParent->configureStreams_3_3(requestedConfiguration, _hidl_cb);
         }
 
         virtual Return<void> configureStreams_3_4(
-                const V3_4::StreamConfiguration& requestedConfiguration,
-                configureStreams_3_3_cb _hidl_cb) override {
+                const StreamConfiguration& requestedConfiguration,
+                configureStreams_3_4_cb _hidl_cb) override {
             return mParent->configureStreams_3_4(requestedConfiguration, _hidl_cb);
         }
 
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
new file mode 100644
index 0000000..404dfe0
--- /dev/null
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -0,0 +1,441 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <include/convert.h>
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+#include "CameraMetadata.h"
+#include "HandleImporter.h"
+#include "utils/KeyedVector.h"
+#include "utils/Mutex.h"
+#include "utils/Thread.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_2::BufferStatus;
+using ::android::hardware::camera::device::V3_2::CameraMetadata;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::CaptureResult;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::MsgType;
+using ::android::hardware::camera::device::V3_2::NotifyMsg;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_4::StreamConfiguration;
+using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamType;
+using ::android::hardware::camera::device::V3_2::DataspaceFlags;
+using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
+using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+using ::android::Mutex;
+using ::android::base::unique_fd;
+
+// TODO: put V4L2 related structs into separate header?
+struct SupportedV4L2Format {
+    uint32_t width;
+    uint32_t height;
+    uint32_t fourcc;
+    // All supported frame rate for this w/h/fourcc combination
+    std::vector<float> frameRates;
+};
+
+// A class provide access to a dequeued V4L2 frame buffer (mostly in MJPG format)
+// Also contains necessary information to enqueue the buffer back to V4L2 buffer queue
+class V4L2Frame : public virtual VirtualLightRefBase {
+public:
+    V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd, uint32_t dataSize);
+    ~V4L2Frame() override;
+    const uint32_t mWidth;
+    const uint32_t mHeight;
+    const uint32_t mFourcc;
+    const int mBufferIndex; // for later enqueue
+    int map(uint8_t** data, size_t* dataSize);
+    int unmap();
+private:
+    Mutex mLock;
+    const int mFd; // used for mmap but doesn't claim ownership
+    const size_t mDataSize;
+    uint8_t* mData = nullptr;
+    bool  mMapped = false;
+};
+
+// A RAII class representing a CPU allocated YUV frame used as intermeidate buffers
+// when generating output images.
+class AllocatedFrame : public virtual VirtualLightRefBase {
+public:
+    AllocatedFrame(uint32_t w, uint32_t h); // TODO: use Size?
+    ~AllocatedFrame() override;
+    const uint32_t mWidth;
+    const uint32_t mHeight;
+    const uint32_t mFourcc; // Only support YU12 format for now
+    int allocate(YCbCrLayout* out = nullptr);
+    int getLayout(YCbCrLayout* out);
+    int getCroppedLayout(const IMapper::Rect&, YCbCrLayout* out); // return non-zero for bad input
+private:
+    Mutex mLock;
+    std::vector<uint8_t> mData;
+};
+
+struct Size {
+    uint32_t width;
+    uint32_t height;
+
+    bool operator==(const Size& other) const {
+        return (width == other.width && height == other.height);
+    }
+};
+
+struct SizeHasher {
+    size_t operator()(const Size& sz) const {
+        size_t result = 1;
+        result = 31 * result + sz.width;
+        result = 31 * result + sz.height;
+        return result;
+    }
+};
+
+enum CroppingType {
+    HORIZONTAL = 0,
+    VERTICAL = 1
+};
+
+struct ExternalCameraDeviceSession : public virtual RefBase {
+
+    ExternalCameraDeviceSession(const sp<ICameraDeviceCallback>&,
+            const std::vector<SupportedV4L2Format>& supportedFormats,
+            const common::V1_0::helper::CameraMetadata& chars,
+            unique_fd v4l2Fd);
+    virtual ~ExternalCameraDeviceSession();
+    // Call by CameraDevice to dump active device states
+    void dumpState(const native_handle_t*);
+    // Caller must use this method to check if CameraDeviceSession ctor failed
+    bool isInitFailed() { return mInitFail; }
+    bool isClosed();
+
+    // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
+    // dealing with minor version revs and simultaneous implementation and interface inheritance
+    virtual sp<ICameraDeviceSession> getInterface() {
+        return new TrampolineSessionInterface_3_4(this);
+    }
+
+    static const int kMaxProcessedStream = 2;
+    static const int kMaxStallStream = 1;
+
+protected:
+
+    // Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow
+
+    Return<void> constructDefaultRequestSettings(
+            RequestTemplate,
+            ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
+
+    Return<void> configureStreams(
+            const V3_2::StreamConfiguration&,
+            ICameraDeviceSession::configureStreams_cb);
+
+    Return<void> getCaptureRequestMetadataQueue(
+        ICameraDeviceSession::getCaptureRequestMetadataQueue_cb);
+
+    Return<void> getCaptureResultMetadataQueue(
+        ICameraDeviceSession::getCaptureResultMetadataQueue_cb);
+
+    Return<void> processCaptureRequest(
+            const hidl_vec<CaptureRequest>&,
+            const hidl_vec<BufferCache>&,
+            ICameraDeviceSession::processCaptureRequest_cb);
+
+    Return<Status> flush();
+    Return<void> close();
+
+    Return<void> configureStreams_3_3(
+            const V3_2::StreamConfiguration&,
+            ICameraDeviceSession::configureStreams_3_3_cb);
+
+    Return<void> configureStreams_3_4(
+            const V3_4::StreamConfiguration& requestedConfiguration,
+            ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb);
+
+    Return<void> processCaptureRequest_3_4(
+            const hidl_vec<V3_4::CaptureRequest>& requests,
+            const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+            ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
+
+protected:
+    struct HalStreamBuffer {
+        int32_t streamId;
+        uint64_t bufferId;
+        uint32_t width;
+        uint32_t height;
+        PixelFormat format;
+        V3_2::BufferUsageFlags usage;
+        buffer_handle_t* bufPtr;
+        int acquireFence;
+        bool fenceTimeout;
+    };
+
+    struct HalRequest {
+        uint32_t frameNumber;
+        common::V1_0::helper::CameraMetadata setting;
+        sp<V4L2Frame> frameIn;
+        nsecs_t shutterTs;
+        std::vector<HalStreamBuffer> buffers;
+    };
+
+    static std::vector<SupportedV4L2Format> sortFormats(
+            const std::vector<SupportedV4L2Format>&);
+    static CroppingType initCroppingType(const std::vector<SupportedV4L2Format>&);
+    bool initialize();
+    Status initStatus() const;
+    status_t initDefaultRequests();
+    status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
+    Status configureStreams(const V3_2::StreamConfiguration&, V3_3::HalStreamConfiguration* out);
+    int configureV4l2StreamLocked(const SupportedV4L2Format& fmt);
+    int v4l2StreamOffLocked();
+
+    // TODO: change to unique_ptr for better tracking
+    sp<V4L2Frame> dequeueV4l2FrameLocked(); // Called with mLock hold
+    void enqueueV4l2Frame(const sp<V4L2Frame>&);
+
+    // Check if input Stream is one of supported stream setting on this device
+    bool isSupported(const Stream&);
+
+    // Validate and import request's output buffers and acquire fence
+    Status importRequest(
+            const CaptureRequest& request,
+            hidl_vec<buffer_handle_t*>& allBufPtrs,
+            hidl_vec<int>& allFences);
+    static void cleanupInflightFences(
+            hidl_vec<int>& allFences, size_t numFences);
+    void cleanupBuffersLocked(int id);
+    void updateBufferCaches(const hidl_vec<BufferCache>& cachesToRemove);
+
+    Status processOneCaptureRequest(const CaptureRequest& request);
+
+    Status processCaptureResult(HalRequest&);
+    Status processCaptureRequestError(HalRequest&);
+    void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs);
+    void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec);
+    void invokeProcessCaptureResultCallback(
+            hidl_vec<CaptureResult> &results, bool tryWriteFmq);
+    static void freeReleaseFences(hidl_vec<CaptureResult>&);
+
+    class OutputThread : public android::Thread {
+    public:
+        OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
+        ~OutputThread();
+
+        Status allocateIntermediateBuffers(
+                const Size& v4lSize, const hidl_vec<Stream>& streams);
+        Status submitRequest(const HalRequest&);
+        void flush();
+        virtual bool threadLoop() override;
+
+    private:
+        static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
+                static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
+                static_cast<uint32_t>('X') << 24;
+        // returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
+        static uint32_t getFourCcFromLayout(const YCbCrLayout&);
+        static int getCropRect(
+                CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out);
+
+        static const int kReqWaitTimeoutSec = 3;
+
+        void waitForNextRequest(HalRequest* out);
+        int cropAndScaleLocked(
+                sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf,
+                YCbCrLayout* out);
+
+        int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
+                Size sz, uint32_t format);
+
+        mutable std::mutex mLock;
+        std::condition_variable mRequestCond;
+        wp<ExternalCameraDeviceSession> mParent;
+        CroppingType mCroppingType;
+        std::list<HalRequest> mRequestList;
+        // V4L2 frameIn
+        // (MJPG decode)-> mYu12Frame
+        // (Scale)-> mScaledYu12Frames
+        // (Format convert) -> output gralloc frames
+        sp<AllocatedFrame> mYu12Frame;
+        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
+        std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
+        YCbCrLayout mYu12FrameLayout;
+    };
+
+    // Protect (most of) HIDL interface methods from synchronized-entering
+    mutable Mutex mInterfaceLock;
+
+    mutable Mutex mLock; // Protect all private members except otherwise noted
+    const sp<ICameraDeviceCallback> mCallback;
+    const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+    unique_fd mV4l2Fd;
+    // device is closed either
+    //    - closed by user
+    //    - init failed
+    //    - camera disconnected
+    bool mClosed = false;
+    bool mInitFail = false;
+    bool mFirstRequest = false;
+    common::V1_0::helper::CameraMetadata mLatestReqSetting;
+
+    bool mV4l2Streaming = false;
+    SupportedV4L2Format mV4l2StreamingFmt;
+    std::vector<unique_fd> mV4l2Buffers;
+
+    static const int kBufferWaitTimeoutSec = 3; // TODO: handle long exposure (or not allowing)
+    std::mutex mV4l2BufferLock; // protect the buffer count and condition below
+    std::condition_variable mV4L2BufferReturned;
+    size_t mNumDequeuedV4l2Buffers = 0;
+
+    const std::vector<SupportedV4L2Format> mSupportedFormats;
+    const CroppingType mCroppingType;
+    sp<OutputThread> mOutputThread;
+
+    // Stream ID -> Camera3Stream cache
+    std::unordered_map<int, Stream> mStreamMap;
+    std::unordered_set<uint32_t>  mInflightFrames;
+
+    // buffers currently circulating between HAL and camera service
+    // key: bufferId sent via HIDL interface
+    // value: imported buffer_handle_t
+    // Buffer will be imported during processCaptureRequest and will be freed
+    // when the its stream is deleted or camera device session is closed
+    typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
+    // Stream ID -> circulating buffers map
+    std::map<int, CirculatingBuffers> mCirculatingBuffers;
+
+    static HandleImporter sHandleImporter;
+
+    /* Beginning of members not changed after initialize() */
+    using RequestMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+    std::unique_ptr<RequestMetadataQueue> mRequestMetadataQueue;
+    using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+    std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+    // Protect against invokeProcessCaptureResultCallback()
+    Mutex mProcessCaptureResultLock;
+
+    std::unordered_map<int, CameraMetadata> mDefaultRequests;
+    /* End of members not changed after initialize() */
+
+private:
+
+    struct TrampolineSessionInterface_3_4 : public ICameraDeviceSession {
+        TrampolineSessionInterface_3_4(sp<ExternalCameraDeviceSession> parent) :
+                mParent(parent) {}
+
+        virtual Return<void> constructDefaultRequestSettings(
+                V3_2::RequestTemplate type,
+                V3_3::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override {
+            return mParent->constructDefaultRequestSettings(type, _hidl_cb);
+        }
+
+        virtual Return<void> configureStreams(
+                const V3_2::StreamConfiguration& requestedConfiguration,
+                V3_3::ICameraDeviceSession::configureStreams_cb _hidl_cb) override {
+            return mParent->configureStreams(requestedConfiguration, _hidl_cb);
+        }
+
+        virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests,
+                const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+                V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override {
+            return mParent->processCaptureRequest(requests, cachesToRemove, _hidl_cb);
+        }
+
+        virtual Return<void> getCaptureRequestMetadataQueue(
+                V3_3::ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) override  {
+            return mParent->getCaptureRequestMetadataQueue(_hidl_cb);
+        }
+
+        virtual Return<void> getCaptureResultMetadataQueue(
+                V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override  {
+            return mParent->getCaptureResultMetadataQueue(_hidl_cb);
+        }
+
+        virtual Return<Status> flush() override {
+            return mParent->flush();
+        }
+
+        virtual Return<void> close() override {
+            return mParent->close();
+        }
+
+        virtual Return<void> configureStreams_3_3(
+                const V3_2::StreamConfiguration& requestedConfiguration,
+                configureStreams_3_3_cb _hidl_cb) override {
+            return mParent->configureStreams_3_3(requestedConfiguration, _hidl_cb);
+        }
+
+        virtual Return<void> configureStreams_3_4(
+                const V3_4::StreamConfiguration& requestedConfiguration,
+                configureStreams_3_4_cb _hidl_cb) override {
+            return mParent->configureStreams_3_4(requestedConfiguration, _hidl_cb);
+        }
+
+        virtual Return<void> processCaptureRequest_3_4(const hidl_vec<V3_4::CaptureRequest>& requests,
+                const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+                ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override {
+            return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb);
+        }
+
+    private:
+        sp<ExternalCameraDeviceSession> mParent;
+    };
+};
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
new file mode 100644
index 0000000..606375e
--- /dev/null
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE_H
+
+#include "utils/Mutex.h"
+#include "CameraMetadata.h"
+
+#include <android/hardware/camera/device/3.2/ICameraDevice.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+#include "ExternalCameraDeviceSession.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+using namespace ::android::hardware::camera::device;
+using ::android::hardware::camera::device::V3_2::ICameraDevice;
+using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
+using ::android::hardware::camera::common::V1_0::CameraResourceCost;
+using ::android::hardware::camera::common::V1_0::TorchMode;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+/*
+ * The camera device HAL implementation is opened lazily (via the open call)
+ */
+struct ExternalCameraDevice : public ICameraDevice {
+
+    // Called by external camera provider HAL.
+    // Provider HAL must ensure the uniqueness of CameraDevice object per cameraId, or there could
+    // be multiple CameraDevice trying to access the same physical camera.  Also, provider will have
+    // to keep track of all CameraDevice objects in order to notify CameraDevice when the underlying
+    // camera is detached.
+    ExternalCameraDevice(const std::string& cameraId);
+    ~ExternalCameraDevice();
+
+    // Caller must use this method to check if CameraDevice ctor failed
+    bool isInitFailed();
+
+    /* Methods from ::android::hardware::camera::device::V3_2::ICameraDevice follow. */
+    // The following method can be called without opening the actual camera device
+    Return<void> getResourceCost(getResourceCost_cb _hidl_cb) override;
+
+    Return<void> getCameraCharacteristics(getCameraCharacteristics_cb _hidl_cb) override;
+
+    Return<Status> setTorchMode(TorchMode) override;
+
+    // Open the device HAL and also return a default capture session
+    Return<void> open(const sp<ICameraDeviceCallback>&, open_cb) override;
+
+    // Forward the dump call to the opened session, or do nothing
+    Return<void> dumpState(const ::android::hardware::hidl_handle&) override;
+    /* End of Methods from ::android::hardware::camera::device::V3_2::ICameraDevice */
+
+protected:
+    void getFrameRateList(int fd, SupportedV4L2Format* format);
+    // Init supported w/h/format/fps in mSupportedFormats. Caller still owns fd
+    void initSupportedFormatsLocked(int fd);
+
+    status_t initCameraCharacteristics();
+    // Init non-device dependent keys
+    status_t initDefaultCharsKeys(::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+    // Init camera control chars keys. Caller still owns fd
+    status_t initCameraControlsCharsKeys(int fd,
+            ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+    // Init camera output configuration related keys.  Caller still owns fd
+    status_t initOutputCharsKeys(int fd,
+            ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+
+    Mutex mLock;
+    bool mInitFailed = false;
+    std::string mCameraId;
+    std::vector<SupportedV4L2Format> mSupportedFormats;
+
+    wp<ExternalCameraDeviceSession> mSession = nullptr;
+
+    ::android::hardware::camera::common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+};
+
+}  // namespace implementation
+}  // namespace V3_4
+}  // namespace device
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE_H
diff --git a/camera/device/3.4/types.hal b/camera/device/3.4/types.hal
index c822717..77e855f 100644
--- a/camera/device/3.4/types.hal
+++ b/camera/device/3.4/types.hal
@@ -16,19 +16,69 @@
 
 package android.hardware.camera.device@3.4;
 
-import @3.2::StreamConfiguration;
 import @3.2::types;
+import @3.2::StreamConfigurationMode;
+import @3.2::Stream;
+import @3.3::HalStream;
+import @3.2::CameraMetadata;
+import @3.2::CaptureRequest;
+
+/**
+ * Stream:
+ *
+ * A descriptor for a single camera input or output stream. A stream is defined
+ * by the framework by its buffer resolution and format, and additionally by the
+ * HAL with the gralloc usage flags and the maximum in-flight buffer count.
+ *
+ * This version extends the @3.2 Stream with the physicalCameraId field.
+ */
+struct Stream {
+    /**
+     * The definition of Stream from the prior version
+     */
+    @3.2::Stream v3_2;
+
+    /**
+     * The physical camera id this stream belongs to.
+     *
+     * If the camera device is not a logical multi camera, or if the camera is a logical
+     * multi camera but the stream is not a physical output stream, this field will point to a
+     * 0-length string.
+     *
+     * A logical multi camera is a camera device backed by multiple physical cameras that
+     * are also exposed to the application. And for a logical multi camera, a physical output
+     * stream is an output stream specifically requested on an underlying physical camera.
+     *
+     * A logical camera is a camera device backed by multiple physical camera
+     * devices. And a physical stream is a stream specifically requested on a
+     * underlying physical camera device.
+     *
+     * For an input stream, this field is guaranteed to be a 0-length string.
+     *
+     * When not empty, this field is the <id> field of one of the full-qualified device
+     * instance names returned by getCameraIdList().
+     */
+    string physicalCameraId;
+};
 
 /**
  * StreamConfiguration:
  *
- * Identical to @3.2::StreamConfiguration, except that it contains session parameters.
+ * Identical to @3.2::StreamConfiguration, except that it contains session
+ * parameters, and the streams vector contains @3.4::Stream.
  */
 struct StreamConfiguration {
     /**
-     * The definition of StreamConfiguration from the prior version.
+     * An array of camera stream pointers, defining the input/output
+     * configuration for the camera HAL device.
      */
-    @3.2::StreamConfiguration v3_2;
+    vec<Stream> streams;
+
+    /**
+     * The definition of operation mode from prior version.
+     *
+     */
+    StreamConfigurationMode operationMode;
 
     /**
      * Session wide camera parameters.
@@ -44,3 +94,105 @@
      */
     CameraMetadata sessionParams;
 };
+
+/**
+ * HalStream:
+ *
+ * The camera HAL's response to each requested stream configuration.
+ *
+ * This version extends the @3.3 HalStream with the physicalCameraId
+ * field
+ */
+struct HalStream {
+    /**
+     * The definition of HalStream from the prior version.
+     */
+    @3.3::HalStream v3_3;
+
+    /**
+     * The physical camera id the current Hal stream belongs to.
+     *
+     * If current camera device isn't a logical camera, or the Hal stream isn't
+     * from a physical camera of the logical camera, this must be an empty
+     * string.
+     *
+     * A logical camera is a camera device backed by multiple physical camera
+     * devices.
+     *
+     * When not empty, this field is the <id> field of one of the full-qualified device
+     * instance names returned by getCameraIdList().
+     */
+    string physicalCameraId;
+};
+
+/**
+ * HalStreamConfiguration:
+ *
+ * Identical to @3.3::HalStreamConfiguration, except that it contains @3.4::HalStream entries.
+ *
+ */
+struct HalStreamConfiguration {
+    vec<HalStream> streams;
+};
+
+/**
+ * PhysicalCameraSetting:
+ *
+ * Individual camera settings for logical camera backed by multiple physical devices.
+ * Clients are allowed to pass separate settings for each physical device.
+ */
+struct PhysicalCameraSetting {
+    /**
+     * If non-zero, read settings from request queue instead
+     * (see ICameraDeviceSession.getCaptureRequestMetadataQueue).
+     * If zero, read settings from .settings field.
+     */
+    uint64_t fmqSettingsSize;
+
+    /**
+     * Contains the physical device camera id. Any settings passed by client here
+     * should be applied for this physical device. In case the physical id is invalid
+     * Hal should fail the process request and return Status::ILLEGAL_ARGUMENT.
+     */
+    string physicalCameraId;
+
+    /**
+     * If fmqSettingsSize is zero, the settings buffer contains the capture and
+     * processing parameters for the physical device with id 'phyCamId'.
+     * In case the individual settings are empty or missing, Hal should fail the
+     * request and return Status::ILLEGAL_ARGUMENT.
+     */
+    CameraMetadata settings;
+};
+
+/**
+ * CaptureRequest:
+ *
+ * A single request for image capture/buffer reprocessing, sent to the Camera
+ * HAL device by the framework in processCaptureRequest().
+ *
+ * The request contains the settings to be used for this capture, and the set of
+ * output buffers to write the resulting image data in. It may optionally
+ * contain an input buffer, in which case the request is for reprocessing that
+ * input buffer instead of capturing a new image with the camera sensor. The
+ * capture is identified by the frameNumber.
+ *
+ * In response, the camera HAL device must send a CaptureResult
+ * structure asynchronously to the framework, using the processCaptureResult()
+ * callback.
+ *
+ * Identical to @3.2::CaptureRequest, except that it contains @3.4::physCamSettings vector.
+ *
+ */
+struct CaptureRequest {
+    /**
+     * The definition of CaptureRequest from prior version.
+     */
+    @3.2::CaptureRequest v3_2;
+
+    /**
+     * A vector containing individual camera settings for logical camera backed by multiple physical
+     * devices. In case the vector is empty, Hal should use the settings field in 'v3_2'.
+     */
+    vec<PhysicalCameraSetting> physicalCameraSettings;
+};
diff --git a/camera/metadata/3.3/Android.bp b/camera/metadata/3.3/Android.bp
index 3f1dabc..166c2ac 100644
--- a/camera/metadata/3.3/Android.bp
+++ b/camera/metadata/3.3/Android.bp
@@ -13,10 +13,16 @@
         "android.hardware.camera.metadata@3.2",
     ],
     types: [
+        "CameraMetadataEnumAndroidControlAeMode",
         "CameraMetadataEnumAndroidControlAfSceneChange",
         "CameraMetadataEnumAndroidControlCaptureIntent",
+        "CameraMetadataEnumAndroidInfoSupportedHardwareLevel",
         "CameraMetadataEnumAndroidLensPoseReference",
+        "CameraMetadataEnumAndroidLogicalMultiCameraSensorSyncType",
         "CameraMetadataEnumAndroidRequestAvailableCapabilities",
+        "CameraMetadataEnumAndroidStatisticsOisDataMode",
+        "CameraMetadataSection",
+        "CameraMetadataSectionStart",
         "CameraMetadataTag",
     ],
     gen_java: true,
diff --git a/camera/metadata/3.3/types.hal b/camera/metadata/3.3/types.hal
index 5c01e35..d2a5886 100644
--- a/camera/metadata/3.3/types.hal
+++ b/camera/metadata/3.3/types.hal
@@ -25,7 +25,30 @@
 /* Include definitions from all prior minor HAL metadata revisions */
 import android.hardware.camera.metadata@3.2;
 
-// No new metadata sections added in this revision
+/**
+ * Top level hierarchy definitions for camera metadata. *_INFO sections are for
+ * the static metadata that can be retrived without opening the camera device.
+ */
+enum CameraMetadataSection : @3.2::CameraMetadataSection {
+    ANDROID_LOGICAL_MULTI_CAMERA =
+        android.hardware.camera.metadata@3.2::CameraMetadataSection:ANDROID_SECTION_COUNT,
+
+    ANDROID_SECTION_COUNT_3_3,
+
+    VENDOR_SECTION_3_3 = 0x8000,
+
+};
+
+/**
+ * Hierarchy positions in enum space. All vendor extension sections must be
+ * defined with tag >= VENDOR_SECTION_START
+ */
+enum CameraMetadataSectionStart : android.hardware.camera.metadata@3.2::CameraMetadataSectionStart {
+    ANDROID_LOGICAL_MULTI_CAMERA_START = CameraMetadataSection:ANDROID_LOGICAL_MULTI_CAMERA << 16,
+
+    VENDOR_SECTION_START_3_3 = CameraMetadataSection:VENDOR_SECTION_3_3 << 16,
+
+};
 
 /**
  * Main enumeration for defining camera metadata tags added in this revision
@@ -60,8 +83,53 @@
      */
     ANDROID_REQUEST_AVAILABLE_SESSION_KEYS = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_REQUEST_END,
 
+    /** android.request.availablePhysicalCameraRequestKeys [static, int32[], hidden]
+     *
+     * <p>A subset of the available request keys that can be overriden for
+     * physical devices backing a logical multi-camera.</p>
+     */
+    ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS,
+
     ANDROID_REQUEST_END_3_3,
 
+    /** android.statistics.oisDataMode [dynamic, enum, public]
+     *
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     */
+    ANDROID_STATISTICS_OIS_DATA_MODE = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_STATISTICS_END,
+
+    /** android.statistics.oisTimestamps [dynamic, int64[], public]
+     *
+     * <p>An array of timestamps of OIS samples, in nanoseconds.</p>
+     */
+    ANDROID_STATISTICS_OIS_TIMESTAMPS,
+
+    /** android.statistics.oisXShifts [dynamic, float[], public]
+     *
+     * <p>An array of shifts of OIS samples, in x direction.</p>
+     */
+    ANDROID_STATISTICS_OIS_X_SHIFTS,
+
+    /** android.statistics.oisYShifts [dynamic, float[], public]
+     *
+     * <p>An array of shifts of OIS samples, in y direction.</p>
+     */
+    ANDROID_STATISTICS_OIS_Y_SHIFTS,
+
+    ANDROID_STATISTICS_END_3_3,
+
+    /** android.statistics.info.availableOisDataModes [static, byte[], public]
+     *
+     * <p>List of OIS data output modes for ANDROID_STATISTICS_OIS_DATA_MODE that
+     * are supported by this camera device.</p>
+     *
+     * @see ANDROID_STATISTICS_OIS_DATA_MODE
+     */
+    ANDROID_STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_STATISTICS_INFO_END,
+
+    ANDROID_STATISTICS_INFO_END_3_3,
+
     /** android.info.version [static, byte, public]
      *
      * <p>A short string for manufacturer version information about the camera device, such as
@@ -71,6 +139,20 @@
 
     ANDROID_INFO_END_3_3,
 
+    /** android.logicalMultiCamera.physicalIds [static, byte[], hidden]
+     *
+     * <p>String containing the ids of the underlying physical cameras.</p>
+     */
+    ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS = CameraMetadataSectionStart:ANDROID_LOGICAL_MULTI_CAMERA_START,
+
+    /** android.logicalMultiCamera.sensorSyncType [static, enum, public]
+     *
+     * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
+     */
+    ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
+
+    ANDROID_LOGICAL_MULTI_CAMERA_END_3_3,
+
 };
 
 /*
@@ -115,4 +197,29 @@
 enum CameraMetadataEnumAndroidRequestAvailableCapabilities :
         @3.2::CameraMetadataEnumAndroidRequestAvailableCapabilities {
     ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING,
+    ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA,
+};
+
+/** android.logicalMultiCamera.sensorSyncType enumeration values
+ * @see ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+ */
+enum CameraMetadataEnumAndroidLogicalMultiCameraSensorSyncType : uint32_t {
+    ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
+    ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED,
+};
+
+/** android.info.supportedHardwareLevel enumeration values added since v3.2
+ * @see ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+enum CameraMetadataEnumAndroidInfoSupportedHardwareLevel :
+        @3.2::CameraMetadataEnumAndroidInfoSupportedHardwareLevel {
+    ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+};
+
+/** android.statistics.oisDataMode enumeration values
+ * @see ANDROID_STATISTICS_OIS_DATA_MODE
+ */
+enum CameraMetadataEnumAndroidStatisticsOisDataMode : uint32_t {
+    ANDROID_STATISTICS_OIS_DATA_MODE_OFF,
+    ANDROID_STATISTICS_OIS_DATA_MODE_ON,
 };
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 99c3e92..1f46b89 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -3,7 +3,8 @@
     defaults: ["hidl_defaults"],
     proprietary: true,
     relative_install_path: "hw",
-    srcs: ["CameraProvider.cpp"],
+    srcs: ["CameraProvider.cpp",
+           "ExternalCameraProvider.cpp"],
     shared_libs: [
         "libhidlbase",
         "libhidltransport",
@@ -17,6 +18,7 @@
         "camera.device@3.2-impl",
         "camera.device@3.3-impl",
         "camera.device@3.4-impl",
+        "camera.device@3.4-external-impl",
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.common@1.0",
         "android.hardware.graphics.mapper@2.0",
@@ -28,6 +30,7 @@
     ],
     header_libs: [
         "camera.device@3.4-impl_headers",
+        "camera.device@3.4-external-impl_headers"
     ],
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
@@ -56,3 +59,25 @@
         "android.hardware.camera.common@1.0",
     ],
 }
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.4-external-service",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: ["external-service.cpp"],
+    compile_multilib: "32",
+    init_rc: ["android.hardware.camera.provider@2.4-external-service.rc"],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.common@1.0",
+    ],
+}
diff --git a/camera/provider/2.4/default/CameraProvider.cpp b/camera/provider/2.4/default/CameraProvider.cpp
index e9588a7..8e37b26 100644
--- a/camera/provider/2.4/default/CameraProvider.cpp
+++ b/camera/provider/2.4/default/CameraProvider.cpp
@@ -19,6 +19,7 @@
 #include <android/log.h>
 
 #include "CameraProvider.h"
+#include "ExternalCameraProvider.h"
 #include "CameraDevice_1_0.h"
 #include "CameraDevice_3_3.h"
 #include "CameraDevice_3_4.h"
@@ -36,6 +37,7 @@
 
 namespace {
 const char *kLegacyProviderName = "legacy/0";
+const char *kExternalProviderName = "external/0";
 // "device@<version>/legacy/<id>"
 const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/legacy/(.+)");
 const char *kHAL3_2 = "3.2";
@@ -106,6 +108,30 @@
     }
 }
 
+void CameraProvider::removeDeviceNames(int camera_id)
+{
+    std::string cameraIdStr = std::to_string(camera_id);
+
+    mCameraIds.remove(cameraIdStr);
+
+    int deviceVersion = mModule->getDeviceVersion(camera_id);
+    auto deviceNamePair = std::make_pair(cameraIdStr,
+                                         getHidlDeviceName(cameraIdStr, deviceVersion));
+    mCameraDeviceNames.remove(deviceNamePair);
+    mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, CameraDeviceStatus::NOT_PRESENT);
+    if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
+        mModule->isOpenLegacyDefined() && mOpenLegacySupported[cameraIdStr]) {
+
+        deviceNamePair = std::make_pair(cameraIdStr,
+                            getHidlDeviceName(cameraIdStr, CAMERA_DEVICE_API_VERSION_1_0));
+        mCameraDeviceNames.remove(deviceNamePair);
+        mCallbacks->cameraDeviceStatusChange(deviceNamePair.second,
+                                             CameraDeviceStatus::NOT_PRESENT);
+    }
+
+    mModule->removeCamera(camera_id);
+}
+
 /**
  * static callback forwarding methods from HAL to instance
  */
@@ -137,8 +163,17 @@
             }
         }
 
-        if (!found) {
-            cp->addDeviceNames(camera_id, status, true);
+        switch (status) {
+        case CameraDeviceStatus::PRESENT:
+        case CameraDeviceStatus::ENUMERATING:
+            if (!found) {
+                cp->addDeviceNames(camera_id, status, true);
+            }
+            break;
+        case CameraDeviceStatus::NOT_PRESENT:
+            if (found) {
+                cp->removeDeviceNames(camera_id);
+            }
         }
     }
 }
@@ -571,20 +606,24 @@
 }
 
 ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
-    if (strcmp(name, kLegacyProviderName) != 0) {
-        return nullptr;
+    if (strcmp(name, kLegacyProviderName) == 0) {
+        CameraProvider* provider = new CameraProvider();
+        if (provider == nullptr) {
+            ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
+            return nullptr;
+        }
+        if (provider->isInitFailed()) {
+            ALOGE("%s: camera provider init failed!", __FUNCTION__);
+            delete provider;
+            return nullptr;
+        }
+        return provider;
+    } else if (strcmp(name, kExternalProviderName) == 0) {
+        ExternalCameraProvider* provider = new ExternalCameraProvider();
+        return provider;
     }
-    CameraProvider* provider = new CameraProvider();
-    if (provider == nullptr) {
-        ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
-        return nullptr;
-    }
-    if (provider->isInitFailed()) {
-        ALOGE("%s: camera provider init failed!", __FUNCTION__);
-        delete provider;
-        return nullptr;
-    }
-    return provider;
+    ALOGE("%s: unknown instance name: %s", __FUNCTION__, name);
+    return nullptr;
 }
 
 } // namespace implementation
diff --git a/camera/provider/2.4/default/CameraProvider.h b/camera/provider/2.4/default/CameraProvider.h
index 2cf251e..0f0959f 100644
--- a/camera/provider/2.4/default/CameraProvider.h
+++ b/camera/provider/2.4/default/CameraProvider.h
@@ -115,6 +115,7 @@
 
     void addDeviceNames(int camera_id, CameraDeviceStatus status = CameraDeviceStatus::PRESENT,
                         bool cam_new = false);
+    void removeDeviceNames(int camera_id);
 };
 
 extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name);
diff --git a/camera/provider/2.4/default/ExternalCameraProvider.cpp b/camera/provider/2.4/default/ExternalCameraProvider.cpp
new file mode 100644
index 0000000..bb5c336
--- /dev/null
+++ b/camera/provider/2.4/default/ExternalCameraProvider.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CamPvdr@2.4-external"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <regex>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <linux/videodev2.h>
+#include "ExternalCameraProvider.h"
+#include "ExternalCameraDevice_3_4.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_4 {
+namespace implementation {
+
+namespace {
+// "device@<version>/external/<id>"
+const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
+const int kMaxDevicePathLen = 256;
+const char* kDevicePath = "/dev/";
+
+bool matchDeviceName(const hidl_string& deviceName, std::string* deviceVersion,
+                     std::string* cameraId) {
+    std::string deviceNameStd(deviceName.c_str());
+    std::smatch sm;
+    if (std::regex_match(deviceNameStd, sm, kDeviceNameRE)) {
+        if (deviceVersion != nullptr) {
+            *deviceVersion = sm[1];
+        }
+        if (cameraId != nullptr) {
+            *cameraId = sm[2];
+        }
+        return true;
+    }
+    return false;
+}
+
+} // anonymous namespace
+
+ExternalCameraProvider::ExternalCameraProvider() : mHotPlugThread(this) {
+    mHotPlugThread.run("ExtCamHotPlug", PRIORITY_BACKGROUND);
+}
+
+ExternalCameraProvider::~ExternalCameraProvider() {
+    mHotPlugThread.requestExit();
+}
+
+
+Return<Status> ExternalCameraProvider::setCallback(
+        const sp<ICameraProviderCallback>& callback) {
+    Mutex::Autolock _l(mLock);
+    mCallbacks = callback;
+    return Status::OK;
+}
+
+Return<void> ExternalCameraProvider::getVendorTags(getVendorTags_cb _hidl_cb) {
+    // No vendor tag support for USB camera
+    hidl_vec<VendorTagSection> zeroSections;
+    _hidl_cb(Status::OK, zeroSections);
+    return Void();
+}
+
+Return<void> ExternalCameraProvider::getCameraIdList(getCameraIdList_cb _hidl_cb) {
+    std::vector<hidl_string> deviceNameList;
+    for (auto const& kvPair : mCameraStatusMap) {
+        if (kvPair.second == CameraDeviceStatus::PRESENT) {
+            deviceNameList.push_back(kvPair.first);
+        }
+    }
+    hidl_vec<hidl_string> hidlDeviceNameList(deviceNameList);
+    ALOGV("ExtCam: number of cameras is %zu", deviceNameList.size());
+    _hidl_cb(Status::OK, hidlDeviceNameList);
+    return Void();
+}
+
+Return<void> ExternalCameraProvider::isSetTorchModeSupported(
+        isSetTorchModeSupported_cb _hidl_cb) {
+    // No torch mode support for USB camera
+    _hidl_cb (Status::OK, false);
+    return Void();
+}
+
+Return<void> ExternalCameraProvider::getCameraDeviceInterface_V1_x(
+        const hidl_string&,
+        getCameraDeviceInterface_V1_x_cb _hidl_cb) {
+    // External Camera HAL does not support HAL1
+    _hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> ExternalCameraProvider::getCameraDeviceInterface_V3_x(
+        const hidl_string& cameraDeviceName,
+        getCameraDeviceInterface_V3_x_cb _hidl_cb) {
+
+    std::string cameraId, deviceVersion;
+    bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
+    if (!match) {
+        _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+        return Void();
+    }
+
+    if (mCameraStatusMap.count(cameraDeviceName) == 0 ||
+            mCameraStatusMap[cameraDeviceName] != CameraDeviceStatus::PRESENT) {
+        _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+        return Void();
+    }
+
+    ALOGV("Constructing v3.4 external camera device");
+    sp<device::V3_2::ICameraDevice> device;
+    sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl =
+            new device::V3_4::implementation::ExternalCameraDevice(
+                    cameraId);
+    if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
+        ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str());
+        device = nullptr;
+        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+        return Void();
+    }
+    device = deviceImpl;
+
+    _hidl_cb (Status::OK, device);
+
+    return Void();
+}
+
+void ExternalCameraProvider::addExternalCamera(const char* devName) {
+    ALOGE("ExtCam: adding %s to External Camera HAL!", devName);
+    Mutex::Autolock _l(mLock);
+    std::string deviceName = std::string("device@3.4/external/") + devName;
+    mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT;
+    if (mCallbacks != nullptr) {
+        mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::PRESENT);
+    }
+}
+
+void ExternalCameraProvider::deviceAdded(const char* devName) {
+    int fd = -1;
+    if ((fd = ::open(devName, O_RDWR)) < 0) {
+        ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
+        return;
+    }
+
+    do {
+        struct v4l2_capability capability;
+        int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
+        if (ret < 0) {
+            ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
+            break;
+        }
+
+        if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
+            ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName);
+            break;
+        }
+
+        addExternalCamera(devName);
+    } while (0);
+
+    close(fd);
+    return;
+}
+
+void ExternalCameraProvider::deviceRemoved(const char* devName) {
+    Mutex::Autolock _l(mLock);
+    std::string deviceName = std::string("device@3.4/external/") + devName;
+    if (mCameraStatusMap.find(deviceName) != mCameraStatusMap.end()) {
+        mCameraStatusMap.erase(deviceName);
+        if (mCallbacks != nullptr) {
+            mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT);
+        }
+    } else {
+        ALOGE("%s: cannot find camera device %s", __FUNCTION__, devName);
+    }
+}
+
+ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent) :
+        Thread(/*canCallJava*/false), mParent(parent) {}
+
+ExternalCameraProvider::HotplugThread::~HotplugThread() {}
+
+bool ExternalCameraProvider::HotplugThread::threadLoop() {
+    // Find existing /dev/video* devices
+    DIR* devdir = opendir(kDevicePath);
+    if(devdir == 0) {
+        ALOGE("%s: cannot open %s! Exiting threadloop", __FUNCTION__, kDevicePath);
+        return false;
+    }
+
+    struct dirent* de;
+    // This list is device dependent. TODO: b/72261897 allow setting it from setprop/device boot
+    std::string internalDevices = "0,1";
+    while ((de = readdir(devdir)) != 0) {
+        // Find external v4l devices that's existing before we start watching and add them
+        if (!strncmp("video", de->d_name, 5)) {
+            // TODO: This might reject some valid devices. Ex: internal is 33 and a device named 3
+            //       is added.
+            if (internalDevices.find(de->d_name + 5) == std::string::npos) {
+                ALOGV("Non-internal v4l device %s found", de->d_name);
+                char v4l2DevicePath[kMaxDevicePathLen];
+                snprintf(v4l2DevicePath, kMaxDevicePathLen,
+                        "%s%s", kDevicePath, de->d_name);
+                mParent->deviceAdded(v4l2DevicePath);
+            }
+        }
+    }
+    closedir(devdir);
+
+    // Watch new video devices
+    mINotifyFD = inotify_init();
+    if (mINotifyFD < 0) {
+        ALOGE("%s: inotify init failed! Exiting threadloop", __FUNCTION__);
+        return true;
+    }
+
+    mWd = inotify_add_watch(mINotifyFD, kDevicePath, IN_CREATE | IN_DELETE);
+    if (mWd < 0) {
+        ALOGE("%s: inotify add watch failed! Exiting threadloop", __FUNCTION__);
+        return true;
+    }
+
+    ALOGI("%s start monitoring new V4L2 devices", __FUNCTION__);
+
+    bool done = false;
+    char eventBuf[512];
+    while (!done) {
+        int offset = 0;
+        int ret = read(mINotifyFD, eventBuf, sizeof(eventBuf));
+        if (ret >= (int)sizeof(struct inotify_event)) {
+            while (offset < ret) {
+                struct inotify_event* event = (struct inotify_event*)&eventBuf[offset];
+                if (event->wd == mWd) {
+                    if (!strncmp("video", event->name, 5)) {
+                        char v4l2DevicePath[kMaxDevicePathLen];
+                        snprintf(v4l2DevicePath, kMaxDevicePathLen,
+                                "%s%s", kDevicePath, event->name);
+                        if (event->mask & IN_CREATE) {
+                            mParent->deviceAdded(v4l2DevicePath);
+                        }
+                        if (event->mask & IN_DELETE) {
+                            mParent->deviceRemoved(v4l2DevicePath);
+                        }
+                    }
+                }
+                offset += sizeof(struct inotify_event) + event->len;
+            }
+        }
+    }
+
+    return true;
+}
+
+}  // namespace implementation
+}  // namespace V2_4
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/provider/2.4/default/ExternalCameraProvider.h b/camera/provider/2.4/default/ExternalCameraProvider.h
new file mode 100644
index 0000000..c7ed99e
--- /dev/null
+++ b/camera/provider/2.4/default/ExternalCameraProvider.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_EXTCAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_EXTCAMERAPROVIDER_H
+
+#include <unordered_map>
+#include "utils/Mutex.h"
+#include "utils/Thread.h"
+#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_4 {
+namespace implementation {
+
+using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::VendorTagSection;
+using ::android::hardware::camera::provider::V2_4::ICameraProvider;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+using ::android::Mutex;
+
+struct ExternalCameraProvider : public ICameraProvider {
+    ExternalCameraProvider();
+    ~ExternalCameraProvider();
+
+    // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
+    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override;
+
+    Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override;
+
+    Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override;
+
+    Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override;
+
+    Return<void> getCameraDeviceInterface_V1_x(
+            const hidl_string&,
+            getCameraDeviceInterface_V1_x_cb) override;
+    Return<void> getCameraDeviceInterface_V3_x(
+            const hidl_string&,
+            getCameraDeviceInterface_V3_x_cb) override;
+
+private:
+
+    void addExternalCamera(const char* devName);
+
+    void deviceAdded(const char* devName);
+
+    void deviceRemoved(const char* devName);
+
+    class HotplugThread : public android::Thread {
+    public:
+        HotplugThread(ExternalCameraProvider* parent);
+        ~HotplugThread();
+
+        virtual bool threadLoop() override;
+
+    private:
+        ExternalCameraProvider* mParent = nullptr;
+
+        int mINotifyFD = -1;
+        int mWd = -1;
+    } mHotPlugThread;
+
+    Mutex mLock;
+    sp<ICameraProviderCallback> mCallbacks = nullptr;
+    std::unordered_map<std::string, CameraDeviceStatus> mCameraStatusMap; // camera id -> status
+};
+
+
+
+}  // namespace implementation
+}  // namespace V2_4
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_EXTCAMERAPROVIDER_H
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc
new file mode 100644
index 0000000..acdb200
--- /dev/null
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc
@@ -0,0 +1,7 @@
+service vendor.camera-provider-2-4-ext /vendor/bin/hw/android.hardware.camera.provider@2.4-external-service
+    class hal
+    user cameraserver
+    group audio camera input drmrpc usb
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.4/default/external-service.cpp b/camera/provider/2.4/default/external-service.cpp
new file mode 100644
index 0000000..f91aa59
--- /dev/null
+++ b/camera/provider/2.4/default/external-service.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.camera.provider@2.4-external-service"
+
+#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <hidl/LegacySupport.h>
+
+#include <binder/ProcessState.h>
+
+using android::hardware::camera::provider::V2_4::ICameraProvider;
+using android::hardware::defaultPassthroughServiceImplementation;
+
+int main()
+{
+    ALOGI("External camera provider service is starting.");
+    // The camera HAL may communicate to other vendor components via
+    // /dev/vndbinder
+    android::ProcessState::initWithDriver("/dev/vndbinder");
+    return defaultPassthroughServiceImplementation<ICameraProvider>("external/0", /*maxThreads*/ 6);
+}
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index d44a54a..4652efd 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2016-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.
@@ -47,6 +47,7 @@
 #include <VtsHalHidlTargetTestBase.h>
 #include <VtsHalHidlTargetTestEnvBase.h>
 
+using namespace ::android::hardware::camera::device;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_handle;
@@ -78,7 +79,6 @@
 using ::android::hardware::camera::device::V3_2::ICameraDeviceSession;
 using ::android::hardware::camera::device::V3_2::NotifyMsg;
 using ::android::hardware::camera::device::V3_2::RequestTemplate;
-using ::android::hardware::camera::device::V3_2::Stream;
 using ::android::hardware::camera::device::V3_2::StreamType;
 using ::android::hardware::camera::device::V3_2::StreamRotation;
 using ::android::hardware::camera::device::V3_2::StreamConfiguration;
@@ -537,6 +537,8 @@
         Return<void> notify(const hidl_vec<NotifyMsg>& msgs) override;
 
      private:
+        bool processCaptureResultLocked(const CaptureResult& results);
+
         CameraHidlTest *mParent;               // Parent object
     };
 
@@ -620,11 +622,16 @@
     void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
             sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
             sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/);
+    void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
+            StreamConfigurationMode configMode,
+            ::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2,
+            ::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4);
+
     void configurePreviewStream(const std::string &name, int32_t deviceVersion,
             sp<ICameraProvider> provider,
             const AvailableStream *previewThreshold,
             sp<ICameraDeviceSession> *session /*out*/,
-            Stream *previewStream /*out*/,
+            V3_2::Stream *previewStream /*out*/,
             HalStreamConfiguration *halStreamConfig /*out*/,
             bool *supportsPartialResults /*out*/,
             uint32_t *partialResultCount /*out*/);
@@ -632,6 +639,9 @@
             std::vector<AvailableStream> &outputStreams,
             const AvailableStream *threshold = nullptr);
     static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
+    static Status isLogicalMultiCamera(camera_metadata_t *staticMeta);
+    static Status getPhysicalCameraIds(camera_metadata_t *staticMeta,
+            std::vector<std::string> *physicalIds/*out*/);
     static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
             AvailableStream &hfrStream);
     static Status isZSLModeAvailable(camera_metadata_t *staticMeta);
@@ -843,121 +853,7 @@
     bool notify = false;
     std::unique_lock<std::mutex> l(mParent->mLock);
     for (size_t i = 0 ; i < results.size(); i++) {
-        uint32_t frameNumber = results[i].frameNumber;
-
-        if ((results[i].result.size() == 0) &&
-                (results[i].outputBuffers.size() == 0) &&
-                (results[i].inputBuffer.buffer == nullptr) &&
-                (results[i].fmqResultSize == 0)) {
-            ALOGE("%s: No result data provided by HAL for frame %d result count: %d",
-                  __func__, frameNumber, (int) results[i].fmqResultSize);
-            ADD_FAILURE();
-            break;
-        }
-
-        ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber);
-        if (::android::NAME_NOT_FOUND == idx) {
-            ALOGE("%s: Unexpected frame number! received: %u",
-                  __func__, frameNumber);
-            ADD_FAILURE();
-            break;
-        }
-
-        bool isPartialResult = false;
-        bool hasInputBufferInRequest = false;
-        InFlightRequest *request = mParent->mInflightMap.editValueAt(idx);
-        ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata;
-        size_t resultSize = 0;
-        if (results[i].fmqResultSize > 0) {
-            resultMetadata.resize(results[i].fmqResultSize);
-            if (request->resultQueue == nullptr) {
-                ADD_FAILURE();
-                break;
-            }
-            if (!request->resultQueue->read(resultMetadata.data(),
-                    results[i].fmqResultSize)) {
-                ALOGE("%s: Frame %d: Cannot read camera metadata from fmq,"
-                        "size = %" PRIu64, __func__, frameNumber,
-                        results[i].fmqResultSize);
-                ADD_FAILURE();
-                break;
-            }
-            resultSize = resultMetadata.size();
-        } else if (results[i].result.size() > 0) {
-            resultMetadata.setToExternal(const_cast<uint8_t *>(
-                    results[i].result.data()), results[i].result.size());
-            resultSize = resultMetadata.size();
-        }
-
-        if (!request->usePartialResult && (resultSize > 0) &&
-                (results[i].partialResult != 1)) {
-            ALOGE("%s: Result is malformed for frame %d: partial_result %u "
-                    "must be 1  if partial result is not supported", __func__,
-                    frameNumber, results[i].partialResult);
-            ADD_FAILURE();
-            break;
-        }
-
-        if (results[i].partialResult != 0) {
-            request->partialResultCount = results[i].partialResult;
-        }
-
-        // Check if this result carries only partial metadata
-        if (request->usePartialResult && (resultSize > 0)) {
-            if ((results[i].partialResult > request->numPartialResults) ||
-                    (results[i].partialResult < 1)) {
-                ALOGE("%s: Result is malformed for frame %d: partial_result %u"
-                        " must be  in the range of [1, %d] when metadata is "
-                        "included in the result", __func__, frameNumber,
-                        results[i].partialResult, request->numPartialResults);
-                ADD_FAILURE();
-                break;
-            }
-            request->collectedResult.append(
-                    reinterpret_cast<const camera_metadata_t*>(
-                            resultMetadata.data()));
-
-            isPartialResult =
-                    (results[i].partialResult < request->numPartialResults);
-        }
-
-        hasInputBufferInRequest = request->hasInputBuffer;
-
-        // Did we get the (final) result metadata for this capture?
-        if ((resultSize > 0) && !isPartialResult) {
-            if (request->haveResultMetadata) {
-                ALOGE("%s: Called multiple times with metadata for frame %d",
-                      __func__, frameNumber);
-                ADD_FAILURE();
-                break;
-            }
-            request->haveResultMetadata = true;
-            request->collectedResult.sort();
-        }
-
-        uint32_t numBuffersReturned = results[i].outputBuffers.size();
-        if (results[i].inputBuffer.buffer != nullptr) {
-            if (hasInputBufferInRequest) {
-                numBuffersReturned += 1;
-            } else {
-                ALOGW("%s: Input buffer should be NULL if there is no input"
-                        " buffer sent in the request", __func__);
-            }
-        }
-        request->numBuffersLeft -= numBuffersReturned;
-        if (request->numBuffersLeft < 0) {
-            ALOGE("%s: Too many buffers returned for frame %d", __func__,
-                    frameNumber);
-            ADD_FAILURE();
-            break;
-        }
-
-        request->resultOutputBuffers.appendArray(results[i].outputBuffers.data(),
-                results[i].outputBuffers.size());
-        // If shutter event is received notify the pending threads.
-        if (request->shutterTimestamp != 0) {
-            notify = true;
-        }
+        notify = processCaptureResultLocked(results[i]);
     }
 
     l.unlock();
@@ -968,6 +864,126 @@
     return Void();
 }
 
+bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& results) {
+    bool notify = false;
+    uint32_t frameNumber = results.frameNumber;
+
+    if ((results.result.size() == 0) &&
+            (results.outputBuffers.size() == 0) &&
+            (results.inputBuffer.buffer == nullptr) &&
+            (results.fmqResultSize == 0)) {
+        ALOGE("%s: No result data provided by HAL for frame %d result count: %d",
+                __func__, frameNumber, (int) results.fmqResultSize);
+        ADD_FAILURE();
+        return notify;
+    }
+
+    ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber);
+    if (::android::NAME_NOT_FOUND == idx) {
+        ALOGE("%s: Unexpected frame number! received: %u",
+                __func__, frameNumber);
+        ADD_FAILURE();
+        return notify;
+    }
+
+    bool isPartialResult = false;
+    bool hasInputBufferInRequest = false;
+    InFlightRequest *request = mParent->mInflightMap.editValueAt(idx);
+    ::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata;
+    size_t resultSize = 0;
+    if (results.fmqResultSize > 0) {
+        resultMetadata.resize(results.fmqResultSize);
+        if (request->resultQueue == nullptr) {
+            ADD_FAILURE();
+            return notify;
+        }
+        if (!request->resultQueue->read(resultMetadata.data(),
+                    results.fmqResultSize)) {
+            ALOGE("%s: Frame %d: Cannot read camera metadata from fmq,"
+                    "size = %" PRIu64, __func__, frameNumber,
+                    results.fmqResultSize);
+            ADD_FAILURE();
+            return notify;
+        }
+        resultSize = resultMetadata.size();
+    } else if (results.result.size() > 0) {
+        resultMetadata.setToExternal(const_cast<uint8_t *>(
+                    results.result.data()), results.result.size());
+        resultSize = resultMetadata.size();
+    }
+
+    if (!request->usePartialResult && (resultSize > 0) &&
+            (results.partialResult != 1)) {
+        ALOGE("%s: Result is malformed for frame %d: partial_result %u "
+                "must be 1  if partial result is not supported", __func__,
+                frameNumber, results.partialResult);
+        ADD_FAILURE();
+        return notify;
+    }
+
+    if (results.partialResult != 0) {
+        request->partialResultCount = results.partialResult;
+    }
+
+    // Check if this result carries only partial metadata
+    if (request->usePartialResult && (resultSize > 0)) {
+        if ((results.partialResult > request->numPartialResults) ||
+                (results.partialResult < 1)) {
+            ALOGE("%s: Result is malformed for frame %d: partial_result %u"
+                    " must be  in the range of [1, %d] when metadata is "
+                    "included in the result", __func__, frameNumber,
+                    results.partialResult, request->numPartialResults);
+            ADD_FAILURE();
+            return notify;
+        }
+        request->collectedResult.append(
+                reinterpret_cast<const camera_metadata_t*>(
+                    resultMetadata.data()));
+
+        isPartialResult =
+            (results.partialResult < request->numPartialResults);
+    }
+
+    hasInputBufferInRequest = request->hasInputBuffer;
+
+    // Did we get the (final) result metadata for this capture?
+    if ((resultSize > 0) && !isPartialResult) {
+        if (request->haveResultMetadata) {
+            ALOGE("%s: Called multiple times with metadata for frame %d",
+                    __func__, frameNumber);
+            ADD_FAILURE();
+            return notify;
+        }
+        request->haveResultMetadata = true;
+        request->collectedResult.sort();
+    }
+
+    uint32_t numBuffersReturned = results.outputBuffers.size();
+    if (results.inputBuffer.buffer != nullptr) {
+        if (hasInputBufferInRequest) {
+            numBuffersReturned += 1;
+        } else {
+            ALOGW("%s: Input buffer should be NULL if there is no input"
+                    " buffer sent in the request", __func__);
+        }
+    }
+    request->numBuffersLeft -= numBuffersReturned;
+    if (request->numBuffersLeft < 0) {
+        ALOGE("%s: Too many buffers returned for frame %d", __func__,
+                frameNumber);
+        ADD_FAILURE();
+        return notify;
+    }
+
+    request->resultOutputBuffers.appendArray(results.outputBuffers.data(),
+            results.outputBuffers.size());
+    // If shutter event is received notify the pending threads.
+    if (request->shutterTimestamp != 0) {
+        notify = true;
+    }
+    return notify;
+}
+
 Return<void> CameraHidlTest::DeviceCb::notify(
         const hidl_vec<NotifyMsg>& messages) {
     std::lock_guard<std::mutex> l(mParent->mLock);
@@ -2353,7 +2369,8 @@
 
         int32_t streamId = 0;
         for (auto& it : outputStreams) {
-            Stream stream = {streamId,
+            V3_2::Stream stream3_2;
+            stream3_2 = {streamId,
                              StreamType::OUTPUT,
                              static_cast<uint32_t>(it.width),
                              static_cast<uint32_t>(it.height),
@@ -2361,25 +2378,27 @@
                              GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                              0,
                              StreamRotation::ROTATION_0};
-            ::android::hardware::hidl_vec<Stream> streams = {stream};
-            ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-            config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+            ::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
+            ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+            ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+            createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
+                                      &config3_2, &config3_4);
             if (session3_4 != nullptr) {
-                ret = session3_4->configureStreams_3_4(config,
-                        [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+                ret = session3_4->configureStreams_3_4(config3_4,
+                        [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                             ASSERT_EQ(Status::OK, s);
                             ASSERT_EQ(1u, halConfig.streams.size());
-                            ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
+                            ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                         });
             } else if (session3_3 != nullptr) {
-                ret = session3_3->configureStreams_3_3(config.v3_2,
+                ret = session3_3->configureStreams_3_3(config3_2,
                         [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                             ASSERT_EQ(Status::OK, s);
                             ASSERT_EQ(1u, halConfig.streams.size());
                             ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
                         });
             } else {
-                ret = session->configureStreams(config.v3_2,
+                ret = session->configureStreams(config3_2,
                         [streamId](Status s, HalStreamConfiguration halConfig) {
                             ASSERT_EQ(Status::OK, s);
                             ASSERT_EQ(1u, halConfig.streams.size());
@@ -2424,7 +2443,7 @@
         ASSERT_NE(0u, outputStreams.size());
 
         int32_t streamId = 0;
-        Stream stream = {streamId++,
+        V3_2::Stream stream3_2 = {streamId++,
                          StreamType::OUTPUT,
                          static_cast<uint32_t>(0),
                          static_cast<uint32_t>(0),
@@ -2432,23 +2451,25 @@
                          GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                          0,
                          StreamRotation::ROTATION_0};
-        ::android::hardware::hidl_vec<Stream> streams = {stream};
-        ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-        config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+        ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream3_2};
+        ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+        ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                                  &config3_2, &config3_4);
         if(session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config,
-                [](Status s, device::V3_3::HalStreamConfiguration) {
+            ret = session3_4->configureStreams_3_4(config3_4,
+                [](Status s, device::V3_4::HalStreamConfiguration) {
                     ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                             (Status::INTERNAL_ERROR == s));
                 });
         } else if(session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2,
+            ret = session3_3->configureStreams_3_3(config3_2,
                 [](Status s, device::V3_3::HalStreamConfiguration) {
                     ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                             (Status::INTERNAL_ERROR == s));
                 });
         } else {
-            ret = session->configureStreams(config.v3_2,
+            ret = session->configureStreams(config3_2,
                 [](Status s, HalStreamConfiguration) {
                     ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                             (Status::INTERNAL_ERROR == s));
@@ -2456,7 +2477,7 @@
         }
         ASSERT_TRUE(ret.isOk());
 
-        stream = {streamId++,
+        stream3_2 = {streamId++,
                   StreamType::OUTPUT,
                   static_cast<uint32_t>(UINT32_MAX),
                   static_cast<uint32_t>(UINT32_MAX),
@@ -2464,20 +2485,21 @@
                   GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                   0,
                   StreamRotation::ROTATION_0};
-        streams[0] = stream;
-        config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+        streams[0] = stream3_2;
+        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                &config3_2, &config3_4);
         if(session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config, [](Status s,
-                        device::V3_3::HalStreamConfiguration) {
+            ret = session3_4->configureStreams_3_4(config3_4, [](Status s,
+                        device::V3_4::HalStreamConfiguration) {
                     ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                 });
         } else if(session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2, [](Status s,
+            ret = session3_3->configureStreams_3_3(config3_2, [](Status s,
                         device::V3_3::HalStreamConfiguration) {
                     ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                 });
         } else {
-            ret = session->configureStreams(config.v3_2, [](Status s,
+            ret = session->configureStreams(config3_2, [](Status s,
                         HalStreamConfiguration) {
                     ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                 });
@@ -2485,7 +2507,7 @@
         ASSERT_TRUE(ret.isOk());
 
         for (auto& it : outputStreams) {
-            stream = {streamId++,
+            stream3_2 = {streamId++,
                       StreamType::OUTPUT,
                       static_cast<uint32_t>(it.width),
                       static_cast<uint32_t>(it.height),
@@ -2493,27 +2515,28 @@
                       GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                       0,
                       StreamRotation::ROTATION_0};
-            streams[0] = stream;
-            config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+            streams[0] = stream3_2;
+            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                    &config3_2, &config3_4);
             if(session3_4 != nullptr) {
-                ret = session3_4->configureStreams_3_4(config,
-                        [](Status s, device::V3_3::HalStreamConfiguration) {
+                ret = session3_4->configureStreams_3_4(config3_4,
+                        [](Status s, device::V3_4::HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
             } else if(session3_3 != nullptr) {
-                ret = session3_3->configureStreams_3_3(config.v3_2,
+                ret = session3_3->configureStreams_3_3(config3_2,
                         [](Status s, device::V3_3::HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
             } else {
-                ret = session->configureStreams(config.v3_2,
+                ret = session->configureStreams(config3_2,
                         [](Status s, HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
             }
             ASSERT_TRUE(ret.isOk());
 
-            stream = {streamId++,
+            stream3_2 = {streamId++,
                       StreamType::OUTPUT,
                       static_cast<uint32_t>(it.width),
                       static_cast<uint32_t>(it.height),
@@ -2521,20 +2544,21 @@
                       GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                       0,
                       static_cast<StreamRotation>(UINT32_MAX)};
-            streams[0] = stream;
-            config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+            streams[0] = stream3_2;
+            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                    &config3_2, &config3_4);
             if(session3_4 != nullptr) {
-                ret = session3_4->configureStreams_3_4(config,
-                        [](Status s, device::V3_3::HalStreamConfiguration) {
+                ret = session3_4->configureStreams_3_4(config3_4,
+                        [](Status s, device::V3_4::HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
             } else if(session3_3 != nullptr) {
-                ret = session3_3->configureStreams_3_3(config.v3_2,
+                ret = session3_3->configureStreams_3_3(config3_2,
                         [](Status s, device::V3_3::HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
             } else {
-                ret = session->configureStreams(config.v3_2,
+                ret = session->configureStreams(config3_2,
                         [](Status s, HalStreamConfiguration) {
                             ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                         });
@@ -2603,7 +2627,7 @@
                       getAvailableOutputStreams(staticMeta, outputStreams,
                               &outputThreshold));
             for (auto& outputIter : outputStreams) {
-                Stream zslStream = {streamId++,
+                V3_2::Stream zslStream = {streamId++,
                                     StreamType::OUTPUT,
                                     static_cast<uint32_t>(input.width),
                                     static_cast<uint32_t>(input.height),
@@ -2611,7 +2635,7 @@
                                     GRALLOC_USAGE_HW_CAMERA_ZSL,
                                     0,
                                     StreamRotation::ROTATION_0};
-                Stream inputStream = {streamId++,
+                V3_2::Stream inputStream = {streamId++,
                                       StreamType::INPUT,
                                       static_cast<uint32_t>(input.width),
                                       static_cast<uint32_t>(input.height),
@@ -2619,7 +2643,7 @@
                                       0,
                                       0,
                                       StreamRotation::ROTATION_0};
-                Stream outputStream = {streamId++,
+                V3_2::Stream outputStream = {streamId++,
                                        StreamType::OUTPUT,
                                        static_cast<uint32_t>(outputIter.width),
                                        static_cast<uint32_t>(outputIter.height),
@@ -2628,24 +2652,26 @@
                                        0,
                                        StreamRotation::ROTATION_0};
 
-                ::android::hardware::hidl_vec<Stream> streams = {inputStream, zslStream,
+                ::android::hardware::hidl_vec<V3_2::Stream> streams = {inputStream, zslStream,
                                                                  outputStream};
-                ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-                config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+                ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+                ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                                          &config3_2, &config3_4);
                 if (session3_4 != nullptr) {
-                    ret = session3_4->configureStreams_3_4(config,
-                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+                    ret = session3_4->configureStreams_3_4(config3_4,
+                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(3u, halConfig.streams.size());
                             });
                 } else if (session3_3 != nullptr) {
-                    ret = session3_3->configureStreams_3_3(config.v3_2,
+                    ret = session3_3->configureStreams_3_3(config3_2,
                             [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(3u, halConfig.streams.size());
                             });
                 } else {
-                    ret = session->configureStreams(config.v3_2,
+                    ret = session->configureStreams(config3_2,
                             [](Status s, HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(3u, halConfig.streams.size());
@@ -2733,7 +2759,8 @@
                 &previewThreshold));
         ASSERT_NE(0u, outputPreviewStreams.size());
 
-        Stream previewStream = {0,
+        V3_4::Stream previewStream;
+        previewStream.v3_2 = {0,
                                 StreamType::OUTPUT,
                                 static_cast<uint32_t>(outputPreviewStreams[0].width),
                                 static_cast<uint32_t>(outputPreviewStreams[0].height),
@@ -2741,15 +2768,16 @@
                                 GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                                 0,
                                 StreamRotation::ROTATION_0};
-        ::android::hardware::hidl_vec<Stream> streams = {previewStream};
+        ::android::hardware::hidl_vec<V3_4::Stream> streams = {previewStream};
         ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-        config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+        config.streams = streams;
+        config.operationMode = StreamConfigurationMode::NORMAL_MODE;
         const camera_metadata_t *sessionParamsBuffer = sessionParams.getAndLock();
         config.sessionParams.setToExternal(
                 reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (sessionParamsBuffer)),
                 get_camera_metadata_size(sessionParamsBuffer));
         ret = session3_4->configureStreams_3_4(config,
-                [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+                [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                     ASSERT_EQ(Status::OK, s);
                     ASSERT_EQ(1u, halConfig.streams.size());
                 });
@@ -2804,7 +2832,7 @@
         int32_t streamId = 0;
         for (auto& blobIter : outputBlobStreams) {
             for (auto& previewIter : outputPreviewStreams) {
-                Stream previewStream = {streamId++,
+                V3_2::Stream previewStream = {streamId++,
                                         StreamType::OUTPUT,
                                         static_cast<uint32_t>(previewIter.width),
                                         static_cast<uint32_t>(previewIter.height),
@@ -2812,7 +2840,7 @@
                                         GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
                                         0,
                                         StreamRotation::ROTATION_0};
-                Stream blobStream = {streamId++,
+                V3_2::Stream blobStream = {streamId++,
                                      StreamType::OUTPUT,
                                      static_cast<uint32_t>(blobIter.width),
                                      static_cast<uint32_t>(blobIter.height),
@@ -2820,24 +2848,26 @@
                                      GRALLOC1_CONSUMER_USAGE_CPU_READ,
                                      0,
                                      StreamRotation::ROTATION_0};
-                ::android::hardware::hidl_vec<Stream> streams = {previewStream,
+                ::android::hardware::hidl_vec<V3_2::Stream> streams = {previewStream,
                                                                  blobStream};
-                ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-                config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+                ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+                ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                                          &config3_2, &config3_4);
                 if (session3_4 != nullptr) {
-                    ret = session3_4->configureStreams_3_4(config,
-                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+                    ret = session3_4->configureStreams_3_4(config3_4,
+                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
                             });
                 } else if (session3_3 != nullptr) {
-                    ret = session3_3->configureStreams_3_3(config.v3_2,
+                    ret = session3_3->configureStreams_3_3(config3_2,
                             [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
                             });
                 } else {
-                    ret = session->configureStreams(config.v3_2,
+                    ret = session->configureStreams(config3_2,
                             [](Status s, HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
@@ -2890,7 +2920,7 @@
         ASSERT_EQ(Status::OK, rc);
 
         int32_t streamId = 0;
-        Stream stream = {streamId,
+        V3_2::Stream stream = {streamId,
                          StreamType::OUTPUT,
                          static_cast<uint32_t>(hfrStream.width),
                          static_cast<uint32_t>(hfrStream.height),
@@ -2898,25 +2928,27 @@
                          GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                          0,
                          StreamRotation::ROTATION_0};
-        ::android::hardware::hidl_vec<Stream> streams = {stream};
-        ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-        config.v3_2 = {streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+        ::android::hardware::hidl_vec<V3_2::Stream> streams = {stream};
+        ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+        ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
+                                  &config3_2, &config3_4);
         if (session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config,
-                    [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+            ret = session3_4->configureStreams_3_4(config3_4,
+                    [streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
                         ASSERT_EQ(1u, halConfig.streams.size());
-                        ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
+                        ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
                     });
         } else if (session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2,
+            ret = session3_3->configureStreams_3_3(config3_2,
                     [streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
                         ASSERT_EQ(1u, halConfig.streams.size());
                         ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
                     });
         } else {
-            ret = session->configureStreams(config.v3_2,
+            ret = session->configureStreams(config3_2,
                     [streamId](Status s, HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
                         ASSERT_EQ(1u, halConfig.streams.size());
@@ -2934,21 +2966,22 @@
                   0,
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
-        config.v3_2 = {streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
+                                  &config3_2, &config3_4);
         if (session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config,
-                    [](Status s, device::V3_3::HalStreamConfiguration) {
+            ret = session3_4->configureStreams_3_4(config3_4,
+                    [](Status s, device::V3_4::HalStreamConfiguration) {
                         ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                 (Status::INTERNAL_ERROR == s));
                     });
         } else if (session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2,
+            ret = session3_3->configureStreams_3_3(config3_2,
                     [](Status s, device::V3_3::HalStreamConfiguration) {
                         ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                 (Status::INTERNAL_ERROR == s));
                     });
         } else {
-            ret = session->configureStreams(config.v3_2,
+            ret = session->configureStreams(config3_2,
                     [](Status s, HalStreamConfiguration) {
                         ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
                                 (Status::INTERNAL_ERROR == s));
@@ -2965,19 +2998,20 @@
                   0,
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
-        config.v3_2 = {streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
+                                  &config3_2, &config3_4);
         if (session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config,
-                    [](Status s, device::V3_3::HalStreamConfiguration) {
+            ret = session3_4->configureStreams_3_4(config3_4,
+                    [](Status s, device::V3_4::HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
         } else if (session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2,
+            ret = session3_3->configureStreams_3_3(config3_2,
                     [](Status s, device::V3_3::HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
         } else {
-            ret = session->configureStreams(config.v3_2,
+            ret = session->configureStreams(config3_2,
                     [](Status s, HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
@@ -2993,19 +3027,20 @@
                   0,
                   StreamRotation::ROTATION_0};
         streams[0] = stream;
-        config.v3_2 = {streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE};
+        createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
+                                  &config3_2, &config3_4);
         if (session3_4 != nullptr) {
-            ret = session3_4->configureStreams_3_4(config,
-                    [](Status s, device::V3_3::HalStreamConfiguration) {
+            ret = session3_4->configureStreams_3_4(config3_4,
+                    [](Status s, device::V3_4::HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
         } else if (session3_3 != nullptr) {
-            ret = session3_3->configureStreams_3_3(config.v3_2,
+            ret = session3_3->configureStreams_3_3(config3_2,
                     [](Status s, device::V3_3::HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
         } else {
-            ret = session->configureStreams(config.v3_2,
+            ret = session->configureStreams(config3_2,
                     [](Status s, HalStreamConfiguration) {
                         ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
                     });
@@ -3062,7 +3097,7 @@
         int32_t streamId = 0;
         for (auto& blobIter : outputBlobStreams) {
             for (auto& videoIter : outputVideoStreams) {
-                Stream videoStream = {streamId++,
+                V3_2::Stream videoStream = {streamId++,
                                       StreamType::OUTPUT,
                                       static_cast<uint32_t>(videoIter.width),
                                       static_cast<uint32_t>(videoIter.height),
@@ -3070,7 +3105,7 @@
                                       GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
                                       0,
                                       StreamRotation::ROTATION_0};
-                Stream blobStream = {streamId++,
+                V3_2::Stream blobStream = {streamId++,
                                      StreamType::OUTPUT,
                                      static_cast<uint32_t>(blobIter.width),
                                      static_cast<uint32_t>(blobIter.height),
@@ -3078,23 +3113,25 @@
                                      GRALLOC1_CONSUMER_USAGE_CPU_READ,
                                      0,
                                      StreamRotation::ROTATION_0};
-                ::android::hardware::hidl_vec<Stream> streams = {videoStream, blobStream};
-                ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-                config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+                ::android::hardware::hidl_vec<V3_2::Stream> streams = {videoStream, blobStream};
+                ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+                ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+                createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
+                                          &config3_2, &config3_4);
                 if (session3_4 != nullptr) {
-                    ret = session3_4->configureStreams_3_4(config,
-                            [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
+                    ret = session3_4->configureStreams_3_4(config3_4,
+                            [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
                             });
                 } else if (session3_3 != nullptr) {
-                    ret = session3_3->configureStreams_3_3(config.v3_2,
+                    ret = session3_3->configureStreams_3_3(config3_2,
                             [](Status s, device::V3_3::HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
                             });
                 } else {
-                    ret = session->configureStreams(config.v3_2,
+                    ret = session->configureStreams(config3_2,
                             [](Status s, HalStreamConfiguration halConfig) {
                                 ASSERT_EQ(Status::OK, s);
                                 ASSERT_EQ(2u, halConfig.streams.size());
@@ -3129,7 +3166,7 @@
             return;
         }
 
-        Stream previewStream;
+        V3_2::Stream previewStream;
         HalStreamConfiguration halStreamConfig;
         sp<ICameraDeviceSession> session;
         bool supportsPartialResults = false;
@@ -3263,6 +3300,171 @@
     }
 }
 
+// Generate and verify a multi-camera capture request
+TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) {
+    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+    AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
+                                        static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
+    uint64_t bufferId = 1;
+    uint32_t frameNumber = 1;
+    ::android::hardware::hidl_vec<uint8_t> settings;
+    ::android::hardware::hidl_vec<uint8_t> emptySettings;
+    hidl_string invalidPhysicalId = "-1";
+
+    for (const auto& name : cameraDeviceNames) {
+        int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+        if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) {
+            continue;
+        }
+        camera_metadata_t* staticMeta;
+        Return<void> ret;
+        sp<ICameraDeviceSession> session;
+        openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/);
+
+        Status rc = isLogicalMultiCamera(staticMeta);
+        if (Status::METHOD_NOT_SUPPORTED == rc) {
+            ret = session->close();
+            ASSERT_TRUE(ret.isOk());
+            continue;
+        }
+        std::vector<std::string> physicalIds;
+        rc = getPhysicalCameraIds(staticMeta, &physicalIds);
+        ASSERT_TRUE(Status::OK == rc);
+        ASSERT_TRUE(physicalIds.size() > 1);
+
+        free_camera_metadata(staticMeta);
+        ret = session->close();
+        ASSERT_TRUE(ret.isOk());
+
+        V3_2::Stream previewStream;
+        HalStreamConfiguration halStreamConfig;
+        bool supportsPartialResults = false;
+        uint32_t partialResultCount = 0;
+        configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
+                &previewStream /*out*/, &halStreamConfig /*out*/,
+                &supportsPartialResults /*out*/,
+                &partialResultCount /*out*/);
+        sp<device::V3_3::ICameraDeviceSession> session3_3;
+        sp<device::V3_4::ICameraDeviceSession> session3_4;
+        castSession(session, deviceVersion, &session3_3, &session3_4);
+        ASSERT_NE(session3_4, nullptr);
+
+        std::shared_ptr<ResultMetadataQueue> resultQueue;
+        auto resultQueueRet =
+            session->getCaptureResultMetadataQueue(
+                [&resultQueue](const auto& descriptor) {
+                    resultQueue = std::make_shared<ResultMetadataQueue>(
+                            descriptor);
+                    if (!resultQueue->isValid() ||
+                            resultQueue->availableToWrite() <= 0) {
+                        ALOGE("%s: HAL returns empty result metadata fmq,"
+                                " not use it", __func__);
+                        resultQueue = nullptr;
+                        // Don't use the queue onwards.
+                    }
+                });
+        ASSERT_TRUE(resultQueueRet.isOk());
+
+        InFlightRequest inflightReq = {1, false, supportsPartialResults,
+                                       partialResultCount, resultQueue};
+
+        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
+        ret = session->constructDefaultRequestSettings(reqTemplate,
+                                                       [&](auto status, const auto& req) {
+                                                           ASSERT_EQ(Status::OK, status);
+                                                           settings = req;
+                                                       });
+        ASSERT_TRUE(ret.isOk());
+
+        sp<GraphicBuffer> gb = new GraphicBuffer(
+            previewStream.width, previewStream.height,
+            static_cast<int32_t>(halStreamConfig.streams[0].overrideFormat), 1,
+            android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
+                                            halStreamConfig.streams[0].consumerUsage));
+        ASSERT_NE(nullptr, gb.get());
+        ::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
+        outputBuffers.resize(1);
+        outputBuffers[0] = {halStreamConfig.streams[0].id,
+                                     bufferId,
+                                     hidl_handle(gb->getNativeBuffer()->handle),
+                                     BufferStatus::OK,
+                                     nullptr,
+                                     nullptr};
+
+        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
+                                         nullptr};
+        hidl_vec<V3_4::PhysicalCameraSetting> camSettings;
+        camSettings.resize(2);
+        camSettings[0] = {0, hidl_string(physicalIds[0]), settings};
+        camSettings[1] = {0, hidl_string(physicalIds[1]), settings};
+        V3_4::CaptureRequest request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
+                                  emptyInputBuffer, outputBuffers}, camSettings};
+
+        {
+            std::unique_lock<std::mutex> l(mLock);
+            mInflightMap.clear();
+            mInflightMap.add(frameNumber, &inflightReq);
+        }
+
+        Status stat = Status::INTERNAL_ERROR;
+        uint32_t numRequestProcessed = 0;
+        hidl_vec<BufferCache> cachesToRemove;
+        Return<void> returnStatus = session3_4->processCaptureRequest_3_4(
+            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
+                stat = s;
+                numRequestProcessed = n;
+            });
+        ASSERT_TRUE(returnStatus.isOk());
+        ASSERT_EQ(Status::OK, stat);
+        ASSERT_EQ(numRequestProcessed, 1u);
+
+        {
+            std::unique_lock<std::mutex> l(mLock);
+            while (!inflightReq.errorCodeValid &&
+                   ((0 < inflightReq.numBuffersLeft) ||
+                           (!inflightReq.haveResultMetadata))) {
+                auto timeout = std::chrono::system_clock::now() +
+                               std::chrono::seconds(kStreamBufferTimeoutSec);
+                ASSERT_NE(std::cv_status::timeout,
+                        mResultCondition.wait_until(l, timeout));
+            }
+
+            ASSERT_FALSE(inflightReq.errorCodeValid);
+            ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
+            ASSERT_EQ(halStreamConfig.streams[0].id,
+                    inflightReq.resultOutputBuffers[0].streamId);
+        }
+
+        // Empty physical camera settings should fail process requests
+        camSettings[1] = {0, hidl_string(physicalIds[1]), emptySettings};
+        frameNumber++;
+        request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
+            emptyInputBuffer, outputBuffers}, camSettings};
+        returnStatus = session3_4->processCaptureRequest_3_4(
+            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
+                stat = s;
+                numRequestProcessed = n;
+            });
+        ASSERT_TRUE(returnStatus.isOk());
+        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat);
+
+        // Invalid physical camera id should fail process requests
+        camSettings[1] = {0, invalidPhysicalId, settings};
+        request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
+            emptyInputBuffer, outputBuffers}, camSettings};
+        returnStatus = session3_4->processCaptureRequest_3_4(
+            {request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
+                stat = s;
+                numRequestProcessed = n;
+            });
+        ASSERT_TRUE(returnStatus.isOk());
+        ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat);
+
+        ret = session->close();
+        ASSERT_TRUE(ret.isOk());
+    }
+}
+
 // Test whether an incorrect capture request with missing settings will
 // be reported correctly.
 TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) {
@@ -3284,7 +3486,7 @@
             return;
         }
 
-        Stream previewStream;
+        V3_2::Stream previewStream;
         HalStreamConfiguration halStreamConfig;
         sp<ICameraDeviceSession> session;
         bool supportsPartialResults = false;
@@ -3351,7 +3553,7 @@
             return;
         }
 
-        Stream previewStream;
+        V3_2::Stream previewStream;
         HalStreamConfiguration halStreamConfig;
         sp<ICameraDeviceSession> session;
         bool supportsPartialResults = false;
@@ -3415,7 +3617,7 @@
             return;
         }
 
-        Stream previewStream;
+        V3_2::Stream previewStream;
         HalStreamConfiguration halStreamConfig;
         sp<ICameraDeviceSession> session;
         bool supportsPartialResults = false;
@@ -3545,7 +3747,7 @@
             return;
         }
 
-        Stream previewStream;
+        V3_2::Stream previewStream;
         HalStreamConfiguration halStreamConfig;
         sp<ICameraDeviceSession> session;
         bool supportsPartialResults = false;
@@ -3610,6 +3812,59 @@
     return Status::OK;
 }
 
+// Check if the camera device has logical multi-camera capability.
+Status CameraHidlTest::isLogicalMultiCamera(camera_metadata_t *staticMeta) {
+    Status ret = Status::METHOD_NOT_SUPPORTED;
+    if (nullptr == staticMeta) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    for (size_t i = 0; i < entry.count; i++) {
+        if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA == entry.data.u8[i]) {
+            ret = Status::OK;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+// Generate a list of physical camera ids backing a logical multi-camera.
+Status CameraHidlTest::getPhysicalCameraIds(camera_metadata_t *staticMeta,
+        std::vector<std::string> *physicalIds) {
+    if ((nullptr == staticMeta) || (nullptr == physicalIds)) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    camera_metadata_ro_entry entry;
+    int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
+            &entry);
+    if (0 != rc) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
+    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) {
+                std::string currentId(reinterpret_cast<const char *> (ids + start));
+                physicalIds->push_back(currentId);
+            }
+            start = i + 1;
+        }
+    }
+
+    return Status::OK;
+}
+
 // Check if constrained mode is supported by using the static
 // camera characteristics.
 Status CameraHidlTest::isConstrainedModeAvailable(camera_metadata_t *staticMeta) {
@@ -3754,12 +4009,31 @@
     return Status::METHOD_NOT_SUPPORTED;
 }
 
+void CameraHidlTest::createStreamConfiguration(
+        const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
+        StreamConfigurationMode configMode,
+        ::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2 /*out*/,
+        ::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4 /*out*/) {
+    ASSERT_NE(nullptr, config3_2);
+    ASSERT_NE(nullptr, config3_4);
+
+    ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(streams3_2.size());
+    size_t idx = 0;
+    for (auto& stream3_2 : streams3_2) {
+        V3_4::Stream stream;
+        stream.v3_2 = stream3_2;
+        streams3_4[idx++] = stream;
+    }
+    *config3_4 = {streams3_4, configMode, {}};
+    *config3_2 = {streams3_2, configMode};
+}
+
 // Open a device session and configure a preview stream.
 void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t deviceVersion,
         sp<ICameraProvider> provider,
         const AvailableStream *previewThreshold,
         sp<ICameraDeviceSession> *session /*out*/,
-        Stream *previewStream /*out*/,
+        V3_2::Stream *previewStream /*out*/,
         HalStreamConfiguration *halStreamConfig /*out*/,
         bool *supportsPartialResults /*out*/,
         uint32_t *partialResultCount /*out*/) {
@@ -3824,26 +4098,35 @@
     ASSERT_EQ(Status::OK, rc);
     ASSERT_FALSE(outputPreviewStreams.empty());
 
-    *previewStream = {0, StreamType::OUTPUT,
+    V3_2::Stream stream3_2 = {0, StreamType::OUTPUT,
             static_cast<uint32_t> (outputPreviewStreams[0].width),
             static_cast<uint32_t> (outputPreviewStreams[0].height),
             static_cast<PixelFormat> (outputPreviewStreams[0].format),
             GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, 0, StreamRotation::ROTATION_0};
-    ::android::hardware::hidl_vec<Stream> streams = {*previewStream};
-    ::android::hardware::camera::device::V3_4::StreamConfiguration config;
-    config.v3_2 = {streams, StreamConfigurationMode::NORMAL_MODE};
+    ::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
+    ::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
+    ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+    createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
+                              &config3_2, &config3_4);
     if (session3_4 != nullptr) {
-        ret = session3_4->configureStreams_3_4(config,
-                [&] (Status s, device::V3_3::HalStreamConfiguration halConfig) {
+        RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
+        ret = session3_4->constructDefaultRequestSettings(reqTemplate,
+                                                       [&config3_4](auto status, const auto& req) {
+                                                           ASSERT_EQ(Status::OK, status);
+                                                           config3_4.sessionParams = req;
+                                                       });
+        ASSERT_TRUE(ret.isOk());
+        ret = session3_4->configureStreams_3_4(config3_4,
+                [&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
                     ASSERT_EQ(Status::OK, s);
                     ASSERT_EQ(1u, halConfig.streams.size());
                     halStreamConfig->streams.resize(halConfig.streams.size());
                     for (size_t i = 0; i < halConfig.streams.size(); i++) {
-                        halStreamConfig->streams[i] = halConfig.streams[i].v3_2;
+                        halStreamConfig->streams[i] = halConfig.streams[i].v3_3.v3_2;
                     }
                 });
     } else if (session3_3 != nullptr) {
-        ret = session3_3->configureStreams_3_3(config.v3_2,
+        ret = session3_3->configureStreams_3_3(config3_2,
                 [&] (Status s, device::V3_3::HalStreamConfiguration halConfig) {
                     ASSERT_EQ(Status::OK, s);
                     ASSERT_EQ(1u, halConfig.streams.size());
@@ -3853,13 +4136,14 @@
                     }
                 });
     } else {
-        ret = (*session)->configureStreams(config.v3_2,
+        ret = (*session)->configureStreams(config3_2,
                 [&] (Status s, HalStreamConfiguration halConfig) {
                     ASSERT_EQ(Status::OK, s);
                     ASSERT_EQ(1u, halConfig.streams.size());
                     *halStreamConfig = halConfig;
                 });
     }
+    *previewStream = stream3_2;
     ASSERT_TRUE(ret.isOk());
 }
 
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
new file mode 100644
index 0000000..c895af6
--- /dev/null
+++ b/drm/1.1/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.drm@1.1",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "ICryptoFactory.hal",
+        "IDrmFactory.hal",
+        "IDrmPlugin.hal",
+    ],
+    interfaces: [
+        "android.hardware.drm@1.0",
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "HdcpLevel",
+        "SecurityLevel",
+    ],
+    gen_java: false,
+}
+
diff --git a/drm/1.1/ICryptoFactory.hal b/drm/1.1/ICryptoFactory.hal
new file mode 100644
index 0000000..a1a7480
--- /dev/null
+++ b/drm/1.1/ICryptoFactory.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+package android.hardware.drm@1.1;
+
+import @1.0::ICryptoFactory;
+import @1.0::ICryptoPlugin;
+
+/**
+ * ICryptoFactory is the main entry point for interacting with a vendor's
+ * crypto HAL to create crypto plugins. Crypto plugins create crypto sessions
+ * which are used by a codec to decrypt protected video content.
+ *
+ * The 1.1 factory must always create 1.1 ICryptoPlugin interfaces, which are
+ * returned via the 1.0 createPlugin method.
+ *
+ * To use 1.1 features the caller must cast the returned interface to a
+ * 1.1 HAL, using V1_1::ICryptoPlugin::castFrom().
+ */
+interface ICryptoFactory extends @1.0::ICryptoFactory {
+};
diff --git a/drm/1.1/IDrmFactory.hal b/drm/1.1/IDrmFactory.hal
new file mode 100644
index 0000000..b6d6bfb
--- /dev/null
+++ b/drm/1.1/IDrmFactory.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+package android.hardware.drm@1.1;
+
+import @1.0::IDrmFactory;
+import @1.0::IDrmPlugin;
+
+/**
+ * IDrmFactory is the main entry point for interacting with a vendor's
+ * drm HAL to create drm plugin instances. A drm plugin instance
+ * creates drm sessions which are used to obtain keys for a crypto
+ * session so it can decrypt protected video content.
+ *
+ * The 1.1 factory must always create 1.1 IDrmPlugin interfaces, which are
+ * returned via the 1.0 createPlugin method.
+ *
+ * To use 1.1 features the caller must cast the returned interface to a
+ * 1.1 HAL, using V1_1::IDrmPlugin::castFrom().
+ */
+
+interface IDrmFactory extends @1.0::IDrmFactory {
+};
diff --git a/drm/1.1/IDrmPlugin.hal b/drm/1.1/IDrmPlugin.hal
new file mode 100644
index 0000000..0660a43
--- /dev/null
+++ b/drm/1.1/IDrmPlugin.hal
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2017 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.
+ */
+package android.hardware.drm@1.1;
+
+import @1.0::IDrmPlugin;
+import @1.0::IDrmPluginListener;
+import @1.0::Status;
+import @1.1::HdcpLevel;
+import @1.1::SecurityLevel;
+
+/**
+ * IDrmPlugin is used to interact with a specific drm plugin that was created by
+ * IDrm::createPlugin. A drm plugin provides methods for obtaining drm keys that
+ * may be used by a codec to decrypt protected video content.
+ */
+interface IDrmPlugin extends @1.0::IDrmPlugin {
+    /**
+     * Return the currently negotiated and max supported HDCP levels.
+     *
+     * The current level is based on the display(s) the device is connected to.
+     * If multiple HDCP-capable displays are simultaneously connected to
+     * separate interfaces, this method returns the lowest negotiated HDCP level
+     * of all interfaces.
+     *
+     * The maximum HDCP level is the highest level that can potentially be
+     * negotiated. It is a constant for any device, i.e. it does not depend on
+     * downstream receiving devices that could be connected. For example, if
+     * the device has HDCP 1.x keys and is capable of negotiating HDCP 1.x, but
+     * does not have HDCP 2.x keys, then the maximum HDCP capability would be
+     * reported as 1.x. If multiple HDCP-capable interfaces are present, it
+     * indicates the highest of the maximum HDCP levels of all interfaces.
+     *
+     * This method should only be used for informational purposes, not for
+     * enforcing compliance with HDCP requirements. Trusted enforcement of HDCP
+     * policies must be handled by the DRM system.
+     *
+     * @return status the status of the call. The status must be OK or
+     *         ERROR_DRM_INVALID_STATE if the HAL is in a state where the HDCP
+     *         level cannot be queried.
+     * @return connectedLevel the lowest HDCP level for any connected
+     *         displays
+     * @return maxLevel the highest HDCP level that can be supported
+     *         by the device
+     */
+    getHdcpLevels() generates (Status status, HdcpLevel connectedLevel,
+            HdcpLevel maxLevel);
+
+    /**
+     * Return the current number of open sessions and the maximum number of
+     * sessions that may be opened simultaneosly among all DRM instances for the
+     * active DRM scheme.
+     *
+     * @return status the status of the call. The status must be OK or
+     *         ERROR_DRM_INVALID_STATE if the HAL is in a state where number of
+     *         sessions cannot be queried.
+     * @return currentSessions the number of currently opened sessions
+     * @return maxSessions the maximum number of sessions that the device
+     *         can support
+     */
+    getNumberOfSessions() generates (Status status, uint32_t currentSessions,
+             uint32_t maxSessions);
+
+    /**
+     * Return the current security level of a session. A session has an initial
+     * security level determined by the robustness of the DRM system's
+     * implementation on the device.
+     *
+     * @param sessionId the session id the call applies to
+     * @return status the status of the call. The status must be OK or one of
+     *         the following errors: ERROR_DRM_SESSION_NOT_OPENED if the
+     *         session is not opened, BAD_VALUE if the sessionId is invalid or
+     *         ERROR_DRM_INVALID_STATE if the HAL is in a state where the
+     *         security level cannot be queried.
+     * @return level the current security level for the session
+     */
+    getSecurityLevel(vec<uint8_t> sessionId) generates(Status status,
+            SecurityLevel level);
+
+    /**
+     * Set the security level of a session. This can be useful if specific
+     * attributes of a lower security level are needed by an application, such
+     * as image manipulation or compositing which requires non-secure decoded
+     * frames. Reducing the security level may limit decryption to lower content
+     * resolutions, depending on the license policy.
+     *
+     * @param sessionId the session id the call applies to
+     * @param level the requested security level
+     * @return status the status of the call. The status must be OK or one of
+     *         the following errors: ERROR_DRM_SESSION_NOT_OPENED if the session
+     *         is not opened, BAD_VALUE if the sessionId or security level is
+     *         invalid or ERROR_DRM_INVALID_STATE if the HAL is in a state where
+     *         the security level cannot be set.
+     */
+    setSecurityLevel(vec<uint8_t> sessionId, SecurityLevel level)
+            generates(Status status);
+};
diff --git a/drm/1.1/types.hal b/drm/1.1/types.hal
new file mode 100644
index 0000000..9447524
--- /dev/null
+++ b/drm/1.1/types.hal
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.drm@1.1;
+
+/**
+ * HDCP specifications are defined by Digital Content Protection LLC (DCP).
+ *   "HDCP Specification Rev. 2.2 Interface Independent Adaptation"
+ *   "HDCP 2.2 on HDMI Specification"
+ */
+enum HdcpLevel : uint32_t {
+    /**
+     * Unable to determine the HDCP level
+     */
+    HDCP_UNKNOWN,
+
+    /**
+     * No HDCP, output is unprotected
+     */
+    HDCP_NONE,
+
+    /**
+     * HDCP version 1.0
+     */
+    HDCP_V1,
+
+    /**
+     * HDCP version 2.0 Type 1.
+     */
+    HDCP_V2,
+
+    /**
+     * HDCP version 2.1 Type 1.
+     */
+    HDCP_V2_1,
+
+    /**
+     *  HDCP version 2.2 Type 1.
+     */
+    HDCP_V2_2,
+
+    /**
+     * No digital output, implicitly secure
+     */
+    HDCP_NO_OUTPUT
+};
+
+enum SecurityLevel : uint32_t {
+    /**
+     * Unable to determine the security level
+     */
+    UNKNOWN,
+
+    /**
+     * Software-based whitebox crypto
+     */
+    SW_SECURE_CRYPTO,
+
+    /**
+     * Software-based whitebox crypto and an obfuscated decoder
+     */
+    SW_SECURE_DECODE,
+
+    /**
+     * DRM key management and crypto operations are performed within a
+     * hardware backed trusted execution environment
+     */
+    HW_SECURE_CRYPTO,
+
+    /**
+     * DRM key management, crypto operations and decoding of content
+     * are performed within a hardware backed trusted execution environment
+     */
+    HW_SECURE_DECODE,
+
+    /**
+     * DRM key management, crypto operations, decoding of content and all
+     * handling of the media (compressed and uncompressed) is handled within
+     * a hardware backed trusted execution environment.
+     */
+    HW_SECURE_ALL,
+};
diff --git a/graphics/composer/2.1/default/Android.bp b/graphics/composer/2.1/default/Android.bp
index 140e50e..b46a1de 100644
--- a/graphics/composer/2.1/default/Android.bp
+++ b/graphics/composer/2.1/default/Android.bp
@@ -17,6 +17,9 @@
         "libsync",
         "libutils",
     ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
 }
 
 cc_library_shared {
@@ -41,6 +44,9 @@
         "libhwc2on1adapter",
         "libhwc2onfbadapter",
     ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
 }
 
 cc_binary {
@@ -65,10 +71,3 @@
         "libutils",
     ],
 }
-
-cc_library_static {
-    name: "libhwcomposer-command-buffer",
-    defaults: ["hidl_defaults"],
-    shared_libs: ["android.hardware.graphics.composer@2.1"],
-    export_include_dirs: ["."],
-}
diff --git a/graphics/composer/2.1/default/ComposerClient.cpp b/graphics/composer/2.1/default/ComposerClient.cpp
index 8aa9af0..0fcb9de 100644
--- a/graphics/composer/2.1/default/ComposerClient.cpp
+++ b/graphics/composer/2.1/default/ComposerClient.cpp
@@ -19,9 +19,8 @@
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <log/log.h>
 
-#include "ComposerClient.h"
 #include "ComposerBase.h"
-#include "IComposerCommandBuffer.h"
+#include "ComposerClient.h"
 
 namespace android {
 namespace hardware {
diff --git a/graphics/composer/2.1/default/ComposerClient.h b/graphics/composer/2.1/default/ComposerClient.h
index fc5c223..104ed5a 100644
--- a/graphics/composer/2.1/default/ComposerClient.h
+++ b/graphics/composer/2.1/default/ComposerClient.h
@@ -21,8 +21,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
 #include <hardware/hwcomposer2.h>
-#include "IComposerCommandBuffer.h"
 #include "ComposerBase.h"
 
 namespace android {
diff --git a/graphics/composer/2.1/default/OWNERS b/graphics/composer/2.1/default/OWNERS
index 3aa5fa1..4714be2 100644
--- a/graphics/composer/2.1/default/OWNERS
+++ b/graphics/composer/2.1/default/OWNERS
@@ -1,4 +1,5 @@
 # Graphics team
+courtneygo@google.com
 jessehall@google.com
 olv@google.com
 stoza@google.com
diff --git a/graphics/composer/2.1/utils/OWNERS b/graphics/composer/2.1/utils/OWNERS
new file mode 100644
index 0000000..d515a23
--- /dev/null
+++ b/graphics/composer/2.1/utils/OWNERS
@@ -0,0 +1,4 @@
+courtneygo@google.com
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/graphics/composer/2.1/utils/command-buffer/Android.bp b/graphics/composer/2.1/utils/command-buffer/Android.bp
new file mode 100644
index 0000000..e8d41c2
--- /dev/null
+++ b/graphics/composer/2.1/utils/command-buffer/Android.bp
@@ -0,0 +1,7 @@
+cc_library_headers {
+    name: "android.hardware.graphics.composer@2.1-command-buffer",
+    defaults: ["hidl_defaults"],
+    vendor_available: true,
+    shared_libs: ["android.hardware.graphics.composer@2.1"],
+    export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.1/default/IComposerCommandBuffer.h b/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
similarity index 69%
rename from graphics/composer/2.1/default/IComposerCommandBuffer.h
rename to graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
index c752f8a..df529ec 100644
--- a/graphics/composer/2.1/default/IComposerCommandBuffer.h
+++ b/graphics/composer/2.1/utils/command-buffer/include/composer-command-buffer/2.1/ComposerCommandBuffer.h
@@ -18,7 +18,7 @@
 #define ANDROID_HARDWARE_GRAPHICS_COMPOSER_COMMAND_BUFFER_H
 
 #ifndef LOG_TAG
-#warn "IComposerCommandBuffer.h included without LOG_TAG"
+#warn "ComposerCommandBuffer.h included without LOG_TAG"
 #endif
 
 #undef LOG_NDEBUG
@@ -33,9 +33,9 @@
 #include <string.h>
 
 #include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <fmq/MessageQueue.h>
 #include <log/log.h>
 #include <sync/sync.h>
-#include <fmq/MessageQueue.h>
 
 namespace android {
 namespace hardware {
@@ -53,21 +53,15 @@
 // This class helps build a command queue.  Note that all sizes/lengths are in
 // units of uint32_t's.
 class CommandWriterBase {
-public:
-    CommandWriterBase(uint32_t initialMaxSize)
-        : mDataMaxSize(initialMaxSize)
-    {
+   public:
+    CommandWriterBase(uint32_t initialMaxSize) : mDataMaxSize(initialMaxSize) {
         mData = std::make_unique<uint32_t[]>(mDataMaxSize);
         reset();
     }
 
-    virtual ~CommandWriterBase()
-    {
-        reset();
-    }
+    virtual ~CommandWriterBase() { reset(); }
 
-    void reset()
-    {
+    void reset() {
         mDataWritten = 0;
         mCommandEnd = 0;
 
@@ -82,16 +76,14 @@
         mTemporaryHandles.clear();
     }
 
-    IComposerClient::Command getCommand(uint32_t offset)
-    {
+    IComposerClient::Command getCommand(uint32_t offset) {
         uint32_t val = (offset < mDataWritten) ? mData[offset] : 0;
-        return static_cast<IComposerClient::Command>(val &
-                static_cast<uint32_t>(IComposerClient::Command::OPCODE_MASK));
+        return static_cast<IComposerClient::Command>(
+            val & static_cast<uint32_t>(IComposerClient::Command::OPCODE_MASK));
     }
 
     bool writeQueue(bool* outQueueChanged, uint32_t* outCommandLength,
-            hidl_vec<hidl_handle>* outCommandHandles)
-    {
+                    hidl_vec<hidl_handle>* outCommandHandles) {
         if (mDataWritten == 0) {
             *outQueueChanged = false;
             *outCommandLength = 0;
@@ -126,8 +118,7 @@
             *outQueueChanged = false;
         } else {
             auto newQueue = std::make_unique<CommandQueueType>(mDataMaxSize);
-            if (!newQueue->isValid() ||
-                    !newQueue->write(mData.get(), mDataWritten)) {
+            if (!newQueue->isValid() || !newQueue->write(mData.get(), mDataWritten)) {
                 ALOGE("failed to prepare a new message queue ");
                 return false;
             }
@@ -137,39 +128,32 @@
         }
 
         *outCommandLength = mDataWritten;
-        outCommandHandles->setToExternal(
-                const_cast<hidl_handle*>(mDataHandles.data()),
-                mDataHandles.size());
+        outCommandHandles->setToExternal(const_cast<hidl_handle*>(mDataHandles.data()),
+                                         mDataHandles.size());
 
         return true;
     }
 
-    const MQDescriptorSync<uint32_t>* getMQDescriptor() const
-    {
+    const MQDescriptorSync<uint32_t>* getMQDescriptor() const {
         return (mQueue) ? mQueue->getDesc() : nullptr;
     }
 
     static constexpr uint16_t kSelectDisplayLength = 2;
-    void selectDisplay(Display display)
-    {
-        beginCommand(IComposerClient::Command::SELECT_DISPLAY,
-                kSelectDisplayLength);
+    void selectDisplay(Display display) {
+        beginCommand(IComposerClient::Command::SELECT_DISPLAY, kSelectDisplayLength);
         write64(display);
         endCommand();
     }
 
     static constexpr uint16_t kSelectLayerLength = 2;
-    void selectLayer(Layer layer)
-    {
-        beginCommand(IComposerClient::Command::SELECT_LAYER,
-                kSelectLayerLength);
+    void selectLayer(Layer layer) {
+        beginCommand(IComposerClient::Command::SELECT_LAYER, kSelectLayerLength);
         write64(layer);
         endCommand();
     }
 
     static constexpr uint16_t kSetErrorLength = 2;
-    void setError(uint32_t location, Error error)
-    {
+    void setError(uint32_t location, Error error) {
         beginCommand(IComposerClient::Command::SET_ERROR, kSetErrorLength);
         write(location);
         writeSigned(static_cast<int32_t>(error));
@@ -177,25 +161,23 @@
     }
 
     static constexpr uint32_t kPresentOrValidateDisplayResultLength = 1;
-    void setPresentOrValidateResult(uint32_t  state) {
-       beginCommand(IComposerClient::Command::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT, kPresentOrValidateDisplayResultLength);
-       write(state);
-       endCommand();
+    void setPresentOrValidateResult(uint32_t state) {
+        beginCommand(IComposerClient::Command::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT,
+                     kPresentOrValidateDisplayResultLength);
+        write(state);
+        endCommand();
     }
 
     void setChangedCompositionTypes(const std::vector<Layer>& layers,
-            const std::vector<IComposerClient::Composition>& types)
-    {
+                                    const std::vector<IComposerClient::Composition>& types) {
         size_t totalLayers = std::min(layers.size(), types.size());
         size_t currentLayer = 0;
 
         while (currentLayer < totalLayers) {
-            size_t count = std::min(totalLayers - currentLayer,
-                    static_cast<size_t>(kMaxLength) / 3);
+            size_t count =
+                std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength) / 3);
 
-            beginCommand(
-                    IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES,
-                    count * 3);
+            beginCommand(IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES, count * 3);
             for (size_t i = 0; i < count; i++) {
                 write64(layers[currentLayer + i]);
                 writeSigned(static_cast<int32_t>(types[currentLayer + i]));
@@ -206,20 +188,16 @@
         }
     }
 
-    void setDisplayRequests(uint32_t displayRequestMask,
-            const std::vector<Layer>& layers,
-            const std::vector<uint32_t>& layerRequestMasks)
-    {
-        size_t totalLayers = std::min(layers.size(),
-                layerRequestMasks.size());
+    void setDisplayRequests(uint32_t displayRequestMask, const std::vector<Layer>& layers,
+                            const std::vector<uint32_t>& layerRequestMasks) {
+        size_t totalLayers = std::min(layers.size(), layerRequestMasks.size());
         size_t currentLayer = 0;
 
         while (currentLayer < totalLayers) {
-            size_t count = std::min(totalLayers - currentLayer,
-                    static_cast<size_t>(kMaxLength - 1) / 3);
+            size_t count =
+                std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength - 1) / 3);
 
-            beginCommand(IComposerClient::Command::SET_DISPLAY_REQUESTS,
-                    1 + count * 3);
+            beginCommand(IComposerClient::Command::SET_DISPLAY_REQUESTS, 1 + count * 3);
             write(displayRequestMask);
             for (size_t i = 0; i < count; i++) {
                 write64(layers[currentLayer + i]);
@@ -232,26 +210,21 @@
     }
 
     static constexpr uint16_t kSetPresentFenceLength = 1;
-    void setPresentFence(int presentFence)
-    {
-        beginCommand(IComposerClient::Command::SET_PRESENT_FENCE,
-                kSetPresentFenceLength);
+    void setPresentFence(int presentFence) {
+        beginCommand(IComposerClient::Command::SET_PRESENT_FENCE, kSetPresentFenceLength);
         writeFence(presentFence);
         endCommand();
     }
 
-    void setReleaseFences(const std::vector<Layer>& layers,
-            const std::vector<int>& releaseFences)
-    {
+    void setReleaseFences(const std::vector<Layer>& layers, const std::vector<int>& releaseFences) {
         size_t totalLayers = std::min(layers.size(), releaseFences.size());
         size_t currentLayer = 0;
 
         while (currentLayer < totalLayers) {
-            size_t count = std::min(totalLayers - currentLayer,
-                    static_cast<size_t>(kMaxLength) / 3);
+            size_t count =
+                std::min(totalLayers - currentLayer, static_cast<size_t>(kMaxLength) / 3);
 
-            beginCommand(IComposerClient::Command::SET_RELEASE_FENCES,
-                    count * 3);
+            beginCommand(IComposerClient::Command::SET_RELEASE_FENCES, count * 3);
             for (size_t i = 0; i < count; i++) {
                 write64(layers[currentLayer + i]);
                 writeFence(releaseFences[currentLayer + i]);
@@ -263,10 +236,8 @@
     }
 
     static constexpr uint16_t kSetColorTransformLength = 17;
-    void setColorTransform(const float* matrix, ColorTransform hint)
-    {
-        beginCommand(IComposerClient::Command::SET_COLOR_TRANSFORM,
-                kSetColorTransformLength);
+    void setColorTransform(const float* matrix, ColorTransform hint) {
+        beginCommand(IComposerClient::Command::SET_COLOR_TRANSFORM, kSetColorTransformLength);
         for (int i = 0; i < 16; i++) {
             writeFloat(matrix[i]);
         }
@@ -274,10 +245,8 @@
         endCommand();
     }
 
-    void setClientTarget(uint32_t slot, const native_handle_t* target,
-            int acquireFence, Dataspace dataspace,
-            const std::vector<IComposerClient::Rect>& damage)
-    {
+    void setClientTarget(uint32_t slot, const native_handle_t* target, int acquireFence,
+                         Dataspace dataspace, const std::vector<IComposerClient::Rect>& damage) {
         bool doWrite = (damage.size() <= (kMaxLength - 4) / 4);
         size_t length = 4 + ((doWrite) ? damage.size() * 4 : 0);
 
@@ -296,11 +265,8 @@
     }
 
     static constexpr uint16_t kSetOutputBufferLength = 3;
-    void setOutputBuffer(uint32_t slot, const native_handle_t* buffer,
-            int releaseFence)
-    {
-        beginCommand(IComposerClient::Command::SET_OUTPUT_BUFFER,
-                kSetOutputBufferLength);
+    void setOutputBuffer(uint32_t slot, const native_handle_t* buffer, int releaseFence) {
+        beginCommand(IComposerClient::Command::SET_OUTPUT_BUFFER, kSetOutputBufferLength);
         write(slot);
         writeHandle(buffer, true);
         writeFence(releaseFence);
@@ -308,67 +274,53 @@
     }
 
     static constexpr uint16_t kValidateDisplayLength = 0;
-    void validateDisplay()
-    {
-        beginCommand(IComposerClient::Command::VALIDATE_DISPLAY,
-                kValidateDisplayLength);
+    void validateDisplay() {
+        beginCommand(IComposerClient::Command::VALIDATE_DISPLAY, kValidateDisplayLength);
         endCommand();
     }
 
     static constexpr uint16_t kPresentOrValidateDisplayLength = 0;
-    void presentOrvalidateDisplay()
-    {
+    void presentOrvalidateDisplay() {
         beginCommand(IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY,
                      kPresentOrValidateDisplayLength);
         endCommand();
     }
 
     static constexpr uint16_t kAcceptDisplayChangesLength = 0;
-    void acceptDisplayChanges()
-    {
-        beginCommand(IComposerClient::Command::ACCEPT_DISPLAY_CHANGES,
-                kAcceptDisplayChangesLength);
+    void acceptDisplayChanges() {
+        beginCommand(IComposerClient::Command::ACCEPT_DISPLAY_CHANGES, kAcceptDisplayChangesLength);
         endCommand();
     }
 
     static constexpr uint16_t kPresentDisplayLength = 0;
-    void presentDisplay()
-    {
-        beginCommand(IComposerClient::Command::PRESENT_DISPLAY,
-                kPresentDisplayLength);
+    void presentDisplay() {
+        beginCommand(IComposerClient::Command::PRESENT_DISPLAY, kPresentDisplayLength);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerCursorPositionLength = 2;
-    void setLayerCursorPosition(int32_t x, int32_t y)
-    {
+    void setLayerCursorPosition(int32_t x, int32_t y) {
         beginCommand(IComposerClient::Command::SET_LAYER_CURSOR_POSITION,
-                kSetLayerCursorPositionLength);
+                     kSetLayerCursorPositionLength);
         writeSigned(x);
         writeSigned(y);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerBufferLength = 3;
-    void setLayerBuffer(uint32_t slot, const native_handle_t* buffer,
-            int acquireFence)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_BUFFER,
-                kSetLayerBufferLength);
+    void setLayerBuffer(uint32_t slot, const native_handle_t* buffer, int acquireFence) {
+        beginCommand(IComposerClient::Command::SET_LAYER_BUFFER, kSetLayerBufferLength);
         write(slot);
         writeHandle(buffer, true);
         writeFence(acquireFence);
         endCommand();
     }
 
-    void setLayerSurfaceDamage(
-            const std::vector<IComposerClient::Rect>& damage)
-    {
+    void setLayerSurfaceDamage(const std::vector<IComposerClient::Rect>& damage) {
         bool doWrite = (damage.size() <= kMaxLength / 4);
         size_t length = (doWrite) ? damage.size() * 4 : 0;
 
-        beginCommand(IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE,
-                length);
+        beginCommand(IComposerClient::Command::SET_LAYER_SURFACE_DAMAGE, length);
         // When there are too many rectangles in the damage region and doWrite
         // is false, we write no rectangle at all which means the entire
         // layer is damaged.
@@ -379,94 +331,76 @@
     }
 
     static constexpr uint16_t kSetLayerBlendModeLength = 1;
-    void setLayerBlendMode(IComposerClient::BlendMode mode)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_BLEND_MODE,
-                kSetLayerBlendModeLength);
+    void setLayerBlendMode(IComposerClient::BlendMode mode) {
+        beginCommand(IComposerClient::Command::SET_LAYER_BLEND_MODE, kSetLayerBlendModeLength);
         writeSigned(static_cast<int32_t>(mode));
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerColorLength = 1;
-    void setLayerColor(IComposerClient::Color color)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_COLOR,
-                kSetLayerColorLength);
+    void setLayerColor(IComposerClient::Color color) {
+        beginCommand(IComposerClient::Command::SET_LAYER_COLOR, kSetLayerColorLength);
         writeColor(color);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerCompositionTypeLength = 1;
-    void setLayerCompositionType(IComposerClient::Composition type)
-    {
+    void setLayerCompositionType(IComposerClient::Composition type) {
         beginCommand(IComposerClient::Command::SET_LAYER_COMPOSITION_TYPE,
-                kSetLayerCompositionTypeLength);
+                     kSetLayerCompositionTypeLength);
         writeSigned(static_cast<int32_t>(type));
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerDataspaceLength = 1;
-    void setLayerDataspace(Dataspace dataspace)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_DATASPACE,
-                kSetLayerDataspaceLength);
+    void setLayerDataspace(Dataspace dataspace) {
+        beginCommand(IComposerClient::Command::SET_LAYER_DATASPACE, kSetLayerDataspaceLength);
         writeSigned(static_cast<int32_t>(dataspace));
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerDisplayFrameLength = 4;
-    void setLayerDisplayFrame(const IComposerClient::Rect& frame)
-    {
+    void setLayerDisplayFrame(const IComposerClient::Rect& frame) {
         beginCommand(IComposerClient::Command::SET_LAYER_DISPLAY_FRAME,
-                kSetLayerDisplayFrameLength);
+                     kSetLayerDisplayFrameLength);
         writeRect(frame);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerPlaneAlphaLength = 1;
-    void setLayerPlaneAlpha(float alpha)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_PLANE_ALPHA,
-                kSetLayerPlaneAlphaLength);
+    void setLayerPlaneAlpha(float alpha) {
+        beginCommand(IComposerClient::Command::SET_LAYER_PLANE_ALPHA, kSetLayerPlaneAlphaLength);
         writeFloat(alpha);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerSidebandStreamLength = 1;
-    void setLayerSidebandStream(const native_handle_t* stream)
-    {
+    void setLayerSidebandStream(const native_handle_t* stream) {
         beginCommand(IComposerClient::Command::SET_LAYER_SIDEBAND_STREAM,
-                kSetLayerSidebandStreamLength);
+                     kSetLayerSidebandStreamLength);
         writeHandle(stream);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerSourceCropLength = 4;
-    void setLayerSourceCrop(const IComposerClient::FRect& crop)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_SOURCE_CROP,
-                kSetLayerSourceCropLength);
+    void setLayerSourceCrop(const IComposerClient::FRect& crop) {
+        beginCommand(IComposerClient::Command::SET_LAYER_SOURCE_CROP, kSetLayerSourceCropLength);
         writeFRect(crop);
         endCommand();
     }
 
     static constexpr uint16_t kSetLayerTransformLength = 1;
-    void setLayerTransform(Transform transform)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_TRANSFORM,
-                kSetLayerTransformLength);
+    void setLayerTransform(Transform transform) {
+        beginCommand(IComposerClient::Command::SET_LAYER_TRANSFORM, kSetLayerTransformLength);
         writeSigned(static_cast<int32_t>(transform));
         endCommand();
     }
 
-    void setLayerVisibleRegion(
-            const std::vector<IComposerClient::Rect>& visible)
-    {
+    void setLayerVisibleRegion(const std::vector<IComposerClient::Rect>& visible) {
         bool doWrite = (visible.size() <= kMaxLength / 4);
         size_t length = (doWrite) ? visible.size() * 4 : 0;
 
-        beginCommand(IComposerClient::Command::SET_LAYER_VISIBLE_REGION,
-                length);
+        beginCommand(IComposerClient::Command::SET_LAYER_VISIBLE_REGION, length);
         // When there are too many rectangles in the visible region and
         // doWrite is false, we write no rectangle at all which means the
         // entire layer is visible.
@@ -477,20 +411,16 @@
     }
 
     static constexpr uint16_t kSetLayerZOrderLength = 1;
-    void setLayerZOrder(uint32_t z)
-    {
-        beginCommand(IComposerClient::Command::SET_LAYER_Z_ORDER,
-                kSetLayerZOrderLength);
+    void setLayerZOrder(uint32_t z) {
+        beginCommand(IComposerClient::Command::SET_LAYER_Z_ORDER, kSetLayerZOrderLength);
         write(z);
         endCommand();
     }
 
-protected:
-    void beginCommand(IComposerClient::Command command, uint16_t length)
-    {
+   protected:
+    void beginCommand(IComposerClient::Command command, uint16_t length) {
         if (mCommandEnd) {
-            LOG_FATAL("endCommand was not called before command 0x%x",
-                    command);
+            LOG_FATAL("endCommand was not called before command 0x%x", command);
         }
 
         growData(1 + length);
@@ -499,8 +429,7 @@
         mCommandEnd = mDataWritten + length;
     }
 
-    void endCommand()
-    {
+    void endCommand() {
         if (!mCommandEnd) {
             LOG_FATAL("beginCommand was not called");
         } else if (mDataWritten > mCommandEnd) {
@@ -516,67 +445,48 @@
         mCommandEnd = 0;
     }
 
-    void write(uint32_t val)
-    {
-        mData[mDataWritten++] = val;
-    }
+    void write(uint32_t val) { mData[mDataWritten++] = val; }
 
-    void writeSigned(int32_t val)
-    {
-        memcpy(&mData[mDataWritten++], &val, sizeof(val));
-    }
+    void writeSigned(int32_t val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); }
 
-    void writeFloat(float val)
-    {
-        memcpy(&mData[mDataWritten++], &val, sizeof(val));
-    }
+    void writeFloat(float val) { memcpy(&mData[mDataWritten++], &val, sizeof(val)); }
 
-    void write64(uint64_t val)
-    {
+    void write64(uint64_t val) {
         uint32_t lo = static_cast<uint32_t>(val & 0xffffffff);
         uint32_t hi = static_cast<uint32_t>(val >> 32);
         write(lo);
         write(hi);
     }
 
-    void writeRect(const IComposerClient::Rect& rect)
-    {
+    void writeRect(const IComposerClient::Rect& rect) {
         writeSigned(rect.left);
         writeSigned(rect.top);
         writeSigned(rect.right);
         writeSigned(rect.bottom);
     }
 
-    void writeRegion(const std::vector<IComposerClient::Rect>& region)
-    {
+    void writeRegion(const std::vector<IComposerClient::Rect>& region) {
         for (const auto& rect : region) {
             writeRect(rect);
         }
     }
 
-    void writeFRect(const IComposerClient::FRect& rect)
-    {
+    void writeFRect(const IComposerClient::FRect& rect) {
         writeFloat(rect.left);
         writeFloat(rect.top);
         writeFloat(rect.right);
         writeFloat(rect.bottom);
     }
 
-    void writeColor(const IComposerClient::Color& color)
-    {
-        write((color.r <<  0) |
-              (color.g <<  8) |
-              (color.b << 16) |
-              (color.a << 24));
+    void writeColor(const IComposerClient::Color& color) {
+        write((color.r << 0) | (color.g << 8) | (color.b << 16) | (color.a << 24));
     }
 
     // ownership of handle is not transferred
-    void writeHandle(const native_handle_t* handle, bool useCache)
-    {
+    void writeHandle(const native_handle_t* handle, bool useCache) {
         if (!handle) {
-            writeSigned(static_cast<int32_t>((useCache) ?
-                        IComposerClient::HandleIndex::CACHED :
-                        IComposerClient::HandleIndex::EMPTY));
+            writeSigned(static_cast<int32_t>((useCache) ? IComposerClient::HandleIndex::CACHED
+                                                        : IComposerClient::HandleIndex::EMPTY));
             return;
         }
 
@@ -584,14 +494,10 @@
         writeSigned(mDataHandles.size() - 1);
     }
 
-    void writeHandle(const native_handle_t* handle)
-    {
-        writeHandle(handle, false);
-    }
+    void writeHandle(const native_handle_t* handle) { writeHandle(handle, false); }
 
     // ownership of fence is transferred
-    void writeFence(int fence)
-    {
+    void writeFence(int fence) {
         native_handle_t* handle = nullptr;
         if (fence >= 0) {
             handle = getTemporaryHandle(1, 0);
@@ -607,8 +513,7 @@
         writeHandle(handle);
     }
 
-    native_handle_t* getTemporaryHandle(int numFds, int numInts)
-    {
+    native_handle_t* getTemporaryHandle(int numFds, int numInts) {
         native_handle_t* handle = native_handle_create(numFds, numInts);
         if (handle) {
             mTemporaryHandles.push_back(handle);
@@ -616,16 +521,14 @@
         return handle;
     }
 
-    static constexpr uint16_t kMaxLength =
-        std::numeric_limits<uint16_t>::max();
+    static constexpr uint16_t kMaxLength = std::numeric_limits<uint16_t>::max();
 
-private:
-    void growData(uint32_t grow)
-    {
+   private:
+    void growData(uint32_t grow) {
         uint32_t newWritten = mDataWritten + grow;
         if (newWritten < mDataWritten) {
-            LOG_ALWAYS_FATAL("buffer overflowed; data written %" PRIu32
-                    ", growing by %" PRIu32, mDataWritten, grow);
+            LOG_ALWAYS_FATAL("buffer overflowed; data written %" PRIu32 ", growing by %" PRIu32,
+                             mDataWritten, grow);
         }
 
         if (newWritten <= mDataMaxSize) {
@@ -651,7 +554,7 @@
     uint32_t mCommandEnd;
 
     std::vector<hidl_handle> mDataHandles;
-    std::vector<native_handle_t *> mTemporaryHandles;
+    std::vector<native_handle_t*> mTemporaryHandles;
 
     std::unique_ptr<CommandQueueType> mQueue;
 };
@@ -659,14 +562,10 @@
 // This class helps parse a command queue.  Note that all sizes/lengths are in
 // units of uint32_t's.
 class CommandReaderBase {
-public:
-    CommandReaderBase() : mDataMaxSize(0)
-    {
-        reset();
-    }
+   public:
+    CommandReaderBase() : mDataMaxSize(0) { reset(); }
 
-    bool setMQDescriptor(const MQDescriptorSync<uint32_t>& descriptor)
-    {
+    bool setMQDescriptor(const MQDescriptorSync<uint32_t>& descriptor) {
         mQueue = std::make_unique<CommandQueueType>(descriptor, false);
         if (mQueue->isValid()) {
             return true;
@@ -676,9 +575,7 @@
         }
     }
 
-    bool readQueue(uint32_t commandLength,
-            const hidl_vec<hidl_handle>& commandHandles)
-    {
+    bool readQueue(uint32_t commandLength, const hidl_vec<hidl_handle>& commandHandles) {
         if (!mQueue) {
             return false;
         }
@@ -689,8 +586,7 @@
             mData = std::make_unique<uint32_t[]>(mDataMaxSize);
         }
 
-        if (commandLength > mDataMaxSize ||
-                !mQueue->read(mData.get(), commandLength)) {
+        if (commandLength > mDataMaxSize || !mQueue->read(mData.get(), commandLength)) {
             ALOGE("failed to read commands from message queue");
             return false;
         }
@@ -699,15 +595,13 @@
         mDataRead = 0;
         mCommandBegin = 0;
         mCommandEnd = 0;
-        mDataHandles.setToExternal(
-                const_cast<hidl_handle*>(commandHandles.data()),
-                commandHandles.size());
+        mDataHandles.setToExternal(const_cast<hidl_handle*>(commandHandles.data()),
+                                   commandHandles.size());
 
         return true;
     }
 
-    void reset()
-    {
+    void reset() {
         mDataSize = 0;
         mDataRead = 0;
         mCommandBegin = 0;
@@ -715,15 +609,10 @@
         mDataHandles.setToExternal(nullptr, 0);
     }
 
-protected:
-    bool isEmpty() const
-    {
-        return (mDataRead >= mDataSize);
-    }
+   protected:
+    bool isEmpty() const { return (mDataRead >= mDataSize); }
 
-    bool beginCommand(IComposerClient::Command* outCommand,
-            uint16_t* outLength)
-    {
+    bool beginCommand(IComposerClient::Command* outCommand, uint16_t* outLength) {
         if (mCommandEnd) {
             LOG_FATAL("endCommand was not called for last command");
         }
@@ -734,13 +623,11 @@
             static_cast<uint32_t>(IComposerClient::Command::LENGTH_MASK);
 
         uint32_t val = read();
-        *outCommand = static_cast<IComposerClient::Command>(
-                val & opcode_mask);
+        *outCommand = static_cast<IComposerClient::Command>(val & opcode_mask);
         *outLength = static_cast<uint16_t>(val & length_mask);
 
         if (mDataRead + *outLength > mDataSize) {
-            ALOGE("command 0x%x has invalid command length %" PRIu16,
-                    *outCommand, *outLength);
+            ALOGE("command 0x%x has invalid command length %" PRIu16, *outCommand, *outLength);
             // undo the read() above
             mDataRead--;
             return false;
@@ -751,8 +638,7 @@
         return true;
     }
 
-    void endCommand()
-    {
+    void endCommand() {
         if (!mCommandEnd) {
             LOG_FATAL("beginCommand was not called");
         } else if (mDataRead > mCommandEnd) {
@@ -767,83 +653,68 @@
         mCommandEnd = 0;
     }
 
-    uint32_t getCommandLoc() const
-    {
-        return mCommandBegin;
-    }
+    uint32_t getCommandLoc() const { return mCommandBegin; }
 
-    uint32_t read()
-    {
-        return mData[mDataRead++];
-    }
+    uint32_t read() { return mData[mDataRead++]; }
 
-    int32_t readSigned()
-    {
+    int32_t readSigned() {
         int32_t val;
         memcpy(&val, &mData[mDataRead++], sizeof(val));
         return val;
     }
 
-    float readFloat()
-    {
+    float readFloat() {
         float val;
         memcpy(&val, &mData[mDataRead++], sizeof(val));
         return val;
     }
 
-    uint64_t read64()
-    {
+    uint64_t read64() {
         uint32_t lo = read();
         uint32_t hi = read();
         return (static_cast<uint64_t>(hi) << 32) | lo;
     }
 
-    IComposerClient::Color readColor()
-    {
+    IComposerClient::Color readColor() {
         uint32_t val = read();
         return IComposerClient::Color{
-            static_cast<uint8_t>((val >>  0) & 0xff),
-            static_cast<uint8_t>((val >>  8) & 0xff),
-            static_cast<uint8_t>((val >> 16) & 0xff),
-            static_cast<uint8_t>((val >> 24) & 0xff),
+            static_cast<uint8_t>((val >> 0) & 0xff), static_cast<uint8_t>((val >> 8) & 0xff),
+            static_cast<uint8_t>((val >> 16) & 0xff), static_cast<uint8_t>((val >> 24) & 0xff),
         };
     }
 
     // ownership of handle is not transferred
-    const native_handle_t* readHandle(bool* outUseCache)
-    {
+    const native_handle_t* readHandle(bool* outUseCache) {
         const native_handle_t* handle = nullptr;
 
         int32_t index = readSigned();
         switch (index) {
-        case static_cast<int32_t>(IComposerClient::HandleIndex::EMPTY):
-            *outUseCache = false;
-            break;
-        case static_cast<int32_t>(IComposerClient::HandleIndex::CACHED):
-            *outUseCache = true;
-            break;
-        default:
-            if (static_cast<size_t>(index) < mDataHandles.size()) {
-                handle = mDataHandles[index].getNativeHandle();
-            } else {
-                ALOGE("invalid handle index %zu", static_cast<size_t>(index));
-            }
-            *outUseCache = false;
-            break;
+            case static_cast<int32_t>(IComposerClient::HandleIndex::EMPTY):
+                *outUseCache = false;
+                break;
+            case static_cast<int32_t>(IComposerClient::HandleIndex::CACHED):
+                *outUseCache = true;
+                break;
+            default:
+                if (static_cast<size_t>(index) < mDataHandles.size()) {
+                    handle = mDataHandles[index].getNativeHandle();
+                } else {
+                    ALOGE("invalid handle index %zu", static_cast<size_t>(index));
+                }
+                *outUseCache = false;
+                break;
         }
 
         return handle;
     }
 
-    const native_handle_t* readHandle()
-    {
+    const native_handle_t* readHandle() {
         bool useCache;
         return readHandle(&useCache);
     }
 
     // ownership of fence is transferred
-    int readFence()
-    {
+    int readFence() {
         auto handle = readHandle();
         if (!handle || handle->numFds == 0) {
             return -1;
@@ -864,7 +735,7 @@
         return fd;
     }
 
-private:
+   private:
     std::unique_ptr<CommandQueueType> mQueue;
     uint32_t mDataMaxSize;
     std::unique_ptr<uint32_t[]> mData;
@@ -879,10 +750,10 @@
     hidl_vec<hidl_handle> mDataHandles;
 };
 
-} // namespace V2_1
-} // namespace composer
-} // namespace graphics
-} // namespace hardware
-} // namespace android
+}  // namespace V2_1
+}  // namespace composer
+}  // namespace graphics
+}  // namespace hardware
+}  // namespace android
 
-#endif // ANDROID_HARDWARE_GRAPHICS_COMPOSER_COMMAND_BUFFER_H
+#endif  // ANDROID_HARDWARE_GRAPHICS_COMPOSER_COMMAND_BUFFER_H
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index 1ba7c9a..40f18c0 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -28,9 +28,11 @@
         "libsync",
     ],
     static_libs: [
-        "libhwcomposer-command-buffer",
         "VtsHalHidlTargetTestBase",
     ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
     cflags: [
         "-Wall",
         "-Wextra",
@@ -58,7 +60,9 @@
         "android.hardware.graphics.mapper@2.0",
         "libVtsHalGraphicsComposerTestUtils",
         "libVtsHalGraphicsMapperTestUtils",
-        "libhwcomposer-command-buffer",
         "libnativehelper",
     ],
+    header_libs: [
+        "android.hardware.graphics.composer@2.1-command-buffer",
+    ],
 }
diff --git a/graphics/composer/2.1/vts/functional/TestCommandReader.h b/graphics/composer/2.1/vts/functional/TestCommandReader.h
index 657a463..ae25d2d 100644
--- a/graphics/composer/2.1/vts/functional/TestCommandReader.h
+++ b/graphics/composer/2.1/vts/functional/TestCommandReader.h
@@ -17,7 +17,7 @@
 #ifndef TEST_COMMAND_READER_H
 #define TEST_COMMAND_READER_H
 
-#include <IComposerCommandBuffer.h>
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
 
 namespace android {
 namespace hardware {
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
index 4e69f61..29b9de3 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
@@ -23,8 +23,8 @@
 #include <unordered_set>
 #include <vector>
 
-#include <IComposerCommandBuffer.h>
 #include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
 #include <utils/StrongPointer.h>
 
 #include "TestCommandReader.h"
diff --git a/health/2.0/Android.bp b/health/2.0/Android.bp
index c444165..0325467 100644
--- a/health/2.0/Android.bp
+++ b/health/2.0/Android.bp
@@ -16,9 +16,9 @@
         "android.hidl.base@1.0",
     ],
     types: [
-        "Result",
         "DiskStats",
         "HealthInfo",
+        "Result",
         "StorageAttribute",
         "StorageInfo",
     ],
diff --git a/health/2.0/README b/health/2.0/README
new file mode 100644
index 0000000..a0a5f08
--- /dev/null
+++ b/health/2.0/README
@@ -0,0 +1,99 @@
+Upgrading from health@1.0 HAL
+
+0. Remove android.hardware.health@1.0* from PRDOUCT_PACKAGES
+   in device/<manufacturer>/<device>/device.mk
+
+1. If the device does not have a vendor-specific libhealthd AND does not
+   implement storage-related APIs, just add the following to PRODUCT_PACKAGES:
+   android.hardware.health@2.0-service
+   Otherwise, continue to Step 2.
+
+2. Create directory
+   device/<manufacturer>/<device>/health
+
+3. Create device/<manufacturer>/<device>/health/Android.bp
+   (or equivalent device/<manufacturer>/<device>/health/Android.mk)
+
+cc_binary {
+    name: "android.hardware.health@2.0-service.<device>",
+    init_rc: ["android.hardware.health@2.0-service.<device>.rc"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: [
+        "HealthService.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libhealthservice",
+        "libbatterymonitor",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+        "android.hardware.health@2.0",
+    ],
+
+    header_libs: ["libhealthd_headers"],
+}
+
+4. Create device/<manufacturer>/<device>/health/android.hardware.health@2.0-service.<device>.rc
+
+service vendor.health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service.<device>
+    class hal
+    user system
+    group system
+
+5. Create device/<manufacturer>/<device>/health/HealthService.cpp:
+
+#include <health2/service.h>
+int main() { return health_service_main(); }
+
+6. libhealthd dependency:
+
+6.1 If the device has a vendor-specific libhealthd.<soc>, add it to static_libs.
+
+6.2 If the device does not have a vendor-specific libhealthd, add the following
+    lines to HealthService.cpp:
+
+#include <healthd/healthd.h>
+void healthd_board_init(struct healthd_config*) {}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+7. Storage related APIs:
+
+7.1 If the device does not implement IHealth.getDiskStats and
+    IHealth.getStorageInfo, add libstoragehealthdefault to static_libs.
+
+7.2 If the device implements one of these two APIs, add and implement the
+    following functions in HealthService.cpp:
+
+void get_storage_info(std::vector<struct StorageInfo>& info) {
+    // ...
+}
+void get_disk_stats(std::vector<struct DiskStats>& stats) {
+    // ...
+}
+
+8. Update necessary SELinux permissions. For example,
+
+# device/<manufacturer>/<device>/sepolicy/vendor/file_contexts
+/vendor/bin/hw/android\.hardware\.health@2\.0-service.<device> u:object_r:hal_health_default_exec:s0
+
+# device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
+# Add device specific permissions to hal_health_default domain, especially
+# if Step 6.2 or Step 7.2 is done.
diff --git a/health/2.0/utils/README b/health/2.0/utils/README
new file mode 100644
index 0000000..1d5c27f
--- /dev/null
+++ b/health/2.0/utils/README
@@ -0,0 +1,28 @@
+* libhealthhalutils
+
+A convenience library for (hwbinder) clients of health HAL to choose between
+the "default" instance (served by vendor service) or "backup" instance (served
+by healthd). C++ clients of health HAL should use this library instead of
+calling IHealth::getService() directly.
+
+Its Java equivalent can be found in BatteryService.HealthServiceWrapper.
+
+* libhealthservice
+
+Common code for all (hwbinder) services of the health HAL, including healthd and
+vendor health service android.hardware.health@2.0-service(.<vendor>). main() in
+those binaries calls health_service_main() directly.
+
+* libhealthstoragedefault
+
+Default implementation for storage related APIs for (hwbinder) services of the
+health HAL. If an implementation of the health HAL do not wish to provide any
+storage info, include this library. Otherwise, it should implement the following
+two functions:
+
+void get_storage_info(std::vector<struct StorageInfo>& info) {
+    // ...
+}
+void get_disk_stats(std::vector<struct DiskStats>& stats) {
+    // ...
+}
diff --git a/health/2.0/libhealthhalutils/Android.bp b/health/2.0/utils/libhealthhalutils/Android.bp
similarity index 100%
rename from health/2.0/libhealthhalutils/Android.bp
rename to health/2.0/utils/libhealthhalutils/Android.bp
diff --git a/health/2.0/libhealthhalutils/HealthHalUtils.cpp b/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
similarity index 100%
rename from health/2.0/libhealthhalutils/HealthHalUtils.cpp
rename to health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
diff --git a/health/2.0/libhealthhalutils/include/healthhalutils/HealthHalUtils.h b/health/2.0/utils/libhealthhalutils/include/healthhalutils/HealthHalUtils.h
similarity index 100%
rename from health/2.0/libhealthhalutils/include/healthhalutils/HealthHalUtils.h
rename to health/2.0/utils/libhealthhalutils/include/healthhalutils/HealthHalUtils.h
diff --git a/health/2.0/utils/libhealthservice/Android.bp b/health/2.0/utils/libhealthservice/Android.bp
new file mode 100644
index 0000000..88c38f2
--- /dev/null
+++ b/health/2.0/utils/libhealthservice/Android.bp
@@ -0,0 +1,29 @@
+// Reasonable defaults for android.hardware.health@2.0-service.<device>.
+// Vendor service can customize by implementing functions defined in
+// libhealthd and libhealthstoragedefault.
+
+
+cc_library_static {
+    name: "libhealthservice",
+    vendor_available: true,
+    srcs: ["HealthServiceCommon.cpp"],
+
+    export_include_dirs: ["include"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "android.hardware.health@2.0",
+    ],
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+    ],
+    export_static_lib_headers: [
+        "android.hardware.health@1.0-convert",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
diff --git a/health/2.0/utils/libhealthservice/HealthServiceCommon.cpp b/health/2.0/utils/libhealthservice/HealthServiceCommon.cpp
new file mode 100644
index 0000000..acb4501
--- /dev/null
+++ b/health/2.0/utils/libhealthservice/HealthServiceCommon.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#define LOG_TAG "health@2.0/"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/Health.h>
+#include <health2/service.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::hardware::IPCThreadState;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::handleTransportPoll;
+using android::hardware::setupTransportPolling;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::implementation::Health;
+
+extern int healthd_main(void);
+
+static int gBinderFd = -1;
+static std::string gInstanceName;
+
+static void binder_event(uint32_t /*epevents*/) {
+    if (gBinderFd >= 0) handleTransportPoll(gBinderFd);
+}
+
+void healthd_mode_service_2_0_init(struct healthd_config* config) {
+    LOG(INFO) << LOG_TAG << gInstanceName << " Hal is starting up...";
+
+    gBinderFd = setupTransportPolling();
+
+    if (gBinderFd >= 0) {
+        if (healthd_register_event(gBinderFd, binder_event))
+            LOG(ERROR) << LOG_TAG << gInstanceName << ": Register for binder events failed";
+    }
+
+    android::sp<IHealth> service = Health::initInstance(config);
+    CHECK_EQ(service->registerAsService(gInstanceName), android::OK)
+        << LOG_TAG << gInstanceName << ": Failed to register HAL";
+
+    LOG(INFO) << LOG_TAG << gInstanceName << ": Hal init done";
+}
+
+int healthd_mode_service_2_0_preparetowait(void) {
+    IPCThreadState::self()->flushCommands();
+    return -1;
+}
+
+void healthd_mode_service_2_0_heartbeat(void) {
+    // noop
+}
+
+void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
+    HealthInfo info;
+    convertToHealthInfo(prop, info.legacy);
+    Health::getImplementation()->notifyListeners(&info);
+}
+
+static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
+    .init = healthd_mode_service_2_0_init,
+    .preparetowait = healthd_mode_service_2_0_preparetowait,
+    .heartbeat = healthd_mode_service_2_0_heartbeat,
+    .battery_update = healthd_mode_service_2_0_battery_update,
+};
+
+int health_service_main(const char* instance) {
+    gInstanceName = instance;
+    if (gInstanceName.empty()) {
+        gInstanceName = "default";
+    }
+    healthd_mode_ops = &healthd_mode_service_2_0_ops;
+    LOG(INFO) << LOG_TAG << gInstanceName << ": Hal starting main loop...";
+    return healthd_main();
+}
diff --git a/health/2.0/utils/libhealthservice/include/health2/service.h b/health/2.0/utils/libhealthservice/include/health2/service.h
new file mode 100644
index 0000000..d260568
--- /dev/null
+++ b/health/2.0/utils/libhealthservice/include/health2/service.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_HEALTH_V2_0_SERVICE_COMMON
+#define ANDROID_HARDWARE_HEALTH_V2_0_SERVICE_COMMON
+
+int health_service_main(const char* instance = "");
+
+#endif  // ANDROID_HARDWARE_HEALTH_V2_0_SERVICE_COMMON
diff --git a/health/2.0/libstoragehealthdefault/Android.bp b/health/2.0/utils/libhealthstoragedefault/Android.bp
similarity index 100%
rename from health/2.0/libstoragehealthdefault/Android.bp
rename to health/2.0/utils/libhealthstoragedefault/Android.bp
diff --git a/health/2.0/libstoragehealthdefault/StorageHealthDefault.cpp b/health/2.0/utils/libhealthstoragedefault/StorageHealthDefault.cpp
similarity index 100%
rename from health/2.0/libstoragehealthdefault/StorageHealthDefault.cpp
rename to health/2.0/utils/libhealthstoragedefault/StorageHealthDefault.cpp
diff --git a/health/2.0/libstoragehealthdefault/include/StorageHealthDefault.h b/health/2.0/utils/libhealthstoragedefault/include/StorageHealthDefault.h
similarity index 100%
rename from health/2.0/libstoragehealthdefault/include/StorageHealthDefault.h
rename to health/2.0/utils/libhealthstoragedefault/include/StorageHealthDefault.h
diff --git a/keymaster/4.0/Android.bp b/keymaster/4.0/Android.bp
index 20c40a0..2daad41 100644
--- a/keymaster/4.0/Android.bp
+++ b/keymaster/4.0/Android.bp
@@ -15,16 +15,26 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "Algorithm",
+        "BlockMode",
         "Constants",
+        "Digest",
+        "EcCurve",
         "ErrorCode",
         "HardwareAuthToken",
+        "HardwareAuthenticatorType",
         "HmacSharingParameters",
+        "KeyBlobUsageRequirements",
         "KeyCharacteristics",
+        "KeyDerivationFunction",
+        "KeyFormat",
         "KeyOrigin",
         "KeyParameter",
         "KeyPurpose",
+        "PaddingMode",
         "SecurityLevel",
         "Tag",
+        "TagType",
         "VerificationToken",
     ],
     gen_java: false,
diff --git a/keymaster/4.0/IKeymasterDevice.hal b/keymaster/4.0/IKeymasterDevice.hal
index 14c9c35..aef81c7 100644
--- a/keymaster/4.0/IKeymasterDevice.hal
+++ b/keymaster/4.0/IKeymasterDevice.hal
@@ -274,6 +274,23 @@
      * @param maskingKey The 32-byte value XOR'd with the transport key in the SecureWrappedKey
      *        structure.
      *
+     * @param unwrappingParams must contain any parameters needed to perform the unwrapping
+     *        operation.  For example, if the wrapping key is an AES key the block and padding modes
+     *        must be specified in this argument.
+     *
+     * @param passwordSid specifies the password secure ID (SID) of the user that owns the key being
+     *        installed.  If the authorization list in wrappedKeyData contains a Tag::USER_SECURE_ID
+     *        with a value that has the HardwareAuthenticatorType::PASSWORD bit set, the constructed
+     *        key must be bound to the SID value provided by this argument.  If the wrappedKeyData
+     *        does not contain such a tag and value, this argument must be ignored.
+     *
+     * @param biometricSid specifies the biometric secure ID (SID) of the user that owns the key
+     *        being installed.  If the authorization list in wrappedKeyData contains a
+     *        Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::FINGERPRINT
+     *        bit set, the constructed key must be bound to the SID value provided by this argument.
+     *        If the wrappedKeyData does not contain such a tag and value, this argument must be
+     *        ignored.
+     *
      * @return error See the ErrorCode enum.
      *
      * @return keyBlob Opaque descriptor of the imported key.  It is recommended that the keyBlob
@@ -281,8 +298,9 @@
      *         hardware.
      */
     importWrappedKey(vec<uint8_t> wrappedKeyData, vec<uint8_t> wrappingKeyBlob,
-                     vec<uint8_t> maskingKey)
-        generates (ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
+                     vec<uint8_t> maskingKey, vec<KeyParameter> unwrappingParams,
+                     uint64_t passwordSid, uint64_t biometricSid)
+        generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
 
     /**
      * Returns the characteristics of the specified key, if the keyBlob is valid (implementations
@@ -356,10 +374,27 @@
     /**
      * Upgrades an old key blob.  Keys can become "old" in two ways: Keymaster can be upgraded to a
      * new version with an incompatible key blob format, or the system can be updated to invalidate
-     * the OS version and/or patch level.  In either case, attempts to use an old key blob with
-     * getKeyCharacteristics(), exportKey(), attestKey() or begin() must result in Keymaster
-     * returning ErrorCode::KEY_REQUIRES_UPGRADE.  The caller must use this method to upgrade the
-     * key blob.
+     * the OS version (OS_VERSION tag), system patch level (OS_PATCHLEVEL tag), vendor patch level
+     * (VENDOR_PATCH_LEVEL tag), boot patch level (BOOT_PATCH_LEVEL tag) or other,
+     * implementation-defined patch level (keymaster implementers are encouraged to extend this HAL
+     * with a minor version extension to define validatable patch levels for other images; tags
+     * must be defined in the implemeter's namespace, starting at 10000).  In either case,
+     * attempts to use an old key blob with getKeyCharacteristics(), exportKey(), attestKey() or
+     * begin() must result in Keymaster returning ErrorCode::KEY_REQUIRES_UPGRADE.  The caller must
+     * use this method to upgrade the key blob.
+     *
+     * If upgradeKey is asked to update a key with any version or patch level that is higher than
+     * the current system version or patch level, it must return ErrorCode::INVALID_ARGUMENT.  There
+     * is one exception: it is always permissible to "upgrade" from any OS_VERSION number to
+     * OS_VERSION 0.  For example, if the key has OS_VERSION 080001, it is permisible to upgrade the
+     * key if the current system version is 080100, because the new version is larger, or if the
+     * current system version is 0, because upgrades to 0 are always allowed.  If the system version
+     * were 080000, however, keymaster must return ErrorCode::INVALID_ARGUMENT because that value is
+     * smaller than 080001.
+     *
+     * Note that Keymaster versions 2 and 3 required that the system and boot images have the same
+     * patch level and OS version.  This requirement is relaxed for Keymaster 4, and the OS version
+     * in the boot image footer is no longer used.
      *
      * @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
      *
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
index 051e570..4054620 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
@@ -74,8 +74,12 @@
     Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
                            const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
 
-    Return<void> importWrappedKey(const hidl_vec<uint8_t>&, const hidl_vec<uint8_t>&,
-                                  const hidl_vec<uint8_t>&, importWrappedKey_cb _hidl_cb) {
+    Return<void> importWrappedKey(const hidl_vec<uint8_t>& /* wrappedKeyData */,
+                                  const hidl_vec<uint8_t>& /* wrappingKeyBlob */,
+                                  const hidl_vec<uint8_t>& /* maskingKey */,
+                                  const hidl_vec<KeyParameter>& /* unwrappingParams */,
+                                  uint64_t /* passwordSid */, uint64_t /* biometricSid */,
+                                  importWrappedKey_cb _hidl_cb) {
         _hidl_cb(ErrorCode::UNIMPLEMENTED, {}, {});
         return Void();
     }
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
index ffddcac..86ef4f8 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
@@ -81,8 +81,11 @@
     Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
                                   const hidl_vec<uint8_t>& wrappingKeyBlob,
                                   const hidl_vec<uint8_t>& maskingKey,
+                                  const hidl_vec<KeyParameter>& unwrappingParams,
+                                  uint64_t passwordSid, uint64_t biometricSid,
                                   importWrappedKey_cb _hidl_cb) {
-        return dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, _hidl_cb);
+        return dev_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, unwrappingParams,
+                                      passwordSid, biometricSid, _hidl_cb);
     }
 
     Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 73e03fb..0dfc735 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -356,6 +356,8 @@
         case Tag::OS_PATCHLEVEL:
         case Tag::MAC_LENGTH:
         case Tag::AUTH_TIMEOUT:
+        case Tag::VENDOR_PATCHLEVEL:
+        case Tag::BOOT_PATCHLEVEL:
             return a.f.integer == b.f.integer;
 
         /* Long integer tags */
diff --git a/keymaster/4.0/types.hal b/keymaster/4.0/types.hal
index e890c6d..5714c4d 100644
--- a/keymaster/4.0/types.hal
+++ b/keymaster/4.0/types.hal
@@ -219,6 +219,30 @@
     ATTESTATION_ID_MODEL = TagType:BYTES | 717, /* Used to provide the device's model name to be
                                                  * included in attestation */
 
+    /**
+     * Patch level of vendor image.  The value is an integer of the form YYYYMM, where YYYY is the
+     * four-digit year when the vendor image was released and MM is the two-digit month.  During
+     * each boot, the bootloader must provide the patch level of the vendor image to keymaser
+     * (mechanism is implemntation-defined).  When keymaster keys are created or updated, the
+     * VENDOR_PATCHLEVEL tag must be cryptographically bound to the keys, with the current value as
+     * provided by the bootloader.  When keys are used, keymaster must verify that the
+     * VENDOR_PATCHLEVEL bound to the key matches the current value.  If they do not match,
+     * keymaster must return ErrorCode::KEY_REQUIRES_UPGRADE.  The client must then call upgradeKey.
+     */
+    VENDOR_PATCHLEVEL = TagType:UINT | 718,
+
+    /**
+     * Patch level of boot image.  The value is an integer of the form YYYYMM, where YYYY is the
+     * four-digit year when the boot image was released and MM is the two-digit month.  During each
+     * boot, the bootloader must provide the patch level of the boot image to keymaser (mechanism is
+     * implemntation-defined).  When keymaster keys are created or updated, the BOOT_PATCHLEVEL tag
+     * must be cryptographically bound to the keys, with the current value as provided by the
+     * bootloader.  When keys are used, keymaster must verify that the BOOT_PATCHLEVEL bound to the
+     * key matches the current value.  If they do not match, keymaster must return
+     * ErrorCode::KEY_REQUIRES_UPGRADE.  The client must then call upgradeKey.
+     */
+    BOOT_PATCHLEVEL = TagType:UINT | 719,
+
     /* Tags used only to provide data to or receive data from operations */
     ASSOCIATED_DATA = TagType:BYTES | 1000, /* Used to provide associated data for AEAD modes. */
     NONCE = TagType:BYTES | 1001,           /* Nonce or Initialization Vector */
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
index 13b6b2f..37d8c42 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
@@ -137,11 +137,14 @@
 
 ErrorCode KeymasterHidlTest::ImportWrappedKey(string wrapped_key, string wrapping_key,
                                               const AuthorizationSet& wrapping_key_desc,
-                                              string masking_key) {
+                                              string masking_key,
+                                              const AuthorizationSet& unwrapping_params) {
     ErrorCode error;
     ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key);
     EXPECT_TRUE(keymaster_
                     ->importWrappedKey(HidlBuf(wrapped_key), key_blob_, HidlBuf(masking_key),
+                                       unwrapping_params.hidl_data(), 0 /* passwordSid */,
+                                       0 /* biometricSid */,
                                        [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
                                            const KeyCharacteristics& hidl_key_characteristics) {
                                            error = hidl_error;
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index 6d28f17..36d3fc2 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -116,7 +116,8 @@
                         const string& key_material);
 
     ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
-                               const AuthorizationSet& wrapping_key_desc, string masking_key);
+                               const AuthorizationSet& wrapping_key_desc, string masking_key,
+                               const AuthorizationSet& unwrapping_params);
 
     ErrorCode ExportKey(KeyFormat format, const HidlBuf& key_blob, const HidlBuf& client_id,
                         const HidlBuf& app_data, HidlBuf* key_material);
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 31d6ad1..1d8dfdf 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -1857,7 +1857,9 @@
                                  .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
 
     ASSERT_EQ(ErrorCode::OK,
-              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key));
+              ImportWrappedKey(
+                  wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                  AuthorizationSetBuilder().Digest(Digest::SHA1).Padding(PaddingMode::RSA_OAEP)));
 
     string message = "Hello World!";
     auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
@@ -1874,7 +1876,9 @@
                                  .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
 
     ASSERT_EQ(ErrorCode::OK,
-              ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key));
+              ImportWrappedKey(
+                  wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key,
+                  AuthorizationSetBuilder().Digest(Digest::SHA1).Padding(PaddingMode::RSA_OAEP)));
 }
 
 TEST_F(ImportWrappedKeyTest, WrongMask) {
@@ -1884,9 +1888,10 @@
                                  .Padding(PaddingMode::RSA_OAEP)
                                  .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
 
-    ASSERT_EQ(
-        ErrorCode::VERIFICATION_FAILED,
-        ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key));
+    ASSERT_EQ(ErrorCode::VERIFICATION_FAILED,
+              ImportWrappedKey(
+                  wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                  AuthorizationSetBuilder().Digest(Digest::SHA1).Padding(PaddingMode::RSA_OAEP)));
 }
 
 TEST_F(ImportWrappedKeyTest, WrongPurpose) {
@@ -1895,9 +1900,10 @@
                                  .Digest(Digest::SHA1)
                                  .Padding(PaddingMode::RSA_OAEP);
 
-    ASSERT_EQ(
-        ErrorCode::INCOMPATIBLE_PURPOSE,
-        ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key));
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              ImportWrappedKey(
+                  wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                  AuthorizationSetBuilder().Digest(Digest::SHA1).Padding(PaddingMode::RSA_OAEP)));
 }
 
 typedef KeymasterHidlTest EncryptionOperationsTest;
diff --git a/light/utils/Android.bp b/light/utils/Android.bp
new file mode 100644
index 0000000..ebcbfa2
--- /dev/null
+++ b/light/utils/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2017 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.
+//
+
+// Turns off the screen.
+// Canonical usage is for init (which can't talk to hals directly).
+cc_binary {
+    name: "blank_screen",
+    init_rc: ["blank_screen.rc"],
+    srcs: ["main.cpp"],
+    shared_libs: [
+        "android.hardware.light@2.0",
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libutils",
+    ],
+}
diff --git a/light/utils/blank_screen.rc b/light/utils/blank_screen.rc
new file mode 100644
index 0000000..7b2a55e
--- /dev/null
+++ b/light/utils/blank_screen.rc
@@ -0,0 +1,5 @@
+service blank_screen /system/bin/blank_screen
+    user system
+    oneshot
+    group system
+    shutdown critical
diff --git a/light/utils/main.cpp b/light/utils/main.cpp
new file mode 100644
index 0000000..1f9cb9c
--- /dev/null
+++ b/light/utils/main.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 <iostream>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android/hardware/light/2.0/ILight.h>
+
+void error(const std::string& msg) {
+    LOG(ERROR) << msg;
+    std::cerr << msg << std::endl;
+}
+
+int main() {
+    using ::android::hardware::light::V2_0::Brightness;
+    using ::android::hardware::light::V2_0::Flash;
+    using ::android::hardware::light::V2_0::ILight;
+    using ::android::hardware::light::V2_0::LightState;
+    using ::android::hardware::light::V2_0::Status;
+    using ::android::hardware::light::V2_0::Type;
+    using ::android::sp;
+
+    sp<ILight> service = ILight::getService();
+    if (service == nullptr) {
+        error("Could not retrieve light service.");
+        return -1;
+    }
+
+    const static LightState off = {
+        .color = 0u, .flashMode = Flash::NONE, .brightnessMode = Brightness::USER,
+    };
+
+    Status ret = service->setLight(Type::BACKLIGHT, off).withDefault(Status::UNKNOWN);
+    if (ret != Status::SUCCESS) {
+        error("Failed to shut off screen");
+    }
+    return 0;
+}
diff --git a/radio/1.2/Android.bp b/radio/1.2/Android.bp
index 3e678bb..56e4afd 100644
--- a/radio/1.2/Android.bp
+++ b/radio/1.2/Android.bp
@@ -36,8 +36,6 @@
         "NetworkScanResult",
         "RadioConst",
         "ScanIntervalRange",
-        "SimSlotStatus",
-        "SlotState",
     ],
     gen_java: true,
 }
diff --git a/radio/1.2/IRadio.hal b/radio/1.2/IRadio.hal
index 73f1024..6ae78a0 100644
--- a/radio/1.2/IRadio.hal
+++ b/radio/1.2/IRadio.hal
@@ -37,46 +37,4 @@
      * Response function is IRadioResponse.startNetworkScanResponse()
      */
     oneway startNetworkScan_1_2(int32_t serial, NetworkScanRequest request);
-
-    /**
-     * Get SIM Slot status.
-     *
-     * Request provides the slot status of all active and inactive SIM slots and whether card is
-     * present in the slots or not.
-     *
-     * @param serial Serial number of request.
-     *
-     * Response callback is IRadioResponse.getSimSlotsStatusResponse()
-     */
-    oneway getSimSlotsStatus(int32_t serial);
-
-    /**
-     * Set SIM Slot mapping.
-
-     * Maps the logical slots to the physical slots. Logical slot is the slot that is seen by modem.
-     * Physical slot is the actual physical slot. Request maps the physical slot to logical slot.
-     * Logical slots that are already mapped to the requested physical slot are not impacted.
-     *
-     * Example no. of logical slots 1 and physical slots 2:
-     * The only logical slot (index 0) can be mapped to first physical slot (value 0) or second
-     * physical slot(value 1), while the other physical slot remains unmapped and inactive.
-     * slotMap[0] = 1 or slotMap[0] = 0
-     *
-     * Example no. of logical slots 2 and physical slots 2:
-     * First logical slot (index 0) can be mapped to physical slot 1 or 2 and other logical slot
-     * can be mapped to other physical slot. Each logical slot must be mapped to a physical slot.
-     * slotMap[0] = 0 and slotMap[1] = 1 or slotMap[0] = 1 and slotMap[1] = 0
-     *
-     * @param serial Serial number of request
-     * @param slotMap Logical to physical slot mapping, size == no. of radio instances. Index is
-     *        mapping to logical slot and value to physical slot, need to provide all the slots
-     *        mapping when sending request in case of multi slot device.
-     *        EX: uint32_t slotMap[logical slot] = physical slot
-     *        index 0 is the first logical_slot number of logical slots is equal to number of Radio
-     *        instances and number of physical slots is equal to size of slotStatus in
-     *        getSimSlotsStatusResponse
-     *
-     * Response callback is IRadioResponse.setSimSlotsMappingResponse()
-     */
-    oneway setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap);
 };
diff --git a/radio/1.2/IRadioIndication.hal b/radio/1.2/IRadioIndication.hal
index e87bb5b..22f655c 100644
--- a/radio/1.2/IRadioIndication.hal
+++ b/radio/1.2/IRadioIndication.hal
@@ -30,15 +30,6 @@
     oneway networkScanResult_1_2(RadioIndicationType type, NetworkScanResult result);
 
     /**
-     * Indicates SIM slot status change.
-     *
-     * @param type Type of radio indication
-     * @param slotStatus new slot status info with size equals to the number of physical slots on
-     *        the device
-     */
-    oneway simSlotsStatusChanged(RadioIndicationType type, vec<SimSlotStatus> slotStatus);
-
-    /**
      * Request all of the current cell information known to the radio.
      * Same information as returned by getCellInfoList() in 1.0::IRadio.
      *
diff --git a/radio/1.2/IRadioResponse.hal b/radio/1.2/IRadioResponse.hal
index cf6bc00..a7ad30c 100644
--- a/radio/1.2/IRadioResponse.hal
+++ b/radio/1.2/IRadioResponse.hal
@@ -50,33 +50,4 @@
      *   RadioError:NONE
      */
     oneway getIccCardStatusResponse_1_2(RadioResponseInfo info, CardStatus cardStatus);
-
-    /**
-     * @param info Response info struct containing response type, serial no. and error
-     * @param slotStatus Sim slot struct containing all the physical SIM slots info with size
-      *       equals to the number of physical slots on the device
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:NO_MEMORY
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:MODEM_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     */
-    oneway getSimSlotsStatusResponse(RadioResponseInfo info, vec<SimSlotStatus> slotStatus);
-
-    /**
-     * @param info Response info struct containing response type, serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:NO_MEMORY
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:MODEM_ERR
-     */
-    oneway setSimSlotsMappingResponse(RadioResponseInfo info);
 };
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 1e28d3b..3aa2446 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -224,17 +224,6 @@
     vec<CellInfoTdscdma> tdscdma;
 };
 
-enum SlotState : int32_t {
-    /**
-     * Physical slot is inactive
-     */
-    INACTIVE  = 0x00,
-    /**
-     * Physical slot is active
-     */
-    ACTIVE    = 0x01,
-};
-
 struct CardStatus {
     @1.0::CardStatus base;
     uint32_t physicalSlotId;
@@ -255,31 +244,3 @@
      */
     string iccid;
 };
-
-struct SimSlotStatus {
-    /**
-     * Card state in the physical slot
-     */
-    CardState cardState;
-    /**
-     * Slot state Active/Inactive
-     */
-    SlotState slotState;
-    /**
-     * An Answer To Reset (ATR) is a message output by a Smart Card conforming to ISO/IEC 7816
-     * standards, following electrical reset of the card's chip. The ATR conveys information about
-     * the communication parameters proposed by the card, and the card's nature and state.
-     *
-     * This data is applicable only when cardState is CardState:PRESENT.
-     */
-    string atr;
-    uint32_t logicalSlotId;
-    /**
-     * Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD. File is
-     * located in the SIM card at EFiccid (0x2FE2) as per ETSI 102.221. The ICCID is defined by
-     * the ITU-T recommendation E.118 ISO/IEC 7816.
-     *
-     * This data is applicable only when cardState is CardState:PRESENT.
-     */
-    string iccid;
-};
diff --git a/radio/config/1.0/Android.bp b/radio/config/1.0/Android.bp
new file mode 100644
index 0000000..c50e71c
--- /dev/null
+++ b/radio/config/1.0/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.radio.config@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IRadioConfig.hal",
+        "IRadioConfigIndication.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "SimSlotStatus",
+        "SlotState",
+    ],
+    gen_java: true,
+}
+
diff --git a/radio/config/1.0/IRadioConfig.hal b/radio/config/1.0/IRadioConfig.hal
new file mode 100644
index 0000000..9b5d4a8
--- /dev/null
+++ b/radio/config/1.0/IRadioConfig.hal
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.0;
+
+import IRadioConfigResponse;
+import IRadioConfigIndication;
+
+/**
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+interface IRadioConfig {
+
+    /**
+     * Set response functions for radio config requests & radio config indications.
+     *
+     * @param radioConfigResponse Object containing radio config response functions
+     * @param radioConfigIndication Object containing radio config indications
+     */
+    setResponseFunctions(IRadioConfigResponse radioConfigResponse,
+            IRadioConfigIndication radioConfigIndication);
+
+    /**
+     * Get SIM Slot status.
+     *
+     * Request provides the slot status of all active and inactive SIM slots and whether card is
+     * present in the slots or not.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioConfigResponse.getSimSlotsStatusResponse()
+     */
+    oneway getSimSlotsStatus(int32_t serial);
+
+    /**
+     * Set SIM Slot mapping.
+
+     * Maps the logical slots to the physical slots. Logical slot is the slot that is seen by modem.
+     * Physical slot is the actual physical slot. Request maps the physical slot to logical slot.
+     * Logical slots that are already mapped to the requested physical slot are not impacted.
+     *
+     * Example no. of logical slots 1 and physical slots 2:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0) or second
+     * physical slot(value 1), while the other physical slot remains unmapped and inactive.
+     * slotMap[0] = 1 or slotMap[0] = 0
+     *
+     * Example no. of logical slots 2 and physical slots 2:
+     * First logical slot (index 0) can be mapped to physical slot 1 or 2 and other logical slot
+     * can be mapped to other physical slot. Each logical slot must be mapped to a physical slot.
+     * slotMap[0] = 0 and slotMap[1] = 1 or slotMap[0] = 1 and slotMap[1] = 0
+     *
+     * @param serial Serial number of request
+     * @param slotMap Logical to physical slot mapping, size == no. of radio instances. Index is
+     *        mapping to logical slot and value to physical slot, need to provide all the slots
+     *        mapping when sending request in case of multi slot device.
+     *        EX: uint32_t slotMap[logical slot] = physical slot
+     *        index 0 is the first logical_slot number of logical slots is equal to number of Radio
+     *        instances and number of physical slots is equal to size of slotStatus in
+     *        getSimSlotsStatusResponse
+     *
+     * Response callback is IRadioConfigResponse.setSimSlotsMappingResponse()
+     */
+    oneway setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap);
+};
diff --git a/radio/config/1.0/IRadioConfigIndication.hal b/radio/config/1.0/IRadioConfigIndication.hal
new file mode 100644
index 0000000..1800c3c
--- /dev/null
+++ b/radio/config/1.0/IRadioConfigIndication.hal
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::RadioIndicationType;
+
+/**
+ * Interface declaring unsolicited radio config indications.
+ */
+interface IRadioConfigIndication {
+
+    /**
+     * Indicates SIM slot status change.
+
+     * This indication must be sent by the modem whenever there is any slot status change, even the
+     * slot is inactive. For example, this indication must be triggered if a SIM card is inserted
+     * into an inactive slot.
+     *
+     * @param type Type of radio indication
+     * @param slotStatus new slot status info with size equals to the number of physical slots on
+     *        the device
+     */
+    oneway simSlotsStatusChanged(RadioIndicationType type, vec<SimSlotStatus> slotStatus);
+};
diff --git a/radio/config/1.0/IRadioConfigResponse.hal b/radio/config/1.0/IRadioConfigResponse.hal
new file mode 100644
index 0000000..917fda1
--- /dev/null
+++ b/radio/config/1.0/IRadioConfigResponse.hal
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::RadioResponseInfo;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse {
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param slotStatus Sim slot struct containing all the physical SIM slots info with size
+      *       equals to the number of physical slots on the device
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     */
+    oneway getSimSlotsStatusResponse(RadioResponseInfo info, vec<SimSlotStatus> slotStatus);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway setSimSlotsMappingResponse(RadioResponseInfo info);
+};
diff --git a/radio/config/1.0/default/Android.bp b/radio/config/1.0/default/Android.bp
new file mode 100644
index 0000000..f52335e
--- /dev/null
+++ b/radio/config/1.0/default/Android.bp
@@ -0,0 +1,20 @@
+cc_binary {
+    name: "android.hardware.radio.config@1.0-service",
+    init_rc: ["android.hardware.radio.config@1.0-service.rc"],
+    relative_install_path: "hw",
+    vendor: true,
+    srcs: [
+        "RadioConfig.cpp",
+        "RadioConfigIndication.cpp",
+        "RadioConfigResponse.cpp",
+        "service.cpp",
+    ],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio@1.0",
+    ],
+}
diff --git a/radio/config/1.0/default/RadioConfig.cpp b/radio/config/1.0/default/RadioConfig.cpp
new file mode 100644
index 0000000..af4b77e
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfig.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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 "RadioConfig.h"
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::config::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+Return<void> RadioConfig::setResponseFunctions(
+    const sp<IRadioConfigResponse>& radioConfigResponse,
+    const sp<IRadioConfigIndication>& radioConfigIndication) {
+    mRadioConfigResponse = radioConfigResponse;
+    mRadioConfigIndication = radioConfigIndication;
+    return Void();
+}
+
+Return<void> RadioConfig::getSimSlotsStatus(int32_t /* serial */) {
+    hidl_vec<SimSlotStatus> slotStatus;
+    ::android::hardware::radio::V1_0::RadioResponseInfo info;
+    mRadioConfigResponse->getSimSlotsStatusResponse(info, slotStatus);
+    return Void();
+}
+
+Return<void> RadioConfig::setSimSlotsMapping(int32_t /* serial */,
+                                             const hidl_vec<uint32_t>& /* slotMap */) {
+    ::android::hardware::radio::V1_0::RadioResponseInfo info;
+    mRadioConfigResponse->setSimSlotsMappingResponse(info);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
diff --git a/radio/config/1.0/default/RadioConfig.h b/radio/config/1.0/default/RadioConfig.h
new file mode 100644
index 0000000..0f0ac75
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfig.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIG_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIG_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct RadioConfig : public IRadioConfig {
+    sp<IRadioConfigResponse> mRadioConfigResponse;
+    sp<IRadioConfigIndication> mRadioConfigIndication;
+    // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+    Return<void> setResponseFunctions(
+        const sp<::android::hardware::radio::config::V1_0::IRadioConfigResponse>&
+            radioConfigResponse,
+        const sp<::android::hardware::radio::config::V1_0::IRadioConfigIndication>&
+            radioConfigIndication) override;
+    Return<void> getSimSlotsStatus(int32_t serial) override;
+    Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap) override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIG_H
diff --git a/radio/config/1.0/default/RadioConfigIndication.cpp b/radio/config/1.0/default/RadioConfigIndication.cpp
new file mode 100644
index 0000000..1005ca3
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigIndication.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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 "RadioConfigIndication.h"
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
+Return<void> RadioConfigIndication::simSlotsStatusChanged(
+    RadioIndicationType /* type */, const hidl_vec<SimSlotStatus>& /* slotStatus */) {
+    // TODO implement
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
diff --git a/radio/config/1.0/default/RadioConfigIndication.h b/radio/config/1.0/default/RadioConfigIndication.h
new file mode 100644
index 0000000..cfab1e6
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigIndication.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGINDICATION_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGINDICATION_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfigIndication.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct RadioConfigIndication : public IRadioConfigIndication {
+    // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
+    Return<void> simSlotsStatusChanged(
+        ::android::hardware::radio::V1_0::RadioIndicationType type,
+        const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
+        override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGINDICATION_H
diff --git a/radio/config/1.0/default/RadioConfigResponse.cpp b/radio/config/1.0/default/RadioConfigResponse.cpp
new file mode 100644
index 0000000..029eab2
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigResponse.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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 "RadioConfigResponse.h"
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::V1_0;
+using namespace ::android::hardware::radio::config::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+    const RadioResponseInfo& /* info */, const hidl_vec<SimSlotStatus>& /* slotStatus */) {
+    // TODO implement
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
+    // TODO implement
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
diff --git a/radio/config/1.0/default/RadioConfigResponse.h b/radio/config/1.0/default/RadioConfigResponse.h
new file mode 100644
index 0000000..7d121fd
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigResponse.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGRESPONSE_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGRESPONSE_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfigResponse.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct RadioConfigResponse : public IRadioConfigResponse {
+    // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
+    Return<void> getSimSlotsStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
+        override;
+    Return<void> setSimSlotsMappingResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace config
+}  // namespace radio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGRESPONSE_H
diff --git a/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc b/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc
new file mode 100644
index 0000000..fad16b1
--- /dev/null
+++ b/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.radio-config-hal-1-0 /vendor/bin/hw/android.hardware.radio.config@1.0-service
+    class hal
+    user system
+    group system
diff --git a/radio/config/1.0/default/service.cpp b/radio/config/1.0/default/service.cpp
new file mode 100644
index 0000000..a06cc22
--- /dev/null
+++ b/radio/config/1.0/default/service.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.1 (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.1
+ *
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.radio.config@1.0-service"
+
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "RadioConfig.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::radio::config::V1_0::IRadioConfig;
+using android::hardware::radio::config::V1_0::implementation::RadioConfig;
+using android::sp;
+using android::status_t;
+using android::OK;
+
+int main() {
+    configureRpcThreadpool(1, true);
+
+    sp<IRadioConfig> radioConfig = new RadioConfig;
+    status_t status = radioConfig->registerAsService();
+    ALOGW_IF(status != OK, "Could not register IRadioConfig");
+    ALOGD("Default service is ready.");
+
+    joinRpcThreadpool();
+    return 0;
+}
\ No newline at end of file
diff --git a/radio/config/1.0/types.hal b/radio/config/1.0/types.hal
new file mode 100644
index 0000000..a493679
--- /dev/null
+++ b/radio/config/1.0/types.hal
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::CardState;
+
+enum SlotState : int32_t {
+    /**
+     * Physical slot is inactive
+     */
+    INACTIVE  = 0x00,
+    /**
+     * Physical slot is active
+     */
+    ACTIVE    = 0x01,
+};
+
+struct SimSlotStatus {
+    /**
+     * Card state in the physical slot
+     */
+    CardState cardState;
+    /**
+     * Slot state Active/Inactive
+     */
+    SlotState slotState;
+    /**
+     * An Answer To Reset (ATR) is a message output by a Smart Card conforming to ISO/IEC 7816
+     * standards, following electrical reset of the card's chip. The ATR conveys information about
+     * the communication parameters proposed by the card, and the card's nature and state.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string atr;
+    uint32_t logicalSlotId;
+    /**
+     * Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD. File is
+     * located in the SIM card at EFiccid (0x2FE2) as per ETSI 102.221. The ICCID is defined by
+     * the ITU-T recommendation E.118 ISO/IEC 7816.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string iccid;
+};
diff --git a/radio/config/1.0/vts/functional/Android.bp b/radio/config/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..66bfd92
--- /dev/null
+++ b/radio/config/1.0/vts/functional/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalRadioConfigV1_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "radio_config_hidl_hal_api.cpp",
+        "radio_config_hidl_hal_test.cpp",
+        "radio_config_response.cpp",
+        "radio_config_indication.cpp",
+    ],
+    static_libs: [
+        "RadioVtsTestUtilBase",
+        "android.hardware.radio.config@1.0",
+    ],
+    header_libs: ["radio.util.header@1.0"],
+}
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp
new file mode 100644
index 0000000..6782314
--- /dev/null
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getSimSlotsStatus()
+ */
+TEST_F(RadioConfigHidlTest, getSimSlotsStatus) {
+    const int serial = GetRandomSerialNumber();
+    Return<void> res = radioConfig->getSimSlotsStatus(serial);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
+    EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
+    ALOGI("getIccSlotsStatus, rspInfo.error = %s\n",
+          toString(radioConfigRsp->rspInfo.error).c_str());
+
+    ASSERT_TRUE(CheckAnyOfErrors(radioConfigRsp->rspInfo.error,
+                                 {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
+}
+
+/*
+ * Test IRadioConfig.setSimSlotsMapping()
+ */
+TEST_F(RadioConfigHidlTest, setSimSlotsMapping) {
+    const int serial = GetRandomSerialNumber();
+    android::hardware::hidl_vec<uint32_t> mapping = {0};
+    Return<void> res = radioConfig->setSimSlotsMapping(serial, mapping);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
+    EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
+    ALOGI("setSimSlotsMapping, rspInfo.error = %s\n",
+          toString(radioConfigRsp->rspInfo.error).c_str());
+
+    ASSERT_TRUE(CheckAnyOfErrors(radioConfigRsp->rspInfo.error,
+                                 {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
+}
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp
new file mode 100644
index 0000000..8df6842
--- /dev/null
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+void RadioConfigHidlTest::SetUp() {
+    radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
+        hidl_string(RADIO_SERVICE_NAME));
+    if (radioConfig == NULL) {
+        sleep(60);
+        radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
+            hidl_string(RADIO_SERVICE_NAME));
+    }
+    ASSERT_NE(nullptr, radioConfig.get());
+
+    radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
+    ASSERT_NE(nullptr, radioConfigRsp.get());
+
+    count_ = 0;
+
+    radioConfigInd = new (std::nothrow) RadioConfigIndication(*this);
+    ASSERT_NE(nullptr, radioConfigInd.get());
+
+    radioConfig->setResponseFunctions(radioConfigRsp, radioConfigInd);
+}
+
+/*
+ * Notify that the response message is received.
+ */
+void RadioConfigHidlTest::notify() {
+    std::unique_lock<std::mutex> lock(mtx_);
+    count_++;
+    cv_.notify_one();
+}
+
+/*
+ * Wait till the response message is notified or till TIMEOUT_PERIOD.
+ */
+std::cv_status RadioConfigHidlTest::wait() {
+    std::unique_lock<std::mutex> lock(mtx_);
+
+    std::cv_status status = std::cv_status::no_timeout;
+    auto now = std::chrono::system_clock::now();
+    while (count_ == 0) {
+        status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+        if (status == std::cv_status::timeout) {
+            return status;
+        }
+    }
+    count_--;
+    return status;
+}
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
new file mode 100644
index 0000000..762cc98
--- /dev/null
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <android-base/logging.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <android/hardware/radio/config/1.0/IRadioConfigIndication.h>
+#include <android/hardware/radio/config/1.0/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.0/types.h>
+
+#include "vts_test_util.h"
+
+using namespace ::android::hardware::radio::config::V1_0;
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::radio::V1_0::RadioIndicationType;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+using ::android::hardware::radio::V1_0::RadioResponseType;
+using ::android::sp;
+
+#define TIMEOUT_PERIOD 75
+#define RADIO_SERVICE_NAME "slot1"
+
+class RadioConfigHidlTest;
+
+/* Callback class for radio config response */
+class RadioConfigResponse : public IRadioConfigResponse {
+   protected:
+    RadioConfigHidlTest& parent;
+
+   public:
+    RadioResponseInfo rspInfo;
+
+    RadioConfigResponse(RadioConfigHidlTest& parent);
+    virtual ~RadioConfigResponse() = default;
+
+    Return<void> getSimSlotsStatusResponse(
+        const RadioResponseInfo& info,
+        const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+
+    Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
+};
+
+/* Callback class for radio config indication */
+class RadioConfigIndication : public IRadioConfigIndication {
+   protected:
+    RadioConfigHidlTest& parent;
+
+   public:
+    RadioConfigIndication(RadioConfigHidlTest& parent);
+    virtual ~RadioConfigIndication() = default;
+
+    Return<void> simSlotsStatusChanged(
+        RadioIndicationType type, const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+};
+
+// The main test class for Radio config HIDL.
+class RadioConfigHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   protected:
+    std::mutex mtx_;
+    std::condition_variable cv_;
+    int count_;
+
+   public:
+    virtual void SetUp() override;
+
+    /* Used as a mechanism to inform the test about data/event callback */
+    void notify();
+
+    /* Test code calls this function to wait for response */
+    std::cv_status wait();
+
+    /* radio config service handle */
+    sp<IRadioConfig> radioConfig;
+
+    /* radio config response handle */
+    sp<RadioConfigResponse> radioConfigRsp;
+
+    /* radio config indication handle */
+    sp<RadioConfigIndication> radioConfigInd;
+};
diff --git a/radio/config/1.0/vts/functional/radio_config_indication.cpp b/radio/config/1.0/vts/functional/radio_config_indication.cpp
new file mode 100644
index 0000000..b3a5843
--- /dev/null
+++ b/radio/config/1.0/vts/functional/radio_config_indication.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+RadioConfigIndication::RadioConfigIndication(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigIndication::simSlotsStatusChanged(
+    RadioIndicationType /*type*/,
+    const ::android::hardware::hidl_vec<SimSlotStatus>& /*slotStatus*/) {
+    return Void();
+}
diff --git a/radio/config/1.0/vts/functional/radio_config_response.cpp b/radio/config/1.0/vts/functional/radio_config_response.cpp
new file mode 100644
index 0000000..97e8dca
--- /dev/null
+++ b/radio/config/1.0/vts/functional/radio_config_response.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+SimSlotStatus slotStatus;
+
+RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+    const RadioResponseInfo& /* info */,
+    const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
+    return Void();
+}
\ No newline at end of file
diff --git a/soundtrigger/2.1/ISoundTriggerHw.hal b/soundtrigger/2.1/ISoundTriggerHw.hal
index 2f2e73a..a9796d8 100644
--- a/soundtrigger/2.1/ISoundTriggerHw.hal
+++ b/soundtrigger/2.1/ISoundTriggerHw.hal
@@ -92,7 +92,7 @@
      * @return modelHandle A unique handle assigned by the HAL for use by the
      *     framework when controlling activity for this sound model.
      */
-    @callflow(next={"startRecognition_2_1", "setSoundModelParameters", "unloadSoundModel"})
+    @callflow(next={"startRecognition_2_1", "unloadSoundModel"})
     loadSoundModel_2_1(SoundModel soundModel,
                    ISoundTriggerHwCallback callback,
                    CallbackCookie cookie)
@@ -126,7 +126,7 @@
      * @return modelHandle A unique handle assigned by the HAL for use by the
      *     framework when controlling activity for this sound model.
      */
-    @callflow(next={"startRecognition_2_1", "setSoundModelParameters", "unloadSoundModel"})
+    @callflow(next={"startRecognition_2_1", "unloadSoundModel"})
     loadPhraseSoundModel_2_1(PhraseSoundModel soundModel,
                    ISoundTriggerHwCallback callback,
                    CallbackCookie cookie)
@@ -161,74 +161,4 @@
                      ISoundTriggerHwCallback callback,
                      CallbackCookie cookie)
             generates (int32_t retval);
-
-
-    struct ParameterValue {
-        string key;
-        string value;
-    };
-
-    /**
-     * Generic method for retrieving vendor-specific parameter values.
-     * The framework does not interpret the parameters, they are passed
-     * in an opaque manner between a vendor application and HAL.
-     *
-     * @param keys parameter keys.
-     * @return retval Operation completion status: 0 in case of success,
-     *     -EINVAL in case of invalid keys,
-     *     -ENOSYS in case if this operation is not supported,
-     *     -ENOMEM in case of memory allocation failure,
-     *     -ENODEV in case of initialization error.
-     * @return parameters parameter key value pairs.
-     */
-    getParameters(vec<string> keys)
-            generates (int32_t retval, vec<ParameterValue> parameters);
-
-    /**
-     * Generic method for setting vendor-specific parameter values.
-     * The framework does not interpret the parameters, they are passed
-     * in an opaque manner between a vendor application and HAL.
-     *
-     * @param parameters parameter key value pairs.
-     * @return retval Operation completion status: 0 in case of success,
-     *     -EINVAL in case of invalid keys,
-     *     -ENOSYS in case if this operation is not supported,
-     *     -ENOMEM in case of memory allocation failure,
-     *     -ENODEV in case of initialization error.
-     */
-    setParameters(vec<ParameterValue> parameters) generates (int32_t retval);
-
-    /**
-     * Generic method for retrieving vendor-specific parameter values.
-     * The framework does not interpret the parameters, they are passed
-     * in an opaque manner between a vendor application and HAL.
-     *
-     * @param modelHandle the handle of the sound model to get parameters about.
-     * @param keys parameter keys.
-     * @return retval Operation completion status: 0 in case of success,
-     *     -EINVAL in case of invalid keys,
-     *     -ENOSYS in case if this operation is not supported,
-     *     -ENOMEM in case of memory allocation failure,
-     *     -ENODEV in case of initialization error.
-     * @return parameters parameter key value pairs.
-     */
-    getSoundModelParameters(SoundModelHandle modelHandle, vec<string> keys)
-            generates (int32_t retval, vec<ParameterValue> parameters);
-
-    /**
-     * Generic method for setting vendor-specific parameter values.
-     * The framework does not interpret the parameters, they are passed
-     * in an opaque manner between a vendor application and HAL.
-     *
-     * @param modelHandle the handle of the sound model to set parameters for.
-     * @param parameters parameter key value pairs.
-     * @return retval Operation completion status: 0 in case of success,
-     *     -EINVAL in case of invalid keys,
-     *     -ENOSYS in case if this operation is not supported,
-     *     -ENOMEM in case of memory allocation failure,
-     *     -ENODEV in case of initialization error.
-     */
-    setSoundModelParameters(
-            SoundModelHandle modelHandle, vec<ParameterValue> parameters)
-            generates (int32_t retval);
 };
diff --git a/soundtrigger/2.1/default/SoundTriggerHw.h b/soundtrigger/2.1/default/SoundTriggerHw.h
index 855d1a6..a5515eb 100644
--- a/soundtrigger/2.1/default/SoundTriggerHw.h
+++ b/soundtrigger/2.1/default/SoundTriggerHw.h
@@ -102,26 +102,6 @@
                                              int32_t /*cookie*/) override {
             return mImpl->startRecognition_2_1(modelHandle, config);
         }
-        Return<void> getParameters(const hidl_vec<hidl_string>& /*keys*/,
-                                   getParameters_cb _hidl_cb) override {
-            _hidl_cb(-ENOSYS, hidl_vec<ParameterValue>());
-            return Void();
-        }
-        Return<int32_t> setParameters(
-            const hidl_vec<V2_1::ISoundTriggerHw::ParameterValue>& /*parameters*/) override {
-            return -ENOSYS;
-        }
-        Return<void> getSoundModelParameters(int32_t /*modelHandle*/,
-                                             const hidl_vec<hidl_string>& /*keys*/,
-                                             getSoundModelParameters_cb _hidl_cb) override {
-            _hidl_cb(-ENOSYS, hidl_vec<ParameterValue>());
-            return Void();
-        }
-        Return<int32_t> setSoundModelParameters(
-            int32_t /*modelHandle*/,
-            const hidl_vec<V2_1::ISoundTriggerHw::ParameterValue>& /*parameters*/) override {
-            return -ENOSYS;
-        }
 
        private:
         sp<SoundTriggerHw> mImpl;
diff --git a/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
index 9876cdd..fdd5f0d 100644
--- a/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
+++ b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
@@ -51,7 +51,6 @@
 using V2_0_ISoundTriggerHwCallback =
     ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
 using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw;
-using ParameterValue = ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw::ParameterValue;
 using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHwCallback;
 using ::android::hidl::allocator::V1_0::IAllocator;
 using ::android::hidl::memory::V1_0::IMemory;
@@ -481,78 +480,3 @@
     EXPECT_TRUE(hidlReturn.isOk());
     EXPECT_TRUE(hidlReturn == 0 || hidlReturn == -ENOSYS);
 }
-
-/**
- * Test ISoundTriggerHw::getParameters() and setParameters() methods
- *
- * Verifies that:
- *  - the implementation implements these optional methods or indicates it is not supported by
- *  returning -ENOSYS
- */
-TEST_F(SoundTriggerHidlTest, getAndSetParameters) {
-    hidl_vec<hidl_string> keys;
-    hidl_vec<ParameterValue> values;
-
-    int32_t ret = -ENODEV;
-    Return<void> hidlReturn =
-        mSoundTriggerHal->getParameters(keys, [&](int32_t retval, auto params) {
-            ret = retval;
-            values = params;
-        });
-    EXPECT_TRUE(hidlReturn.isOk());
-    EXPECT_TRUE(ret == 0 || ret == -ENOSYS);
-    if (ret == 0) {
-        Return<int32_t> hidlReturn = mSoundTriggerHal->setParameters(values);
-        EXPECT_TRUE(hidlReturn.isOk());
-        EXPECT_EQ(0, hidlReturn);
-    }
-}
-
-/**
- * Test ISoundTriggerHw::setParameters() method
- *
- * Verifies that:
- *  - the implementation accepts empty parameters to be set or indicates it is not supported by
- *  returning -ENOSYS
- */
-TEST_F(SoundTriggerHidlTest, setParameters) {
-    hidl_vec<ParameterValue> values;
-    Return<int32_t> hidlReturn = mSoundTriggerHal->setParameters(values);
-    EXPECT_TRUE(hidlReturn.isOk());
-    EXPECT_TRUE(hidlReturn == 0 || hidlReturn == -ENOSYS);
-}
-
-/**
- * Test ISoundTriggerHw::getSoundModelParameters() and setSoundModelParameters() methods
- *
- * Verifies that:
- *  - the implementation implements these optional methods or indicates it is not supported by
- *  returning -ENOSYS;
- *  - if the methods are supported, the implementation returns an error when called without
- *  an active recognition running.
- *
- */
-TEST_F(SoundTriggerHidlTest, getAndSetSoundModelParameters) {
-    SoundModelHandle handle = 0;
-    hidl_vec<hidl_string> keys;
-    hidl_vec<ParameterValue> values;
-
-    {
-        int32_t ret = 0;
-        Return<void> hidlReturn = mSoundTriggerHal->getSoundModelParameters(
-            handle, keys, [&](int32_t retval, auto params) {
-                ret = retval;
-                values = params;
-            });
-        EXPECT_TRUE(hidlReturn.isOk());
-        EXPECT_NE(0, ret);
-        EXPECT_EQ(0u, values.size());
-    }
-
-    values.resize(0);
-    {
-        Return<int32_t> hidlReturn = mSoundTriggerHal->setSoundModelParameters(handle, values);
-        EXPECT_TRUE(hidlReturn.isOk());
-        EXPECT_NE(0, hidlReturn);
-    }
-}
diff --git a/usb/gadget/1.0/Android.bp b/usb/gadget/1.0/Android.bp
new file mode 100644
index 0000000..f38002f
--- /dev/null
+++ b/usb/gadget/1.0/Android.bp
@@ -0,0 +1,23 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.usb.gadget@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "IUsbGadget.hal",
+        "IUsbGadgetCallback.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "GadgetFunction",
+        "Status",
+    ],
+    gen_java: true,
+}
+
diff --git a/usb/gadget/1.0/IUsbGadget.hal b/usb/gadget/1.0/IUsbGadget.hal
new file mode 100644
index 0000000..41be401
--- /dev/null
+++ b/usb/gadget/1.0/IUsbGadget.hal
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.usb.gadget@1.0;
+
+import IUsbGadgetCallback;
+
+interface IUsbGadget {
+    /**
+     * This function is used to set the current USB gadget configuration.
+     * Usb gadget needs to teared down if an USB configuration is already
+     * active.
+     *
+     * @param functions list of functions defined by GadgetFunction to be
+     *                  included in the gadget composition.
+     * @param callback IUsbGadgetCallback::setCurrentUsbFunctionsCb used to
+     *                 propagate back the status.
+     * @param timeout The maximum time (in milliseconds) within which the
+     *                IUsbGadgetCallback needs to be returned.
+     */
+    oneway setCurrentUsbFunctions(bitfield<GadgetFunction> functions,
+                                  IUsbGadgetCallback callback,
+                                  uint64_t timeout);
+
+    /**
+     * This function is used to query the USB functions included in the
+     * current USB configuration.
+     *
+     * @param callback IUsbGadgetCallback::getCurrentUsbFunctionsCb used to
+     *                 propagate the current functions list.
+     */
+    oneway getCurrentUsbFunctions(IUsbGadgetCallback callback);
+
+};
diff --git a/usb/gadget/1.0/IUsbGadgetCallback.hal b/usb/gadget/1.0/IUsbGadgetCallback.hal
new file mode 100644
index 0000000..8876abb
--- /dev/null
+++ b/usb/gadget/1.0/IUsbGadgetCallback.hal
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.usb.gadget@1.0;
+
+interface IUsbGadgetCallback {
+    /**
+     * Callback function used to propagate the status of configuration
+     * switch to the caller.
+     *
+     * @param functions list of functions defined by GadgetFunction
+     *                  included in the current USB gadget composition.
+     * @param status SUCCESS when the functions are applied.
+     *               FUNCTIONS_NOT_SUPPORTED when the configuration is
+     *                                       not supported.
+     *               ERROR otherwise.
+     */
+    oneway setCurrentUsbFunctionsCb(bitfield<GadgetFunction> functions,
+                                    Status status);
+
+    /**
+     * Callback function used to propagate the current USB gadget
+     * configuration.
+     * @param functions list of functions defined by GadgetFunction
+     *                  included in the current USB gadget composition.
+     * @param status FUNCTIONS_APPLIED when list of functions have been
+     *                                 applied.
+     *               FUNCTIONS_NOT_APPLIED when the functions have not
+     *                                     been applied.
+     *               ERROR otherwise.
+     */
+    oneway getCurrentUsbFunctionsCb(bitfield<GadgetFunction> functions,
+                                    Status status);
+};
diff --git a/usb/gadget/1.0/types.hal b/usb/gadget/1.0/types.hal
new file mode 100644
index 0000000..3793fe2
--- /dev/null
+++ b/usb/gadget/1.0/types.hal
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.hardware.usb.gadget@1.0;
+
+enum GadgetFunction : uint64_t {
+    /**
+     * Removes all the functions and pulls down the gadget.
+     */
+    NONE = 0,
+
+    /**
+     * Android Debug Bridge function.
+     */
+    ADB = 1 << 0,
+
+    /**
+     * Android open accessory protocol function.
+     */
+    ACCESSORY = 1 << 1,
+
+    /**
+     * Media Transfer protocol function.
+     */
+    MTP = 1 << 2,
+
+    /**
+     * Peripheral mode USB Midi function.
+     */
+    MIDI = 1 << 3,
+
+    /**
+     * Picture transfer protocol function.
+     */
+    PTP = 1 << 4,
+
+    /**
+     * Tethering function.
+     */
+    RNDIS = 1 << 5,
+
+    /**
+     * AOAv2.0 - Audio Source function.
+     */
+    AUDIO_SOURCE = 1 << 6,
+};
+
+enum Status : uint32_t {
+    SUCCESS = 0,
+
+    /**
+     * Error value when the HAL operation fails for reasons not listed here.
+     */
+    ERROR = 1,
+
+    /**
+     * USB configuration applied successfully.
+     */
+    FUNCTIONS_APPLIED = 2,
+
+    /**
+     * USB confgiuration failed to apply.
+     */
+    FUNCTIONS_NOT_APPLIED = 3,
+
+    /**
+     * USB configuration not supported.
+     */
+    CONFIGURATION_NOT_SUPPORTED = 4,
+};
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 95414bc..8d0262b 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_SRC_FILES := \
     hidl_struct_util.cpp \
     hidl_sync_util.cpp \
+    ringbuffer.cpp \
     wifi.cpp \
     wifi_ap_iface.cpp \
     wifi_chip.cpp \
@@ -97,6 +98,7 @@
     tests/mock_wifi_feature_flags.cpp \
     tests/mock_wifi_legacy_hal.cpp \
     tests/mock_wifi_mode_controller.cpp \
+    tests/ringbuffer_unit_tests.cpp \
     tests/wifi_chip_unit_tests.cpp
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
diff --git a/wifi/1.2/default/ringbuffer.cpp b/wifi/1.2/default/ringbuffer.cpp
new file mode 100644
index 0000000..5511f2f
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "ringbuffer.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+Ringbuffer::Ringbuffer(size_t maxSize) : size_(0), maxSize_(maxSize) {}
+
+void Ringbuffer::append(const std::vector<uint8_t>& input) {
+    if (input.size() == 0) {
+        return;
+    }
+    data_.push_back(input);
+    size_ += input.size() * sizeof(input[0]);
+    while (size_ > maxSize_) {
+        size_ -= data_.front().size() * sizeof(data_.front()[0]);
+        data_.pop_front();
+    }
+}
+
+const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const {
+    return data_;
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/ringbuffer.h b/wifi/1.2/default/ringbuffer.h
new file mode 100644
index 0000000..4808e40
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include <list>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+/**
+ * Ringbuffer object used to store debug data.
+ */
+class Ringbuffer {
+   public:
+    explicit Ringbuffer(size_t maxSize);
+
+    // Appends the data buffer and deletes from the front until buffer is
+    // within |maxSize_|.
+    void append(const std::vector<uint8_t>& input);
+    const std::list<std::vector<uint8_t>>& getData() const;
+
+   private:
+    std::list<std::vector<uint8_t>> data_;
+    size_t size_;
+    size_t maxSize_;
+};
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // RINGBUFFER_H_
diff --git a/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
new file mode 100644
index 0000000..1b332f9
--- /dev/null
+++ b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "ringbuffer.h"
+
+using testing::Return;
+using testing::Test;
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+class RingbufferTest : public Test {
+   public:
+    const uint32_t maxBufferSize_ = 10;
+    Ringbuffer buffer_{maxBufferSize_};
+};
+
+TEST_F(RingbufferTest, CreateEmptyBuffer) {
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, CanUseFullBufferCapacity) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    buffer_.append(input);
+    buffer_.append(input2);
+    ASSERT_EQ(2u, buffer_.getData().size());
+    EXPECT_EQ(input, buffer_.getData().front());
+    EXPECT_EQ(input2, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, OldDataIsRemovedOnOverflow) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    const std::vector<uint8_t> input3 = {'G'};
+    buffer_.append(input);
+    buffer_.append(input2);
+    buffer_.append(input3);
+    ASSERT_EQ(2u, buffer_.getData().size());
+    EXPECT_EQ(input2, buffer_.getData().front());
+    EXPECT_EQ(input3, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, MultipleOldDataIsRemovedOnOverflow) {
+    const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+    const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+    const std::vector<uint8_t> input3(maxBufferSize_, '2');
+    buffer_.append(input);
+    buffer_.append(input2);
+    buffer_.append(input3);
+    ASSERT_EQ(1u, buffer_.getData().size());
+    EXPECT_EQ(input3, buffer_.getData().front());
+}
+
+TEST_F(RingbufferTest, AppendingEmptyBufferDoesNotAddGarbage) {
+    const std::vector<uint8_t> input = {};
+    buffer_.append(input);
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, OversizedAppendIsDropped) {
+    const std::vector<uint8_t> input(maxBufferSize_ + 1, '0');
+    buffer_.append(input);
+    ASSERT_TRUE(buffer_.getData().empty());
+}
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/wifi.cpp b/wifi/1.2/default/wifi.cpp
index 06f5058..d6106b4 100644
--- a/wifi/1.2/default/wifi.cpp
+++ b/wifi/1.2/default/wifi.cpp
@@ -77,6 +77,12 @@
                            &Wifi::getChipInternal, hidl_status_cb, chip_id);
 }
 
+Return<void> Wifi::debug(const hidl_handle& handle,
+                         const hidl_vec<hidl_string>&) {
+    LOG(INFO) << "-----------Debug is called----------------";
+    return chip_->debug(handle, {});
+}
+
 WifiStatus Wifi::registerEventCallbackInternal(
     const sp<IWifiEventCallback>& event_callback) {
     if (!event_cb_handler_.addCallback(event_callback)) {
diff --git a/wifi/1.2/default/wifi.h b/wifi/1.2/default/wifi.h
index 440c3c7..86919b1 100644
--- a/wifi/1.2/default/wifi.h
+++ b/wifi/1.2/default/wifi.h
@@ -56,6 +56,8 @@
     Return<void> stop(stop_cb hidl_status_cb) override;
     Return<void> getChipIds(getChipIds_cb hidl_status_cb) override;
     Return<void> getChip(ChipId chip_id, getChip_cb hidl_status_cb) override;
+    Return<void> debug(const hidl_handle& handle,
+                       const hidl_vec<hidl_string>& options) override;
 
    private:
     enum class RunState { STOPPED, STARTED, STOPPING };
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 8d9cfc6..bc3404a 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <cutils/properties.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
 
 #include "hidl_return_util.h"
 #include "hidl_struct_util.h"
@@ -23,6 +28,7 @@
 #include "wifi_status_util.h"
 
 namespace {
+using android::base::unique_fd;
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::wifi::V1_0::ChipModeId;
@@ -39,6 +45,11 @@
 // Mode ID for V2
 constexpr ChipModeId kV2ChipModeId = 2;
 
+constexpr char kCpioMagic[] = "070701";
+constexpr size_t kMaxBufferSizeBytes = 1024 * 1024;
+constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60;
+constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
+
 template <typename Iface>
 void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
     iface->invalidate();
@@ -93,6 +104,165 @@
     return buffer.data();
 }
 
+// delete files older than a predefined time in the wifi tombstone dir
+bool removeOldFilesInternal() {
+    time_t now = time(0);
+    const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
+    DIR* dir_dump = opendir(kTombstoneFolderPath);
+    if (!dir_dump) {
+        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        return false;
+    }
+    unique_fd dir_auto_closer(dirfd(dir_dump));
+    struct dirent* dp;
+    bool success = true;
+    while ((dp = readdir(dir_dump))) {
+        if (dp->d_type != DT_REG) {
+            continue;
+        }
+        std::string cur_file_name(dp->d_name);
+        struct stat cur_file_stat;
+        std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+        if (stat(cur_file_path.c_str(), &cur_file_stat) != -1) {
+            if (cur_file_stat.st_mtime < delete_files_before) {
+                if (unlink(cur_file_path.c_str()) != 0) {
+                    LOG(ERROR) << "Error deleting file " << strerror(errno);
+                    success = false;
+                }
+            }
+        } else {
+            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+                       << ": " << strerror(errno);
+            success = false;
+        }
+    }
+    return success;
+}
+
+// Archives all files in |input_dir| and writes result into |out_fd|
+// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
+// portion
+size_t cpioFilesInDir(int out_fd, const char* input_dir) {
+    struct dirent* dp;
+    size_t n_error = 0;
+    char read_buf[32 * 1024];
+    DIR* dir_dump = opendir(input_dir);
+    if (!dir_dump) {
+        LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+        n_error++;
+        return n_error;
+    }
+    unique_fd dir_auto_closer(dirfd(dir_dump));
+    while ((dp = readdir(dir_dump))) {
+        if (dp->d_type != DT_REG) {
+            continue;
+        }
+        std::string cur_file_name(dp->d_name);
+        const size_t file_name_len =
+            cur_file_name.size() + 1;  // string.size() does not include the
+                                       // null terminator. The cpio FreeBSD file
+                                       // header expects the null character to
+                                       // be included in the length.
+        struct stat st;
+        ssize_t llen;
+        const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+        if (stat(cur_file_path.c_str(), &st) != -1) {
+            const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
+            unique_fd file_auto_closer(fd_read);
+            if (fd_read == -1) {
+                LOG(ERROR) << "Failed to read file " << cur_file_path << " "
+                           << strerror(errno);
+                n_error++;
+                continue;
+            }
+            llen = sprintf(
+                read_buf,
+                "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
+                kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid,
+                st.st_gid, static_cast<int>(st.st_nlink),
+                static_cast<int>(st.st_mtime), static_cast<int>(st.st_size),
+                major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
+                minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
+            if (write(out_fd, read_buf, llen) == -1) {
+                LOG(ERROR) << "Error writing cpio header to file "
+                           << cur_file_path << " " << strerror(errno);
+                n_error++;
+                return n_error;
+            }
+            if (write(out_fd, cur_file_name.c_str(), file_name_len) == -1) {
+                LOG(ERROR) << "Error writing filename to file " << cur_file_path
+                           << " " << strerror(errno);
+                n_error++;
+                return n_error;
+            }
+
+            // NUL Pad header up to 4 multiple bytes.
+            llen = (llen + file_name_len) % 4;
+            if (llen != 0) {
+                const uint32_t zero = 0;
+                if (write(out_fd, &zero, 4 - llen) == -1) {
+                    LOG(ERROR) << "Error padding 0s to file " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+            }
+
+            // writing content of file
+            llen = st.st_size;
+            while (llen > 0) {
+                ssize_t bytes_read = read(fd_read, read_buf, sizeof(read_buf));
+                if (bytes_read == -1) {
+                    LOG(ERROR) << "Error reading file " << cur_file_path << " "
+                               << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+                llen -= bytes_read;
+                if (write(out_fd, read_buf, bytes_read) == -1) {
+                    LOG(ERROR) << "Error writing data to file " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    return n_error;
+                }
+                if (bytes_read ==
+                    0) {  // this should never happen, but just in case
+                          // to unstuck from while loop
+                    LOG(ERROR) << "Unexpected file size for " << cur_file_path
+                               << " " << strerror(errno);
+                    n_error++;
+                    break;
+                }
+            }
+            llen = st.st_size % 4;
+            if (llen != 0) {
+                const uint32_t zero = 0;
+                write(out_fd, &zero, 4 - llen);
+            }
+        } else {
+            LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+                       << ": " << strerror(errno);
+            n_error++;
+        }
+    }
+    memset(read_buf, 0, sizeof(read_buf));
+    if (write(out_fd, read_buf,
+              sprintf(read_buf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0) +
+                  4) == -1) {
+        LOG(ERROR) << "Error writing trailing bytes " << strerror(errno);
+        n_error++;
+    }
+    return n_error;
+}
+
+// Helper function to create a non-const char*.
+std::vector<char> makeCharVec(const std::string& str) {
+    std::vector<char> vec(str.size() + 1);
+    vec.assign(str.begin(), str.end());
+    vec.push_back('\0');
+    return vec;
+}
+
 }  // namespace
 
 namespace android {
@@ -350,6 +520,24 @@
                            hidl_status_cb);
 }
 
+Return<void> WifiChip::debug(const hidl_handle& handle,
+                             const hidl_vec<hidl_string>&) {
+    if (handle != nullptr && handle->numFds >= 1) {
+        int fd = handle->data[0];
+        if (!writeRingbufferFilesInternal()) {
+            LOG(ERROR) << "Error writing files to flash";
+        }
+        uint32_t n_error = cpioFilesInDir(fd, kTombstoneFolderPath);
+        if (n_error != 0) {
+            LOG(ERROR) << n_error << " errors occured in cpio function";
+        }
+        fsync(fd);
+    } else {
+        LOG(ERROR) << "File handle error";
+    }
+    return Void();
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
     invalidateAndClearAll(ap_ifaces_);
     invalidateAndClearAll(nan_ifaces_);
@@ -727,6 +915,8 @@
                 std::underlying_type<WifiDebugRingBufferVerboseLevel>::type>(
                 verbose_level),
             max_interval_in_sec, min_data_size_in_bytes);
+    ringbuffer_map_.insert(std::pair<std::string, Ringbuffer>(
+        ring_name, Ringbuffer(kMaxBufferSizeBytes)));
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -738,6 +928,7 @@
     }
     legacy_hal::wifi_error legacy_status =
         legacy_hal_.lock()->getRingBufferData(getWlan0IfaceName(), ring_name);
+
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -849,7 +1040,7 @@
 
     android::wp<WifiChip> weak_ptr_this(this);
     const auto& on_ring_buffer_data_callback =
-        [weak_ptr_this](const std::string& /* name */,
+        [weak_ptr_this](const std::string& name,
                         const std::vector<uint8_t>& data,
                         const legacy_hal::wifi_ring_buffer_status& status) {
             const auto shared_ptr_this = weak_ptr_this.promote();
@@ -863,13 +1054,13 @@
                 LOG(ERROR) << "Error converting ring buffer status";
                 return;
             }
-            for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
-                if (!callback->onDebugRingBufferDataAvailable(hidl_status, data)
-                         .isOk()) {
-                    LOG(ERROR)
-                        << "Failed to invoke onDebugRingBufferDataAvailable"
-                        << " callback on: " << toString(callback);
-                }
+            const auto& target = shared_ptr_this->ringbuffer_map_.find(name);
+            if (target != shared_ptr_this->ringbuffer_map_.end()) {
+                Ringbuffer& cur_buffer = target->second;
+                cur_buffer.append(data);
+            } else {
+                LOG(ERROR) << "Ringname " << name << " not found";
+                return;
             }
         };
     legacy_hal::wifi_error legacy_status =
@@ -1080,6 +1271,35 @@
     return {};
 }
 
+bool WifiChip::writeRingbufferFilesInternal() {
+    if (!removeOldFilesInternal()) {
+        LOG(ERROR) << "Error occurred while deleting old tombstone files";
+        return false;
+    }
+    // write ringbuffers to file
+    for (const auto& item : ringbuffer_map_) {
+        const Ringbuffer& cur_buffer = item.second;
+        if (cur_buffer.getData().empty()) {
+            continue;
+        }
+        const std::string file_path_raw =
+            kTombstoneFolderPath + item.first + "XXXXXXXXXX";
+        const int dump_fd = mkstemp(makeCharVec(file_path_raw).data());
+        if (dump_fd == -1) {
+            LOG(ERROR) << "create file failed: " << strerror(errno);
+            return false;
+        }
+        unique_fd file_auto_closer(dump_fd);
+        for (const auto& cur_block : cur_buffer.getData()) {
+            if (write(dump_fd, cur_block.data(),
+                      sizeof(cur_block[0]) * cur_block.size()) == -1) {
+                LOG(ERROR) << "Error writing to file " << strerror(errno);
+            }
+        }
+    }
+    return true;
+}
+
 }  // namespace implementation
 }  // namespace V1_2
 }  // namespace wifi
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index b5dcc8c..4ffa8da 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -17,12 +17,14 @@
 #ifndef WIFI_CHIP_H_
 #define WIFI_CHIP_H_
 
+#include <list>
 #include <map>
 
 #include <android-base/macros.h>
 #include <android/hardware/wifi/1.2/IWifiChip.h>
 
 #include "hidl_callback_util.h"
+#include "ringbuffer.h"
 #include "wifi_ap_iface.h"
 #include "wifi_feature_flags.h"
 #include "wifi_legacy_hal.h"
@@ -134,6 +136,8 @@
         selectTxPowerScenario_cb hidl_status_cb) override;
     Return<void> resetTxPowerScenario(
         resetTxPowerScenario_cb hidl_status_cb) override;
+    Return<void> debug(const hidl_handle& handle,
+                       const hidl_vec<hidl_string>& options) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -204,6 +208,7 @@
     bool canCurrentModeSupportIfaceOfType(IfaceType type);
     bool isValidModeId(ChipModeId mode_id);
     std::string allocateApOrStaIfaceName();
+    bool writeRingbufferFilesInternal();
 
     ChipId chip_id_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
@@ -214,6 +219,7 @@
     std::vector<sp<WifiP2pIface>> p2p_ifaces_;
     std::vector<sp<WifiStaIface>> sta_ifaces_;
     std::vector<sp<WifiRttController>> rtt_controllers_;
+    std::map<std::string, Ringbuffer> ringbuffer_map_;
     bool is_valid_;
     // Members pertaining to chip configuration.
     uint32_t current_mode_id_;