Merge "Shuffle the encoding to prevent systematic errors"
diff --git a/automotive/can/1.0/vts/functional/Android.bp b/automotive/can/1.0/vts/functional/Android.bp
index b4d9132..e3e770b 100644
--- a/automotive/can/1.0/vts/functional/Android.bp
+++ b/automotive/can/1.0/vts/functional/Android.bp
@@ -16,13 +16,16 @@
 
 cc_defaults {
     name: "android.hardware.automotive.can@vts-defaults",
-    defaults: ["VtsHalTargetTestDefaults", "android.hardware.automotive.can@defaults"],
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "android.hardware.automotive.can@defaults",
+    ],
     header_libs: [
         "android.hardware.automotive.can@hidl-utils-lib",
-        "android.hardware.automotive.can@vts-utils-lib",
     ],
     static_libs: [
         "android.hardware.automotive.can@1.0",
+        "android.hardware.automotive.can@vts-utils-lib",
         "libgmock",
     ],
     test_suites: ["general-tests"],
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
index 1663663..ca661fe 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
@@ -21,6 +21,7 @@
 #include <android/hardware/automotive/can/1.0/ICanController.h>
 #include <android/hardware/automotive/can/1.0/types.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
+#include <can-vts-utils/bus-enumerator.h>
 #include <can-vts-utils/can-hal-printers.h>
 #include <can-vts-utils/environment-utils.h>
 #include <gmock/gmock.h>
@@ -139,14 +140,20 @@
 
     Bus makeBus();
 
+  protected:
+    static hidl_vec<hidl_string> mBusNames;
+
   private:
     unsigned mLastIface = 0;
     static sp<ICanController> mCanController;
     static bool mVirtualSupported;
+    static bool mTestCaseInitialized;
 };
 
 sp<ICanController> CanBusVirtualHalTest::mCanController = nullptr;
 bool CanBusVirtualHalTest::mVirtualSupported;
+hidl_vec<hidl_string> CanBusVirtualHalTest::mBusNames;
+bool CanBusVirtualHalTest::mTestCaseInitialized = false;
 
 static CanMessage makeMessage(CanMessageId id) {
     CanMessage msg = {};
@@ -160,6 +167,7 @@
 
 void CanBusVirtualHalTest::SetUp() {
     if (!mVirtualSupported) GTEST_SKIP();
+    ASSERT_TRUE(mTestCaseInitialized);
 }
 
 void CanBusVirtualHalTest::SetUpTestCase() {
@@ -170,6 +178,11 @@
     hidl_vec<InterfaceType> supported;
     mCanController->getSupportedInterfaceTypes(hidl_utils::fill(&supported)).assertOk();
     mVirtualSupported = supported.contains(InterfaceType::VIRTUAL);
+
+    mBusNames = utils::getBusNames();
+    ASSERT_NE(0u, mBusNames.size()) << "No ICanBus HALs defined in device manifest";
+
+    mTestCaseInitialized = true;
 }
 
 void CanBusVirtualHalTest::TearDownTestCase() {
@@ -177,10 +190,11 @@
 }
 
 Bus CanBusVirtualHalTest::makeBus() {
-    const auto idx = ++mLastIface;
+    const auto idx = mLastIface++;
+    EXPECT_LT(idx, mBusNames.size());
 
     ICanController::BusConfiguration config = {};
-    config.name = "test" + std::to_string(idx);
+    config.name = mBusNames[idx];
     config.iftype = InterfaceType::VIRTUAL;
     config.interfaceId.address("vcan50");
 
@@ -207,6 +221,7 @@
 }
 
 TEST_F(CanBusVirtualHalTest, SendAndRecv) {
+    if (mBusNames.size() < 2u) GTEST_SKIP() << "Not testable with less than two CAN buses.";
     auto bus1 = makeBus();
     auto bus2 = makeBus();
 
@@ -226,6 +241,8 @@
 }
 
 TEST_F(CanBusVirtualHalTest, DownOneOfTwo) {
+    if (mBusNames.size() < 2u) GTEST_SKIP() << "Not testable with less than two CAN buses.";
+
     auto bus1 = makeBus();
     auto bus2 = makeBus();
 
@@ -235,6 +252,7 @@
 }
 
 TEST_F(CanBusVirtualHalTest, Filter) {
+    if (mBusNames.size() < 2u) GTEST_SKIP() << "Not testable with less than two CAN buses.";
     auto bus1 = makeBus();
     auto bus2 = makeBus();
 
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
index 22dec2c..9bc789a 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
@@ -21,6 +21,7 @@
 #include <android/hardware/automotive/can/1.0/ICanController.h>
 #include <android/hardware/automotive/can/1.0/types.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
+#include <can-vts-utils/bus-enumerator.h>
 #include <can-vts-utils/can-hal-printers.h>
 #include <can-vts-utils/environment-utils.h>
 #include <gmock/gmock.h>
@@ -37,6 +38,7 @@
   protected:
     virtual void SetUp() override;
     virtual void TearDown() override;
+    static void SetUpTestCase();
 
     hidl_vec<InterfaceType> getSupportedInterfaceTypes();
     bool isSupported(InterfaceType iftype);
@@ -46,9 +48,18 @@
     void assertRegistered(const std::string srvname, bool expectRegistered);
 
     sp<ICanController> mCanController;
+    static hidl_vec<hidl_string> mBusNames;
+
+  private:
+    static bool mTestCaseInitialized;
 };
 
+hidl_vec<hidl_string> CanControllerHalTest::mBusNames;
+bool CanControllerHalTest::mTestCaseInitialized = false;
+
 void CanControllerHalTest::SetUp() {
+    ASSERT_TRUE(mTestCaseInitialized);
+
     const auto serviceName = gEnv->getServiceName<ICanController>();
     mCanController = getService<ICanController>(serviceName);
     ASSERT_TRUE(mCanController) << "Couldn't open CAN Controller: " << serviceName;
@@ -58,6 +69,13 @@
     mCanController.clear();
 }
 
+void CanControllerHalTest::SetUpTestCase() {
+    mBusNames = utils::getBusNames();
+    ASSERT_NE(0u, mBusNames.size()) << "No ICanBus HALs defined in device manifest";
+
+    mTestCaseInitialized = true;
+}
+
 hidl_vec<InterfaceType> CanControllerHalTest::getSupportedInterfaceTypes() {
     hidl_vec<InterfaceType> iftypesResult;
     mCanController->getSupportedInterfaceTypes(hidl_utils::fill(&iftypesResult)).assertOk();
@@ -104,7 +122,7 @@
 }
 
 TEST_F(CanControllerHalTest, BringUpDown) {
-    const std::string name = "dummy";
+    const std::string name = mBusNames[0];
 
     assertRegistered(name, false);
     if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::OK)) GTEST_SKIP();
@@ -122,7 +140,7 @@
 }
 
 TEST_F(CanControllerHalTest, UpTwice) {
-    const std::string name = "dummy";
+    const std::string name = mBusNames[0];
 
     assertRegistered(name, false);
     if (!up(InterfaceType::VIRTUAL, name, "vcan72", ICanController::Result::OK)) GTEST_SKIP();
@@ -211,7 +229,7 @@
 }
 
 TEST_F(CanControllerHalTest, FailBadVirtualAddress) {
-    const std::string name = "dummy";
+    const std::string name = mBusNames[0];
 
     assertRegistered(name, false);
     if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_ADDRESS)) GTEST_SKIP();
@@ -219,7 +237,7 @@
 }
 
 TEST_F(CanControllerHalTest, FailBadSocketcanAddress) {
-    const std::string name = "dummy";
+    const std::string name = mBusNames[0];
 
     assertRegistered(name, false);
     if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_ADDRESS)) {
diff --git a/automotive/can/1.0/vts/utils/Android.bp b/automotive/can/1.0/vts/utils/Android.bp
index e925c8f..d03ead3 100644
--- a/automotive/can/1.0/vts/utils/Android.bp
+++ b/automotive/can/1.0/vts/utils/Android.bp
@@ -14,7 +14,17 @@
 // limitations under the License.
 //
 
-cc_library_headers {
+cc_library_static {
     name: "android.hardware.automotive.can@vts-utils-lib",
+    defaults: ["android.hardware.automotive.can@defaults"],
+    srcs: [
+        "bus-enumerator.cpp",
+    ],
     export_include_dirs: ["include"],
+    header_libs: [
+        "android.hardware.automotive.can@hidl-utils-lib",
+    ],
+    static_libs: [
+        "android.hardware.automotive.can@1.0",
+    ],
 }
diff --git a/automotive/can/1.0/vts/utils/bus-enumerator.cpp b/automotive/can/1.0/vts/utils/bus-enumerator.cpp
new file mode 100644
index 0000000..c012dd2
--- /dev/null
+++ b/automotive/can/1.0/vts/utils/bus-enumerator.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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/hidl/manager/1.2/IServiceManager.h>
+#include <can-vts-utils/bus-enumerator.h>
+#include <hidl-utils/hidl-utils.h>
+
+namespace android::hardware::automotive::can::V1_0::vts::utils {
+
+hidl_vec<hidl_string> getBusNames() {
+    auto manager = hidl::manager::V1_2::IServiceManager::getService();
+    hidl_vec<hidl_string> services;
+    manager->listManifestByInterface(ICanBus::descriptor, hidl_utils::fill(&services));
+    return services;
+}
+
+}  // namespace android::hardware::automotive::can::V1_0::vts::utils
diff --git a/automotive/can/1.0/vts/utils/include/can-vts-utils/bus-enumerator.h b/automotive/can/1.0/vts/utils/include/can-vts-utils/bus-enumerator.h
new file mode 100644
index 0000000..ef385eb
--- /dev/null
+++ b/automotive/can/1.0/vts/utils/include/can-vts-utils/bus-enumerator.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/can/1.0/ICanBus.h>
+
+namespace android::hardware::automotive::can::V1_0::vts::utils {
+
+hidl_vec<hidl_string> getBusNames();
+
+}  // namespace android::hardware::automotive::can::V1_0::vts::utils
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index 975b6c6..fc68e60 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -34,6 +34,23 @@
     getCameraInfo_1_1() generates (CameraDesc info);
 
     /**
+     * Returns the description of the physical camera device that backs this
+     * logical camera.
+     *
+     * If a requested device does not either exist or back this logical device,
+     * this method returns a null camera descriptor.  And, if this is called on
+     * a physical camera device, this method is the same as getCameraInfo_1_1()
+     * method if a given device ID is matched.  Otherwise, this will return a
+     * null camera descriptor.
+     *
+     * @param  deviceId Physical camera device identifier string.
+     * @return info     The description of a member physical camera device.
+     *                  This must be the same value as reported by
+     *                  EvsEnumerator::getCameraList_1_1().
+     */
+    getPhysicalCameraInfo(string deviceId) generates (CameraDesc info);
+
+    /**
      * Requests to pause EVS camera stream events.
      *
      * Like stopVideoStream(), events may continue to arrive for some time
@@ -51,7 +68,7 @@
     resumeVideoStream() generates (EvsResult result);
 
     /**
-     * Returns a frame that was delivered by to the IEvsCameraStream.
+     * Returns frame that were delivered by to the IEvsCameraStream.
      *
      * When done consuming a frame delivered to the IEvsCameraStream
      * interface, it must be returned to the IEvsCamera for reuse.
@@ -59,10 +76,10 @@
      * as one), and if the supply is exhausted, no further frames may be
      * delivered until a buffer is returned.
      *
-     * @param  buffer A buffer to be returned.
+     * @param  buffer Buffers to be returned.
      * @return result Return EvsResult::OK if this call is successful.
      */
-    doneWithFrame_1_1(BufferDesc buffer) generates (EvsResult result);
+    doneWithFrame_1_1(vec<BufferDesc> buffer) generates (EvsResult result);
 
     /**
      * Requests to be a master client.
@@ -127,8 +144,13 @@
         generates (int32_t min, int32_t max, int32_t step);
 
     /**
-     * Requests to set a camera parameter.  Only a request from the master
-     * client will be processed successfully.
+     * Requests to set a camera parameter.
+     *
+     * Only a request from the master client will be processed successfully.
+     * When this method is called on a logical camera device, it will be forwarded
+     * to each physical device and, if it fails to program any physical device,
+     * it will return an error code with the same number of effective values as
+     * the number of backing camera devices.
      *
      * @param  id             The identifier of camera parameter, CameraParam enum.
      *         value          A desired parameter value.
@@ -138,21 +160,22 @@
      *                        parameter is not supported.
      *                        EvsResult::UNDERLYING_SERVICE_ERROR if it fails to
      *                        program a value by any other reason.
-     *         effectiveValue A programmed parameter value.  This may differ
+     *         effectiveValue Programmed parameter values.  This may differ
      *                        from what the client gives if, for example, the
      *                        driver does not support a target parameter.
      */
     setIntParameter(CameraParam id, int32_t value)
-        generates (EvsResult result, int32_t effectiveValue);
+        generates (EvsResult result, vec<int32_t> effectiveValue);
 
     /**
-     * Retrieves a value of given camera parameter.
+     * Retrieves values of given camera parameter.
      *
      * @param  id     The identifier of camera parameter, CameraParam enum.
      * @return result EvsResult::OK if it succeeds to read a parameter.
      *                EvsResult::INVALID_ARG if either a requested parameter is
      *                not supported.
-     *         value  A value of requested camera parameter.
+     *         value  Values of requested camera parameter, the same number of
+     *                values as backing camera devices.
      */
-    getIntParameter(CameraParam id) generates(EvsResult result, int32_t value);
+    getIntParameter(CameraParam id) generates(EvsResult result, vec<int32_t> value);
 };
diff --git a/automotive/evs/1.1/IEvsCameraStream.hal b/automotive/evs/1.1/IEvsCameraStream.hal
index 9e4ea19..aa35c62 100644
--- a/automotive/evs/1.1/IEvsCameraStream.hal
+++ b/automotive/evs/1.1/IEvsCameraStream.hal
@@ -18,7 +18,7 @@
 
 import @1.0::IEvsCameraStream;
 import @1.1::BufferDesc;
-import @1.1::EvsEvent;
+import @1.1::EvsEventDesc;
 
 /**
  * Implemented on client side to receive asynchronous streaming event deliveries.
@@ -26,7 +26,7 @@
 interface IEvsCameraStream extends @1.0::IEvsCameraStream {
 
     /**
-     * Receives calls from the HAL each time a video frame is ready for inspection.
+     * Receives calls from the HAL each time video frames is ready for inspection.
      * Buffer handles received by this method must be returned via calls to
      * IEvsCamera::doneWithFrame_1_1(). When the video stream is stopped via a call
      * to IEvsCamera::stopVideoStream(), this callback may continue to happen for
@@ -35,14 +35,19 @@
      * event must be delivered.  No further frame deliveries may happen
      * thereafter.
      *
-     * @param buffer a buffer descriptor of a delivered image frame.
+     * A camera device will deliver the same number of frames as number of
+     * backing physical camera devices; it means, a physical camera device
+     * sends always a single frame and a logical camera device sends multiple
+     * frames as many as number of backing physical camera devices.
+     *
+     * @param buffer Buffer descriptors of delivered image frames.
      */
-    oneway deliverFrame_1_1(BufferDesc buffer);
+    oneway deliverFrame_1_1(vec<BufferDesc> buffer);
 
     /**
      * Receives calls from the HAL each time an event happens.
      *
      * @param  event EVS event with possible event information.
      */
-    oneway notify(EvsEvent event);
+    oneway notify(EvsEventDesc event);
 };
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index 41cb426..88fd657 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -16,7 +16,7 @@
     shared_libs: [
         "android.hardware.automotive.evs@1.0",
         "android.hardware.automotive.evs@1.1",
-        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
         "libbase",
         "libbinder",
         "liblog",
diff --git a/automotive/evs/1.1/default/ConfigManager.cpp b/automotive/evs/1.1/default/ConfigManager.cpp
index 96a2f98..986793e 100644
--- a/automotive/evs/1.1/default/ConfigManager.cpp
+++ b/automotive/evs/1.1/default/ConfigManager.cpp
@@ -42,55 +42,32 @@
     while (curElem != nullptr) {
         if (!strcmp(curElem->Name(), "group")) {
             /* camera group identifier */
-            const char *group_id = curElem->FindAttribute("group_id")->Value();
+            const char *id = curElem->FindAttribute("id")->Value();
 
-            /* create CameraGroup */
-            unique_ptr<ConfigManager::CameraGroup> aCameraGroup(new ConfigManager::CameraGroup());
+            /* create a camera group to be filled */
+            CameraGroupInfo *aCamera = new CameraGroupInfo();
 
-            /* add a camera device to its group */
-            addCameraDevices(curElem->FindAttribute("device_id")->Value(), aCameraGroup);
-
-            /* a list of camera stream configurations */
-            const XMLElement *childElem =
-                curElem->FirstChildElement("caps")->FirstChildElement("stream");
-            while (childElem != nullptr) {
-                /* read 5 attributes */
-                const XMLAttribute *idAttr     = childElem->FindAttribute("id");
-                const XMLAttribute *widthAttr  = childElem->FindAttribute("width");
-                const XMLAttribute *heightAttr = childElem->FindAttribute("height");
-                const XMLAttribute *fmtAttr    = childElem->FindAttribute("format");
-                const XMLAttribute *fpsAttr    = childElem->FindAttribute("framerate");
-
-                const int32_t id = stoi(idAttr->Value());
-                int32_t framerate = 0;
-                if (fpsAttr != nullptr) {
-                    framerate = stoi(fpsAttr->Value());
-                }
-
-                int32_t pixFormat;
-                if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(),
-                                                            pixFormat)) {
-                    RawStreamConfiguration cfg = {
-                        id,
-                        stoi(widthAttr->Value()),
-                        stoi(heightAttr->Value()),
-                        pixFormat,
-                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
-                        framerate
-                    };
-                    aCameraGroup->streamConfigurations[id] = cfg;
-                }
-
-                childElem = childElem->NextSiblingElement("stream");
+            /* read camera device information */
+            if (!readCameraDeviceInfo(aCamera, curElem)) {
+                ALOGW("Failed to read a camera information of %s", id);
+                delete aCamera;
+                continue;
             }
 
             /* camera group synchronization */
             const char *sync = curElem->FindAttribute("synchronized")->Value();
-            aCameraGroup->synchronized =
-                static_cast<bool>(strcmp(sync, "false"));
+            if (!strcmp(sync, "CALIBRATED")) {
+                aCamera->synchronized =
+                    ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
+            } else if (!strcmp(sync, "APPROXIMATE")) {
+                aCamera->synchronized =
+                    ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
+            } else {
+                aCamera->synchronized = 0; // Not synchronized
+            }
 
             /* add a group to hash map */
-            mCameraGroups[group_id] = std::move(aCameraGroup);
+            mCameraGroupInfos.insert_or_assign(id, unique_ptr<CameraGroupInfo>(aCamera));
         } else if (!strcmp(curElem->Name(), "device")) {
             /* camera unique identifier */
             const char *id = curElem->FindAttribute("id")->Value();
@@ -98,8 +75,18 @@
             /* camera mount location */
             const char *pos = curElem->FindAttribute("position")->Value();
 
+            /* create a camera device to be filled */
+            CameraInfo *aCamera = new CameraInfo();
+
+            /* read camera device information */
+            if (!readCameraDeviceInfo(aCamera, curElem)) {
+                ALOGW("Failed to read a camera information of %s", id);
+                delete aCamera;
+                continue;
+            }
+
             /* store read camera module information */
-            mCameraInfo[id] = readCameraDeviceInfo(curElem);
+            mCameraInfo.insert_or_assign(id, unique_ptr<CameraInfo>(aCamera));
 
             /* assign a camera device to a position group */
             mCameraPosition[pos].emplace(id);
@@ -113,15 +100,13 @@
 }
 
 
-unique_ptr<ConfigManager::CameraInfo>
-ConfigManager::readCameraDeviceInfo(const XMLElement *aDeviceElem) {
-    if (aDeviceElem == nullptr) {
-        return nullptr;
+bool
+ConfigManager::readCameraDeviceInfo(CameraInfo *aCamera,
+                                    const XMLElement *aDeviceElem) {
+    if (aCamera == nullptr || aDeviceElem == nullptr) {
+        return false;
     }
 
-    /* create a CameraInfo to be filled */
-    unique_ptr<ConfigManager::CameraInfo> aCamera(new ConfigManager::CameraInfo());
-
     /* size information to allocate camera_metadata_t */
     size_t totalEntries = 0;
     size_t totalDataSize = 0;
@@ -145,14 +130,15 @@
               "allocated memory was not large enough");
     }
 
-    return aCamera;
+    return true;
 }
 
 
-size_t ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
-                                             unique_ptr<ConfigManager::CameraInfo> &aCamera,
-                                             size_t &dataSize) {
-    if (aCapElem == nullptr) {
+size_t
+ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
+                                      CameraInfo *aCamera,
+                                      size_t &dataSize) {
+    if (aCapElem == nullptr || aCamera == nullptr) {
         return 0;
     }
 
@@ -214,7 +200,7 @@
                 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
                 framerate
             };
-            aCamera->streamConfigurations[id] = cfg;
+            aCamera->streamConfigurations.insert_or_assign(id, cfg);
         }
 
         curElem = curElem->NextSiblingElement("stream");
