Hanlding Streams and States
Test: Build && `atest android.hardware.automotive.evs-aidl-default-service_cam_state_test`
Bug: 277861838
Change-Id: Idd8fe8e76876dfd93a177d3529ccf118b626001d
diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp
index c56fe2b..6b638a3 100644
--- a/automotive/evs/aidl/impl/default/Android.bp
+++ b/automotive/evs/aidl/impl/default/Android.bp
@@ -105,4 +105,21 @@
"android.hardware.automotive.evs-aidl-default-service-lib",
"libgmock",
],
+ test_suites: [
+ "general-tests",
+ ],
+}
+
+cc_test {
+ name: "android.hardware.automotive.evs-aidl-default-service_cam_state_test",
+ defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
+ vendor: true,
+ srcs: ["tests/EvsCameraStateTest.cpp"],
+ static_libs: [
+ "android.hardware.automotive.evs-aidl-default-service-lib",
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
}
diff --git a/automotive/evs/aidl/impl/default/include/EvsCamera.h b/automotive/evs/aidl/impl/default/include/EvsCamera.h
index 3663e3d..5774db5 100644
--- a/automotive/evs/aidl/impl/default/include/EvsCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsCamera.h
@@ -18,6 +18,7 @@
#include "EvsCameraBase.h"
+#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
#include <cutils/native_handle.h>
#include <cstddef>
@@ -45,11 +46,41 @@
ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
+ ndk::ScopedAStatus startVideoStream(
+ const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
+
+ ndk::ScopedAStatus stopVideoStream() override;
+
+ ndk::ScopedAStatus pauseVideoStream() override;
+
+ ndk::ScopedAStatus resumeVideoStream() override;
+
protected:
virtual ::android::status_t allocateOneFrame(buffer_handle_t* handle) = 0;
virtual void freeOneFrame(const buffer_handle_t handle);
+ virtual bool preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck);
+
+ virtual bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) = 0;
+
+ virtual bool postVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck);
+
+ virtual bool preVideoStreamStop_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck);
+
+ virtual bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) = 0;
+
+ virtual bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck);
+
void shutdown() override;
void closeAllBuffers_unsafe();
@@ -81,6 +112,15 @@
bool inUse{false};
};
+ enum class StreamState {
+ STOPPED = 0,
+ RUNNING = 1,
+ STOPPING = 2,
+ DEAD = 3,
+ };
+
+ StreamState mStreamState{StreamState::STOPPED};
+
std::mutex mMutex;
// Graphics buffers to transfer images, always in the order of:
diff --git a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
index db3be8f..208a1a4 100644
--- a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
+++ b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
@@ -32,6 +32,9 @@
// Safeguards against unreasonable resource consumption and provides a testable limit
constexpr std::size_t kMaxBuffersInFlight = 100;
+// Minimum number of buffers to run a video stream
+constexpr int kMinimumBuffersInFlight = 1;
+
EvsCamera::~EvsCamera() {
shutdown();
}
@@ -108,6 +111,113 @@
alloc.free(handle);
}
+bool EvsCamera::preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& /* lck */) {
+ if (!receiver) {
+ LOG(ERROR) << __func__ << ": Null receiver.";
+ status = ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::INVALID_ARG));
+ return false;
+ }
+
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == StreamState::DEAD) {
+ LOG(ERROR) << __func__ << ": Ignoring when camera has been lost.";
+ status = ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::OWNERSHIP_LOST));
+ return false;
+ }
+
+ if (mStreamState != StreamState::STOPPED) {
+ LOG(ERROR) << __func__ << ": Ignoring when a stream is already running.";
+ status = ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
+ return false;
+ }
+
+ // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+ if (mAvailableFrames < kMinimumBuffersInFlight &&
+ !setAvailableFrames_unsafe(kMinimumBuffersInFlight)) {
+ LOG(ERROR) << __func__ << "Failed to because we could not get a graphics buffer.";
+ status = ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
+ return false;
+ }
+ mStreamState = StreamState::RUNNING;
+ return true;
+}
+
+bool EvsCamera::postVideoStreamStart_locked(
+ const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
+ ndk::ScopedAStatus& /* status */, std::unique_lock<std::mutex>& /* lck */) {
+ return true;
+}
+
+bool EvsCamera::preVideoStreamStop_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& /* lck */) {
+ if (mStreamState != StreamState::RUNNING) {
+ // Terminate the stop process because a stream is not running.
+ status = ndk::ScopedAStatus::ok();
+ return false;
+ }
+ mStreamState = StreamState::STOPPING;
+ return true;
+}
+
+bool EvsCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& /* status */,
+ std::unique_lock<std::mutex>& /* lck */) {
+ mStreamState = StreamState::STOPPED;
+ return true;
+}
+
+ndk::ScopedAStatus EvsCamera::startVideoStream(
+ const std::shared_ptr<evs::IEvsCameraStream>& receiver) {
+ bool needShutdown = false;
+ auto status = ndk::ScopedAStatus::ok();
+ {
+ std::unique_lock lck(mMutex);
+ if (!preVideoStreamStart_locked(receiver, status, lck)) {
+ return status;
+ }
+
+ if ((!startVideoStreamImpl_locked(receiver, status, lck) ||
+ !postVideoStreamStart_locked(receiver, status, lck)) &&
+ !status.isOk()) {
+ needShutdown = true;
+ }
+ }
+ if (needShutdown) {
+ shutdown();
+ }
+ return status;
+}
+
+ndk::ScopedAStatus EvsCamera::stopVideoStream() {
+ bool needShutdown = false;
+ auto status = ndk::ScopedAStatus::ok();
+ {
+ std::unique_lock lck(mMutex);
+ if ((!preVideoStreamStop_locked(status, lck) || !stopVideoStreamImpl_locked(status, lck) ||
+ !postVideoStreamStop_locked(status, lck)) &&
+ !status.isOk()) {
+ needShutdown = true;
+ }
+ }
+ if (needShutdown) {
+ shutdown();
+ }
+ return status;
+}
+
+ndk::ScopedAStatus EvsCamera::pauseVideoStream() {
+ return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
+ndk::ScopedAStatus EvsCamera::resumeVideoStream() {
+ return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
+}
+
bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) {
if (bufferCount < 1) {
LOG(ERROR) << "Ignoring request to set buffer count to zero.";
@@ -159,8 +269,10 @@
}
void EvsCamera::shutdown() {
+ stopVideoStream();
std::lock_guard lck(mMutex);
closeAllBuffers_unsafe();
+ mStreamState = StreamState::DEAD;
}
void EvsCamera::closeAllBuffers_unsafe() {
diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
index 48f0890..3cbc04a 100644
--- a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
+++ b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
@@ -77,8 +77,6 @@
(const std::string& in_deviceId,
::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return),
(override));
- MOCK_METHOD(::ndk::ScopedAStatus, pauseVideoStream, (), (override));
- MOCK_METHOD(::ndk::ScopedAStatus, resumeVideoStream, (), (override));
MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo,
(int32_t in_opaqueIdentifier, const std::vector<uint8_t>& in_opaqueValue),
(override));
@@ -87,12 +85,13 @@
std::vector<int32_t>* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override));
- MOCK_METHOD(::ndk::ScopedAStatus, startVideoStream,
- (const std::shared_ptr<
- ::aidl::android::hardware::automotive::evs::IEvsCameraStream>& in_receiver),
- (override));
- MOCK_METHOD(::ndk::ScopedAStatus, stopVideoStream, (), (override));
MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override));
+ MOCK_METHOD(bool, startVideoStreamImpl_locked,
+ (const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck),
+ (override));
+ MOCK_METHOD(bool, stopVideoStreamImpl_locked,
+ (ndk::ScopedAStatus & status, std::unique_lock<std::mutex>& lck), (override));
};
TEST(EvsCameraBufferTest, ChangeBufferPoolSize) {
@@ -118,7 +117,7 @@
evsCam->checkBufferOrder();
}
-TEST(EvsCameraForTest, UseAndReturn) {
+TEST(EvsCameraBufferTest, UseAndReturn) {
constexpr std::size_t kNumOfHandles = 20;
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
new file mode 100644
index 0000000..1925c79
--- /dev/null
+++ b/automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2023 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 "EvsCamera.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <cstdint>
+#include <unordered_set>
+#include <vector>
+
+namespace aidl::android::hardware::automotive::evs::implementation {
+
+class EvsCameraForTest : public EvsCamera {
+ private:
+ using Base = EvsCamera;
+
+ public:
+ using EvsCamera::mStreamState;
+ using EvsCamera::shutdown;
+ using EvsCamera::StreamState;
+
+ ~EvsCameraForTest() override { shutdown(); }
+
+ ::android::status_t allocateOneFrame(buffer_handle_t* handle) override {
+ static std::intptr_t handle_cnt = 0;
+ *handle = reinterpret_cast<buffer_handle_t>(++handle_cnt);
+ return ::android::OK;
+ }
+
+ void freeOneFrame(const buffer_handle_t /* handle */) override {
+ // Nothing to free because the handles are fake.
+ }
+
+ bool preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) override {
+ mPreStartCalled = true;
+ EXPECT_EQ(mStreamState, StreamState::STOPPED);
+ EXPECT_FALSE(mStreamStarted);
+ EXPECT_FALSE(mStreamStopped);
+ return Base::preVideoStreamStart_locked(receiver, status, lck);
+ }
+
+ bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
+ ndk::ScopedAStatus& /* status */,
+ std::unique_lock<std::mutex>& /* lck */) override {
+ EXPECT_EQ(mStreamState, StreamState::RUNNING);
+ EXPECT_FALSE(mStreamStarted);
+ EXPECT_FALSE(mStreamStopped);
+ mStreamStarted = true;
+ return true;
+ }
+
+ bool postVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+ ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) override {
+ mPostStartCalled = true;
+ EXPECT_EQ(mStreamState, StreamState::RUNNING);
+ EXPECT_TRUE(mStreamStarted);
+ EXPECT_FALSE(mStreamStopped);
+ return Base::postVideoStreamStart_locked(receiver, status, lck);
+ }
+
+ bool preVideoStreamStop_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) override {
+ // Skip the check if stop was called before.
+ if (!mPreStopCalled) {
+ mPreStopCalled = true;
+ EXPECT_EQ(mStreamState, StreamState::RUNNING);
+ EXPECT_TRUE(mStreamStarted);
+ EXPECT_FALSE(mStreamStopped);
+ }
+ return Base::preVideoStreamStop_locked(status, lck);
+ }
+
+ bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
+ std::unique_lock<std::mutex>& /* lck */) override {
+ EXPECT_EQ(mStreamState, StreamState::STOPPING);
+ EXPECT_TRUE(mStreamStarted);
+ EXPECT_FALSE(mStreamStopped);
+ mStreamStopped = true;
+ return true;
+ }
+
+ bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+ std::unique_lock<std::mutex>& lck) override {
+ mPostStopCalled = true;
+ const auto ret = Base::postVideoStreamStop_locked(status, lck);
+ EXPECT_EQ(mStreamState, StreamState::STOPPED);
+ EXPECT_TRUE(mStreamStarted);
+ EXPECT_TRUE(mStreamStopped);
+ return ret;
+ }
+
+ MOCK_METHOD(::ndk::ScopedAStatus, forcePrimaryClient,
+ (const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>&
+ in_display),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getCameraInfo,
+ (::aidl::android::hardware::automotive::evs::CameraDesc * _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getExtendedInfo,
+ (int32_t in_opaqueIdentifier, std::vector<uint8_t>* _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter,
+ (::aidl::android::hardware::automotive::evs::CameraParam in_id,
+ std::vector<int32_t>* _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getIntParameterRange,
+ (::aidl::android::hardware::automotive::evs::CameraParam in_id,
+ ::aidl::android::hardware::automotive::evs::ParameterRange* _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getParameterList,
+ (std::vector<::aidl::android::hardware::automotive::evs::CameraParam> *
+ _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getPhysicalCameraInfo,
+ (const std::string& in_deviceId,
+ ::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo,
+ (int32_t in_opaqueIdentifier, const std::vector<uint8_t>& in_opaqueValue),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter,
+ (::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value,
+ std::vector<int32_t>* _aidl_return),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override));
+
+ bool mStreamStarted = false;
+ bool mStreamStopped = false;
+ bool mPreStartCalled = false;
+ bool mPostStartCalled = false;
+ bool mPreStopCalled = false;
+ bool mPostStopCalled = false;
+};
+
+class MockEvsCameraStream : public evs::IEvsCameraStream {
+ MOCK_METHOD(::ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+ MOCK_METHOD(
+ ::ndk::ScopedAStatus, deliverFrame,
+ (const std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>& in_buffer),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, notify,
+ (const ::aidl::android::hardware::automotive::evs::EvsEventDesc& in_event),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
+};
+
+using StreamState = EvsCameraForTest::StreamState;
+
+TEST(EvsCameraStateTest, StateChangeHooks) {
+ auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
+ auto mockStream = ndk::SharedRefBase::make<MockEvsCameraStream>();
+ EXPECT_FALSE(evsCam->mPreStartCalled);
+ EXPECT_FALSE(evsCam->mPostStartCalled);
+ EXPECT_FALSE(evsCam->mPreStopCalled);
+ EXPECT_FALSE(evsCam->mPostStopCalled);
+ EXPECT_FALSE(evsCam->mStreamStarted);
+ EXPECT_FALSE(evsCam->mStreamStopped);
+ EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED);
+ evsCam->startVideoStream(mockStream);
+
+ EXPECT_TRUE(evsCam->mPreStartCalled);
+ EXPECT_TRUE(evsCam->mPostStartCalled);
+ EXPECT_FALSE(evsCam->mPreStopCalled);
+ EXPECT_FALSE(evsCam->mPostStopCalled);
+ EXPECT_TRUE(evsCam->mStreamStarted);
+ EXPECT_FALSE(evsCam->mStreamStopped);
+ EXPECT_EQ(evsCam->mStreamState, StreamState::RUNNING);
+ evsCam->stopVideoStream();
+
+ EXPECT_TRUE(evsCam->mPreStartCalled);
+ EXPECT_TRUE(evsCam->mPostStartCalled);
+ EXPECT_TRUE(evsCam->mPreStopCalled);
+ EXPECT_TRUE(evsCam->mPostStopCalled);
+ EXPECT_TRUE(evsCam->mStreamStarted);
+ EXPECT_TRUE(evsCam->mStreamStopped);
+ EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED);
+
+ evsCam->shutdown();
+ EXPECT_EQ(evsCam->mStreamState, StreamState::DEAD);
+}
+
+} // namespace aidl::android::hardware::automotive::evs::implementation