From 0d14a8297b2fb614ac12c56e6cf86a781790ec26 Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Tue, 29 Aug 2023 20:34:08 +0000 Subject: [PATCH] Hanlding Streams and States Test: Build && `atest android.hardware.automotive.evs-aidl-default-service_cam_state_test` Bug: 277861838 Change-Id: Idd8fe8e76876dfd93a177d3529ccf118b626001d --- automotive/evs/aidl/impl/default/Android.bp | 17 ++ .../evs/aidl/impl/default/include/EvsCamera.h | 40 ++++ .../evs/aidl/impl/default/src/EvsCamera.cpp | 112 ++++++++++ .../default/tests/EvsCameraBufferTest.cpp | 15 +- .../impl/default/tests/EvsCameraStateTest.cpp | 202 ++++++++++++++++++ 5 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp index c56fe2bfc7..6b638a3e19 100644 --- a/automotive/evs/aidl/impl/default/Android.bp +++ b/automotive/evs/aidl/impl/default/Android.bp @@ -105,4 +105,21 @@ cc_test { "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 3663e3d05a..5774db5590 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 #include #include @@ -45,11 +46,41 @@ class EvsCamera : public EvsCameraBase { ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override; + ndk::ScopedAStatus startVideoStream( + const std::shared_ptr& 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& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool startVideoStreamImpl_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck) = 0; + + virtual bool postVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool preVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck); + + virtual bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck) = 0; + + virtual bool postVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& lck); + void shutdown() override; void closeAllBuffers_unsafe(); @@ -81,6 +112,15 @@ class EvsCamera : public EvsCameraBase { 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 db3be8fb36..208a1a4a8c 100644 --- a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp +++ b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp @@ -32,6 +32,9 @@ namespace aidl::android::hardware::automotive::evs::implementation { // 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 @@ void EvsCamera::freeOneFrame(const buffer_handle_t handle) { alloc.free(handle); } +bool EvsCamera::preVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& /* lck */) { + if (!receiver) { + LOG(ERROR) << __func__ << ": Null receiver."; + status = ndk::ScopedAStatus::fromServiceSpecificError( + static_cast(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(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(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(EvsResult::BUFFER_NOT_AVAILABLE)); + return false; + } + mStreamState = StreamState::RUNNING; + return true; +} + +bool EvsCamera::postVideoStreamStart_locked( + const std::shared_ptr& /* receiver */, + ndk::ScopedAStatus& /* status */, std::unique_lock& /* lck */) { + return true; +} + +bool EvsCamera::preVideoStreamStop_locked(ndk::ScopedAStatus& status, + std::unique_lock& /* 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& /* lck */) { + mStreamState = StreamState::STOPPED; + return true; +} + +ndk::ScopedAStatus EvsCamera::startVideoStream( + const std::shared_ptr& 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(EvsResult::NOT_SUPPORTED)); +} + +ndk::ScopedAStatus EvsCamera::resumeVideoStream() { + return ndk::ScopedAStatus::fromServiceSpecificError(static_cast(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 @@ bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) { } 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 48f0890e69..3cbc04a9c9 100644 --- a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp +++ b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp @@ -77,8 +77,6 @@ class EvsCameraForTest : public EvsCamera { (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& in_opaqueValue), (override)); @@ -87,12 +85,13 @@ class EvsCameraForTest : public EvsCamera { std::vector* _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& receiver, ndk::ScopedAStatus& status, + std::unique_lock& lck), + (override)); + MOCK_METHOD(bool, stopVideoStreamImpl_locked, + (ndk::ScopedAStatus & status, std::unique_lock& lck), (override)); }; TEST(EvsCameraBufferTest, ChangeBufferPoolSize) { @@ -118,7 +117,7 @@ TEST(EvsCameraBufferTest, ChangeBufferPoolSize) { evsCam->checkBufferOrder(); } -TEST(EvsCameraForTest, UseAndReturn) { +TEST(EvsCameraBufferTest, UseAndReturn) { constexpr std::size_t kNumOfHandles = 20; auto evsCam = ndk::SharedRefBase::make(); 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 0000000000..1925c793f7 --- /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 +#include + +#include +#include +#include + +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(++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& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& 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& /* receiver */, + ndk::ScopedAStatus& /* status */, + std::unique_lock& /* lck */) override { + EXPECT_EQ(mStreamState, StreamState::RUNNING); + EXPECT_FALSE(mStreamStarted); + EXPECT_FALSE(mStreamStopped); + mStreamStarted = true; + return true; + } + + bool postVideoStreamStart_locked(const std::shared_ptr& receiver, + ndk::ScopedAStatus& status, + std::unique_lock& 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& 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& /* 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& 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* _aidl_return), (override)); + MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, + std::vector* _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& in_opaqueValue), + (override)); + MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter, + (::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value, + std::vector* _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(); + auto mockStream = ndk::SharedRefBase::make(); + 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