@@ -232,10 +218,11 @@
 }
 
 
-size_t ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
-                                       unique_ptr<ConfigManager::CameraInfo> &aCamera,
-                                       size_t &dataSize) {
-    if (aParamElem == nullptr) {
+size_t
+ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
+                                  CameraInfo *aCamera,
+                                  size_t &dataSize) {
+    if (aParamElem == nullptr || aCamera == nullptr) {
         return 0;
     }
 
@@ -258,8 +245,9 @@
                                         count
                                    );
 
-                    aCamera->cameraMetadata[tag] =
-                        make_pair(make_unique<void *>(data), count);
+                    aCamera->cameraMetadata.insert_or_assign(
+                        tag, make_pair(make_unique<void *>(data), count)
+                    );
 
                     ++numEntries;
                     dataSize += calculate_camera_metadata_entry_data_size(
@@ -269,6 +257,52 @@
                     break;
                 }
 
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+                    camera_metadata_enum_android_request_available_capabilities_t *data =
+                        new camera_metadata_enum_android_request_available_capabilities_t[1];
+                    if (ConfigManagerUtil::convertToCameraCapability(
+                            curElem->FindAttribute("value")->Value(), *data)) {
+                                        curElem->FindAttribute("value")->Value(),
+                        aCamera->cameraMetadata.insert_or_assign(
+                            tag, make_pair(make_unique<void *>(data), 1)
+                        );
+
+                        ++numEntries;
+                        dataSize += calculate_camera_metadata_entry_data_size(
+                                        get_camera_metadata_tag_type(tag), 1
+                                    );
+                    }
+                    break;
+                }
+
+                case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+                    /* a comma-separated list of physical camera devices */
+                    size_t len = strlen(curElem->FindAttribute("value")->Value());
+                    char *data = new char[len + 1];
+                    memcpy(data,
+                           curElem->FindAttribute("value")->Value(),
+                           len * sizeof(char));
+
+                    /* replace commas with null char */
+                    char *p = data;
+                    while (*p != '\0') {
+                        if (*p == ',') {
+                            *p = '\0';
+                        }
+                        ++p;
+                    }
+
+                    aCamera->cameraMetadata.insert_or_assign(
+                        tag, make_pair(make_unique<void *>(data), len)
+                    );
+
+                    ++numEntries;
+                    dataSize += calculate_camera_metadata_entry_data_size(
+                                    get_camera_metadata_tag_type(tag), len
+                                );
+                    break;
+                }
+
                 default:
                     ALOGW("Parameter %s is not supported",
                           curElem->FindAttribute("name")->Value());
@@ -283,10 +317,11 @@
 }
 
 
-bool ConfigManager::constructCameraMetadata(unique_ptr<CameraInfo> &aCamera,
-                                            const size_t totalEntries,
-                                            const size_t totalDataSize) {
-    if (!aCamera->allocate(totalEntries, totalDataSize)) {
+bool
+ConfigManager::constructCameraMetadata(CameraInfo *aCamera,
+                                       const size_t totalEntries,
+                                       const size_t totalDataSize) {
+    if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
         ALOGE("Failed to allocate memory for camera metadata");
         return false;
     }
@@ -401,14 +436,14 @@
                         ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
                         0   // unused
                     };
-                    dpy->streamConfigurations[id] = cfg;
+                    dpy->streamConfigurations.insert_or_assign(id, cfg);
                 }
 
                 curStream = curStream->NextSiblingElement("stream");
             }
         }
 
-        mDisplayInfo[id] = std::move(dpy);
+        mDisplayInfo.insert_or_assign(id, std::move(dpy));
         curDev = curDev->NextSiblingElement("device");
     }
 
@@ -457,16 +492,6 @@
 }
 
 
-void ConfigManager::addCameraDevices(const char *devices,
-                                     unique_ptr<CameraGroup> &aGroup) {
-    stringstream device_list(devices);
-    string token;
-    while (getline(device_list, token, ',')) {
-        aGroup->devices.emplace(token);
-    }
-}
-
-
 std::unique_ptr<ConfigManager> ConfigManager::Create(const char *path) {
     unique_ptr<ConfigManager> cfgMgr(new ConfigManager(path));
 
diff --git a/automotive/evs/1.1/default/ConfigManager.h b/automotive/evs/1.1/default/ConfigManager.h
index 0275f90..870af1c 100644
--- a/automotive/evs/1.1/default/ConfigManager.h
+++ b/automotive/evs/1.1/default/ConfigManager.h
@@ -82,9 +82,6 @@
         unordered_map<CameraParam,
                       tuple<int32_t, int32_t, int32_t>> controls;
 
-        /* List of supported frame rates */
-        unordered_set<int32_t> frameRates;
-
         /*
          * List of supported output stream configurations; each array stores
          * format, width, height, and direction values in the order.
@@ -102,21 +99,15 @@
         camera_metadata_t *characteristics;
     };
 
-    class CameraGroup {
+    class CameraGroupInfo : public CameraInfo {
     public:
-        CameraGroup() {}
+        CameraGroupInfo() {}
 
         /* ID of member camera devices */
         unordered_set<string> devices;
 
         /* The capture operation of member camera devices are synchronized */
         bool synchronized = false;
-
-        /*
-         * List of stream configurations that are supposed by all camera devices
-         * in this group.
-         */
-        unordered_map<int32_t, RawStreamConfiguration> streamConfigurations;
     };
 
     class SystemInfo {
@@ -165,11 +156,11 @@
     /*
      * Return a list of cameras
      *
-     * @return CameraGroup
+     * @return CameraGroupInfo
      *         A pointer to a camera group identified by a given id.
      */
-    unique_ptr<CameraGroup>& getCameraGroup(const string& gid) {
-        return mCameraGroups[gid];
+    unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const string& gid) {
+        return mCameraGroupInfos[gid];
     }
 
 
@@ -203,8 +194,8 @@
     /* Internal data structure for camera device information */
     unordered_map<string, unique_ptr<DisplayInfo>> mDisplayInfo;
 
-    /* Camera groups are stored in <groud id, CameraGroup> hash map */
-    unordered_map<string, unique_ptr<CameraGroup>> mCameraGroups;
+    /* Camera groups are stored in <groud id, CameraGroupInfo> hash map */
+    unordered_map<string, unique_ptr<CameraGroupInfo>> mCameraGroupInfos;
 
     /*
      * Camera positions are stored in <position, camera id set> hash map.
@@ -253,16 +244,19 @@
     /*
      * read camera device information
      *
-     * @param  aDeviceElem
+     * @param  aCamera
+     *         A pointer to CameraInfo that will be completed by this
+     *         method.
+     *         aDeviceElem
      *         A pointer to "device" XML element that contains camera module
      *         capability info and its characteristics.
      *
-     * @return unique_ptr<CameraInfo>
-     *         A pointer to CameraInfo class that contains camera module
-     *         capability and characteristics.  Please note that this transfers
-     *         the ownership of created CameraInfo to the caller.
+     * @return bool
+     *         Return false upon any failure in reading and processing camera
+     *         device information.
      */
-    unique_ptr<CameraInfo> readCameraDeviceInfo(const XMLElement *aDeviceElem);
+    bool readCameraDeviceInfo(CameraInfo *aCamera,
+                              const XMLElement *aDeviceElem);
 
     /*
      * read camera metadata
@@ -280,7 +274,7 @@
      *         Number of camera metadata entries
      */
     size_t readCameraCapabilities(const XMLElement * const aCapElem,
-                                  unique_ptr<CameraInfo> &aCamera,
+                                  CameraInfo *aCamera,
                                   size_t &dataSize);
 
     /*
@@ -298,7 +292,7 @@
      *         Number of camera metadata entries
      */
     size_t readCameraMetadata(const XMLElement * const aParamElem,
-                              unique_ptr<CameraInfo> &aCamera,
+                              CameraInfo *aCamera,
                               size_t &dataSize);
 
     /*
@@ -316,21 +310,9 @@
      *         or its size is not large enough to add all found camera metadata
      *         entries.
      */
-    bool constructCameraMetadata(unique_ptr<CameraInfo> &aCamera,
+    bool constructCameraMetadata(CameraInfo *aCamera,
                                  const size_t totalEntries,
                                  const size_t totalDataSize);
-
-    /*
-     * parse a comma-separated list of camera devices and add them to
-     * CameraGroup.
-     *
-     * @param  devices
-     *         A comma-separated list of camera device identifiers.
-     * @param  aGroup
-     *         Camera group which cameras will be added to.
-     */
-    void addCameraDevices(const char *devices,
-                          unique_ptr<CameraGroup> &aGroup);
 };
 #endif // CONFIG_MANAGER_H
 
diff --git a/automotive/evs/1.1/default/ConfigManagerUtil.cpp b/automotive/evs/1.1/default/ConfigManagerUtil.cpp
index 8206daa..d10f236 100644
--- a/automotive/evs/1.1/default/ConfigManagerUtil.cpp
+++ b/automotive/evs/1.1/default/ConfigManagerUtil.cpp
@@ -90,6 +90,30 @@
         aTag =  ANDROID_LENS_POSE_ROTATION;
     } else if (!strcmp(name, "LENS_POSE_TRANSLATION")) {
         aTag =  ANDROID_LENS_POSE_TRANSLATION;
+    } else if (!strcmp(name, "REQUEST_AVAILABLE_CAPABILITIES")) {
+        aTag =  ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
+    } else if (!strcmp(name, "LOGICAL_MULTI_CAMERA_PHYSICAL_IDS")) {
+        aTag =  ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool ConfigManagerUtil::convertToCameraCapability(
+    const char *name,
+    camera_metadata_enum_android_request_available_capabilities_t &cap) {
+
+    if (!strcmp(name, "DEPTH_OUTPUT")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT;
+    } else if (!strcmp(name, "LOGICAL_MULTI_CAMERA")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
+    } else if (!strcmp(name, "MONOCHROME")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
+    } else if (!strcmp(name, "SECURE_IMAGE_DATA")) {
+        cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
     } else {
         return false;
     }
diff --git a/automotive/evs/1.1/default/ConfigManagerUtil.h b/automotive/evs/1.1/default/ConfigManagerUtil.h
index 8c89ae7..1710cac 100644
--- a/automotive/evs/1.1/default/ConfigManagerUtil.h
+++ b/automotive/evs/1.1/default/ConfigManagerUtil.h
@@ -55,6 +55,14 @@
      */
     static string trimString(const string &src,
                              const string &ws = " \n\r\t\f\v");
+
+    /**
+     * Convert a given string to corresponding camera capabilities
+     */
+    static bool convertToCameraCapability(
+        const char *name,
+        camera_metadata_enum_android_request_available_capabilities_t &cap);
+
 };
 
 #endif // CONFIG_MANAGER_UTIL_H
diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp
index 5ba753d..b7e4efa 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -21,7 +21,7 @@
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
-
+#include <utils/SystemClock.h>
 
 namespace android {
 namespace hardware {
@@ -240,9 +240,23 @@
 }
 
 
-Return<EvsResult> EvsCamera::doneWithFrame_1_1(const BufferDesc_1_1& bufDesc)  {
+Return<void> EvsCamera::getPhysicalCameraInfo(const hidl_string& id,
+                                              getCameraInfo_1_1_cb _hidl_cb) {
+    ALOGD("%s", __FUNCTION__);
+
+    // This works exactly same as getCameraInfo_1_1() in default implementation.
+    (void)id;
+    _hidl_cb(mDescription);
+    return Void();
+}
+
+
+Return<EvsResult> EvsCamera::doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffers)  {
     std::lock_guard <std::mutex> lock(mAccessLock);
-    returnBuffer(bufDesc.bufferId, bufDesc.buffer.nativeHandle);
+
+    for (auto&& buffer : buffers) {
+        returnBuffer(buffer.bufferId, buffer.buffer.nativeHandle);
+    }
 
     return EvsResult::OK;
 }
@@ -490,12 +504,17 @@
             newBuffer.buffer.nativeHandle = mBuffers[idx].handle;
             newBuffer.pixelSize = sizeof(uint32_t);
             newBuffer.bufferId = idx;
+            newBuffer.deviceId = mDescription.v1.cameraId;
+            newBuffer.timestamp = elapsedRealtimeNano();
 
             // Write test data into the image buffer
             fillTestFrame(newBuffer);
 
             // Issue the (asynchronous) callback to the client -- can't be holding the lock
-            auto result = mStream->deliverFrame_1_1(newBuffer);
+            hidl_vec<BufferDesc_1_1> frames;
+            frames.resize(1);
+            frames[0] = newBuffer;
+            auto result = mStream->deliverFrame_1_1(frames);
             if (result.isOk()) {
                 ALOGD("Delivered %p as id %d",
                       newBuffer.buffer.nativeHandle.getNativeHandle(), newBuffer.bufferId);
@@ -527,7 +546,7 @@
     }
 
     // If we've been asked to stop, send an event to signal the actual end of stream
-    EvsEvent event;
+    EvsEventDesc event;
     event.aType = EvsEventType::STREAM_STOPPED;
     auto result = mStream->notify(event);
     if (!result.isOk()) {
diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h
index c15b4b1..72a1b57 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -62,9 +62,11 @@
 
     // Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow.
     Return<void>      getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb)  override;
+    Return<void>      getPhysicalCameraInfo(const hidl_string& id,
+                                            getPhysicalCameraInfo_cb _hidl_cb)  override;
     Return<EvsResult> pauseVideoStream() override;
     Return<EvsResult> resumeVideoStream() override;
-    Return<EvsResult> doneWithFrame_1_1(const BufferDesc_1_1& buffer) override;
+    Return<EvsResult> doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffer) override;
     Return<EvsResult> setMaster() override;
     Return<EvsResult> forceMaster(const sp<IEvsDisplay>& display) override;
     Return<EvsResult> unsetMaster() override;
diff --git a/automotive/evs/1.1/default/resources/evs_default_configuration.xml b/automotive/evs/1.1/default/resources/evs_default_configuration.xml
index 692102e..a79e7c2 100644
--- a/automotive/evs/1.1/default/resources/evs_default_configuration.xml
+++ b/automotive/evs/1.1/default/resources/evs_default_configuration.xml
@@ -28,8 +28,31 @@
         <num_cameras value='1'/>
     </system>
 
-    <!-- camera device information -->
+    <!-- camera information -->
     <camera>
+        <!-- camera group starts -->
+        <group id='group1' synchronized='APPROXIMATE'>
+            <caps>
+                <stream id='0' width='640'  height='360'  format='RGBA_8888' framerate='30'/>
+            </caps>
+
+            <!-- list of parameters -->
+            <characteristics>
+                <parameter
+                    name='REQUEST_AVAILABLE_CAPABILITIES'
+                    type='enum'
+                    size='1'
+                    value='LOGICAL_MULTI_CAMERA'
+                />
+                <parameter
+                    name='LOGICAL_MULTI_CAMERA_PHYSICAL_IDS'
+                    type='byte[]'
+                    size='1'
+                    value='/dev/video1'
+                />
+            </characteristics>
+        </group>
+
         <!-- camera device starts -->
         <device id='/dev/video1' position='rear'>
             <caps>
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index dcb2abb..f88d223 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -61,6 +61,14 @@
      * Opaque value from driver
      */
     uint32_t bufferId;
+    /**
+     * Unique identifier of the physical camera device that produces this buffer.
+     */
+    string deviceId;
+    /**
+     * Time that this buffer is being filled.
+     */
+    int64_t timestamp;
 };
 
 /**
@@ -97,12 +105,16 @@
 /**
  * Structure that describes informative events occurred during EVS is streaming
  */
