Add a method to override a master

EVS considers the client, which owns the display, as the high priority
client and allows it to steal a master role from existing master that
does not own the display.

Bug: 139536751
Test: VtsHalEvsV1_1Target
Change-Id: Idecaeedd90f187de57de912a09f4782bfb81a996
Signed-off-by: Changyeon Jo <changyeon@google.com>
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index d4263b7..21ca79e 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -17,6 +17,7 @@
 package android.hardware.automotive.evs@1.1;
 
 import @1.0::IEvsCamera;
+import @1.0::IEvsDisplay;
 import @1.0::EvsResult;
 import IEvsCameraStream;
 
@@ -71,6 +72,23 @@
      */
     setMaster() generates (EvsResult result);
 
+    /**
+     * Sets to be a master client forcibly.
+     *
+     * The client, which owns the display, has a high priority and can take over
+     * a master role from other clients without the display.
+     *
+     * @param  display IEvsDisplay handle.  If a given display is in either
+     *                 NOT_VISIBLE, VISIBLE_ON_NEXT_FRAME, or VISIBLE state, the
+     *                 calling client is considered as the high priority client
+     *                 and therefore allowed to take over a master role from
+     *                 existing master client.
+     *
+     * @return result  EvsResult::OK if a master role is granted.
+     *                 EvsResult::INVALID_ARG if a given display handle is null
+     *                 or in valid states.
+     */
+    forceMaster(IEvsDisplay display) generates (EvsResult result);
 
     /**
      * Retires from a master client role.
diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp
index ae293b6..2d55566 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -264,6 +264,12 @@
     return EvsResult::OK;
 }
 
+Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay>& ) {
+    // Default implementation does not expect multiple subscribers and therefore
+    // return a success code always.
+    return EvsResult::OK;
+}
+
 
 Return<EvsResult> EvsCamera::unsetMaster() {
     // Default implementation does not expect multiple subscribers and therefore
diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h
index 6cb1cfb..47a3164 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -20,6 +20,7 @@
 #include <android/hardware/automotive/evs/1.1/types.h>
 #include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
 #include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
+#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
 #include <ui/GraphicBuffer.h>
 
 #include <thread>
@@ -30,6 +31,7 @@
 using IEvsCameraStream_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;
 using ::android::hardware::automotive::evs::V1_0::EvsResult;
 using ::android::hardware::automotive::evs::V1_0::CameraDesc;
+using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
 
 
 namespace android {
@@ -61,6 +63,7 @@
     Return<EvsResult> resumeVideoStream() override;
     Return<EvsResult> doneWithFrame_1_1(const BufferDesc_1_1& buffer) override;
     Return<EvsResult> setMaster() override;
+    Return<EvsResult> forceMaster(const sp<IEvsDisplay>& display) override;
     Return<EvsResult> unsetMaster() override;
     Return<void>      setParameter(CameraParam id, int32_t value,
                                    setParameter_cb _hidl_cb) override;
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.cpp b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
index a39346b..1627689 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.cpp
@@ -21,11 +21,14 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <chrono>
 
 #include <android/log.h>
 #include <cutils/native_handle.h>
 #include <ui/GraphicBuffer.h>
 
+using namespace std::chrono_literals;
+
 FrameHandler::FrameHandler(android::sp <IEvsCamera> pCamera, CameraDesc cameraInfo,
                            android::sp <IEvsDisplay> pDisplay,
                            BufferControlFlag mode) :
@@ -148,7 +151,7 @@
             ALOGD("Camera parameter 0x%X is changed to 0x%X",
                   mLatestEventDesc.payload[0], mLatestEventDesc.payload[1]);
         } else {
-            ALOGD("Received an event 0x%X", mLatestEventDesc.aType);
+            ALOGD("Received an event %s", eventToString(mLatestEventDesc.aType));
         }
         mLock.unlock();
         mEventSignal.notify_all();
@@ -339,19 +342,42 @@
     }
 }
 
-void FrameHandler::waitForEvent(const InfoEventType aTargetEvent,
+bool FrameHandler::waitForEvent(const InfoEventType aTargetEvent,
                                 InfoEventDesc &eventDesc) {
     // Wait until we get an expected parameter change event.
     std::unique_lock<std::mutex> lock(mLock);
-    mEventSignal.wait(lock, [this, aTargetEvent, &eventDesc](){
-        bool flag = mLatestEventDesc.aType == aTargetEvent;
-        if (flag) {
-            eventDesc.aType = mLatestEventDesc.aType;
-            eventDesc.payload[0] = mLatestEventDesc.payload[0];
-            eventDesc.payload[1] = mLatestEventDesc.payload[1];
-        }
+    auto now = std::chrono::system_clock::now();
+    bool result = mEventSignal.wait_until(lock, now + 5s,
+        [this, aTargetEvent, &eventDesc](){
+            bool flag = mLatestEventDesc.aType == aTargetEvent;
+            if (flag) {
+                eventDesc.aType = mLatestEventDesc.aType;
+                eventDesc.payload[0] = mLatestEventDesc.payload[0];
+                eventDesc.payload[1] = mLatestEventDesc.payload[1];
+            }
 
-        return flag;
-    });
+            return flag;
+        }
+    );
+
+    return !result;
 }
 
+const char *FrameHandler::eventToString(const InfoEventType aType) {
+    switch (aType) {
+        case InfoEventType::STREAM_STARTED:
+            return "STREAM_STARTED";
+        case InfoEventType::STREAM_STOPPED:
+            return "STREAM_STOPPED";
+        case InfoEventType::FRAME_DROPPED:
+            return "FRAME_DROPPED";
+        case InfoEventType::TIMEOUT:
+            return "TIMEOUT";
+        case InfoEventType::PARAMETER_CHANGED:
+            return "PARAMETER_CHANGED";
+        case InfoEventType::MASTER_RELEASED:
+            return "MASTER_RELEASED";
+        default:
+            return "Unknown";
+    }
+}
diff --git a/automotive/evs/1.1/vts/functional/FrameHandler.h b/automotive/evs/1.1/vts/functional/FrameHandler.h
index c5faecd..7f87cb4 100644
--- a/automotive/evs/1.1/vts/functional/FrameHandler.h
+++ b/automotive/evs/1.1/vts/functional/FrameHandler.h
@@ -67,7 +67,7 @@
     bool isRunning();
 
     void waitForFrameCount(unsigned frameCount);
-    void waitForEvent(const InfoEventType aTargetEvent,
+    bool waitForEvent(const InfoEventType aTargetEvent,
                             InfoEventDesc &eventDesc);
     void getFramesCounters(unsigned* received, unsigned* displayed);
     void getFrameDimension(unsigned* width, unsigned* height);
@@ -79,6 +79,7 @@
 
     // Local implementation details
     bool copyBufferContents(const BufferDesc_1_0& tgtBuffer, const BufferDesc_1_1& srcBuffer);
+    const char *eventToString(const InfoEventType aType);
 
     // Values initialized as startup
     android::sp <IEvsCamera>    mCamera;
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 0d88c07..a6e4881 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -935,6 +935,132 @@
 }
 
 
+/*
+ * HighPriorityCameraClient:
+ * EVS client, which owns the display, is priortized and therefore can take over
+ * a master role from other EVS clients without the display.
+ */
+TEST_F(EvsHidlTest, HighPriorityCameraClient) {
+    ALOGI("Starting HighPriorityCameraClient test");
+
+    // Get the camera list
+    loadCameraList();
+
+    // Request exclusive access to the EVS display
+    sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
+    ASSERT_NE(pDisplay, nullptr);
+
+    // Test each reported camera
+    for (auto&& cam: cameraInfo) {
+        // Create two clients
+        sp<IEvsCamera_1_1> pCam0 =
+            IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+            .withDefault(nullptr);
+        ASSERT_NE(pCam0, nullptr);
+
+        sp<IEvsCamera_1_1> pCam1 =
+            IEvsCamera_1_1::castFrom(pEnumerator->openCamera(cam.cameraId))
+            .withDefault(nullptr);
+        ASSERT_NE(pCam1, nullptr);
+
+        // Set up a frame receiver object which will fire up its own thread.
+        sp<FrameHandler> frameHandler0 = new FrameHandler(pCam0, cam,
+                                                          pDisplay,
+                                                          FrameHandler::eAutoReturn);
+        sp<FrameHandler> frameHandler1 = new FrameHandler(pCam1, cam,
+                                                          nullptr,
+                                                          FrameHandler::eAutoReturn);
+
+        // Activate the display
+        pDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
+
+        // 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 master and programs a brightness.
+        EvsResult result = EvsResult::OK;
+        int32_t val0 = 100;
+        int32_t val1 = 0;
+
+        result = pCam1->setMaster();
+        ASSERT_TRUE(result == EvsResult::OK);
+
+        pCam1->setParameter(CameraParam::BRIGHTNESS, val0,
+                            [&result, &val1](auto status, auto effectiveValue) {
+                                result = status;
+                                val1 = effectiveValue;
+                            });
+        ASSERT_TRUE(result == EvsResult::OK ||
+                    result == EvsResult::INVALID_ARG);
+
+
+        // Verify a change notification
+        InfoEventDesc aNotification = {};
+        if (result == EvsResult::OK) {
+            bool timeout =
+                frameHandler0->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+            ASSERT_FALSE(timeout) << "Expected event does not arrive";
+            ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+                      InfoEventType::PARAMETER_CHANGED);
+            ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+                      CameraParam::BRIGHTNESS);
+            ASSERT_EQ(val1,
+                      static_cast<int32_t>(aNotification.payload[1]));
+        }
+
+        // Client 0 steals a master role
+        ASSERT_EQ(EvsResult::OK, pCam0->forceMaster(pDisplay));
+
+        frameHandler1->waitForEvent(InfoEventType::MASTER_RELEASED, aNotification);
+        ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+                  InfoEventType::MASTER_RELEASED);
+
+        // Client 0 programs a brightness
+        val0 = 50;
+        val1 = 0;
+        pCam0->setParameter(CameraParam::BRIGHTNESS, val0,
+                            [&result, &val1](auto status, auto effectiveValue) {
+                                result = status;
+                                val1 = effectiveValue;
+                            });
+        ASSERT_TRUE(result == EvsResult::OK ||
+                    result == EvsResult::INVALID_ARG);
+
+        // Verify a change notification
+        if (result == EvsResult::OK) {
+            bool timeout =
+                frameHandler1->waitForEvent(InfoEventType::PARAMETER_CHANGED, aNotification);
+            ASSERT_FALSE(timeout) << "Expected event does not arrive";
+            ASSERT_EQ(static_cast<InfoEventType>(aNotification.aType),
+                      InfoEventType::PARAMETER_CHANGED);
+            ASSERT_EQ(static_cast<CameraParam>(aNotification.payload[0]),
+                      CameraParam::BRIGHTNESS);
+            ASSERT_EQ(val1,
+                      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);
+
+        // Shut down the streamer
+        frameHandler0->shutdown();
+        frameHandler1->shutdown();
+
+        // Explicitly release the camera
+        pEnumerator->closeCamera(pCam0);
+        pEnumerator->closeCamera(pCam1);
+    }
+
+    // Explicitly release the display
+    pEnumerator->closeDisplay(pDisplay);
+}
+
+
 int main(int argc, char** argv) {
     ::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);