Add Stable AIDL version of EVS HAL

This CL add a new set of EVS APIs written in Stable AIDL with an empty
IEvsEnumerator implementation.

Bug: 170401743
Bug: 195672428
Bug: 202031799
Bug: 202669715
Test: m -j
Change-Id: I28ff4391ba51b72299ebfae801d21a8ab0e37917
diff --git a/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
new file mode 100644
index 0000000..c709d40
--- /dev/null
+++ b/automotive/evs/aidl/vts/VtsHalEvsTargetTest.cpp
@@ -0,0 +1,2170 @@
+/*
+ * Copyright (C) 2022 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 "FrameHandler.h"
+#include "FrameHandlerUltrasonics.h"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
+#include <aidl/android/hardware/automotive/evs/CameraParam.h>
+#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
+#include <aidl/android/hardware/automotive/evs/DisplayState.h>
+#include <aidl/android/hardware/automotive/evs/EvsEventDesc.h>
+#include <aidl/android/hardware/automotive/evs/EvsEventType.h>
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
+#include <aidl/android/hardware/automotive/evs/IEvsCamera.h>
+#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
+#include <aidl/android/hardware/automotive/evs/IEvsEnumerator.h>
+#include <aidl/android/hardware/automotive/evs/IEvsUltrasonicsArray.h>
+#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
+#include <aidl/android/hardware/automotive/evs/Stream.h>
+#include <aidl/android/hardware/automotive/evs/UltrasonicsArrayDesc.h>
+#include <aidl/android/hardware/common/NativeHandle.h>
+#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/binder_status.h>
+#include <system/camera_metadata.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <utils/Timers.h>
+
+#include <deque>
+#include <thread>
+#include <unordered_set>
+
+namespace {
+
+// These values are called out in the EVS design doc (as of Mar 8, 2017)
+constexpr int kMaxStreamStartMilliseconds = 500;
+constexpr int kMinimumFramesPerSecond = 10;
+constexpr int kSecondsToMilliseconds = 1000;
+constexpr int kMillisecondsToMicroseconds = 1000;
+constexpr float kNanoToMilliseconds = 0.000001f;
+constexpr float kNanoToSeconds = 0.000000001f;
+
+/*
+ * Please note that this is different from what is defined in
+ * libhardware/modules/camera/3_4/metadata/types.h; this has one additional
+ * field to store a framerate.
+ */
+typedef struct {
+    int32_t id;
+    int32_t width;
+    int32_t height;
+    int32_t format;
+    int32_t direction;
+    int32_t framerate;
+} RawStreamConfig;
+constexpr size_t kStreamCfgSz = sizeof(RawStreamConfig) / sizeof(int32_t);
+
+}  // namespace
+
+using ::aidl::android::hardware::automotive::evs::BufferDesc;
+using ::aidl::android::hardware::automotive::evs::CameraDesc;
+using ::aidl::android::hardware::automotive::evs::CameraParam;
+using ::aidl::android::hardware::automotive::evs::DisplayDesc;
+using ::aidl::android::hardware::automotive::evs::DisplayState;
+using ::aidl::android::hardware::automotive::evs::EvsEventDesc;
+using ::aidl::android::hardware::automotive::evs::EvsEventType;
+using ::aidl::android::hardware::automotive::evs::EvsResult;
+using ::aidl::android::hardware::automotive::evs::IEvsCamera;
+using ::aidl::android::hardware::automotive::evs::IEvsDisplay;
+using ::aidl::android::hardware::automotive::evs::IEvsEnumerator;
+using ::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray;
+using ::aidl::android::hardware::automotive::evs::ParameterRange;
+using ::aidl::android::hardware::automotive::evs::Stream;
+using ::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::HardwareBufferDescription;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using std::chrono_literals::operator""s;
+
+// The main test class for EVS
+class EvsAidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        // Make sure we can connect to the enumerator
+        std::string service_name = GetParam();
+        AIBinder* binder = AServiceManager_waitForService(service_name.data());
+        ASSERT_NE(binder, nullptr);
+        mEnumerator = IEvsEnumerator::fromBinder(::ndk::SpAIBinder(binder));
+        LOG(INFO) << "Test target service: " << service_name;
+
+        ASSERT_TRUE(mEnumerator->isHardware(&mIsHwModule).isOk());
+    }
+
+    virtual void TearDown() override {
+        // Attempt to close any active camera
+        for (auto&& cam : mActiveCameras) {
+            if (cam != nullptr) {
+                mEnumerator->closeCamera(cam);
+            }
+        }
+        mActiveCameras.clear();
+    }
+
+  protected:
+    void loadCameraList() {
+        // SetUp() must run first!
+        ASSERT_NE(mEnumerator, nullptr);
+
+        // Get the camera list
+        ASSERT_TRUE(mEnumerator->getCameraList(&mCameraInfo).isOk())
+                << "Failed to get a list of available cameras";
+        LOG(INFO) << "We have " << mCameraInfo.size() << " cameras.";
+    }
+
+    void loadUltrasonicsArrayList() {
+        // SetUp() must run first!
+        ASSERT_NE(mEnumerator, nullptr);
+
+        // Get the ultrasonics array list
+        ASSERT_TRUE(mEnumerator->getUltrasonicsArrayList(&mUltrasonicsArraysInfo).isOk())
+                << "Failed to get a list of available ultrasonics arrays";
+        LOG(INFO) << "We have " << mCameraInfo.size() << " ultrasonics arrays.";
+    }
+
+    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 (rc != 0) {
+            // 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;
+        const auto it = std::find_if(mCameraInfo.begin(), mCameraInfo.end(),
+                                     [&id](const CameraDesc& desc) { return id == desc.id; });
+        if (it == mCameraInfo.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.
+            LOG(INFO) << id << " is not a logical camera device.";
+            physicalCameras.insert(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);
+        if (rc != 0) {
+            LOG(ERROR) << "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.insert(id);
+                }
+                start = i + 1;
+            }
+        }
+
+        LOG(INFO) << id << " consists of " << physicalCameras.size() << " physical camera devices";
+        return physicalCameras;
+    }
+
+    Stream getFirstStreamConfiguration(camera_metadata_t* metadata) {
+        Stream targetCfg = {};
+        camera_metadata_entry_t streamCfgs;
+        if (!find_camera_metadata_entry(metadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                                        &streamCfgs)) {
+            // Stream configurations are found in metadata
+            RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
+            for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                    targetCfg.width = ptr->width;
+                    targetCfg.height = ptr->height;
+                    targetCfg.format = static_cast<PixelFormat>(ptr->format);
+                    break;
+                }
+                ++ptr;
+            }
+        }
+
+        return targetCfg;
+    }
+
+    // Every test needs access to the service
+    std::shared_ptr<IEvsEnumerator> mEnumerator;
+    // Empty unless/util loadCameraList() is called
+    std::vector<CameraDesc> mCameraInfo;
+    // boolean to tell current module under testing is HW module implementation
+    // or not
+    bool mIsHwModule;
+    // A list of active camera handles that are need to be cleaned up
+    std::deque<std::shared_ptr<IEvsCamera>> mActiveCameras;
+    // Empty unless/util loadUltrasonicsArrayList() is called
+    std::vector<UltrasonicsArrayDesc> mUltrasonicsArraysInfo;
+    // A list of active ultrasonics array handles that are to be cleaned up
+    std::deque<std::weak_ptr<IEvsUltrasonicsArray>> mActiveUltrasonicsArrays;
+};
+
+// Test cases, their implementations, and corresponding requirements are
+// documented at go/aae-evs-public-api-test.
+
+/*
+ * CameraOpenClean:
+ * Opens each camera reported by the enumerator and then explicitly closes it via a
+ * call to closeCamera.  Then repeats the test to ensure all cameras can be reopened.
+ */
+TEST_P(EvsAidlTest, CameraOpenClean) {
+    LOG(INFO) << "Starting CameraOpenClean test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Open and close each camera twice
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            LOG(INFO) << "Skip a logical device, " << cam.id << " for HW target.";
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        for (int pass = 0; pass < 2; pass++) {
+            std::shared_ptr<IEvsCamera> pCam;
+            ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+            ASSERT_NE(pCam, nullptr);
+
+            CameraDesc cameraInfo;
+            for (auto&& devName : devices) {
+                ASSERT_TRUE(pCam->getPhysicalCameraInfo(devName, &cameraInfo).isOk());
+                EXPECT_EQ(devName, cameraInfo.id);
+            }
+
+            // Store a camera handle for a clean-up
+            mActiveCameras.push_back(pCam);
+
+            // Verify that this camera self-identifies correctly
+            ASSERT_TRUE(pCam->getCameraInfo(&cameraInfo).isOk());
+            EXPECT_EQ(cam.id, cameraInfo.id);
+
+            // Verify methods for extended info
+            const auto id = 0xFFFFFFFF;  // meaningless id
+            std::vector<uint8_t> values;
+            auto status = pCam->setExtendedInfo(id, values);
+            if (isLogicalCam) {
+                EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() ==
+                                                      static_cast<int>(EvsResult::NOT_SUPPORTED));
+            } else {
+                EXPECT_TRUE(status.isOk());
+            }
+
+            status = pCam->getExtendedInfo(id, &values);
+            if (isLogicalCam) {
+                EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() ==
+                                                      static_cast<int>(EvsResult::NOT_SUPPORTED));
+            } else {
+                EXPECT_TRUE(status.isOk());
+            }
+
+            // Explicitly close the camera so resources are released right away
+            ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+            mActiveCameras.clear();
+        }
+    }
+}
+
+/*
+ * CameraOpenAggressive:
+ * Opens each camera reported by the enumerator twice in a row without an intervening closeCamera
+ * call.  This ensures that the intended "aggressive open" behavior works.  This is necessary for
+ * the system to be tolerant of shutdown/restart race conditions.
+ */
+TEST_P(EvsAidlTest, CameraOpenAggressive) {
+    LOG(INFO) << "Starting CameraOpenAggressive test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Open and close each camera twice
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            LOG(INFO) << "Skip a logical device, " << cam.id << " for HW target.";
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        mActiveCameras.clear();
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Verify that this camera self-identifies correctly
+        CameraDesc cameraInfo;
+        ASSERT_TRUE(pCam->getCameraInfo(&cameraInfo).isOk());
+        EXPECT_EQ(cam.id, cameraInfo.id);
+
+        std::shared_ptr<IEvsCamera> pCam2;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam2).isOk());
+        EXPECT_NE(pCam2, nullptr);
+        EXPECT_NE(pCam, pCam2);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam2);
+
+        auto status = pCam->setMaxFramesInFlight(2);
+        if (mIsHwModule) {
+            // Verify that the old camera rejects calls via HW module.
+            EXPECT_TRUE(!status.isOk() && status.getServiceSpecificError() ==
+                                                  static_cast<int>(EvsResult::OWNERSHIP_LOST));
+        } else {
+            // default implementation supports multiple clients.
+            EXPECT_TRUE(status.isOk());
+        }
+
+        // Close the superseded camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.pop_front();
+
+        // Verify that the second camera instance self-identifies correctly
+        ASSERT_TRUE(pCam2->getCameraInfo(&cameraInfo).isOk());
+        EXPECT_EQ(cam.id, cameraInfo.id);
+
+        // Close the second camera instance
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam2).isOk());
+        mActiveCameras.pop_front();
+    }
+
+    // Sleep here to ensure the destructor cleanup has time to run so we don't break follow on tests
+    sleep(1);  // I hate that this is an arbitrary time to wait.  :(  b/36122635
+}
+
+/*
+ * CameraStreamPerformance:
+ * Measure and qualify the stream start up time and streaming frame rate of each reported camera
+ */
+TEST_P(EvsAidlTest, CameraStreamPerformance) {
+    LOG(INFO) << "Starting CameraStreamPerformance test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            LOG(INFO) << "Skip a logical device " << cam.id;
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Set up a frame receiver object which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start the camera's video stream
+        nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // Ensure the first frame arrived within the expected time
+        frameHandler->waitForFrameCount(1);
+        nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC);
+        nsecs_t timeToFirstFrame = systemTime(SYSTEM_TIME_MONOTONIC) - start;
+
+        // 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.id.data(),
+               timeToFirstFrame * kNanoToMilliseconds);
+        LOG(INFO) << cam.id << ": Measured time to first frame " << std::scientific
+                  << timeToFirstFrame * kNanoToMilliseconds << " ms.";
+
+        // Check aspect ratio
+        unsigned width = 0, height = 0;
+        frameHandler->getFrameDimension(&width, &height);
+        EXPECT_GE(width, height);
+
+        // 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
+        nsecs_t runTime = end - firstFrame;
+        float framesPerSecond = framesReceived / (runTime * kNanoToSeconds);
+        printf("Measured camera rate %3.2f fps\n", framesPerSecond);
+        LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond << " fps.";
+        EXPECT_GE(framesPerSecond, kMinimumFramesPerSecond);
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * CameraStreamBuffering:
+ * Ensure the camera implementation behaves properly when the client holds onto buffers for more
+ * than one frame time.  The camera must cleanly skip frames until the client is ready again.
+ */
+TEST_P(EvsAidlTest, CameraStreamBuffering) {
+    LOG(INFO) << "Starting CameraStreamBuffering test";
+
+    // Arbitrary constant (should be > 1 and not too big)
+    static const unsigned int kBuffersToHold = 6;
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            LOG(INFO) << "Skip a logical device " << cam.id << " for HW target.";
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Ask for a very large number of buffers in flight to ensure it errors correctly
+        auto badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF);
+        EXPECT_TRUE(!badResult.isOk() && badResult.getServiceSpecificError() ==
+                                                 static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+
+        // Now ask for exactly two buffers in flight as we'll test behavior in that case
+        ASSERT_TRUE(pCam->setMaxFramesInFlight(kBuffersToHold).isOk());
+
+        // Set up a frame receiver object which will fire up its own thread.
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eNoAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // 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(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";
+
+        // Give back one buffer
+        ASSERT_TRUE(frameHandler->returnHeldBuffer());
+
+        // Once we return a buffer, it shouldn't take more than 1/10 second to get a new one
+        // filled since we require 10fps minimum -- but give a 10% allowance just in case.
+        usleep(110 * kMillisecondsToMicroseconds);
+        frameHandler->getFramesCounters(&framesReceived, nullptr);
+        EXPECT_EQ(kBuffersToHold + 1, framesReceived) << "Stream should've resumed";
+
+        // 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
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * CameraToDisplayRoundTrip:
+ * End to end test of data flowing from the camera to the display.  Each delivered frame of camera
+ * imagery is simply copied to the display buffer and presented on screen.  This is the one test
+ * which a human could observe to see the operation of the system on the physical display.
+ */
+TEST_P(EvsAidlTest, CameraToDisplayRoundTrip) {
+    LOG(INFO) << "Starting CameraToDisplayRoundTrip test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Request available display IDs
+    uint8_t targetDisplayId = 0;
+    std::vector<uint8_t> displayIds;
+    ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+    EXPECT_GT(displayIds.size(), 0);
+    targetDisplayId = displayIds[0];
+
+    // Request exclusive access to the first EVS display
+    std::shared_ptr<IEvsDisplay> pDisplay;
+    ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk());
+    EXPECT_NE(pDisplay, nullptr);
+    LOG(INFO) << "Display " << targetDisplayId << " is in use.";
+
+    // Get the display descriptor
+    DisplayDesc displayDesc;
+    ASSERT_TRUE(pDisplay->getDisplayInfo(&displayDesc).isOk());
+    LOG(INFO) << "    Resolution: " << displayDesc.width << "x" << displayDesc.height;
+    ASSERT_GT(displayDesc.width, 0);
+    ASSERT_GT(displayDesc.height, 0);
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (mIsHwModule && isLogicalCam) {
+            LOG(INFO) << "Skip a logical device " << cam.id << " for HW target.";
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Set up a frame receiver object which will fire up its own thread.
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, pDisplay, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Activate the display
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk());
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // Wait a while to let the data flow
+        static const int kSecondsToWait = 5;
+        const int streamTimeMs =
+                kSecondsToWait * kSecondsToMilliseconds - kMaxStreamStartMilliseconds;
+        const unsigned minimumFramesExpected =
+                streamTimeMs * kMinimumFramesPerSecond / kSecondsToMilliseconds;
+        sleep(kSecondsToWait);
+        unsigned framesReceived = 0;
+        unsigned framesDisplayed = 0;
+        frameHandler->getFramesCounters(&framesReceived, &framesDisplayed);
+        EXPECT_EQ(framesReceived, framesDisplayed);
+        EXPECT_GE(framesDisplayed, minimumFramesExpected);
+
+        // Turn off the display (yes, before the stream stops -- it should be handled)
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk());
+
+        // Shut down the streamer
+        frameHandler->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+    }
+
+    // Explicitly release the display
+    ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+}
+
+/*
+ * MultiCameraStream:
+ * Verify that each client can start and stop video streams on the same
+ * underlying camera.
+ */
+TEST_P(EvsAidlTest, MultiCameraStream) {
+    LOG(INFO) << "Starting MultiCameraStream test";
+
+    if (mIsHwModule) {
+        // This test is not for HW module implementation.
+        return;
+    }
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Create two camera clients.
+        std::shared_ptr<IEvsCamera> pCam0;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk());
+        EXPECT_NE(pCam0, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam0);
+
+        std::shared_ptr<IEvsCamera> pCam1;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk());
+        EXPECT_NE(pCam1, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam1);
+
+        // Set up per-client frame receiver objects which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandler0 =
+                std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn);
+        std::shared_ptr<FrameHandler> frameHandler1 =
+                std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler0, nullptr);
+        EXPECT_NE(frameHandler1, nullptr);
+
+        // Start the camera's video stream via client 0
+        ASSERT_TRUE(frameHandler0->startStream());
+        ASSERT_TRUE(frameHandler1->startStream());
+
+        // Ensure the stream starts
+        frameHandler0->waitForFrameCount(1);
+        frameHandler1->waitForFrameCount(1);
+
+        nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        // Wait a bit, then ensure both clients get at least the required minimum number of frames
+        sleep(5);
+        nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+        unsigned framesReceived0 = 0, framesReceived1 = 0;
+        frameHandler0->getFramesCounters(&framesReceived0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceived1, nullptr);
+        framesReceived0 = framesReceived0 - 1;  // Back out the first frame we already waited for
+        framesReceived1 = framesReceived1 - 1;  // Back out the first frame we already waited for
+        nsecs_t runTime = end - firstFrame;
+        float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds);
+        float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds);
+        LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond0 << " fps and "
+                  << framesPerSecond1 << " fps";
+        EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond);
+        EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond);
+
+        // Shutdown one client
+        frameHandler0->shutdown();
+
+        // Read frame counters again
+        frameHandler0->getFramesCounters(&framesReceived0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceived1, nullptr);
+
+        // Wait a bit again
+        sleep(5);
+        unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0;
+        frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr);
+        EXPECT_EQ(framesReceived0, framesReceivedAfterStop0);
+        EXPECT_LT(framesReceived1, framesReceivedAfterStop1);
+
+        // Shutdown another
+        frameHandler1->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk());
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk());
+        mActiveCameras.clear();
+
+        // 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);
+    }
+}
+
+/*
+ * CameraParameter:
+ * Verify that a client can adjust a camera parameter.
+ */
+TEST_P(EvsAidlTest, CameraParameter) {
+    LOG(INFO) << "Starting CameraParameter test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            LOG(INFO) << "Skip a logical device " << cam.id;
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Create a camera client
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera
+        mActiveCameras.push_back(pCam);
+
+        // Get the parameter list
+        std::vector<CameraParam> cmds;
+        ASSERT_TRUE(pCam->getParameterList(&cmds).isOk());
+        if (cmds.size() < 1) {
+            continue;
+        }
+
+        // Set up per-client frame receiver objects which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // Ensure the stream starts
+        frameHandler->waitForFrameCount(1);
+
+        // Set current client is the primary client
+        ASSERT_TRUE(pCam->setPrimaryClient().isOk());
+        for (auto& cmd : cmds) {
+            // Get a valid parameter value range
+            ParameterRange range;
+            ASSERT_TRUE(pCam->getIntParameterRange(cmd, &range).isOk());
+
+            std::vector<int32_t> values;
+            if (cmd == CameraParam::ABSOLUTE_FOCUS) {
+                // Try to turn off auto-focus
+                ASSERT_TRUE(pCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk());
+                for (auto&& v : values) {
+                    EXPECT_EQ(v, 0);
+                }
+            }
+
+            // Try to program a parameter with a random value [minVal, maxVal]
+            int32_t val0 = range.min + (std::rand() % (range.max - range.min));
+
+            // Rounding down
+            val0 = val0 - (val0 % range.step);
+            values.clear();
+            ASSERT_TRUE(pCam->setIntParameter(cmd, val0, &values).isOk());
+
+            values.clear();
+            ASSERT_TRUE(pCam->getIntParameter(cmd, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_EQ(val0, v) << "Values are not matched.";
+            }
+        }
+        ASSERT_TRUE(pCam->unsetPrimaryClient().isOk());
+
+        // Shutdown
+        frameHandler->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * CameraPrimaryClientRelease
+ * Verify that non-primary client gets notified when the primary client either
+ * terminates or releases a role.
+ */
+TEST_P(EvsAidlTest, CameraPrimaryClientRelease) {
+    LOG(INFO) << "Starting CameraPrimaryClientRelease test";
+
+    if (mIsHwModule) {
+        // This test is not for HW module implementation.
+        return;
+    }
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            LOG(INFO) << "Skip a logical device " << cam.id;
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Create two camera clients.
+        std::shared_ptr<IEvsCamera> pPrimaryCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pPrimaryCam).isOk());
+        EXPECT_NE(pPrimaryCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pPrimaryCam);
+
+        std::shared_ptr<IEvsCamera> pSecondaryCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pSecondaryCam).isOk());
+        EXPECT_NE(pSecondaryCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pSecondaryCam);
+
+        // Set up per-client frame receiver objects which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandlerPrimary = std::make_shared<FrameHandler>(
+                pPrimaryCam, cam, nullptr, FrameHandler::eAutoReturn);
+        std::shared_ptr<FrameHandler> frameHandlerSecondary = std::make_shared<FrameHandler>(
+                pSecondaryCam, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandlerPrimary, nullptr);
+        EXPECT_NE(frameHandlerSecondary, nullptr);
+
+        // Set one client as the primary client
+        ASSERT_TRUE(pPrimaryCam->setPrimaryClient().isOk());
+
+        // Try to set another client as the primary client.
+        ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk());
+
+        // Start the camera's video stream via a primary client client.
+        ASSERT_TRUE(frameHandlerPrimary->startStream());
+
+        // Ensure the stream starts
+        frameHandlerPrimary->waitForFrameCount(1);
+
+        // Start the camera's video stream via another client
+        ASSERT_TRUE(frameHandlerSecondary->startStream());
+
+        // Ensure the stream starts
+        frameHandlerSecondary->waitForFrameCount(1);
+
+        // Non-primary client expects to receive a primary client role relesed
+        // notification.
+        EvsEventDesc aTargetEvent = {};
+        EvsEventDesc aNotification = {};
+
+        bool listening = false;
+        std::mutex eventLock;
+        std::condition_variable eventCond;
+        std::thread listener =
+                std::thread([&aNotification, &frameHandlerSecondary, &listening, &eventCond]() {
+                    // Notify that a listening thread is running.
+                    listening = true;
+                    eventCond.notify_all();
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                    if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification, true)) {
+                        LOG(WARNING) << "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 primary client role.
+        ASSERT_TRUE(pPrimaryCam->unsetPrimaryClient().isOk());
+
+        // Join a listening thread.
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        // Verify change notifications.
+        ASSERT_EQ(EvsEventType::MASTER_RELEASED, static_cast<EvsEventType>(aNotification.aType));
+
+        // Non-primary becomes a primary client.
+        ASSERT_TRUE(pSecondaryCam->setPrimaryClient().isOk());
+
+        // Previous primary client fails to become a primary client.
+        ASSERT_FALSE(pPrimaryCam->setPrimaryClient().isOk());
+
+        listening = false;
+        listener = std::thread([&aNotification, &frameHandlerPrimary, &listening, &eventCond]() {
+            // Notify that a listening thread is running.
+            listening = true;
+            eventCond.notify_all();
+
+            EvsEventDesc aTargetEvent;
+            aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+            if (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification, true)) {
+                LOG(WARNING) << "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 primary client.
+        frameHandlerSecondary->shutdown();
+
+        // Join a listening thread.
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        // Verify change notifications.
+        ASSERT_EQ(EvsEventType::MASTER_RELEASED, static_cast<EvsEventType>(aNotification.aType));
+
+        // Closing streams.
+        frameHandlerPrimary->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pPrimaryCam).isOk());
+        ASSERT_TRUE(mEnumerator->closeCamera(pSecondaryCam).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * MultiCameraParameter:
+ * Verify that primary and non-primary clients behave as expected when they try to adjust
+ * camera parameters.
+ */
+TEST_P(EvsAidlTest, MultiCameraParameter) {
+    LOG(INFO) << "Starting MultiCameraParameter test";
+
+    if (mIsHwModule) {
+        // This test is not for HW module implementation.
+        return;
+    }
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (isLogicalCam) {
+            // TODO(b/145465724): Support camera parameter programming on
+            // logical devices.
+            LOG(INFO) << "Skip a logical device " << cam.id;
+            continue;
+        }
+
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Create two camera clients.
+        std::shared_ptr<IEvsCamera> pPrimaryCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pPrimaryCam).isOk());
+        EXPECT_NE(pPrimaryCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pPrimaryCam);
+
+        std::shared_ptr<IEvsCamera> pSecondaryCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pSecondaryCam).isOk());
+        EXPECT_NE(pSecondaryCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pSecondaryCam);
+
+        // Get the parameter list
+        std::vector<CameraParam> camPrimaryCmds, camSecondaryCmds;
+        ASSERT_TRUE(pPrimaryCam->getParameterList(&camPrimaryCmds).isOk());
+        ASSERT_TRUE(pSecondaryCam->getParameterList(&camSecondaryCmds).isOk());
+        if (camPrimaryCmds.size() < 1 || camSecondaryCmds.size() < 1) {
+            // Skip a camera device if it does not support any parameter.
+            continue;
+        }
+
+        // Set up per-client frame receiver objects which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandlerPrimary = std::make_shared<FrameHandler>(
+                pPrimaryCam, cam, nullptr, FrameHandler::eAutoReturn);
+        std::shared_ptr<FrameHandler> frameHandlerSecondary = std::make_shared<FrameHandler>(
+                pSecondaryCam, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandlerPrimary, nullptr);
+        EXPECT_NE(frameHandlerSecondary, nullptr);
+
+        // Set one client as the primary client.
+        ASSERT_TRUE(pPrimaryCam->setPrimaryClient().isOk());
+
+        // Try to set another client as the primary client.
+        ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk());
+
+        // Start the camera's video stream via a primary client client.
+        ASSERT_TRUE(frameHandlerPrimary->startStream());
+
+        // Ensure the stream starts
+        frameHandlerPrimary->waitForFrameCount(1);
+
+        // Start the camera's video stream via another client
+        ASSERT_TRUE(frameHandlerSecondary->startStream());
+
+        // Ensure the stream starts
+        frameHandlerSecondary->waitForFrameCount(1);
+
+        int32_t val0 = 0;
+        std::vector<int32_t> values;
+        EvsEventDesc aNotification0 = {};
+        EvsEventDesc aNotification1 = {};
+        for (auto& cmd : camPrimaryCmds) {
+            // Get a valid parameter value range
+            ParameterRange range;
+            ASSERT_TRUE(pPrimaryCam->getIntParameterRange(cmd, &range).isOk());
+            if (cmd == CameraParam::ABSOLUTE_FOCUS) {
+                // Try to turn off auto-focus
+                values.clear();
+                ASSERT_TRUE(
+                        pPrimaryCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk());
+                for (auto&& v : values) {
+                    EXPECT_EQ(v, 0);
+                }
+            }
+
+            // Calculate a parameter value to program.
+            val0 = range.min + (std::rand() % (range.max - range.min));
+            val0 = val0 - (val0 % range.step);
+
+            // Prepare and start event listeners.
+            bool listening0 = false;
+            bool listening1 = false;
+            std::condition_variable eventCond;
+            std::thread listener0 = std::thread([cmd, val0, &aNotification0, &frameHandlerPrimary,
+                                                 &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 (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) {
+                    LOG(WARNING) << "A timer is expired before a target event is fired.";
+                }
+            });
+            std::thread listener1 = std::thread([cmd, val0, &aNotification1, &frameHandlerSecondary,
+                                                 &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 (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) {
+                    LOG(WARNING) << "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();
+            ASSERT_TRUE(pPrimaryCam->setIntParameter(cmd, val0, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_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 primary client client adjusts it.
+            values.clear();
+            ASSERT_TRUE(pPrimaryCam->getIntParameter(cmd, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_EQ(val0, v) << "Values are not matched.";
+            }
+        }
+
+        // Try to adjust a parameter via non-primary client
+        values.clear();
+        ASSERT_FALSE(pSecondaryCam->setIntParameter(camSecondaryCmds[0], val0, &values).isOk());
+
+        // Non-primary client attempts to be a primary client
+        ASSERT_FALSE(pSecondaryCam->setPrimaryClient().isOk());
+
+        // Primary client retires from a primary client role
+        bool listening = false;
+        std::condition_variable eventCond;
+        std::thread listener =
+                std::thread([&aNotification0, &frameHandlerSecondary, &listening, &eventCond]() {
+                    listening = true;
+                    eventCond.notify_all();
+
+                    EvsEventDesc aTargetEvent;
+                    aTargetEvent.aType = EvsEventType::MASTER_RELEASED;
+                    if (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification0, true)) {
+                        LOG(WARNING) << "A timer is expired before a target event is fired.";
+                    }
+                });
+
+        std::mutex eventLock;
+        auto timer = std::chrono::system_clock::now();
+        std::unique_lock<std::mutex> lock(eventLock);
+        while (!listening) {
+            eventCond.wait_until(lock, timer + 1s);
+        }
+        lock.unlock();
+
+        ASSERT_TRUE(pPrimaryCam->unsetPrimaryClient().isOk());
+
+        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();
+        ASSERT_FALSE(pPrimaryCam->setIntParameter(camPrimaryCmds[0], val0, &values).isOk());
+
+        // Non-primary client becomes a primary client
+        ASSERT_TRUE(pSecondaryCam->setPrimaryClient().isOk());
+
+        // Try to adjust a parameter via new primary client
+        for (auto& cmd : camSecondaryCmds) {
+            // Get a valid parameter value range
+            ParameterRange range;
+            ASSERT_TRUE(pSecondaryCam->getIntParameterRange(cmd, &range).isOk());
+
+            values.clear();
+            if (cmd == CameraParam::ABSOLUTE_FOCUS) {
+                // Try to turn off auto-focus
+                values.clear();
+                ASSERT_TRUE(
+                        pSecondaryCam->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk());
+                for (auto&& v : values) {
+                    EXPECT_EQ(v, 0);
+                }
+            }
+
+            // Calculate a parameter value to program.  This is being rounding down.
+            val0 = range.min + (std::rand() % (range.max - range.min));
+            val0 = val0 - (val0 % range.step);
+
+            // Prepare and start event listeners.
+            bool listening0 = false;
+            bool listening1 = false;
+            std::condition_variable eventCond;
+            std::thread listener0 = std::thread([&]() {
+                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 (!frameHandlerPrimary->waitForEvent(aTargetEvent, aNotification0)) {
+                    LOG(WARNING) << "A timer is expired before a target event is fired.";
+                }
+            });
+            std::thread listener1 = std::thread([&]() {
+                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 (!frameHandlerSecondary->waitForEvent(aTargetEvent, aNotification1)) {
+                    LOG(WARNING) << "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();
+            ASSERT_TRUE(pSecondaryCam->setIntParameter(cmd, val0, &values).isOk());
+
+            // Clients expects to receive a parameter change notification
+            // whenever a primary client client adjusts it.
+            values.clear();
+            ASSERT_TRUE(pSecondaryCam->getIntParameter(cmd, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_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]));
+            }
+        }
+
+        // New primary client retires from the role
+        ASSERT_TRUE(pSecondaryCam->unsetPrimaryClient().isOk());
+
+        // Shutdown
+        frameHandlerPrimary->shutdown();
+        frameHandlerSecondary->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pPrimaryCam).isOk());
+        ASSERT_TRUE(mEnumerator->closeCamera(pSecondaryCam).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * HighPriorityCameraClient:
+ * EVS client, which owns the display, is priortized and therefore can take over
+ * a primary client role from other EVS clients without the display.
+ */
+TEST_P(EvsAidlTest, HighPriorityCameraClient) {
+    LOG(INFO) << "Starting HighPriorityCameraClient test";
+
+    if (mIsHwModule) {
+        // This test is not for HW module implementation.
+        return;
+    }
+
+    // Get the camera list
+    loadCameraList();
+
+    // Request available display IDs
+    uint8_t targetDisplayId = 0;
+    std::vector<uint8_t> displayIds;
+    ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+    EXPECT_GT(displayIds.size(), 0);
+    targetDisplayId = displayIds[0];
+
+    // Request exclusive access to the EVS display
+    std::shared_ptr<IEvsDisplay> pDisplay;
+    ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk());
+    EXPECT_NE(pDisplay, nullptr);
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Create two clients
+        std::shared_ptr<IEvsCamera> pCam0;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk());
+        EXPECT_NE(pCam0, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam0);
+
+        std::shared_ptr<IEvsCamera> pCam1;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk());
+        EXPECT_NE(pCam1, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam1);
+
+        // Get the parameter list; this test will use the first command in both
+        // lists.
+        std::vector<CameraParam> cam0Cmds, cam1Cmds;
+        ASSERT_TRUE(pCam0->getParameterList(&cam0Cmds).isOk());
+        ASSERT_TRUE(pCam1->getParameterList(&cam1Cmds).isOk());
+        if (cam0Cmds.size() < 1 || cam1Cmds.size() < 1) {
+            // Cannot execute this test.
+            return;
+        }
+
+        // Set up a frame receiver object which will fire up its own thread.
+        std::shared_ptr<FrameHandler> frameHandler0 =
+                std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn);
+        std::shared_ptr<FrameHandler> frameHandler1 =
+                std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler0, nullptr);
+        EXPECT_NE(frameHandler1, nullptr);
+
+        // Activate the display
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk());
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler0->startStream());
+        ASSERT_TRUE(frameHandler1->startStream());
+
+        // Ensure the stream starts
+        frameHandler0->waitForFrameCount(1);
+        frameHandler1->waitForFrameCount(1);
+
+        // Client 1 becomes a primary client and programs a parameter.
+
+        // Get a valid parameter value range
+        ParameterRange range;
+        ASSERT_TRUE(pCam1->getIntParameterRange(cam1Cmds[0], &range).isOk());
+
+        // Client1 becomes a primary client
+        ASSERT_TRUE(pCam1->setPrimaryClient().isOk());
+
+        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)) {
+                            LOG(WARNING) << "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
+            ASSERT_TRUE(pCam1->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_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 = range.min + (std::rand() % (range.max - range.min));
+        val0 = val0 - (val0 % range.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)) {
+                        LOG(WARNING) << "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();
+        ASSERT_TRUE(pCam1->setIntParameter(cam1Cmds[0], val0, &values).isOk());
+        for (auto&& v : values) {
+            EXPECT_EQ(val0, v);
+        }
+
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        // Verify a change notification
+        ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::PARAMETER_CHANGED);
+        ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), cam1Cmds[0]);
+        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)) {
+                LOG(WARNING) << "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 primary client role
+        ASSERT_TRUE(pCam0->forcePrimaryClient(pDisplay).isOk());
+
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
+
+        ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::MASTER_RELEASED);
+
+        // Client 0 programs a parameter
+        val0 = range.min + (std::rand() % (range.max - range.min));
+
+        // Rounding down
+        val0 = val0 - (val0 % range.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)) {
+                            LOG(WARNING) << "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
+            values.clear();
+            ASSERT_TRUE(pCam0->setIntParameter(CameraParam::AUTO_FOCUS, 0, &values).isOk());
+            for (auto&& v : values) {
+                EXPECT_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)) {
+                        LOG(WARNING) << "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();
+        ASSERT_TRUE(pCam0->setIntParameter(cam0Cmds[0], val0, &values).isOk());
+
+        // Join a listener
+        if (listener.joinable()) {
+            listener.join();
+        }
+        // Verify a change notification
+        ASSERT_EQ(static_cast<EvsEventType>(aNotification.aType), EvsEventType::PARAMETER_CHANGED);
+        ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]), cam0Cmds[0]);
+        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)
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk());
+
+        // Shut down the streamer
+        frameHandler0->shutdown();
+        frameHandler1->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk());
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk());
+        mActiveCameras.clear();
+    }
+
+    // Explicitly release the display
+    ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+}
+
+/*
+ * CameraUseStreamConfigToDisplay:
+ * End to end test of data flowing from the camera to the display.  Similar to
+ * CameraToDisplayRoundTrip test case but this case retrieves available stream
+ * configurations from EVS and uses one of them to start a video stream.
+ */
+TEST_P(EvsAidlTest, CameraUseStreamConfigToDisplay) {
+    LOG(INFO) << "Starting CameraUseStreamConfigToDisplay test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Request available display IDs
+    uint8_t targetDisplayId = 0;
+    std::vector<uint8_t> displayIds;
+    ASSERT_TRUE(mEnumerator->getDisplayIdList(&displayIds).isOk());
+    EXPECT_GT(displayIds.size(), 0);
+    targetDisplayId = displayIds[0];
+
+    // Request exclusive access to the EVS display
+    std::shared_ptr<IEvsDisplay> pDisplay;
+    ASSERT_TRUE(mEnumerator->openDisplay(targetDisplayId, &pDisplay).isOk());
+    EXPECT_NE(pDisplay, nullptr);
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        // choose a configuration that has a frame rate faster than minReqFps.
+        Stream targetCfg = {};
+        const int32_t minReqFps = 15;
+        int32_t maxArea = 0;
+        camera_metadata_entry_t streamCfgs;
+        bool foundCfg = false;
+        if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()),
+                                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                                        &streamCfgs)) {
+            // Stream configurations are found in metadata
+            RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
+            for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                    if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) {
+                        targetCfg.width = ptr->width;
+                        targetCfg.height = ptr->height;
+
+                        maxArea = ptr->width * ptr->height;
+                        foundCfg = true;
+                    }
+                }
+                ++ptr;
+            }
+        }
+        targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
+
+        if (!foundCfg) {
+            // Current EVS camera does not provide stream configurations in the
+            // metadata.
+            continue;
+        }
+
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Set up a frame receiver object which will fire up its own thread.
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, pDisplay, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Activate the display
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME).isOk());
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // Wait a while to let the data flow
+        static const int kSecondsToWait = 5;
+        const int streamTimeMs =
+                kSecondsToWait * kSecondsToMilliseconds - kMaxStreamStartMilliseconds;
+        const unsigned minimumFramesExpected =
+                streamTimeMs * kMinimumFramesPerSecond / kSecondsToMilliseconds;
+        sleep(kSecondsToWait);
+        unsigned framesReceived = 0;
+        unsigned framesDisplayed = 0;
+        frameHandler->getFramesCounters(&framesReceived, &framesDisplayed);
+        EXPECT_EQ(framesReceived, framesDisplayed);
+        EXPECT_GE(framesDisplayed, minimumFramesExpected);
+
+        // Turn off the display (yes, before the stream stops -- it should be handled)
+        ASSERT_TRUE(pDisplay->setDisplayState(DisplayState::NOT_VISIBLE).isOk());
+
+        // Shut down the streamer
+        frameHandler->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+    }
+
+    // Explicitly release the display
+    ASSERT_TRUE(mEnumerator->closeDisplay(pDisplay).isOk());
+}
+
+/*
+ * MultiCameraStreamUseConfig:
+ * Verify that each client can start and stop video streams on the same
+ * underlying camera with same configuration.
+ */
+TEST_P(EvsAidlTest, MultiCameraStreamUseConfig) {
+    LOG(INFO) << "Starting MultiCameraStream test";
+
+    if (mIsHwModule) {
+        // This test is not for HW module implementation.
+        return;
+    }
+
+    // Get the camera list
+    loadCameraList();
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        // choose a configuration that has a frame rate faster than minReqFps.
+        Stream targetCfg = {};
+        const int32_t minReqFps = 15;
+        int32_t maxArea = 0;
+        camera_metadata_entry_t streamCfgs;
+        bool foundCfg = false;
+        if (!find_camera_metadata_entry(reinterpret_cast<camera_metadata_t*>(cam.metadata.data()),
+                                        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                                        &streamCfgs)) {
+            // Stream configurations are found in metadata
+            RawStreamConfig* ptr = reinterpret_cast<RawStreamConfig*>(streamCfgs.data.i32);
+            for (unsigned offset = 0; offset < streamCfgs.count; offset += kStreamCfgSz) {
+                if (ptr->direction == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    ptr->format == HAL_PIXEL_FORMAT_RGBA_8888) {
+                    if (ptr->width * ptr->height > maxArea && ptr->framerate >= minReqFps) {
+                        targetCfg.width = ptr->width;
+                        targetCfg.height = ptr->height;
+
+                        maxArea = ptr->width * ptr->height;
+                        foundCfg = true;
+                    }
+                }
+                ++ptr;
+            }
+        }
+        targetCfg.format = static_cast<PixelFormat>(HAL_PIXEL_FORMAT_RGBA_8888);
+
+        if (!foundCfg) {
+            LOG(INFO) << "Device " << cam.id
+                      << " does not provide a list of supported stream configurations, skipped";
+            continue;
+        }
+
+        // Create the first camera client with a selected stream configuration.
+        std::shared_ptr<IEvsCamera> pCam0;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam0).isOk());
+        EXPECT_NE(pCam0, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam0);
+
+        // Try to create the second camera client with different stream
+        // configuration.
+        int32_t id = targetCfg.id;
+        targetCfg.id += 1;  // EVS manager sees only the stream id.
+        std::shared_ptr<IEvsCamera> pCam1;
+        ASSERT_FALSE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk());
+
+        // Try again with same stream configuration.
+        targetCfg.id = id;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam1).isOk());
+        EXPECT_NE(pCam1, nullptr);
+
+        // Set up per-client frame receiver objects which will fire up its own thread
+        std::shared_ptr<FrameHandler> frameHandler0 =
+                std::make_shared<FrameHandler>(pCam0, cam, nullptr, FrameHandler::eAutoReturn);
+        std::shared_ptr<FrameHandler> frameHandler1 =
+                std::make_shared<FrameHandler>(pCam1, cam, nullptr, FrameHandler::eAutoReturn);
+        EXPECT_NE(frameHandler0, nullptr);
+        EXPECT_NE(frameHandler1, nullptr);
+
+        // Start the camera's video stream via client 0
+        ASSERT_TRUE(frameHandler0->startStream());
+        ASSERT_TRUE(frameHandler1->startStream());
+
+        // Ensure the stream starts
+        frameHandler0->waitForFrameCount(1);
+        frameHandler1->waitForFrameCount(1);
+
+        nsecs_t firstFrame = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        // Wait a bit, then ensure both clients get at least the required minimum number of frames
+        sleep(5);
+        nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+        unsigned framesReceived0 = 0, framesReceived1 = 0;
+        frameHandler0->getFramesCounters(&framesReceived0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceived1, nullptr);
+        framesReceived0 = framesReceived0 - 1;  // Back out the first frame we already waited for
+        framesReceived1 = framesReceived1 - 1;  // Back out the first frame we already waited for
+        nsecs_t runTime = end - firstFrame;
+        float framesPerSecond0 = framesReceived0 / (runTime * kNanoToSeconds);
+        float framesPerSecond1 = framesReceived1 / (runTime * kNanoToSeconds);
+        LOG(INFO) << "Measured camera rate " << std::scientific << framesPerSecond0 << " fps and "
+                  << framesPerSecond1 << " fps";
+        EXPECT_GE(framesPerSecond0, kMinimumFramesPerSecond);
+        EXPECT_GE(framesPerSecond1, kMinimumFramesPerSecond);
+
+        // Shutdown one client
+        frameHandler0->shutdown();
+
+        // Read frame counters again
+        frameHandler0->getFramesCounters(&framesReceived0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceived1, nullptr);
+
+        // Wait a bit again
+        sleep(5);
+        unsigned framesReceivedAfterStop0 = 0, framesReceivedAfterStop1 = 0;
+        frameHandler0->getFramesCounters(&framesReceivedAfterStop0, nullptr);
+        frameHandler1->getFramesCounters(&framesReceivedAfterStop1, nullptr);
+        EXPECT_EQ(framesReceived0, framesReceivedAfterStop0);
+        EXPECT_LT(framesReceived1, framesReceivedAfterStop1);
+
+        // Shutdown another
+        frameHandler1->shutdown();
+
+        // Explicitly release the camera
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam0).isOk());
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam1).isOk());
+        mActiveCameras.clear();
+    }
+}
+
+/*
+ * LogicalCameraMetadata:
+ * Opens logical camera reported by the enumerator and validate its metadata by
+ * checking its capability and locating supporting physical camera device
+ * identifiers.
+ */
+TEST_P(EvsAidlTest, LogicalCameraMetadata) {
+    LOG(INFO) << "Starting LogicalCameraMetadata test";
+
+    // Get the camera list
+    loadCameraList();
+
+    // Open and close each camera twice
+    for (auto&& cam : mCameraInfo) {
+        bool isLogicalCam = false;
+        auto devices = getPhysicalCameraIds(cam.id, isLogicalCam);
+        if (isLogicalCam) {
+            ASSERT_GE(devices.size(), 1) << "Logical camera device must have at least one physical "
+                                            "camera device ID in its metadata.";
+        }
+    }
+}
+
+/*
+ * CameraStreamExternalBuffering:
+ * This is same with CameraStreamBuffering except frame buffers are allocated by
+ * the test client and then imported by EVS framework.
+ */
+TEST_P(EvsAidlTest, CameraStreamExternalBuffering) {
+    LOG(INFO) << "Starting CameraStreamExternalBuffering test";
+
+    // Arbitrary constant (should be > 1 and not too big)
+    static const unsigned int kBuffersToHold = 3;
+
+    // Get the camera list
+    loadCameraList();
+
+    // Acquire the graphics buffer allocator
+    android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get());
+    const auto usage =
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
+
+    // Test each reported camera
+    for (auto&& cam : mCameraInfo) {
+        // Read a target resolution from the metadata
+        Stream targetCfg = getFirstStreamConfiguration(
+                reinterpret_cast<camera_metadata_t*>(cam.metadata.data()));
+        ASSERT_GT(targetCfg.width, 0);
+        ASSERT_GT(targetCfg.height, 0);
+
+        // Allocate buffers to use
+        std::vector<BufferDesc> buffers;
+        buffers.resize(kBuffersToHold);
+        for (auto i = 0; i < kBuffersToHold; ++i) {
+            unsigned pixelsPerLine;
+            buffer_handle_t memHandle = nullptr;
+            android::status_t result =
+                    alloc.allocate(targetCfg.width, targetCfg.height,
+                                   static_cast<android::PixelFormat>(targetCfg.format),
+                                   /* layerCount = */ 1, usage, &memHandle, &pixelsPerLine,
+                                   /* graphicBufferId = */ 0,
+                                   /* requestorName = */ "CameraStreamExternalBufferingTest");
+            if (result != android::NO_ERROR) {
+                LOG(ERROR) << __FUNCTION__ << " failed to allocate memory.";
+                // Release previous allocated buffers
+                for (auto j = 0; j < i; j++) {
+                    alloc.free(::android::dupFromAidl(buffers[i].buffer.handle));
+                }
+                return;
+            } else {
+                BufferDesc buf;
+                HardwareBufferDescription* pDesc =
+                        reinterpret_cast<HardwareBufferDescription*>(&buf.buffer.description);
+                pDesc->width = targetCfg.width;
+                pDesc->height = targetCfg.height;
+                pDesc->layers = 1;
+                pDesc->format = targetCfg.format;
+                pDesc->usage = static_cast<BufferUsage>(usage);
+                pDesc->stride = pixelsPerLine;
+                buf.buffer.handle = ::android::dupToAidl(memHandle);
+                buf.bufferId = i;  // Unique number to identify this buffer
+                buffers[i] = std::move(buf);
+            }
+        }
+
+        bool isLogicalCam = false;
+        getPhysicalCameraIds(cam.id, isLogicalCam);
+
+        std::shared_ptr<IEvsCamera> pCam;
+        ASSERT_TRUE(mEnumerator->openCamera(cam.id, targetCfg, &pCam).isOk());
+        EXPECT_NE(pCam, nullptr);
+
+        // Store a camera handle for a clean-up
+        mActiveCameras.push_back(pCam);
+
+        // Request to import buffers
+        int delta = 0;
+        auto status = pCam->importExternalBuffers(buffers, &delta);
+        if (isLogicalCam) {
+            ASSERT_FALSE(status.isOk());
+            continue;
+        }
+
+        ASSERT_TRUE(status.isOk());
+        EXPECT_GE(delta, kBuffersToHold);
+
+        // Set up a frame receiver object which will fire up its own thread.
+        std::shared_ptr<FrameHandler> frameHandler =
+                std::make_shared<FrameHandler>(pCam, cam, nullptr, FrameHandler::eNoAutoReturn);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start the camera's video stream
+        ASSERT_TRUE(frameHandler->startStream());
+
+        // 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(1);  // 1 second should be enough for at least 5 frames to be delivered worst case
+        unsigned framesReceived = 0;
+        frameHandler->getFramesCounters(&framesReceived, nullptr);
+        ASSERT_LE(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
+
+        // Give back one buffer
+        EXPECT_TRUE(frameHandler->returnHeldBuffer());
+
+        // Once we return a buffer, it shouldn't take more than 1/10 second to get a new one
+        // filled since we require 10fps minimum -- but give a 10% allowance just in case.
+        unsigned framesReceivedAfter = 0;
+        usleep(110 * kMillisecondsToMicroseconds);
+        frameHandler->getFramesCounters(&framesReceivedAfter, nullptr);
+        EXPECT_EQ(framesReceived + 1, framesReceivedAfter) << "Stream should've resumed";
+
+        // 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
+        ASSERT_TRUE(mEnumerator->closeCamera(pCam).isOk());
+        mActiveCameras.clear();
+        // Release buffers
+        for (auto& b : buffers) {
+            alloc.free(::android::dupFromAidl(b.buffer.handle));
+        }
+        buffers.resize(0);
+    }
+}
+
+/*
+ * UltrasonicsArrayOpenClean:
+ * Opens each ultrasonics arrays reported by the enumerator and then explicitly closes it via a
+ * call to closeUltrasonicsArray. Then repeats the test to ensure all ultrasonics arrays
+ * can be reopened.
+ */
+TEST_P(EvsAidlTest, UltrasonicsArrayOpenClean) {
+    LOG(INFO) << "Starting UltrasonicsArrayOpenClean test";
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // Open and close each ultrasonics array twice
+    for (auto&& ultraInfo : mUltrasonicsArraysInfo) {
+        for (int pass = 0; pass < 2; pass++) {
+            std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray;
+            ASSERT_TRUE(
+                    mEnumerator
+                            ->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray)
+                            .isOk());
+            EXPECT_NE(pUltrasonicsArray, nullptr);
+
+            // Verify that this ultrasonics array self-identifies correctly
+            UltrasonicsArrayDesc desc;
+            ASSERT_TRUE(pUltrasonicsArray->getUltrasonicArrayInfo(&desc).isOk());
+            EXPECT_EQ(ultraInfo.ultrasonicsArrayId, desc.ultrasonicsArrayId);
+            LOG(DEBUG) << "Found ultrasonics array " << ultraInfo.ultrasonicsArrayId;
+
+            // Explicitly close the ultrasonics array so resources are released right away
+            ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk());
+        }
+    }
+}
+
+// Starts a stream and verifies all data received is valid.
+TEST_P(EvsAidlTest, UltrasonicsVerifyStreamData) {
+    LOG(INFO) << "Starting UltrasonicsVerifyStreamData";
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // For each ultrasonics array.
+    for (auto&& ultraInfo : mUltrasonicsArraysInfo) {
+        LOG(DEBUG) << "Testing ultrasonics array: " << ultraInfo.ultrasonicsArrayId;
+
+        std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray;
+        ASSERT_TRUE(
+                mEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray)
+                        .isOk());
+        EXPECT_NE(pUltrasonicsArray, nullptr);
+
+        std::shared_ptr<FrameHandlerUltrasonics> frameHandler =
+                std::make_shared<FrameHandlerUltrasonics>(pUltrasonicsArray);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start stream.
+        ASSERT_TRUE(pUltrasonicsArray->startStream(frameHandler).isOk());
+
+        // Wait 5 seconds to receive frames.
+        sleep(5);
+
+        // Stop stream.
+        ASSERT_TRUE(pUltrasonicsArray->stopStream().isOk());
+
+        EXPECT_GT(frameHandler->getReceiveFramesCount(), 0);
+        EXPECT_TRUE(frameHandler->areAllFramesValid());
+
+        // Explicitly close the ultrasonics array so resources are released right away
+        ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk());
+    }
+}
+
+// Sets frames in flight before and after start of stream and verfies success.
+TEST_P(EvsAidlTest, UltrasonicsSetFramesInFlight) {
+    LOG(INFO) << "Starting UltrasonicsSetFramesInFlight";
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // For each ultrasonics array.
+    for (auto&& ultraInfo : mUltrasonicsArraysInfo) {
+        LOG(DEBUG) << "Testing ultrasonics array: " << ultraInfo.ultrasonicsArrayId;
+
+        std::shared_ptr<IEvsUltrasonicsArray> pUltrasonicsArray;
+        ASSERT_TRUE(
+                mEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId, &pUltrasonicsArray)
+                        .isOk());
+        EXPECT_NE(pUltrasonicsArray, nullptr);
+
+        ASSERT_TRUE(pUltrasonicsArray->setMaxFramesInFlight(10).isOk());
+
+        std::shared_ptr<FrameHandlerUltrasonics> frameHandler =
+                std::make_shared<FrameHandlerUltrasonics>(pUltrasonicsArray);
+        EXPECT_NE(frameHandler, nullptr);
+
+        // Start stream.
+        ASSERT_TRUE(pUltrasonicsArray->startStream(frameHandler).isOk());
+        ASSERT_TRUE(pUltrasonicsArray->setMaxFramesInFlight(5).isOk());
+
+        // Stop stream.
+        ASSERT_TRUE(pUltrasonicsArray->stopStream().isOk());
+
+        // Explicitly close the ultrasonics array so resources are released right away
+        ASSERT_TRUE(mEnumerator->closeUltrasonicsArray(pUltrasonicsArray).isOk());
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EvsAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, EvsAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IEvsEnumerator::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}