-struct EvsEvent {
+struct EvsEventDesc {
     /**
      * Type of an informative event
      */
     EvsEventType aType;
     /**
+     * Device identifier
+     */
+    string deviceId;
+    /**
      * Possible additional information
      */
     uint32_t[4] payload;
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.cpp b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
index 6d53652..ebf488a 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
@@ -80,7 +80,7 @@
     asyncStopStream();
 
     // Wait until the stream has actually stopped
-    std::unique_lock<std::mutex> lock(mLock);
+    std::unique_lock<std::mutex> lock(mEventLock);
     if (mRunning) {
         mEventSignal.wait(lock, [this]() { return !mRunning; });
     }
@@ -88,7 +88,7 @@
 
 
 bool FrameHandler::returnHeldBuffer() {
-    std::unique_lock<std::mutex> lock(mLock);
+    std::lock_guard<std::mutex> lock(mLock);
 
     // Return the oldest buffer we're holding
     if (mHeldBuffers.empty()) {
@@ -96,16 +96,16 @@
         return false;
     }
 
-    BufferDesc_1_1 buffer = mHeldBuffers.front();
+    hidl_vec<BufferDesc_1_1> buffers = mHeldBuffers.front();
     mHeldBuffers.pop();
-    mCamera->doneWithFrame_1_1(buffer);
+    mCamera->doneWithFrame_1_1(buffers);
 
     return true;
 }
 
 
 bool FrameHandler::isRunning() {
-    std::unique_lock<std::mutex> lock(mLock);
+    std::lock_guard<std::mutex> lock(mLock);
     return mRunning;
 }
 
@@ -120,7 +120,7 @@
 
 
 void FrameHandler::getFramesCounters(unsigned* received, unsigned* displayed) {
-    std::unique_lock<std::mutex> lock(mLock);
+    std::lock_guard<std::mutex> lock(mLock);
 
     if (received) {
         *received = mFramesReceived;
@@ -138,11 +138,17 @@
 }
 
 
-Return<void> FrameHandler::deliverFrame_1_1(const BufferDesc_1_1& bufDesc) {
+Return<void> FrameHandler::deliverFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffers) {
+    mLock.lock();
+    // For VTS tests, FrameHandler uses a single frame among delivered frames.
+    auto bufferIdx = mFramesDisplayed % buffers.size();
+    auto buffer = buffers[bufferIdx];
+    mLock.unlock();
+
     const AHardwareBuffer_Desc* pDesc =
-        reinterpret_cast<const AHardwareBuffer_Desc *>(&bufDesc.buffer.description);
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&buffer.buffer.description);
     ALOGD("Received a frame from the camera (%p)",
-          bufDesc.buffer.nativeHandle.getNativeHandle());
+          buffer.buffer.nativeHandle.getNativeHandle());
 
     // Store a dimension of a received frame.
     mFrameWidth = pDesc->width;
@@ -150,6 +156,7 @@
 
     // If we were given an opened display at construction time, then send the received
     // image back down the camera.
+    bool displayed = false;
     if (mDisplay.get()) {
         // Get the output buffer we'll use to display the imagery
         BufferDesc_1_0 tgtBuffer = {};
@@ -163,7 +170,7 @@
             ALOGE("Didn't get requested output buffer -- skipping this frame.");
         } else {
             // Copy the contents of the of buffer.memHandle into tgtBuffer
-            copyBufferContents(tgtBuffer, bufDesc);
+            copyBufferContents(tgtBuffer, buffer);
 
             // Send the target buffer back for display
             Return<EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer);
@@ -179,40 +186,42 @@
             } else {
                 // Everything looks good!
                 // Keep track so tests or watch dogs can monitor progress
-                mLock.lock();
-                mFramesDisplayed++;
-                mLock.unlock();
+                displayed = true;
             }
         }
     }
 
+    mLock.lock();
+    // increases counters
+    ++mFramesReceived;
+    mFramesDisplayed += (int)displayed;
+    mLock.unlock();
+    mFrameSignal.notify_all();
 
     switch (mReturnMode) {
     case eAutoReturn:
         // Send the camera buffer back now that the client has seen it
         ALOGD("Calling doneWithFrame");
-        mCamera->doneWithFrame_1_1(bufDesc);
+        mCamera->doneWithFrame_1_1(buffers);
         break;
     case eNoAutoReturn:
-        // Hang onto the buffer handle for now -- the client will return it explicitly later
-        mHeldBuffers.push(bufDesc);
+        // Hang onto the buffer handles for now -- the client will return it explicitly later
+        mHeldBuffers.push(buffers);
+        break;
     }
 
-    mLock.lock();
-    ++mFramesReceived;
-    mLock.unlock();
-    mFrameSignal.notify_all();
-
     ALOGD("Frame handling complete");
 
     return Void();
 }
 
 
-Return<void> FrameHandler::notify(const EvsEvent& event) {
+Return<void> FrameHandler::notify(const EvsEventDesc& event) {
     // Local flag we use to keep track of when the stream is stopping
-    mLock.lock();
-    mLatestEventDesc = event;
+    std::unique_lock<std::mutex> lock(mEventLock);
+    mLatestEventDesc.aType = event.aType;
+    mLatestEventDesc.payload[0] = event.payload[0];
+    mLatestEventDesc.payload[1] = event.payload[1];
     if (mLatestEventDesc.aType == EvsEventType::STREAM_STOPPED) {
         // Signal that the last frame has been received and the stream is stopped
         mRunning = false;
@@ -222,8 +231,8 @@
     } else {
         ALOGD("Received an event %s", eventToString(mLatestEventDesc.aType));
     }
-    mLock.unlock();
-    mEventSignal.notify_all();
+    lock.unlock();
+    mEventSignal.notify_one();
 
     return Void();
 }
@@ -342,25 +351,34 @@
     }
 }
 
-bool FrameHandler::waitForEvent(const EvsEventType aTargetEvent,
-                                EvsEvent &event) {
+bool FrameHandler::waitForEvent(const EvsEventDesc& aTargetEvent,
+                                      EvsEventDesc& aReceivedEvent,
+                                      bool ignorePayload) {
     // Wait until we get an expected parameter change event.
-    std::unique_lock<std::mutex> lock(mLock);
+    std::unique_lock<std::mutex> lock(mEventLock);
     auto now = std::chrono::system_clock::now();
-    bool result = mEventSignal.wait_until(lock, now + 5s,
-        [this, aTargetEvent, &event](){
-            bool flag = mLatestEventDesc.aType == aTargetEvent;
-            if (flag) {
-                event.aType = mLatestEventDesc.aType;
-                event.payload[0] = mLatestEventDesc.payload[0];
-                event.payload[1] = mLatestEventDesc.payload[1];
+    bool found = false;
+    while (!found) {
+        bool result = mEventSignal.wait_until(lock, now + 5s,
+            [this, aTargetEvent, ignorePayload, &aReceivedEvent, &found](){
+                found = (mLatestEventDesc.aType == aTargetEvent.aType) &&
+                        (ignorePayload || (mLatestEventDesc.payload[0] == aTargetEvent.payload[0] &&
+                                           mLatestEventDesc.payload[1] == aTargetEvent.payload[1]));
+
+                aReceivedEvent.aType = mLatestEventDesc.aType;
+                aReceivedEvent.payload[0] = mLatestEventDesc.payload[0];
+                aReceivedEvent.payload[1] = mLatestEventDesc.payload[1];
+                return found;
             }
+        );
 
-            return flag;
+        if (!result) {
+            ALOGW("A timer is expired before a target event has happened.");
+            break;
         }
-    );
+    }
 
-    return !result;
+    return found;
 }
 
 const char *FrameHandler::eventToString(const EvsEventType aType) {
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.h b/automotive/evs/1.1/vts/functional/FrameHandler.h
index e5f1b8f..21e85fe 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.h
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.h
@@ -73,8 +73,9 @@
     bool isRunning();
 
     void waitForFrameCount(unsigned frameCount);
-    bool waitForEvent(const EvsEventType aTargetEvent,
-                            EvsEvent &eventDesc);
+    bool waitForEvent(const EvsEventDesc& aTargetEvent,
+                            EvsEventDesc& aReceivedEvent,
+                            bool ignorePayload = false);
     void getFramesCounters(unsigned* received, unsigned* displayed);
     void getFrameDimension(unsigned* width, unsigned* height);
 
@@ -83,8 +84,8 @@
     Return<void> deliverFrame(const BufferDesc_1_0& buffer) override;
 
     // Implementation for ::android::hardware::automotive::evs::V1_1::IEvsCameraStream
-    Return<void> deliverFrame_1_1(const BufferDesc_1_1& buffer) override;
-    Return<void> notify(const EvsEvent& event) override;
+    Return<void> deliverFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffer) override;
+    Return<void> notify(const EvsEventDesc& event) override;
 
     // Local implementation details
     bool copyBufferContents(const BufferDesc_1_0& tgtBuffer, const BufferDesc_1_1& srcBuffer);
@@ -99,17 +100,18 @@
     // Since we get frames delivered to us asynchronously via the IEvsCameraStream interface,
     // we need to protect all member variables that may be modified while we're streaming
     // (ie: those below)
-    std::mutex                  mLock;
-    std::condition_variable     mEventSignal;
-    std::condition_variable     mFrameSignal;
+    std::mutex                            mLock;
+    std::mutex                            mEventLock;
+    std::condition_variable               mEventSignal;
+    std::condition_variable               mFrameSignal;
+    std::queue<hidl_vec<BufferDesc_1_1>>  mHeldBuffers;
 
-    std::queue<BufferDesc_1_1>  mHeldBuffers;
     bool                        mRunning = false;
     unsigned                    mFramesReceived = 0;    // Simple counter -- rolls over eventually!
     unsigned                    mFramesDisplayed = 0;   // Simple counter -- rolls over eventually!
     unsigned                    mFrameWidth = 0;
     unsigned                    mFrameHeight = 0;
-    EvsEvent                    mLatestEventDesc;
+    EvsEventDesc                mLatestEventDesc;
 };
 
 
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 1d3fd87..4fc4e4c 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -41,6 +41,8 @@
 #include <cstdio>
 #include <cstring>
 #include <cstdlib>
+#include <thread>
+#include <unordered_set>
 
 #include <hidl/HidlTransportSupport.h>
 #include <hwbinder/ProcessState.h>
@@ -67,6 +69,7 @@
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::sp;
+using ::android::wp;
 using ::android::hardware::camera::device::V3_2::Stream;
 using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
 using ::android::hardware::automotive::evs::V1_0::DisplayState;
@@ -117,7 +120,15 @@
         mIsHwModule = !service_name.compare(kEnumeratorName);
     }
 
-    virtual void TearDown() override {}
+    virtual void TearDown() override {
+        // Attempt to close any active camera
+        for (auto &&c : activeCameras) {
+            sp<IEvsCamera_1_1> cam = c.promote();
+            if (cam != nullptr) {
+                pEnumerator->closeCamera(cam);
+            }
+        }
+    }
 
 protected:
     void loadCameraList() {
@@ -141,10 +152,90 @@
         ASSERT_GE(cameraInfo.size(), 1u);
     }
 
-    sp<IEvsEnumerator>        pEnumerator;    // Every test needs access to the service
-    std::vector <CameraDesc>  cameraInfo;     // Empty unless/until loadCameraList() is called
-    bool                      mIsHwModule;    // boolean to tell current module under testing
-                                              // is HW module implementation.
+    bool isLogicalCamera(const camera_metadata_t *metadata) {
+        if (metadata == nullptr) {
+            // A logical camera device must have a valid camera metadata.
+            return false;
+        }
+
+        // Looking for LOGICAL_MULTI_CAMERA capability from metadata.
+        camera_metadata_ro_entry_t entry;
+        int rc = find_camera_metadata_ro_entry(metadata,
+                                               ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+                                               &entry);
+        if (0 != rc) {
+            // No capabilities are found.
+            return false;
+        }
+
+        for (size_t i = 0; i < entry.count; ++i) {
+            uint8_t cap = entry.data.u8[i];
+            if (cap == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    std::unordered_set<std::string> getPhysicalCameraIds(const std::string& id,
+                                                         bool& flag) {
+        std::unordered_set<std::string> physicalCameras;
+
+        auto it = cameraInfo.begin();
+        while (it != cameraInfo.end()) {
+            if (it->v1.cameraId == id) {
+                break;
+            }
+            ++it;
+        }
+
+        if (it == cameraInfo.end()) {
+            // Unknown camera is requested.  Return an empty list.
+            return physicalCameras;
+        }
+
+        const camera_metadata_t *metadata =
+            reinterpret_cast<camera_metadata_t *>(&it->metadata[0]);
+        flag = isLogicalCamera(metadata);
+        if (!flag) {
+            // EVS assumes that the device w/o a valid metadata is a physical
+            // device.
+            ALOGI("%s is not a logical camera device.", id.c_str());
+            physicalCameras.emplace(id);
+            return physicalCameras;
+        }
+
+        // Look for physical camera identifiers
+        camera_metadata_ro_entry entry;
+        int rc = find_camera_metadata_ro_entry(metadata,
+                                               ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
+                                               &entry);
+        ALOGE_IF(rc, "No physical camera ID is found for a logical camera device");
+
+        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 id(reinterpret_cast<const char *>(ids + start));
+                    physicalCameras.emplace(id);
+                }
+                start = i + 1;
+            }
+        }
+
+        ALOGI("%s consists of %d physical camera devices.", id.c_str(), (int)physicalCameras.size());
+        return physicalCameras;
+    }
+
+
+    sp<IEvsEnumerator>              pEnumerator;   // Every test needs access to the service
+    std::vector<CameraDesc>         cameraInfo;    // Empty unless/until loadCameraList() is called
+    bool                            mIsHwModule;   // boolean to tell current module under testing
+                                                   // is HW module implementation.
+    std::deque<wp<IEvsCamera_1_1>>  activeCameras; // A list of active camera handles that are
+                                                   // needed to be cleaned up.
 };
 
 
