Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d14a829 authored by Hao Chen's avatar Hao Chen
Browse files

Hanlding Streams and States

Test: Build && `atest android.hardware.automotive.evs-aidl-default-service_cam_state_test`
Bug: 277861838
Change-Id: Idd8fe8e76876dfd93a177d3529ccf118b626001d
parent 1052df38
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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",
    ],
}
+40 −0
Original line number Diff line number Diff line
@@ -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:
+112 −0
Original line number Diff line number Diff line
@@ -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() {
+7 −8
Original line number Diff line number Diff line
@@ -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>();

+202 −0
Original line number Diff line number Diff line
/*
 * 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