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

Commit 287a83db authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Hanlding Streams and States" into main

parents c133a32f 0d14a829
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