@@ -168,12 +259,32 @@
 
     // Open and close each camera twice
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            ALOGI("Skip a logical device %s for HW module", cam.v1.cameraId.c_str());
+            continue;
+        }
+
         for (int pass = 0; pass < 2; pass++) {
+            activeCameras.clear();
             sp<IEvsCamera_1_1> pCam =
                 IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
                 .withDefault(nullptr);
             ASSERT_NE(pCam, nullptr);
 
+            for (auto&& devName : devices) {
+                bool matched = false;
+                pCam->getPhysicalCameraInfo(devName,
+                                            [&devName, &matched](const CameraDesc& info) {
+                                                matched = devName == info.v1.cameraId;
+                                            });
+                ASSERT_TRUE(matched);
+            }
+
+            // Store a camera handle for a clean-up
+            activeCameras.push_back(pCam);
+
             // Verify that this camera self-identifies correctly
             pCam->getCameraInfo_1_1([&cam](CameraDesc desc) {
                                         ALOGD("Found camera %s", desc.v1.cameraId.c_str());
@@ -206,11 +317,22 @@
 
     // Open and close each camera twice
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            ALOGI("Skip a logical device %s for HW module", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         sp<IEvsCamera_1_1> pCam =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam);
+
         // Verify that this camera self-identifies correctly
         pCam->getCameraInfo_1_1([&cam](CameraDesc desc) {
                                     ALOGD("Found camera %s", desc.v1.cameraId.c_str());
@@ -221,9 +343,13 @@
         sp<IEvsCamera_1_1> pCam2 =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
-        ASSERT_NE(pCam, pCam2);
         ASSERT_NE(pCam2, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam2);
+
+        ASSERT_NE(pCam, pCam2);
+
         Return<EvsResult> result = pCam->setMaxFramesInFlight(2);
         if (mIsHwModule) {
             // Verify that the old camera rejects calls via HW module.
@@ -268,11 +394,22 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            ALOGI("Skip a logical device %s", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         sp<IEvsCamera_1_1> pCam =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam);
+
         // Set up a frame receiver object which will fire up its own thread
         sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
                                                          nullptr,
@@ -280,6 +417,7 @@
 
         // Start the camera's video stream
         nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+
         bool startResult = frameHandler->startStream();
         ASSERT_TRUE(startResult);
 
@@ -287,9 +425,17 @@
         frameHandler->waitForFrameCount(1);
         nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC);
         nsecs_t timeToFirstFrame = systemTime(SYSTEM_TIME_MONOTONIC) - start;
-        EXPECT_LE(nanoseconds_to_milliseconds(timeToFirstFrame), kMaxStreamStartMilliseconds);
-        printf("Measured time to first frame %0.2f ms\n", timeToFirstFrame * kNanoToMilliseconds);
-        ALOGI("Measured time to first frame %0.2f ms", timeToFirstFrame * kNanoToMilliseconds);
+
+        // Extra delays are expected when we attempt to start a video stream on
+        // the logical camera device.  The amount of delay is expected the
+        // number of physical camera devices multiplied by
+        // kMaxStreamStartMilliseconds at most.
+        EXPECT_LE(nanoseconds_to_milliseconds(timeToFirstFrame),
+                  kMaxStreamStartMilliseconds * devices.size());
+        printf("%s: Measured time to first frame %0.2f ms\n",
+               cam.v1.cameraId.c_str(), timeToFirstFrame * kNanoToMilliseconds);
+        ALOGI("%s: Measured time to first frame %0.2f ms",
+              cam.v1.cameraId.c_str(), timeToFirstFrame * kNanoToMilliseconds);
 
         // Check aspect ratio
         unsigned width = 0, height = 0;
@@ -299,6 +445,13 @@
         // Wait a bit, then ensure we get at least the required minimum number of frames
         sleep(5);
         nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        // Even when the camera pointer goes out of scope, the FrameHandler object will
+        // keep the stream alive unless we tell it to shutdown.
+        // Also note that the FrameHandle and the Camera have a mutual circular reference, so
+        // we have to break that cycle in order for either of them to get cleaned up.
+        frameHandler->shutdown();
+
         unsigned framesReceived = 0;
         frameHandler->getFramesCounters(&framesReceived, nullptr);
         framesReceived = framesReceived - 1;    // Back out the first frame we already waited for
@@ -308,12 +461,6 @@
         ALOGI("Measured camera rate %3.2f fps", framesPerSecond);
         EXPECT_GE(framesPerSecond, kMinimumFramesPerSecond);
 
-        // Even when the camera pointer goes out of scope, the FrameHandler object will
-        // keep the stream alive unless we tell it to shutdown.
-        // Also note that the FrameHandle and the Camera have a mutual circular reference, so
-        // we have to break that cycle in order for either of them to get cleaned up.
-        frameHandler->shutdown();
-
         // Explicitly release the camera
         pEnumerator->closeCamera(pCam);
     }
@@ -340,12 +487,22 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            ALOGI("Skip a logical device %s for HW module", cam.v1.cameraId.c_str());
+            continue;
+        }
 
+        activeCameras.clear();
         sp<IEvsCamera_1_1> pCam =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam);
+
         // Ask for a crazy number of buffers in flight to ensure it errors correctly
         Return<EvsResult> badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF);
         EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
@@ -366,7 +523,7 @@
 
         // Check that the video stream stalls once we've gotten exactly the number of buffers
         // we requested since we told the frameHandler not to return them.
-        sleep(2);   // 1 second should be enough for at least 5 frames to be delivered worst case
+        sleep(1);   // 1 second should be enough for at least 5 frames to be delivered worst case
         unsigned framesReceived = 0;
         frameHandler->getFramesCounters(&framesReceived, nullptr);
         ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
@@ -416,11 +573,22 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            ALOGI("Skip a logical device %s for HW module", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         sp<IEvsCamera_1_1> pCam =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam);
+
         // Set up a frame receiver object which will fire up its own thread.
         sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
                                                          pDisplay,
@@ -484,17 +652,24 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        activeCameras.clear();
         // Create two camera clients.
         sp<IEvsCamera_1_1> pCam0 =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam0, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam0);
+
         sp<IEvsCamera_1_1> pCam1 =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam1, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam1);
+
         // Set up per-client frame receiver objects which will fire up its own thread
         sp<FrameHandler> frameHandler0 = new FrameHandler(pCam0, cam,
                                                           nullptr,
@@ -554,6 +729,11 @@
         // Explicitly release the camera
         pEnumerator->closeCamera(pCam0);
         pEnumerator->closeCamera(pCam1);
+
+        // TODO(b/145459970, b/145457727): below sleep() is added to ensure the
+        // destruction of active camera objects; this may be related with two
+        // issues.
+        sleep(1);
     }
 }
 
@@ -575,12 +755,25 @@
     // Test each reported camera
     Return<EvsResult> result = EvsResult::OK;
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            ALOGI("Skip a logical device %s", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         // Create a camera client
         sp<IEvsCamera_1_1> pCam =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera
+        activeCameras.push_back(pCam);
+
         // Get the parameter list
         std::vector<CameraParam> cmds;
         pCam->getParameterList([&cmds](hidl_vec<CameraParam> cmdList) {
@@ -626,48 +819,54 @@
             EvsResult result = EvsResult::OK;
             if (cmd == CameraParam::ABSOLUTE_FOCUS) {
                 // Try to turn off auto-focus
-                int32_t val1 = 0;
-                pCam->getIntParameter(CameraParam::AUTO_FOCUS,
-                                   [&result, &val1](auto status, auto value) {
+                std::vector<int32_t> values;
+                pCam->setIntParameter(CameraParam::AUTO_FOCUS, 0,
+                                   [&result, &values](auto status, auto effectiveValues) {
                                        result = status;
                                        if (status == EvsResult::OK) {
-                                          val1 = value;
+                                          for (auto &&v : effectiveValues) {
+                                              values.push_back(v);
+                                          }
                                        }
                                    });
-                if (val1 != 0) {
-                    pCam->setIntParameter(CameraParam::AUTO_FOCUS, 0,
-                                       [&result, &val1](auto status, auto effectiveValue) {
-                                           result = status;
-                                           val1 = effectiveValue;
-                                       });
-                    ASSERT_EQ(EvsResult::OK, result);
-                    ASSERT_EQ(val1, 0);
+                ASSERT_EQ(EvsResult::OK, result);
+                for (auto &&v : values) {
+                    ASSERT_EQ(v, 0);
                 }
             }
 
             // Try to program a parameter with a random value [minVal, maxVal]
             int32_t val0 = minVal + (std::rand() % (maxVal - minVal));
-            int32_t val1 = 0;
+            std::vector<int32_t> values;
 
             // Rounding down
             val0 = val0 - (val0 % step);
             pCam->setIntParameter(cmd, val0,
-                               [&result, &val1](auto status, auto effectiveValue) {
+                               [&result, &values](auto status, auto effectiveValues) {
                                    result = status;
-                                   val1 = effectiveValue;
+                                   if (status == EvsResult::OK) {
+                                      for (auto &&v : effectiveValues) {
+                                          values.push_back(v);
+                                      }
+                                   }
                                });
 
             ASSERT_EQ(EvsResult::OK, result);
 
+            values.clear();
             pCam->getIntParameter(cmd,
-                               [&result, &val1](auto status, auto value) {
+                               [&result, &values](auto status, auto readValues) {
                                    result = status;
                                    if (status == EvsResult::OK) {
-                                      val1 = value;
+                                      for (auto &&v : readValues) {
+                                          values.push_back(v);
+                                      }
                                    }
                                });
             ASSERT_EQ(EvsResult::OK, result);
-            ASSERT_EQ(val0, val1) << "Values are not matched.";
+            for (auto &&v : values) {
+                ASSERT_EQ(val0, v) << "Values are not matched.";
+            }
         }
 
         result = pCam->unsetMaster();
@@ -704,16 +903,33 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            ALOGI("Skip a logical device %s", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         // Create two camera clients.
         sp<IEvsCamera_1_1> pCamMaster =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCamMaster, nullptr);
+
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCamMaster);
+
         sp<IEvsCamera_1_1> pCamNonMaster =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCamNonMaster, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCamNonMaster);
+
         // Set up per-client frame receiver objects which will fire up its own thread
         sp<FrameHandler> frameHandlerMaster =
             new FrameHandler(pCamMaster, cam,
@@ -750,13 +966,45 @@
 
         // Non-master client expects to receive a master role relesed
         // notification.
-        EvsEvent aNotification = {};
+        EvsEventDesc aTargetEvent  = {};
+        EvsEventDesc aNotification = {};
+
+        bool listening = false;
+        std::mutex eventLock;
+        std::condition_variable eventCond;
+        std::thread listener = std::thread(
+            [&aNotification, &frameHandlerNonMaster, &listening, &eventCond]() {
+                // Notify that a listening thread is running.
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification, true)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+
+            }
+        );
+
+        // Wait until a listening thread starts.
+        std::unique_lock<std::mutex> lock(eventLock);
+        auto timer = std::chrono::system_clock::now();
+        while (!listening) {
+            timer += 1s;
+            eventCond.wait_until(lock, timer);
+        }
+        lock.unlock();
 
         // Release a master role.
         pCamMaster->unsetMaster();
 
-        // Verify a change notification.
-        frameHandlerNonMaster->waitForEvent(EvsEventType::MASTER_RELEASED, aNotification);
+        // Join a listening thread.
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        // Verify change notifications.
         ASSERT_EQ(EvsEventType::MASTER_RELEASED,
                   static_cast<EvsEventType>(aNotification.aType));
 
@@ -768,23 +1016,49 @@
         result = pCamMaster->setMaster();
         ASSERT_TRUE(result == EvsResult::OWNERSHIP_LOST);
 
+        listening = false;
+        listener = std::thread(
+            [&aNotification, &frameHandlerMaster, &listening, &eventCond]() {
+                // Notify that a listening thread is running.
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification, true)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+
+            }
+        );
+
+        // Wait until a listening thread starts.
+        timer = std::chrono::system_clock::now();
+        lock.lock();
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
+
         // Closing current master client.
         frameHandlerNonMaster->shutdown();
 
-        // Verify a change notification.
-        frameHandlerMaster->waitForEvent(EvsEventType::MASTER_RELEASED, aNotification);
+        // Join a listening thread.
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        // Verify change notifications.
         ASSERT_EQ(EvsEventType::MASTER_RELEASED,
                   static_cast<EvsEventType>(aNotification.aType));
 
-        // Closing another stream.
+        // Closing streams.
         frameHandlerMaster->shutdown();
 
         // Explicitly release the camera
         pEnumerator->closeCamera(pCamMaster);
         pEnumerator->closeCamera(pCamNonMaster);
     }
-
-
 }
 
 
@@ -810,16 +1084,33 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            ALOGI("Skip a logical device %s", cam.v1.cameraId.c_str());
+            continue;
+        }
+
+        activeCameras.clear();
         // Create two camera clients.
         sp<IEvsCamera_1_1> pCamMaster =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCamMaster, nullptr);
+
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCamMaster);
+
         sp<IEvsCamera_1_1> pCamNonMaster =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCamNonMaster, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCamNonMaster);
