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);