Merge "Hanlding Streams and States" into main

This commit is contained in:
Treehugger Robot
2023-09-22 23:19:50 +00:00
committed by Android (Google) Code Review
5 changed files with 378 additions and 8 deletions

View File

@@ -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",
],
}

View File

@@ -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 @@ class EvsCamera : public EvsCameraBase {
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 @@ 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:

View File

@@ -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<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 @@ 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() {

View File

@@ -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<uint8_t>& in_opaqueValue),
(override));
@@ -87,12 +85,13 @@ class EvsCameraForTest : public EvsCamera {
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 @@ TEST(EvsCameraBufferTest, ChangeBufferPoolSize) {
evsCam->checkBufferOrder();
}
TEST(EvsCameraForTest, UseAndReturn) {
TEST(EvsCameraBufferTest, UseAndReturn) {
constexpr std::size_t kNumOfHandles = 20;
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();

View File

@@ -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