+
         // Get the parameter list
         std::vector<CameraParam> camMasterCmds, camNonMasterCmds;
         pCamMaster->getParameterList([&camMasterCmds](hidl_vec<CameraParam> cmdList) {
@@ -879,7 +1170,9 @@
         frameHandlerNonMaster->waitForFrameCount(1);
 
         int32_t val0 = 0;
-        int32_t val1 = 0;
+        std::vector<int32_t> values;
+        EvsEventDesc aNotification0 = {};
+        EvsEventDesc aNotification1 = {};
         for (auto &cmd : camMasterCmds) {
             // Get a valid parameter value range
             int32_t minVal, maxVal, step;
@@ -895,60 +1188,143 @@
             EvsResult result = EvsResult::OK;
             if (cmd == CameraParam::ABSOLUTE_FOCUS) {
                 // Try to turn off auto-focus
-                int32_t val1 = 1;
+                values.clear();
                 pCamMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0,
-                                   [&result, &val1](auto status, auto effectiveValue) {
+                                   [&result, &values](auto status, auto effectiveValues) {
                                        result = status;
-                                       val1 = effectiveValue;
+                                       if (status == EvsResult::OK) {
+                                          for (auto &&v : effectiveValues) {
+                                              values.push_back(v);
+                                          }
+                                       }
                                    });
                 ASSERT_EQ(EvsResult::OK, result);
-                ASSERT_EQ(val1, 0);
+                for (auto &&v : values) {
+                    ASSERT_EQ(v, 0);
+                }
             }
 
-            // Try to program a parameter
+            // Calculate a parameter value to program.
             val0 = minVal + (std::rand() % (maxVal - minVal));
-
-            // Rounding down
             val0 = val0 - (val0 % step);
+
+            // Prepare and start event listeners.
+            bool listening0 = false;
+            bool listening1 = false;
+            std::condition_variable eventCond;
+            std::thread listener0 = std::thread(
+                [cmd, val0,
+                 &aNotification0, &frameHandlerMaster, &listening0, &listening1, &eventCond]() {
+                    listening0 = true;
+                    if (listening1) {
+                        eventCond.notify_all();
+                    }
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
+                    aTargetEvent.payload[1] = val0;
+                    if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification0)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+            std::thread listener1 = std::thread(
+                [cmd, val0,
+                 &aNotification1, &frameHandlerNonMaster, &listening0, &listening1, &eventCond]() {
+                    listening1 = true;
+                    if (listening0) {
+                        eventCond.notify_all();
+                    }
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
+                    aTargetEvent.payload[1] = val0;
+                    if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification1)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+
+            // Wait until a listening thread starts.
+            std::mutex eventLock;
+            std::unique_lock<std::mutex> lock(eventLock);
+            auto timer = std::chrono::system_clock::now();
+            while (!listening0 || !listening1) {
+                eventCond.wait_until(lock, timer + 1s);
+            }
+            lock.unlock();
+
+            // Try to program a parameter
+            values.clear();
             pCamMaster->setIntParameter(cmd, val0,
-                                     [&result, &val1](auto status, auto effectiveValue) {
-                                         result = status;
-                                         val1 = effectiveValue;
-                                     });
-            ASSERT_EQ(EvsResult::OK, result);
-
-            // Wait a moment
-            sleep(1);
-
-            // Non-master client expects to receive a parameter change notification
-            // whenever a master client adjusts it.
-            EvsEvent aNotification = {};
-
-            pCamMaster->getIntParameter(cmd,
-                                     [&result, &val1](auto status, auto value) {
+                                     [&result, &values](auto status, auto effectiveValues) {
                                          result = status;
                                          if (status == EvsResult::OK) {
-                                            val1 = value;
+                                            for (auto &&v : effectiveValues) {
+                                                values.push_back(v);
+                                            }
+                                         }
+                                     });
+
+            ASSERT_EQ(EvsResult::OK, result);
+            for (auto &&v : values) {
+                ASSERT_EQ(val0, v) << "Values are not matched.";
+            }
+
+            // Join a listening thread.
+            if (listener0.joinable()) {
+                listener0.join();
+            }
+            if (listener1.joinable()) {
+                listener1.join();
+            }
+
+            // Verify a change notification
+            ASSERT_EQ(EvsEventType::PARAMETER_CHANGED,
+                      static_cast<EvsEventType>(aNotification0.aType));
+            ASSERT_EQ(EvsEventType::PARAMETER_CHANGED,
+                      static_cast<EvsEventType>(aNotification1.aType));
+            ASSERT_EQ(cmd,
+                      static_cast<CameraParam>(aNotification0.payload[0]));
+            ASSERT_EQ(cmd,
+                      static_cast<CameraParam>(aNotification1.payload[0]));
+            for (auto &&v : values) {
+                ASSERT_EQ(v,
+                          static_cast<int32_t>(aNotification0.payload[1]));
+                ASSERT_EQ(v,
+                          static_cast<int32_t>(aNotification1.payload[1]));
+            }
+
+            // Clients expects to receive a parameter change notification
+            // whenever a master client adjusts it.
+            values.clear();
+            pCamMaster->getIntParameter(cmd,
+                                     [&result, &values](auto status, auto readValues) {
+                                         result = status;
+                                         if (status == EvsResult::OK) {
+                                            for (auto &&v : readValues) {
+                                                values.push_back(v);
+                                            }
                                          }
                                      });
             ASSERT_EQ(EvsResult::OK, result);
-            ASSERT_EQ(val0, val1) << "Values are not matched.";
-
-            // Verify a change notification
-            frameHandlerNonMaster->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification);
-            ASSERT_EQ(EvsEventType::PARAMETER_CHANGED,
-                      static_cast<EvsEventType>(aNotification.aType));
-            ASSERT_EQ(cmd,
-                      static_cast<CameraParam>(aNotification.payload[0]));
-            ASSERT_EQ(val1,
-                      static_cast<int32_t>(aNotification.payload[1]));
+            for (auto &&v : values) {
+                ASSERT_EQ(val0, v) << "Values are not matched.";
+            }
         }
 
         // Try to adjust a parameter via non-master client
+        values.clear();
         pCamNonMaster->setIntParameter(camNonMasterCmds[0], val0,
-                                    [&result, &val1](auto status, auto effectiveValue) {
+                                    [&result, &values](auto status, auto effectiveValues) {
                                         result = status;
-                                        val1 = effectiveValue;
+                                        if (status == EvsResult::OK) {
+                                            for (auto &&v : effectiveValues) {
+                                                values.push_back(v);
+                                            }
+                                        }
                                     });
         ASSERT_EQ(EvsResult::INVALID_ARG, result);
 
@@ -957,14 +1333,48 @@
         ASSERT_EQ(EvsResult::OWNERSHIP_LOST, result);
 
         // Master client retires from a master role
+        bool listening = false;
+        std::condition_variable eventCond;
+        std::thread listener = std::thread(
+            [&aNotification0, &frameHandlerNonMaster, &listening, &eventCond]() {
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification0, true)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+            }
+        );
+
+        std::mutex eventLock;
+        auto timer = std::chrono::system_clock::now();
+        unique_lock<std::mutex> lock(eventLock);
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
+
         result = pCamMaster->unsetMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
+        if (listener.joinable()) {
+            listener.join();
+        }
+        ASSERT_EQ(EvsEventType::MASTER_RELEASED,
+                  static_cast<EvsEventType>(aNotification0.aType));
+
         // Try to adjust a parameter after being retired
+        values.clear();
         pCamMaster->setIntParameter(camMasterCmds[0], val0,
-                                 [&result, &val1](auto status, auto effectiveValue) {
+                                 [&result, &values](auto status, auto effectiveValues) {
                                      result = status;
-                                     val1 = effectiveValue;
+                                     if (status == EvsResult::OK) {
+                                        for (auto &&v : effectiveValues) {
+                                            values.push_back(v);
+                                        }
+                                     }
                                  });
         ASSERT_EQ(EvsResult::INVALID_ARG, result);
 
@@ -986,55 +1396,128 @@
             );
 
             EvsResult result = EvsResult::OK;
+            values.clear();
             if (cmd == CameraParam::ABSOLUTE_FOCUS) {
                 // Try to turn off auto-focus
-                int32_t val1 = 1;
+                values.clear();
                 pCamNonMaster->setIntParameter(CameraParam::AUTO_FOCUS, 0,
-                                   [&result, &val1](auto status, auto effectiveValue) {
+                                   [&result, &values](auto status, auto effectiveValues) {
                                        result = status;
-                                       val1 = effectiveValue;
+                                       if (status == EvsResult::OK) {
+                                          for (auto &&v : effectiveValues) {
+                                              values.push_back(v);
+                                          }
+                                       }
                                    });
                 ASSERT_EQ(EvsResult::OK, result);
-                ASSERT_EQ(val1, 0);
+                for (auto &&v : values) {
+                    ASSERT_EQ(v, 0);
+                }
             }
 
-            // Try to program a parameter
+            // Calculate a parameter value to program.  This is being rounding down.
             val0 = minVal + (std::rand() % (maxVal - minVal));
-
-            // Rounding down
             val0 = val0 - (val0 % step);
+
+            // Prepare and start event listeners.
+            bool listening0 = false;
+            bool listening1 = false;
+            std::condition_variable eventCond;
+            std::thread listener0 = std::thread(
+                [&cmd, &val0, &aNotification0, &frameHandlerMaster, &listening0, &listening1, &eventCond]() {
+                    listening0 = true;
+                    if (listening1) {
+                        eventCond.notify_all();
+                    }
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
+                    aTargetEvent.payload[1] = val0;
+                    if (!frameHandlerMaster->waitForEvent(aTargetEvent, aNotification0)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+            std::thread listener1 = std::thread(
+                [&cmd, &val0, &aNotification1, &frameHandlerNonMaster, &listening0, &listening1, &eventCond]() {
+                    listening1 = true;
+                    if (listening0) {
+                        eventCond.notify_all();
+                    }
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(cmd);
+                    aTargetEvent.payload[1] = val0;
+                    if (!frameHandlerNonMaster->waitForEvent(aTargetEvent, aNotification1)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+
+            // Wait until a listening thread starts.
+            std::mutex eventLock;
+            std::unique_lock<std::mutex> lock(eventLock);
+            auto timer = std::chrono::system_clock::now();
+            while (!listening0 || !listening1) {
+                eventCond.wait_until(lock, timer + 1s);
+            }
+            lock.unlock();
+
+            // Try to program a parameter
+            values.clear();
             pCamNonMaster->setIntParameter(cmd, val0,
-                                        [&result, &val1](auto status, auto effectiveValue) {
-                                            result = status;
-                                            val1 = effectiveValue;
-                                        });
-            ASSERT_EQ(EvsResult::OK, result);
-
-            // Wait a moment
-            sleep(1);
-
-            // Non-master client expects to receive a parameter change notification
-            // whenever a master client adjusts it.
-            EvsEvent aNotification = {};
-
-            pCamNonMaster->getIntParameter(cmd,
-                                        [&result, &val1](auto status, auto value) {
+                                        [&result, &values](auto status, auto effectiveValues) {
                                             result = status;
                                             if (status == EvsResult::OK) {
-                                               val1 = value;
+                                                for (auto &&v : effectiveValues) {
+                                                    values.push_back(v);
+                                                }
                                             }
                                         });
             ASSERT_EQ(EvsResult::OK, result);
-            ASSERT_EQ(val0, val1) << "Values are not matched.";
+
+            // Clients expects to receive a parameter change notification
+            // whenever a master client adjusts it.
+            values.clear();
+            pCamNonMaster->getIntParameter(cmd,
+                                        [&result, &values](auto status, auto readValues) {
+                                            result = status;
+                                            if (status == EvsResult::OK) {
+                                                for (auto &&v : readValues) {
+                                                    values.push_back(v);
+                                                }
+                                            }
+                                        });
+            ASSERT_EQ(EvsResult::OK, result);
+            for (auto &&v : values) {
+                ASSERT_EQ(val0, v) << "Values are not matched.";
+            }
+
+            // Join a listening thread.
+            if (listener0.joinable()) {
+                listener0.join();
+            }
+            if (listener1.joinable()) {
+                listener1.join();
+            }
 
             // Verify a change notification
-            frameHandlerMaster->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification);
             ASSERT_EQ(EvsEventType::PARAMETER_CHANGED,
-                      static_cast<EvsEventType>(aNotification.aType));
+                      static_cast<EvsEventType>(aNotification0.aType));
+            ASSERT_EQ(EvsEventType::PARAMETER_CHANGED,
+                      static_cast<EvsEventType>(aNotification1.aType));
             ASSERT_EQ(cmd,
-                      static_cast<CameraParam>(aNotification.payload[0]));
-            ASSERT_EQ(val1,
-                      static_cast<int32_t>(aNotification.payload[1]));
+                      static_cast<CameraParam>(aNotification0.payload[0]));
+            ASSERT_EQ(cmd,
+                      static_cast<CameraParam>(aNotification1.payload[0]));
+            for (auto &&v : values) {
+                ASSERT_EQ(v,
+                          static_cast<int32_t>(aNotification0.payload[1]));
+                ASSERT_EQ(v,
+                          static_cast<int32_t>(aNotification1.payload[1]));
+            }
         }
 
         // New master retires from a master role
@@ -1078,17 +1561,25 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        activeCameras.clear();
+
         // Create two clients
         sp<IEvsCamera_1_1> pCam0 =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam0, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam0);
+
         sp<IEvsCamera_1_1> pCam1 =
             IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(cam.v1.cameraId, nullCfg))
             .withDefault(nullptr);
         ASSERT_NE(pCam1, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam1);
+
         // Get the parameter list; this test will use the first command in both
         // lists.
         std::vector<CameraParam> cam0Cmds, cam1Cmds;
@@ -1144,108 +1635,260 @@
             }
         );
 
-        if (cam1Cmds[0] == CameraParam::ABSOLUTE_FOCUS) {
-            // Try to turn off auto-focus
-            int32_t val1 = 0;
-            pCam1->getIntParameter(CameraParam::AUTO_FOCUS,
-                               [&result, &val1](auto status, auto value) {
-                                   result = status;
-                                   if (status == EvsResult::OK) {
-                                      val1 = value;
-                                   }
-                               });
-            if (val1 != 0) {
-                pCam1->setIntParameter(CameraParam::AUTO_FOCUS, 0,
-                                   [&result, &val1](auto status, auto effectiveValue) {
-                                       result = status;
-                                       val1 = effectiveValue;
-                                   });
-                ASSERT_EQ(EvsResult::OK, result);
-                ASSERT_EQ(val1, 0);
-            }
-        }
-
-        // Try to program a parameter with a random value [minVal, maxVal]
-        int32_t val0 = minVal + (std::rand() % (maxVal - minVal));
-        int32_t val1 = 0;
-
-        // Rounding down
-        val0 = val0 - (val0 % step);
-
+        // Client1 becomes a master
         result = pCam1->setMaster();
         ASSERT_EQ(EvsResult::OK, result);
 
+        std::vector<int32_t> values;
+        EvsEventDesc aTargetEvent  = {};
+        EvsEventDesc aNotification = {};
+        bool listening = false;
+        std::mutex eventLock;
+        std::condition_variable eventCond;
+        if (cam1Cmds[0] == CameraParam::ABSOLUTE_FOCUS) {
+            std::thread listener = std::thread(
+                [&frameHandler0, &aNotification, &listening, &eventCond] {
+                    listening = true;
+                    eventCond.notify_all();
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(CameraParam::AUTO_FOCUS);
+                    aTargetEvent.payload[1] = 0;
+                    if (!frameHandler0->waitForEvent(aTargetEvent, aNotification)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+
+            // Wait until a lister starts.
+            std::unique_lock<std::mutex> lock(eventLock);
+            auto timer = std::chrono::system_clock::now();
+            while (!listening) {
+                eventCond.wait_until(lock, timer + 1s);
+            }
+            lock.unlock();
+
+            // Try to turn off auto-focus
+            pCam1->setIntParameter(CameraParam::AUTO_FOCUS, 0,
+                               [&result, &values](auto status, auto effectiveValues) {
+                                   result = status;
+                                   if (status == EvsResult::OK) {
+                                      for (auto &&v : effectiveValues) {
+                                          values.push_back(v);
+                                      }
+                                   }
+                               });
+            ASSERT_EQ(EvsResult::OK, result);
+            for (auto &&v : values) {
+                ASSERT_EQ(v, 0);
+            }
+
+            // Join a listener
+            if (listener.joinable()) {
+                listener.join();
+            }
+
+            // Make sure AUTO_FOCUS is off.
+            ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType),
+                      EvsEventType::PARAMETER_CHANGED);
+        }
+
+        // Try to program a parameter with a random value [minVal, maxVal] after
+        // rounding it down.
+        int32_t val0 = minVal + (std::rand() % (maxVal - minVal));
+        val0 = val0 - (val0 % step);
+
+        std::thread listener = std::thread(
+            [&frameHandler1, &aNotification, &listening, &eventCond, &cam1Cmds, val0] {
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                aTargetEvent.payload[0] = static_cast<uint32_t>(cam1Cmds[0]);
+                aTargetEvent.payload[1] = val0;
+                if (!frameHandler1->waitForEvent(aTargetEvent, aNotification)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+            }
+        );
+
+        // Wait until a lister starts.
+        listening = false;
+        std::unique_lock<std::mutex> lock(eventLock);
+        auto timer = std::chrono::system_clock::now();
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
+
+        values.clear();
         pCam1->setIntParameter(cam1Cmds[0], val0,
-                            [&result, &val1](auto status, auto effectiveValue) {
+                            [&result, &values](auto status, auto effectiveValues) {
                                 result = status;
-                                val1 = effectiveValue;
+                                if (status == EvsResult::OK) {
+                                    for (auto &&v : effectiveValues) {
+                                        values.push_back(v);
+                                    }
+                                }
                             });
         ASSERT_EQ(EvsResult::OK, result);
+        for (auto &&v : values) {
+            ASSERT_EQ(val0, v);
+        }
+
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
 
         // Verify a change notification
-        EvsEvent aNotification = {};
-        bool timeout =
-            frameHandler0->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification);
-        ASSERT_FALSE(timeout) << "Expected event does not arrive";
         ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType),
                   EvsEventType::PARAMETER_CHANGED);
         ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
                   cam1Cmds[0]);
-        ASSERT_EQ(val1,
-                  static_cast<int32_t>(aNotification.payload[1]));
+        for (auto &&v : values) {
+            ASSERT_EQ(v, static_cast<int32_t>(aNotification.payload[1]));
+        }
+
+        listener = std::thread(
+            [&frameHandler1, &aNotification, &listening, &eventCond] {
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                if (!frameHandler1->waitForEvent(aTargetEvent, aNotification, true)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+            }
+        );
+
+        // Wait until a lister starts.
+        listening = false;
+        lock.lock();
+        timer = std::chrono::system_clock::now();
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
 
         // Client 0 steals a master role
         ASSERT_EQ(EvsResult::OK, pCam0->forceMaster(pDisplay));
 
-        frameHandler1->waitForEvent(EvsEventType::MASTER_RELEASED, aNotification);
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
+
         ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType),
                   EvsEventType::MASTER_RELEASED);
 
         // Client 0 programs a parameter
         val0 = minVal + (std::rand() % (maxVal - minVal));
-        val1 = 0;
 
         // Rounding down
         val0 = val0 - (val0 % step);
 
         if (cam0Cmds[0] == CameraParam::ABSOLUTE_FOCUS) {
+            std::thread listener = std::thread(
+                [&frameHandler1, &aNotification, &listening, &eventCond] {
+                    listening = true;
+                    eventCond.notify_all();
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                    aTargetEvent.payload[0] = static_cast<uint32_t>(CameraParam::AUTO_FOCUS);
+                    aTargetEvent.payload[1] = 0;
+                    if (!frameHandler1->waitForEvent(aTargetEvent, aNotification)) {
+                        ALOGW("A timer is expired before a target event is fired.");
+                    }
+                }
+            );
+
+            // Wait until a lister starts.
+            std::unique_lock<std::mutex> lock(eventLock);
+            auto timer = std::chrono::system_clock::now();
+            while (!listening) {
+                eventCond.wait_until(lock, timer + 1s);
+            }
+            lock.unlock();
+
             // Try to turn off auto-focus
-            int32_t val1 = 0;
-            pCam0->getIntParameter(CameraParam::AUTO_FOCUS,
-                               [&result, &val1](auto status, auto value) {
+            values.clear();
+            pCam0->setIntParameter(CameraParam::AUTO_FOCUS, 0,
+                               [&result, &values](auto status, auto effectiveValues) {
                                    result = status;
                                    if (status == EvsResult::OK) {
-                                      val1 = value;
+                                      for (auto &&v : effectiveValues) {
+                                          values.push_back(v);
+                                      }
                                    }
                                });
-            if (val1 != 0) {
-                pCam0->setIntParameter(CameraParam::AUTO_FOCUS, 0,
-                                   [&result, &val1](auto status, auto effectiveValue) {
-                                       result = status;
-                                       val1 = effectiveValue;
-                                   });
-                ASSERT_EQ(EvsResult::OK, result);
-                ASSERT_EQ(val1, 0);
+            ASSERT_EQ(EvsResult::OK, result);
+            for (auto &&v : values) {
+                ASSERT_EQ(v, 0);
             }
+
+            // Join a listener
+            if (listener.joinable()) {
+                listener.join();
+            }
+
+            // Make sure AUTO_FOCUS is off.
+            ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType),
+                      EvsEventType::PARAMETER_CHANGED);
         }
 
+        listener = std::thread(
+            [&frameHandler0, &aNotification, &listening, &eventCond, &cam0Cmds, val0] {
+                listening = true;
+                eventCond.notify_all();
+
+                EvsEventDesc aTargetEvent;
+                aTargetEvent.aType = EvsEventType::PARAMETER_CHANGED;
+                aTargetEvent.payload[0] = static_cast<uint32_t>(cam0Cmds[0]);
+                aTargetEvent.payload[1] = val0;
+                if (!frameHandler0->waitForEvent(aTargetEvent, aNotification)) {
+                    ALOGW("A timer is expired before a target event is fired.");
+                }
+            }
+        );
+
+        // Wait until a lister starts.
+        listening = false;
+        timer = std::chrono::system_clock::now();
+        lock.lock();
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
+
+        values.clear();
         pCam0->setIntParameter(cam0Cmds[0], val0,
-                            [&result, &val1](auto status, auto effectiveValue) {
+                            [&result, &values](auto status, auto effectiveValues) {
                                 result = status;
-                                val1 = effectiveValue;
+                                if (status == EvsResult::OK) {
+                                    for (auto &&v : effectiveValues) {
+                                        values.push_back(v);
+                                    }
+                                }
                             });
         ASSERT_EQ(EvsResult::OK, result);
 
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
         // Verify a change notification
-        timeout =
-            frameHandler1->waitForEvent(EvsEventType::PARAMETER_CHANGED, aNotification);
-        ASSERT_FALSE(timeout) << "Expected event does not arrive";
         ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType),
                   EvsEventType::PARAMETER_CHANGED);
         ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
                   cam0Cmds[0]);
-        ASSERT_EQ(val1,
-                  static_cast<int32_t>(aNotification.payload[1]));
+        for (auto &&v : values) {
+            ASSERT_EQ(v, static_cast<int32_t>(aNotification.payload[1]));
+        }
 
         // Turn off the display (yes, before the stream stops -- it should be handled)
         pDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
@@ -1282,6 +1925,7 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        activeCameras.clear();
         // choose a configuration that has a frame rate faster than minReqFps.
         Stream targetCfg = {};
         const int32_t minReqFps = 15;
@@ -1324,6 +1968,9 @@
             .withDefault(nullptr);
         ASSERT_NE(pCam, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam);
+
         // Set up a frame receiver object which will fire up its own thread.
         sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
                                                          pDisplay,
@@ -1383,6 +2030,7 @@
 
     // Test each reported camera
     for (auto&& cam: cameraInfo) {
+        activeCameras.clear();
         // choose a configuration that has a frame rate faster than minReqFps.
         Stream targetCfg = {};
         const int32_t minReqFps = 15;
@@ -1427,6 +2075,9 @@
             .withDefault(nullptr);
         ASSERT_NE(pCam0, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam0);
+
         // Try to create the second camera client with different stream
         // configuration.
         int32_t id = targetCfg.id;
@@ -1436,6 +2087,9 @@
             .withDefault(nullptr);
         ASSERT_EQ(pCam1, nullptr);
 
+        // Store a camera handle for a clean-up
+        activeCameras.push_back(pCam0);
+
         // Try again with same stream configuration.
         targetCfg.id = id;
         pCam1 =
@@ -1506,6 +2160,30 @@
 }
 
 
+/*
+ * LogicalCameraMetadata:
+ * Opens logical camera reported by the enumerator and validate its metadata by
+ * checking its capability and locating supporting physical camera device
+ * identifiers.
+ */
+TEST_F(EvsHidlTest, LogicalCameraMetadata) {
+    ALOGI("Starting LogicalCameraMetadata test");
+
+    // Get the camera list
+    loadCameraList();
+
+    // Open and close each camera twice
+    for (auto&& cam: cameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.v1.cameraId, isLogicalCam);
+        if (isLogicalCam) {
+            ASSERT_GE(devices.size(), 1) <<
+                "Logical camera device must have at least one physical camera device ID in its metadata.";
+        }
+    }
+}
+
+
 int main(int argc, char** argv) {
     ::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/current.txt b/current.txt
index f026d7d..68e4713 100644
--- a/current.txt
+++ b/current.txt
@@ -650,16 +650,16 @@
 94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
 cf1d55e8c68300090747ab90b94c22e4c859b29c84ced68a317c595bb115eab2 android.hardware.neuralnetworks@1.3::types
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
-36b3acf78ac4ecf8156be8741c1d8332cdce7a1ebf4dfa1562952f14a94e6c87 android.hardware.wifi.hostapd@1.2::IHostapd
-2defa258951e25a132aaeb36e3febe6f41bf9c6dbb1b1ebdf0b41708ab4e107e android.hardware.wifi.hostapd@1.2::types
+9bc274c9d73aae170fd9e18df2476ade4c19b629cfb38dd03dd237a6cc2d932b android.hardware.wifi.hostapd@1.2::IHostapd
+11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types
 a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
 c72cb37b3f66ef65aeb5c6438a3fbe17bbe847fdf62d1a76eafd7f3a8a526105 android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
 342a8e12db4dca643f2755eb4167e8f103d96502053a25a1f51f42107a4530f1 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
 5477f8bafb29548875622fa83f1c0a29cee641acee613315eb747731001f4aff android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
 91015479f5a0fba9872e98d3cca4680995de64f42ae71461b4b7e5acc5a196ab android.hardware.wifi.supplicant@1.3::types
-7a4ba60b5ddedf497e5d2bdff7d72b7d4a811969000e28677dd9e2389e683b34 android.hardware.radio@1.5::types
+d9044563a5ac5a17a239303b8dec1e51167761ac46e965f61e31654cc034d31b android.hardware.radio@1.5::types
 afa2d6cf4c0ba4b8482d5bcc097594ad5bc49be0bf3003034f75955cdaf66045 android.hardware.radio@1.5::IRadio
-3afac66f21a33bc9c4b80481c7d5540038348651d9a7d8af64ea13610af138da android.hardware.radio@1.5::IRadioIndication
+bc59237dbd93949238081f762710552e76670cb648c0e198138551460ac54b1e android.hardware.radio@1.5::IRadioIndication
 f4888f9676890b43a459c6380f335fea7a6ad32ed3bafafeb018a88d6c0be8a4 android.hardware.radio@1.5::IRadioResponse
 55f0a15642869ec98a55ea0a5ac049d3e1a6245ff7750deb6bcb7182057eee83 android.hardware.radio.config@1.3::types
 b27ab0cd40b0b078cdcd024bfe1061c4c4c065f3519eeb9347fa359a3268a5ae android.hardware.radio.config@1.3::IRadioConfig
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index f23536c..06b4c5e 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -48,6 +48,12 @@
          * with protected buffers.
          */
         PROTECTED_CONTENTS = 4,
+
+        /**
+         * Indicates that both the composer HAL implementation and the given display
+         * support a low latency mode, such as HDMI 2.1 Auto Low Latency Mode.
+         */
+        AUTO_LOW_LATENCY_MODE = 5,
     };
 
     /**
@@ -64,6 +70,18 @@
         EXTERNAL = 1,
     };
 
+    enum ContentType : uint32_t {
+        NONE = 0,
+
+        /**
+         * These modes correspond to those found in the HDMI 1.4 specification.
+         */
+        GRAPHICS = 1,
+        PHOTO = 2,
+        CINEMA = 3,
+        GAME = 4,
+    };
+
     /**
      * Constraints for changing vsync period.
      */
@@ -172,4 +190,60 @@
     setActiveConfigWithConstraints(Display display, Config config,
         VsyncPeriodChangeConstraints vsyncPeriodChangeConstraints)
         generates (Error error, VsyncPeriodChangeTimeline timeline);
+
+    /**
+     * Requests the display to enable/disable its low latency mode.
+     *
+     * If the display is connected via HDMI 2.1, then Auto Low Latency Mode should be triggered. If
+     * the display is internally connected and a custom low latency mode is available, that should
+     * be triggered.
+     *
+     * This function should only be called if the display reports support for
+     * DisplayCapability::AUTO_LOW_LATENCY_MODE from getDisplayCapabilities_2_4.
+     *
+     * @return error is NONE upon success. Otherwise,
+     *     BAD_DISPLAY when an invalid display handle was passed in.
+     *     UNSUPPORTED when AUTO_LOW_LATENCY_MODE is not supported by the composer
+     *         implementation or the given display
+     */
+    setAutoLowLatencyMode(Display display, bool on)
+        generates (Error error);
+
+    /**
+     * Provides a list of all the content types supported by this display (any of
+     * ContentType::{GRAPHICS, PHOTO, CINEMA, GAME}). This list must not change after
+     * initialization.
+     *
+     * Content types are introduced in HDMI 1.4 and supporting them is optional. The
+     * ContentType::NONE is always supported and will not be returned by this method..
+     *
+     * @return error is NONE upon success. Otherwise,
+     *     BAD_DISPLAY when an invalid display handle was passed in.
+     * @return supportedContentTypes is a list of supported content types.
+     */
+    getSupportedContentTypes(Display display)
+        generates(Error error, vec<ContentType> supportedContentTypes);
+
+    /**
+     * Instructs the connected display that the content being shown is of the given type - one of
+     * GRAPHICS, PHOTO, CINEMA, GAME.
+     *
+     * Content types are introduced in HDMI 1.4 and supporting them is optional. If they are
+     * supported, this signal should switch the display to a mode that is optimal for the given
+     * type of content. See HDMI 1.4 specification for more information.
+     *
+     * If the display is internally connected (not through HDMI), and such modes are available,
+     * this method should trigger them.
+     *
+     * This function should only be called if the display reports support for the corresponding
+     * content type (ContentType::{GRAPHICS, PHOTO, CINEMA, GAME}) from getSupportedContentTypes.
+     * ContentType::NONE is supported by default and can always be set.
+     *
+     * @return error is NONE upon success. Otherwise,
+     *     BAD_DISPLAY when an invalid display handle was passed in.
+     *     UNSUPPORTED when the given content type is not supported by the composer
+     *         implementation or the given display
+     */
+    setContentType(Display display, ContentType type)
+        generates (Error error);
 };
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index 4160ed9..dcd959d 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -139,6 +139,24 @@
         return Void();
     }
 
+    Return<Error> setAutoLowLatencyMode(Display display, bool on) override {
+        return mHal->setAutoLowLatencyMode(display, on);
+    }
+
+    Return<void> getSupportedContentTypes(
+            Display display, IComposerClient::getSupportedContentTypes_cb hidl_cb) override {
+        std::vector<IComposerClient::ContentType> supportedContentTypes;
+        Error error = mHal->getSupportedContentTypes(display, &supportedContentTypes);
+
+        hidl_cb(error, supportedContentTypes);
+        return Void();
+    }
+
+    Return<Error> setContentType(Display display,
+                                 IComposerClient::ContentType contentType) override {
+        return mHal->setContentType(display, contentType);
+    }
+
     static std::unique_ptr<ComposerClientImpl> create(Hal* hal) {
         auto client = std::make_unique<ComposerClientImpl>(hal);
         return client->init() ? std::move(client) : nullptr;
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
index 89dbe66..a1e56ae 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
@@ -67,6 +67,11 @@
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* timeline) = 0;
+    virtual Error setAutoLowLatencyMode(Display display, bool on) = 0;
+    virtual Error getSupportedContentTypes(
+            Display display,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+    virtual Error setContentType(Display display, IComposerClient::ContentType contentType) = 0;
 };
 
 }  // namespace hal
diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
index d59d0d5..a27582a 100644
--- a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
+++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
@@ -167,6 +167,57 @@
         return Error::NONE;
     }
 
+    Error setAutoLowLatencyMode(Display display, bool on) override {
+        if (!mDispatch.setAutoLowLatencyMode) {
+            return Error::UNSUPPORTED;
+        }
+
+        int32_t error = mDispatch.setAutoLowLatencyMode(mDevice, display, on);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
+    Error getSupportedContentTypes(
+            Display display,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override {
+        if (!mDispatch.getSupportedContentTypes) {
+            return Error::UNSUPPORTED;
+        }
+
+        uint32_t count = 0;
+        int32_t error = mDispatch.getSupportedContentTypes(mDevice, display, &count, nullptr);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+
+        outSupportedContentTypes->resize(count);
+
+        error = mDispatch.getSupportedContentTypes(
+                mDevice, display, &count,
+                reinterpret_cast<std::underlying_type<IComposerClient::ContentType>::type*>(
+                        outSupportedContentTypes->data()));
+        if (error != HWC2_ERROR_NONE) {
+            *outSupportedContentTypes = std::vector<IComposerClient::ContentType>();
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
+    Error setContentType(Display display, IComposerClient::ContentType contentType) override {
+        if (!mDispatch.setContentType) {
+            return Error::UNSUPPORTED;
+        }
+
+        int32_t error =
+                mDispatch.setContentType(mDevice, display, static_cast<int32_t>(contentType));
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
   protected:
     bool initDispatch() override {
         if (!BaseType2_3::initDispatch()) {
@@ -179,6 +230,11 @@
                                    &mDispatch.getDisplayVsyncPeriod);
         this->initOptionalDispatch(HWC2_FUNCTION_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS,
                                    &mDispatch.setActiveConfigWithConstraints);
+        this->initOptionalDispatch(HWC2_FUNCTION_SET_AUTO_LOW_LATENCY_MODE,
+                                   &mDispatch.setAutoLowLatencyMode);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_SUPPORTED_CONTENT_TYPES,
+                                   &mDispatch.getSupportedContentTypes);
+        this->initOptionalDispatch(HWC2_FUNCTION_SET_CONTENT_TYPE, &mDispatch.setContentType);
         return true;
     }
 
@@ -222,6 +278,9 @@
         HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE getDisplayConnectionType;
         HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD getDisplayVsyncPeriod;
         HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS setActiveConfigWithConstraints;
+        HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE setAutoLowLatencyMode;
+        HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES getSupportedContentTypes;
+        HWC2_PFN_SET_CONTENT_TYPE setContentType;
     } mDispatch = {};
 
     hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr;
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index 35ac23f..5b06d6d 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -106,6 +106,25 @@
     return error;
 }
 
+Error ComposerClient::setAutoLowLatencyMode(Display display, bool on) {
+    return mClient->setAutoLowLatencyMode(display, on);
+}
+
+Error ComposerClient::getSupportedContentTypes(
+        Display display, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+    Error error = Error::NONE;
+    mClient->getSupportedContentTypes(
+            display, [&](const auto& tmpError, const auto& tmpSupportedContentTypes) {
+                error = tmpError;
+                *outSupportedContentTypes = tmpSupportedContentTypes;
+            });
+    return error;
+}
+
+Error ComposerClient::setContentType(Display display, IComposerClient::ContentType contentType) {
+    return mClient->setContentType(display, contentType);
+}
+
 }  // namespace vts
 }  // namespace V2_4
 }  // namespace composer
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index 83e74ed..b094bc8 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -84,6 +84,13 @@
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* timeline);
 
+    Error setAutoLowLatencyMode(Display display, bool on);
+
+    Error getSupportedContentTypes(
+            Display display, std::vector<IComposerClient::ContentType>* outSupportedContentTypes);
+
+    Error setContentType(Display display, IComposerClient::ContentType contentType);
+
   private:
     const sp<IComposerClient> mClient;
 };
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 7c5f0ee..5a96d77 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -47,6 +47,9 @@
 using mapper::V2_0::IMapper;
 using mapper::V2_0::vts::Gralloc;
 
+using ContentType = IComposerClient::ContentType;
+using DisplayCapability = IComposerClient::DisplayCapability;
+
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
   protected:
     void SetUp() override {
@@ -117,6 +120,11 @@
     void Test_setActiveConfigWithConstraints(
             const IComposerClient::VsyncPeriodChangeConstraints& constraints);
 
+    void Test_setContentType(const ContentType& contentType, const char* contentTypeStr);
+    void Test_setContentTypeForDisplay(const Display& display,
+                                       const std::vector<ContentType>& capabilities,
+                                       const ContentType& contentType, const char* contentTypeStr);
+
     std::unique_ptr<Composer> mComposer;
     std::unique_ptr<ComposerClient> mComposerClient;
     sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
@@ -384,6 +392,117 @@
     Test_setActiveConfigWithConstraints(constraints);
 }
 
+TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyModeBadDisplay) {
+    EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setAutoLowLatencyMode(mInvalidDisplayId, true));
+    EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setAutoLowLatencyMode(mInvalidDisplayId, false));
+}
+
+TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyMode) {
+    for (Display display : mComposerCallback->getDisplays()) {
+        std::vector<DisplayCapability> capabilities;
+        const auto error = mComposerClient->getDisplayCapabilities(display, &capabilities);
+        EXPECT_EQ(Error::NONE, error);
+
+        const bool allmSupport =
+                std::find(capabilities.begin(), capabilities.end(),
+                          DisplayCapability::AUTO_LOW_LATENCY_MODE) != capabilities.end();
+
+        if (!allmSupport) {
+            EXPECT_EQ(Error::UNSUPPORTED,
+                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
+            EXPECT_EQ(Error::UNSUPPORTED,
+                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+            GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
+                            << to_string(display) << ", skipping test";
+            return;
+        }
+
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+    }
+}
+
+TEST_P(GraphicsComposerHidlTest, getSupportedContentTypesBadDisplay) {
+    std::vector<ContentType> supportedContentTypes;
+    const auto error =
+            mComposerClient->getSupportedContentTypes(mInvalidDisplayId, &supportedContentTypes);
+    EXPECT_EQ(Error::BAD_DISPLAY, error);
+}
+
+TEST_P(GraphicsComposerHidlTest, getSupportedContentTypes) {
+    std::vector<ContentType> supportedContentTypes;
+    for (Display display : mComposerCallback->getDisplays()) {
+        supportedContentTypes.clear();
+        const auto error =
+                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+        const bool noneSupported =
+                std::find(supportedContentTypes.begin(), supportedContentTypes.end(),
+                          ContentType::NONE) != supportedContentTypes.end();
+        EXPECT_EQ(Error::NONE, error);
+        EXPECT_FALSE(noneSupported);
+    }
+}
+
+TEST_P(GraphicsComposerHidlTest, setContentTypeNoneAlwaysAccepted) {
+    for (Display display : mComposerCallback->getDisplays()) {
+        const auto error = mComposerClient->setContentType(display, ContentType::NONE);
+        EXPECT_NE(Error::UNSUPPORTED, error);
+    }
+}
+
+TEST_P(GraphicsComposerHidlTest, setContentTypeBadDisplay) {
+    const auto types = {ContentType::NONE, ContentType::GRAPHICS, ContentType::PHOTO,
+                        ContentType::CINEMA, ContentType::GAME};
+    for (auto type : types) {
+        EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setContentType(mInvalidDisplayId, type));
+    }
+}
+
+void GraphicsComposerHidlTest::Test_setContentTypeForDisplay(
+        const Display& display, const std::vector<ContentType>& capabilities,
+        const ContentType& contentType, const char* contentTypeStr) {
+    const bool contentTypeSupport =
+            std::find(capabilities.begin(), capabilities.end(), contentType) != capabilities.end();
+
+    if (!contentTypeSupport) {
+        EXPECT_EQ(Error::UNSUPPORTED, mComposerClient->setContentType(display, contentType));
+        GTEST_SUCCEED() << contentTypeStr << " content type is not supported on display "
+                        << to_string(display) << ", skipping test";
+        return;
+    }
+
+    EXPECT_EQ(Error::NONE, mComposerClient->setContentType(display, contentType));
+    EXPECT_EQ(Error::NONE, mComposerClient->setContentType(display, ContentType::NONE));
+}
+
+void GraphicsComposerHidlTest::Test_setContentType(const ContentType& contentType,
+                                                   const char* contentTypeStr) {
+    for (Display display : mComposerCallback->getDisplays()) {
+        std::vector<ContentType> supportedContentTypes;
+        const auto error =
+                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+        EXPECT_EQ(Error::NONE, error);
+
+        Test_setContentTypeForDisplay(display, supportedContentTypes, contentType, contentTypeStr);
+    }
+}
+
+TEST_P(GraphicsComposerHidlTest, setGraphicsContentType) {
+    Test_setContentType(ContentType::GRAPHICS, "GRAPHICS");
+}
+
+TEST_P(GraphicsComposerHidlTest, setPhotoContentType) {
+    Test_setContentType(ContentType::PHOTO, "PHOTO");
+}
+
+TEST_P(GraphicsComposerHidlTest, setCinemaContentType) {
+    Test_setContentType(ContentType::CINEMA, "CINEMA");
+}
+
+TEST_P(GraphicsComposerHidlTest, setGameContentType) {
+    Test_setContentType(ContentType::GAME, "GAME");
+}
+
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GraphicsComposerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
index 07409f6..7241984 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
@@ -201,22 +201,6 @@
     CheckedDeleteKey(&key_blob_);
 }
 
-void KeymasterHidlTest::CheckCreationDateTime(
-        const AuthorizationSet& sw_enforced,
-        std::chrono::time_point<std::chrono::system_clock> creation) {
-    for (int i = 0; i < sw_enforced.size(); i++) {
-        if (sw_enforced[i].tag == TAG_CREATION_DATETIME) {
-            std::chrono::time_point<std::chrono::system_clock> now =
-                    std::chrono::system_clock::now();
-            std::chrono::time_point<std::chrono::system_clock> reported_time{
-                    std::chrono::milliseconds(sw_enforced[i].f.dateTime)};
-            // The test is flaky for EC keys, so a buffer time of 120 seconds will be added.
-            EXPECT_LE(creation - std::chrono::seconds(120), reported_time);
-            EXPECT_LE(reported_time, now + std::chrono::seconds(1));
-        }
-    }
-}
-
 void KeymasterHidlTest::CheckGetCharacteristics(const HidlBuf& key_blob, const HidlBuf& client_id,
                                                 const HidlBuf& app_data,
                                                 KeyCharacteristics* key_characteristics) {
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index adceead..4bd8b26 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -113,9 +113,6 @@
     void CheckedDeleteKey(HidlBuf* key_blob, bool keep_key_blob = false);
     void CheckedDeleteKey();
 
-    static void CheckCreationDateTime(const AuthorizationSet& sw_enforced,
-                                      std::chrono::time_point<std::chrono::system_clock> creation);
-
     void CheckGetCharacteristics(const HidlBuf& key_blob, const HidlBuf& client_id,
                                  const HidlBuf& app_data, KeyCharacteristics* key_characteristics);
     ErrorCode GetCharacteristics(const HidlBuf& key_blob, const HidlBuf& client_id,
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 f78eb43..66132ad 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -320,8 +320,7 @@
 bool verify_attestation_record(const string& challenge, const string& app_id,
                                AuthorizationSet expected_sw_enforced,
                                AuthorizationSet expected_hw_enforced, SecurityLevel security_level,
-                               const hidl_vec<uint8_t>& attestation_cert,
-                               std::chrono::time_point<std::chrono::system_clock> creation_time) {
+                               const hidl_vec<uint8_t>& attestation_cert) {
     X509_Ptr cert(parse_cert_blob(attestation_cert));
     EXPECT_TRUE(!!cert.get());
     if (!cert.get()) return false;
@@ -405,8 +404,6 @@
     EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
     EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
 
-    KeymasterHidlTest::CheckCreationDateTime(att_sw_enforced, creation_time);
-
     if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
         // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
         EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
@@ -559,24 +556,6 @@
 }
 
 /*
- * NewKeyGenerationTest.RsaCheckCreationDateTime
- *
- * Verifies that creation date time is correct.
- */
-TEST_P(NewKeyGenerationTest, RsaCheckCreationDateTime) {
-    KeyCharacteristics key_characteristics;
-    auto creation_time = std::chrono::system_clock::now();
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .RsaSigningKey(2048, 3)
-                                                 .Digest(Digest::NONE)
-                                                 .Padding(PaddingMode::NONE)));
-    GetCharacteristics(key_blob_, &key_characteristics);
-    AuthorizationSet sw_enforced = key_characteristics.softwareEnforced;
-    CheckCreationDateTime(sw_enforced, creation_time);
-}
-
-/*
  * NewKeyGenerationTest.NoInvalidRsaSizes
  *
  * Verifies that keymaster cannot generate any RSA key sizes that are designated as invalid.
@@ -641,23 +620,6 @@
 }
 
 /*
- * NewKeyGenerationTest.EcCheckCreationDateTime
- *
- * Verifies that creation date time is correct.
- */
-TEST_P(NewKeyGenerationTest, EcCheckCreationDateTime) {
-    KeyCharacteristics key_characteristics;
-    auto creation_time = std::chrono::system_clock::now();
-    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .EcdsaSigningKey(256)
-                                                 .Digest(Digest::NONE)));
-    GetCharacteristics(key_blob_, &key_characteristics);
-    AuthorizationSet sw_enforced = key_characteristics.softwareEnforced;
-    CheckCreationDateTime(sw_enforced, creation_time);
-}
-
-/*
  * NewKeyGenerationTest.EcdsaDefaultSize
  *
  * Verifies that failing to specify a key size for EC key generation returns UNSUPPORTED_KEY_SIZE.
@@ -2645,8 +2607,10 @@
                                    .Padding(PaddingMode::NONE));
     ASSERT_EQ(ErrorCode::OK, err) << "Got " << err;
 
-    err = Begin(KeyPurpose::DECRYPT,
-                AuthorizationSetBuilder().BlockMode(BlockMode::GCM).Padding(PaddingMode::NONE));
+    err = Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
     EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
 
     CheckedDeleteKey();
@@ -2659,8 +2623,10 @@
                                                  .Authorization(TAG_MIN_MAC_LENGTH, 128)
                                                  .Padding(PaddingMode::NONE)));
 
-    err = Begin(KeyPurpose::ENCRYPT,
-                AuthorizationSetBuilder().BlockMode(BlockMode::GCM).Padding(PaddingMode::NONE));
+    err = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
     EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
 }
 
@@ -4238,7 +4204,6 @@
  * Verifies that attesting to RSA keys works and generates the expected output.
  */
 TEST_P(AttestationTest, RsaAttestation) {
-    auto creation_time = std::chrono::system_clock::now();
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .RsaSigningKey(2048, 65537)
@@ -4263,7 +4228,7 @@
     EXPECT_TRUE(verify_attestation_record("challenge", "foo",                     //
                                           key_characteristics_.softwareEnforced,  //
                                           key_characteristics_.hardwareEnforced,  //
-                                          SecLevel(), cert_chain[0], creation_time));
+                                          SecLevel(), cert_chain[0]));
 }
 
 /*
@@ -4292,7 +4257,6 @@
  * Verifies that attesting to EC keys works and generates the expected output.
  */
 TEST_P(AttestationTest, EcAttestation) {
-    auto creation_time = std::chrono::system_clock::now();
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .EcdsaSigningKey(EcCurve::P_256)
@@ -4314,7 +4278,7 @@
     EXPECT_TRUE(verify_attestation_record("challenge", "foo",                     //
                                           key_characteristics_.softwareEnforced,  //
                                           key_characteristics_.hardwareEnforced,  //
-                                          SecLevel(), cert_chain[0], creation_time));
+                                          SecLevel(), cert_chain[0]));
 }
 
 /*
@@ -4347,7 +4311,6 @@
 TEST_P(AttestationTest, AttestationApplicationIDLengthProperlyEncoded) {
     std::vector<uint32_t> app_id_lengths{143, 258};
     for (uint32_t length : app_id_lengths) {
-        auto creation_time = std::chrono::system_clock::now();
         ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                                      .EcdsaSigningKey(EcCurve::P_256)
@@ -4365,7 +4328,7 @@
         EXPECT_TRUE(verify_attestation_record("challenge", app_id,                    //
                                               key_characteristics_.softwareEnforced,  //
                                               key_characteristics_.hardwareEnforced,  //
-                                              SecLevel(), cert_chain[0], creation_time));
+                                              SecLevel(), cert_chain[0]));
         CheckedDeleteKey();
     }
 }
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index be894f2..e3c5376 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -452,7 +452,7 @@
             EvaluatePreparedModel(preparedModel, testModel, TestKind::DYNAMIC_SHAPE);
         } break;
         case TestKind::QUANTIZATION_COUPLING: {
-            ASSERT_TRUE(testModel.hasQuant8AsymmOperands());
+            ASSERT_TRUE(testModel.hasQuant8CoupledOperands());
             createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ false);
             TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel);
             sp<IPreparedModel> preparedCoupledModel;
@@ -521,7 +521,7 @@
                            [](const TestModel& testModel) { return !testModel.expectFailure; });
 
 INSTANTIATE_GENERATED_TEST(DISABLED_QuantizationCouplingTest, [](const TestModel& testModel) {
-    return testModel.hasQuant8AsymmOperands() && testModel.operations.size() == 1;
+    return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1;
 });
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 65880b7..14ab897 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -330,6 +330,8 @@
         // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
         // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
         // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
+        // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of
+        //     TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED.
         switch (operation.type) {
             case OperationType::LSH_PROJECTION: {
                 if (operand == operation.inputs[1]) {
@@ -385,6 +387,13 @@
                     return true;
                 }
             } break;
+            case OperationType::AXIS_ALIGNED_BBOX_TRANSFORM: {
+                if (operand == operation.inputs[1] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
+                    return true;
+                }
+            } break;
             default:
                 break;
         }
diff --git a/radio/1.5/IRadioIndication.hal b/radio/1.5/IRadioIndication.hal
index 81452ab..879e9ad 100644
--- a/radio/1.5/IRadioIndication.hal
+++ b/radio/1.5/IRadioIndication.hal
@@ -30,4 +30,33 @@
      * @param enabled whether uiccApplications are enabled, or disabled
      */
     oneway uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
+
+    /**
+     * Report that Registration or a Location/Routing/Tracking Area update has failed.
+     *
+     * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+     * area update fails. This includes procedures that do not necessarily result in a change of
+     * the modem's registration status. If the modem's registration status changes, that is
+     * reflected in the onNetworkStateChanged() and subsequent get{Voice/Data}RegistrationState().
+     *
+     * @param cellIdentity the CellIdentity, which must include the globally unique identifier for
+     *        the cell (for example, all components of the CGI or ECGI).
+     * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+     *        cell that was chosen for the failed registration attempt.
+     * @param domain Domain::CS, Domain::PS, or both in case of a combined procedure.
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+     *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+     *        MAX_INT if this value is unused.
+     * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+     *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+     *        included as an additionalCauseCode.
+     *        For LTE (ESM), cause codes are in TS 24.301 9.9.4.4
+     *        MAX_INT if this value is unused.
+     */
+    oneway registrationFailed(
+            RadioIndicationType type, CellIdentity cellIdentity, string chosenPlmn,
+            bitfield<Domain> domain, int32_t causeCode, int32_t additionalCauseCode);
 };
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index 73751b8..7d6ec41 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -22,14 +22,22 @@
 import @1.1::RadioAccessSpecifier;
 import @1.1::ScanType;
 import @1.1::UtranBands;
+import @1.2::CellIdentityCdma;
+import @1.2::CellIdentityGsm;
+import @1.2::CellIdentityWcdma;
+import @1.2::CellIdentityTdscdma;
+import @1.2::CellIdentityLte;
 import @1.2::NetworkScanRequest;
 import @1.4::AccessNetwork;
 import @1.4::ApnTypes;
+import @1.4::CellIdentityNr;
 import @1.4::DataCallFailCause;
 import @1.4::DataConnActiveStatus;
 import @1.4::DataProfileInfo;
 import @1.4::PdpProtocolType;
 
+import android.hidl.safe_union@1.0::Monostate;
+
 /**
  * Defining signal strength type.
  */
@@ -57,9 +65,9 @@
     RSRP = 3,
     /**
      * Reference Signal Received Quality
-     * Range: -20 dB to -3 dB;
+     * Range: -34 dB to 3 dB;
      * Used RAN: EUTRAN
-     * Reference: 3GPP TS 36.133 9.1.7
+     * Reference: 3GPP TS 36.133 v12.6.0 section 9.1.7
      */
     RSRQ = 4,
     /**
@@ -397,3 +405,22 @@
     int32_t mtu;
 };
 
+enum Domain : int32_t {
+    /** Circuit-switched */
+    CS = 1 << 0,
+
+    /** Packet-switched */
+    PS = 1 << 1,
+};
+
+/** A union representing the CellIdentity of a single cell */
+safe_union CellIdentity {
+    Monostate noinit;
+
+    CellIdentityGsm gsm;
+    CellIdentityWcdma wcdma;
+    CellIdentityTdscdma tdscdma;
+    CellIdentityCdma cdma;
+    CellIdentityLte lte;
+    CellIdentityNr nr;
+};
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index ba11257..49a315d 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -739,6 +739,13 @@
 
     Return<void> modemReset(RadioIndicationType type,
                             const ::android::hardware::hidl_string& reason);
+
+    Return<void> registrationFailed(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_5::CellIdentity& cellIdentity,
+            const ::android::hardware::hidl_string& chosenPlmn,
+            ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_5::Domain> domain,
+            int32_t causeCode, int32_t additionalCauseCode);
 };
 
 // Test environment for Radio HIDL HAL.
diff --git a/radio/1.5/vts/functional/radio_indication.cpp b/radio/1.5/vts/functional/radio_indication.cpp
index acffbbe..2416605 100644
--- a/radio/1.5/vts/functional/radio_indication.cpp
+++ b/radio/1.5/vts/functional/radio_indication.cpp
@@ -333,3 +333,12 @@
                                                                      bool /*enabled*/) {
     return Void();
 }
+
+Return<void> RadioIndication_v1_5::registrationFailed(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
+        const ::android::hardware::hidl_string& /*chosenPlmn*/,
+        ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_5::Domain> /*domain*/,
+        int32_t /*causeCode*/, int32_t /*additionalCauseCode*/) {
+    return Void();
+}
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index 1dea04e..fa3c08b 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -1197,6 +1197,7 @@
 /**
  *  Scan type for Frontend.
  */
+@export
 enum FrontendScanType : uint32_t {
     SCAN_UNDEFINED = 0,
     SCAN_AUTO = 1 << 0,
@@ -1206,6 +1207,7 @@
 /**
  *  Scan Message Type for Frontend.
  */
+@export
 enum FrontendScanMessageType : uint32_t {
     /**
      * Scan locked the signal.
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
index ec96fcf..62874ef 100644
--- a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
@@ -543,12 +543,16 @@
 
     const auto& status_and_rtt_controller =
         HIDL_INVOKE(wifi_chip_, createRttController, iface);
-    EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_rtt_controller.first.code);
-    EXPECT_NE(nullptr, status_and_rtt_controller.second.get());
+    if (status_and_rtt_controller.first.code !=
+        WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        EXPECT_EQ(WifiStatusCode::SUCCESS,
+                  status_and_rtt_controller.first.code);
+        EXPECT_NE(nullptr, status_and_rtt_controller.second.get());
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, WifiChipHidlTest,
     testing::ValuesIn(
         android::hardware::getAllHalInstanceNames(IWifi::descriptor)),
-    android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
index d584d4b..26e4821 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
@@ -209,23 +209,6 @@
     return status_and_iface.second;
 }
 
-sp<IWifiRttController> getWifiRttController(const std::string& instance_name) {
-    sp<IWifiChip> wifi_chip = getWifiChip(instance_name);
-    if (!wifi_chip.get()) {
-        return nullptr;
-    }
-    sp<IWifiStaIface> wifi_sta_iface = getWifiStaIface(instance_name);
-    if (!wifi_sta_iface.get()) {
-        return nullptr;
-    }
-    const auto& status_and_controller =
-        HIDL_INVOKE(wifi_chip, createRttController, wifi_sta_iface);
-    if (status_and_controller.first.code != WifiStatusCode::SUCCESS) {
-        return nullptr;
-    }
-    return status_and_controller.second;
-}
-
 bool configureChipToSupportIfaceType(const sp<IWifiChip>& wifi_chip,
                                      IfaceType type,
                                      ChipModeId* configured_mode_id) {
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
index bdee2ec..8660134 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
@@ -44,8 +44,6 @@
     const std::string& instance_name = "");
 android::sp<android::hardware::wifi::V1_0::IWifiStaIface> getWifiStaIface(
     const std::string& instance_name = "");
-android::sp<android::hardware::wifi::V1_0::IWifiRttController>
-getWifiRttController(const std::string& instance_name = "");
 // Configure the chip in a mode to support the creation of the provided
 // iface type.
 bool configureChipToSupportIfaceType(
diff --git a/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp
index e1ee34f..6c01995 100644
--- a/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_rtt_controller_hidl_test.cpp
@@ -22,11 +22,15 @@
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
 
+#include "wifi_hidl_call_util.h"
 #include "wifi_hidl_test_utils.h"
 
 using ::android::sp;
 using ::android::hardware::wifi::V1_0::IWifi;
+using ::android::hardware::wifi::V1_0::IWifiChip;
 using ::android::hardware::wifi::V1_0::IWifiRttController;
+using ::android::hardware::wifi::V1_0::IWifiStaIface;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
 
 /**
  * Fixture to use for all RTT controller HIDL interface tests.
@@ -48,11 +52,26 @@
  */
 TEST_P(WifiRttControllerHidlTest, Create) {
     stopWifi(GetInstanceName());
-    EXPECT_NE(nullptr, getWifiRttController(GetInstanceName()).get());
+
+    const std::string& instance_name = GetInstanceName();
+
+    sp<IWifiChip> wifi_chip = getWifiChip(instance_name);
+    EXPECT_NE(nullptr, wifi_chip.get());
+
+    sp<IWifiStaIface> wifi_sta_iface = getWifiStaIface(instance_name);
+    EXPECT_NE(nullptr, wifi_sta_iface.get());
+
+    const auto& status_and_controller =
+        HIDL_INVOKE(wifi_chip, createRttController, wifi_sta_iface);
+    if (status_and_controller.first.code !=
+        WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+        EXPECT_NE(nullptr, status_and_controller.second.get());
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, WifiRttControllerHidlTest,
     testing::ValuesIn(
         android::hardware::getAllHalInstanceNames(IWifi::descriptor)),
-    android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp
index 47faec8..93aa0f3 100644
--- a/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.2/vts/functional/wifi_chip_hidl_test.cpp
@@ -176,11 +176,15 @@
     sp<WifiChipEventCallback> wifiChipEventCallback = new WifiChipEventCallback();
     const auto& status =
         HIDL_INVOKE(wifi_chip_, registerEventCallback_1_2, wifiChipEventCallback);
-    EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+
+    if (status.code != WifiStatusCode::SUCCESS) {
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status.code);
+        return;
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, WifiChipHidlTest,
     testing::ValuesIn(android::hardware::getAllHalInstanceNames(
         ::android::hardware::wifi::V1_2::IWifi::descriptor)),
-    android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
index f3f76e1..d5d87ce 100644
--- a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -481,15 +481,18 @@
     callbackType = INVALID;
     NanEnableRequest nanEnableRequest = {};
     NanConfigRequestSupplemental nanConfigRequestSupp = {};
-    ASSERT_EQ(WifiStatusCode::SUCCESS,
-              HIDL_INVOKE(iwifiNanIface, enableRequest_1_2, inputCmdId,
-                          nanEnableRequest, nanConfigRequestSupp)
-                  .code);
-    // wait for a callback
-    ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_ENABLE_RESPONSE));
-    ASSERT_EQ(NOTIFY_ENABLE_RESPONSE, callbackType);
-    ASSERT_EQ(id, inputCmdId);
-    ASSERT_EQ(status.status, NanStatusType::INVALID_ARGS);
+    const auto& halStatus =
+        HIDL_INVOKE(iwifiNanIface, enableRequest_1_2, inputCmdId,
+                    nanEnableRequest, nanConfigRequestSupp);
+    if (halStatus.code != WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        ASSERT_EQ(WifiStatusCode::SUCCESS, halStatus.code);
+
+        // wait for a callback
+        ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_ENABLE_RESPONSE));
+        ASSERT_EQ(NOTIFY_ENABLE_RESPONSE, callbackType);
+        ASSERT_EQ(id, inputCmdId);
+        ASSERT_EQ(status.status, NanStatusType::INVALID_ARGS);
+    }
 }
 
 /*
@@ -502,10 +505,12 @@
     nanEnableRequest.configParams.numberOfPublishServiceIdsInBeacon =
         128;  // must be <= 127
     NanConfigRequestSupplemental nanConfigRequestSupp = {};
-    ASSERT_EQ(WifiStatusCode::ERROR_INVALID_ARGS,
-              HIDL_INVOKE(iwifiNanIface, enableRequest_1_2, inputCmdId,
-                          nanEnableRequest, nanConfigRequestSupp)
-                  .code);
+    const auto& halStatus =
+        HIDL_INVOKE(iwifiNanIface, enableRequest_1_2, inputCmdId,
+                    nanEnableRequest, nanConfigRequestSupp);
+    if (halStatus.code != WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        ASSERT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, halStatus.code);
+    }
 }
 
 /*
@@ -516,15 +521,19 @@
     callbackType = INVALID;
     NanConfigRequest nanConfigRequest = {};
     NanConfigRequestSupplemental nanConfigRequestSupp = {};
-    ASSERT_EQ(WifiStatusCode::SUCCESS,
-              HIDL_INVOKE(iwifiNanIface, configRequest_1_2, inputCmdId,
-                          nanConfigRequest, nanConfigRequestSupp)
-                  .code);
-    // wait for a callback
-    ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CONFIG_RESPONSE));
-    ASSERT_EQ(NOTIFY_CONFIG_RESPONSE, callbackType);
-    ASSERT_EQ(id, inputCmdId);
-    ASSERT_EQ(status.status, NanStatusType::INVALID_ARGS);
+    const auto& halStatus =
+        HIDL_INVOKE(iwifiNanIface, configRequest_1_2, inputCmdId,
+                    nanConfigRequest, nanConfigRequestSupp);
+
+    if (halStatus.code != WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        ASSERT_EQ(WifiStatusCode::SUCCESS, halStatus.code);
+
+        // wait for a callback
+        ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CONFIG_RESPONSE));
+        ASSERT_EQ(NOTIFY_CONFIG_RESPONSE, callbackType);
+        ASSERT_EQ(id, inputCmdId);
+        ASSERT_EQ(status.status, NanStatusType::INVALID_ARGS);
+    }
 }
 
 /*
@@ -536,14 +545,16 @@
     NanConfigRequest nanConfigRequest = {};
     nanConfigRequest.numberOfPublishServiceIdsInBeacon = 128;  // must be <= 127
     NanConfigRequestSupplemental nanConfigRequestSupp = {};
-    ASSERT_EQ(WifiStatusCode::ERROR_INVALID_ARGS,
-              HIDL_INVOKE(iwifiNanIface, configRequest_1_2, inputCmdId,
-                          nanConfigRequest, nanConfigRequestSupp)
-                  .code);
+    const auto& halStatus =
+        HIDL_INVOKE(iwifiNanIface, configRequest_1_2, inputCmdId,
+                    nanConfigRequest, nanConfigRequestSupp);
+    if (halStatus.code != WifiStatusCode::ERROR_NOT_SUPPORTED) {
+        ASSERT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, halStatus.code);
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, WifiNanIfaceHidlTest,
     testing::ValuesIn(android::hardware::getAllHalInstanceNames(
         ::android::hardware::wifi::V1_2::IWifi::descriptor)),
-    android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.4/default/wifi_chip.cpp b/wifi/1.4/default/wifi_chip.cpp
index a70457b..3498510 100644
--- a/wifi/1.4/default/wifi_chip.cpp
+++ b/wifi/1.4/default/wifi_chip.cpp
@@ -992,7 +992,7 @@
 std::pair<WifiStatus, sp<V1_0::IWifiRttController>>
 WifiChip::createRttControllerInternal(const sp<IWifiIface>& /*bound_iface*/) {
     LOG(ERROR) << "createRttController is not supported on this HAL";
-    return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    return {createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED), {}};
 }
 
 std::pair<WifiStatus, std::vector<WifiDebugRingBufferStatus>>
diff --git a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
index 1804d8c..345cf31 100644
--- a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
@@ -233,7 +233,11 @@
 TEST_P(HostapdHidlTest, AddPskAccessPointWithoutAcs) {
     auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_1,
                               getIfaceParamsWithoutAcs(), getPskNwParams());
-    EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    // FAILURE_UNKNOWN is used by higher versions to indicate this API is no
+    // longer supported (replaced by an upgraded API)
+    if (status.code != HostapdStatusCode::FAILURE_UNKNOWN) {
+        EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    }
 }
 
 /**
@@ -243,7 +247,11 @@
 TEST_P(HostapdHidlTest, AddOpenAccessPointWithoutAcs) {
     auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_1,
                               getIfaceParamsWithoutAcs(), getOpenNwParams());
-    EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    // FAILURE_UNKNOWN is used by higher versions to indicate this API is no
+    // longer supported (replaced by an upgraded API)
+    if (status.code != HostapdStatusCode::FAILURE_UNKNOWN) {
+        EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    }
 }
 
 /**
@@ -269,10 +277,14 @@
 TEST_P(HostapdHidlTest, RemoveAccessPointWithoutAcs) {
     auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_1,
                               getIfaceParamsWithoutAcs(), getPskNwParams());
-    EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
-    status =
-        HIDL_INVOKE(hostapd_, removeAccessPoint, getPrimaryWlanIfaceName());
-    EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    // FAILURE_UNKNOWN is used by higher versions to indicate this API is no
+    // longer supported (replaced by an upgraded API)
+    if (status.code != HostapdStatusCode::FAILURE_UNKNOWN) {
+        EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+        status =
+            HIDL_INVOKE(hostapd_, removeAccessPoint, getPrimaryWlanIfaceName());
+        EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+    }
 }
 
 /**
diff --git a/wifi/hostapd/1.2/IHostapd.hal b/wifi/hostapd/1.2/IHostapd.hal
index 1bac1e7..c296cd5 100644
--- a/wifi/hostapd/1.2/IHostapd.hal
+++ b/wifi/hostapd/1.2/IHostapd.hal
@@ -21,6 +21,7 @@
 import HostapdStatus;
 import MacAddress;
 import Ieee80211ReasonCode;
+import DebugLevel;
 
 /**
  * Top-level object for managing SoftAPs.
@@ -120,4 +121,17 @@
      */
     forceClientDisconnect(string ifaceName, MacAddress clientAddress,
         Ieee80211ReasonCode reasonCode) generates (HostapdStatus status);
+
+    /**
+     * Set debug parameters for the hostapd.
+     *
+     * @param level Debug logging level for the hostapd.
+     *        (one of |DebugLevel| values).
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |HostapdStatusCode.SUCCESS|,
+     *         |HostapdStatusCode.FAILURE_UNKNOWN|
+     */
+    setDebugParams(DebugLevel level)
+        generates (HostapdStatus status);
 };
diff --git a/wifi/hostapd/1.2/types.hal b/wifi/hostapd/1.2/types.hal
index 06e890b..54e6529 100644
--- a/wifi/hostapd/1.2/types.hal
+++ b/wifi/hostapd/1.2/types.hal
@@ -53,3 +53,17 @@
      */
     string debugMessage;
 };
+
+/**
+ * Debug levels for the hostapd.
+ * Only log messages with a level greater than the set level
+ * (via |setDebugParams|) will be logged.
+ */
+enum DebugLevel : uint32_t {
+    EXCESSIVE = 0,
+    MSGDUMP = 1,
+    DEBUG = 2,
+    INFO = 3,
+    WARNING = 4,
+    ERROR = 5
+};
diff --git a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
index b092d00..8245f8f 100644
--- a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
@@ -31,6 +31,7 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+using ::android::hardware::wifi::hostapd::V1_2::DebugLevel;
 using ::android::hardware::wifi::hostapd::V1_2::HostapdStatusCode;
 using ::android::hardware::wifi::hostapd::V1_2::Ieee80211ReasonCode;
 using ::android::hardware::wifi::hostapd::V1_2::IHostapd;
@@ -319,6 +320,14 @@
     EXPECT_EQ(HostapdStatusCode::FAILURE_CLIENT_UNKNOWN, status_1_2.code);
 }
 
+/*
+ * SetDebugParams
+ */
+TEST_P(HostapdHidlTest, SetDebugParams) {
+    auto status = HIDL_INVOKE(hostapd_, setDebugParams, DebugLevel::EXCESSIVE);
+    EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
 INSTANTIATE_TEST_CASE_P(
     PerInstance, HostapdHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 8c9f9cd..adc955e 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -109,10 +109,9 @@
 
         // We need to first get the key management capabilities from the device.
         // If DPP is not supported, we just pass the test.
-        sta_iface_->getKeyMgmtCapabilities(
+        sta_iface_->getKeyMgmtCapabilities_1_3(
             [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
                 EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
-
                 keyMgmtMask = keyMgmtMaskInternal;
             });