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

Commit 5ed789cd authored by Hao Chen's avatar Hao Chen
Browse files

Add EvsCamera Class and Buffer Manipulation

Test: Build and `atest android.hardware.automotive.evs-aidl-default-service_cam_buffer_test`
Bug: 277861838
Change-Id: I28218f8e22e7daca3d8ce0bbec64c054a4462564
parent c55537df
Loading
Loading
Loading
Loading
+48 −22
Original line number Diff line number Diff line
@@ -21,45 +21,41 @@ package {
    default_applicable_licenses: ["hardware_interfaces_license"],
}

cc_binary {
    name: "android.hardware.automotive.evs-aidl-default-service",
cc_defaults {
    name: "android.hardware.automotive.evs-aidl-default-service-default",
    defaults: ["EvsHalDefaults"],
    vintf_fragments: ["manifest_evs-default-service.xml"],
    init_rc: ["evs-default-service.rc"],
    vendor: true,
    relative_install_path: "hw",
    cflags: [
        "-DGL_GLEXT_PROTOTYPES",
        "-DEGL_EGLEXT_PROTOTYPES",
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wthread-safety",
    ],
    srcs: [
        ":libgui_frame_event_aidl",
        "src/*.cpp",
    ],
    shared_libs: [
        "android.hardware.graphics.bufferqueue@1.0",
        "android.hardware.graphics.bufferqueue@2.0",
        "android.hidl.token@1.0-utils",
        "libEGL",
        "libGLESv2",
        "libbase",
        "libbinder_ndk",
        "libbufferqueueconverter",
        "libcamera_metadata",
        "libhardware_legacy",
        "libhidlbase",
        "liblog",
        "libnativewindow",
        "libtinyxml2",
        "libui",
        "libutils",
        "libyuv",
    ],
    static_libs: [
}

cc_library {
    name: "android.hardware.automotive.evs-aidl-default-service-lib",
    defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
    vendor: true,
    cflags: [
        "-DGL_GLEXT_PROTOTYPES",
        "-DEGL_EGLEXT_PROTOTYPES",
    ],
    srcs: [
        ":libgui_frame_event_aidl",
        "src/*.cpp",
    ],
    exclude_srcs: ["src/service.cpp"],
    whole_static_libs: [
        "android.frameworks.automotive.display-V2-ndk",
        "android.hardware.automotive.evs-V2-ndk",
        "android.hardware.common-V2-ndk",
@@ -71,6 +67,25 @@ cc_binary {
    ],
    local_include_dirs: ["include"],
    include_dirs: ["frameworks/native/include/"],
    export_include_dirs: ["include"],
}

cc_binary {
    name: "android.hardware.automotive.evs-aidl-default-service",
    defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
    vintf_fragments: ["manifest_evs-default-service.xml"],
    init_rc: ["evs-default-service.rc"],
    vendor: true,
    relative_install_path: "hw",
    cflags: [
        "-DGL_GLEXT_PROTOTYPES",
        "-DEGL_EGLEXT_PROTOTYPES",
    ],
    srcs: ["src/service.cpp"],
    static_libs: [
        "android.hardware.automotive.evs-aidl-default-service-lib",
    ],
    include_dirs: ["frameworks/native/include/"],
    required: ["evs_mock_hal_configuration.xml"],
}

@@ -80,3 +95,14 @@ prebuilt_etc {
    src: "resources/evs_mock_configuration.xml",
    sub_dir: "automotive/evs",
}

cc_test {
    name: "android.hardware.automotive.evs-aidl-default-service_cam_buffer_test",
    defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
    vendor: true,
    srcs: ["tests/EvsCameraBufferTest.cpp"],
    static_libs: [
        "android.hardware.automotive.evs-aidl-default-service-lib",
        "libgmock",
    ],
}
+98 −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.
 */

#pragma once

#include "EvsCameraBase.h"

#include <cutils/native_handle.h>

#include <cstddef>
#include <mutex>
#include <utility>
#include <vector>

namespace aidl::android::hardware::automotive::evs::implementation {

class EvsCamera : public EvsCameraBase {
  private:
    using Base = EvsCameraBase;
    using Self = EvsCamera;

  public:
    using Base::Base;

    ~EvsCamera() override;

    // Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
    ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;

    ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
                                             int32_t* _aidl_return) override;

    ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;

  protected:
    virtual ::android::status_t allocateOneFrame(buffer_handle_t* handle) = 0;

    virtual void freeOneFrame(const buffer_handle_t handle);

    void shutdown() override;

    void closeAllBuffers_unsafe();

    // Returns (ID, handle) if succeeds. (static_cast<size_t>(-1), nullptr) otherwise.
    [[nodiscard]] std::pair<std::size_t, buffer_handle_t> useBuffer_unsafe();

    void returnBuffer_unsafe(const std::size_t id);

    bool increaseAvailableFrames_unsafe(const buffer_handle_t handle);

    bool decreaseAvailableFrames_unsafe();

    bool setAvailableFrames_unsafe(const std::size_t bufferCount);

    void swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2);

    struct BufferRecord {
        BufferRecord() = default;
        BufferRecord(const BufferRecord&) = default;
        BufferRecord(BufferRecord&&) = default;
        BufferRecord& operator=(const BufferRecord&) = default;
        BufferRecord& operator=(BufferRecord&&) = default;
        ~BufferRecord() = default;

        explicit BufferRecord(buffer_handle_t h) : handle(h) {}

        buffer_handle_t handle{nullptr};
        bool inUse{false};
    };

    std::mutex mMutex;

    // Graphics buffers to transfer images, always in the order of:
    // In use buffers ... available buffers ... unavailable (unallocated) buffers.
    std::vector<BufferRecord> mBuffers;

    // Double-mapping between buffer position and ID.
    std::vector<std::size_t> mBufferPosToId;
    std::vector<std::size_t> mBufferIdToPos;

    std::size_t mAvailableFrames{0};
    std::size_t mFramesInUse{0};
};

}  // namespace aidl::android::hardware::automotive::evs::implementation
+266 −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 <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/hardware_buffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>

#include <cstddef>
#include <mutex>

namespace aidl::android::hardware::automotive::evs::implementation {

// Arbitrary limit on number of graphics buffers allowed to be allocated
// Safeguards against unreasonable resource consumption and provides a testable limit
constexpr std::size_t kMaxBuffersInFlight = 100;

EvsCamera::~EvsCamera() {
    shutdown();
}

ndk::ScopedAStatus EvsCamera::doneWithFrame(const std::vector<evs::BufferDesc>& buffers) {
    std::lock_guard lck(mMutex);
    for (const auto& desc : buffers) {
        returnBuffer_unsafe(desc.bufferId);
    }
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus EvsCamera::importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
                                                    int32_t* _aidl_return) {
    if (buffers.empty()) {
        LOG(DEBUG) << __func__
                   << ": Ignoring a request to import external buffers with an empty list.";
        return ndk::ScopedAStatus::ok();
    }
    static auto& mapper = ::android::GraphicBufferMapper::get();
    std::lock_guard lck(mMutex);
    std::size_t numBuffersToAdd = std::min(buffers.size(), kMaxBuffersInFlight - mAvailableFrames);
    if (numBuffersToAdd == 0) {
        LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
                     << kMaxBuffersInFlight << "). Stop importing.";
        return ndk::ScopedAStatus::ok();
    } else if (numBuffersToAdd < buffers.size()) {
        LOG(WARNING) << "Exceeds the limit on the number of buffers. Only " << numBuffersToAdd
                     << " buffers will be imported. " << buffers.size() << " are asked.";
    }
    const size_t before = mAvailableFrames;
    for (std::size_t idx = 0; idx < numBuffersToAdd; ++idx) {
        auto& buffer = buffers[idx];
        const AHardwareBuffer_Desc* pDesc =
                reinterpret_cast<const AHardwareBuffer_Desc*>(&buffer.buffer.description);

        buffer_handle_t handleToImport = ::android::dupFromAidl(buffer.buffer.handle);
        buffer_handle_t handleToStore = nullptr;
        if (handleToImport == nullptr) {
            LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer "
                         << buffer.bufferId;
            continue;
        }

        ::android::status_t result =
                mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
                                    pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
        if (result != ::android::NO_ERROR || handleToStore == nullptr ||
            !increaseAvailableFrames_unsafe(handleToStore)) {
            LOG(WARNING) << "Failed to import a buffer " << buffer.bufferId;
        }
    }
    *_aidl_return = mAvailableFrames - before;
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus EvsCamera::setMaxFramesInFlight(int32_t bufferCount) {
    std::lock_guard lock(mMutex);
    if (bufferCount < 1) {
        LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
        return ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int>(EvsResult::INVALID_ARG));
    }
    if (!setAvailableFrames_unsafe(bufferCount)) {
        LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
        return ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
    }
    return ndk::ScopedAStatus::ok();
}

void EvsCamera::freeOneFrame(const buffer_handle_t handle) {
    static auto& alloc = ::android::GraphicBufferAllocator::get();
    alloc.free(handle);
}

bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) {
    if (bufferCount < 1) {
        LOG(ERROR) << "Ignoring request to set buffer count to zero.";
        return false;
    }
    if (bufferCount > kMaxBuffersInFlight) {
        LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
        return false;
    }

    if (bufferCount > mAvailableFrames) {
        bool success = true;
        const std::size_t numBufferBeforeAlloc = mAvailableFrames;
        for (int numBufferToAllocate = bufferCount - mAvailableFrames;
             success && numBufferToAllocate > 0; --numBufferToAllocate) {
            buffer_handle_t handle = nullptr;
            const auto result = allocateOneFrame(&handle);
            if (result != ::android::NO_ERROR || !handle) {
                LOG(ERROR) << __func__ << ": Failed to allocate a graphics buffer. Error " << result
                           << ", handle: " << handle;
                success = false;
                break;
            }
            success &= increaseAvailableFrames_unsafe(handle);
        }
        if (!success) {
            // Rollback when failure.
            for (int numBufferToRelease = mAvailableFrames - numBufferBeforeAlloc;
                 numBufferToRelease > 0; --numBufferToRelease) {
                decreaseAvailableFrames_unsafe();
            }
            return false;
        }
    } else {
        for (int numBufferToRelease = mAvailableFrames - std::max(bufferCount, mFramesInUse);
             numBufferToRelease > 0; --numBufferToRelease) {
            decreaseAvailableFrames_unsafe();
        }
        if (mAvailableFrames > bufferCount) {
            // This shouldn't happen with a properly behaving client because the client
            // should only make this call after returning sufficient outstanding buffers
            // to allow a clean resize.
            LOG(ERROR) << "Buffer queue shrink failed, asked: " << bufferCount
                       << ", actual: " << mAvailableFrames
                       << " -- too many buffers currently in use?";
        }
    }
    return true;
}

void EvsCamera::shutdown() {
    std::lock_guard lck(mMutex);
    closeAllBuffers_unsafe();
}

void EvsCamera::closeAllBuffers_unsafe() {
    if (mFramesInUse > 0) {
        LOG(WARNING) << __func__ << ": Closing while " << mFramesInUse
                     << " frame(s) are still in use.";
    }
    for (auto& buffer : mBuffers) {
        freeOneFrame(buffer.handle);
        buffer.handle = nullptr;
    }
    mBuffers.clear();
    mBufferPosToId.clear();
    mBufferIdToPos.clear();
}

std::pair<std::size_t, buffer_handle_t> EvsCamera::useBuffer_unsafe() {
    if (mFramesInUse >= mAvailableFrames) {
        DCHECK_EQ(mFramesInUse, mAvailableFrames);
        return {static_cast<std::size_t>(-1), nullptr};
    }
    const std::size_t pos = mFramesInUse++;
    auto& buffer = mBuffers[pos];
    DCHECK(!buffer.inUse);
    DCHECK(buffer.handle);
    buffer.inUse = true;
    return {mBufferPosToId[pos], buffer.handle};
}

void EvsCamera::returnBuffer_unsafe(const std::size_t id) {
    if (id >= mBuffers.size()) {
        LOG(ERROR) << __func__ << ": ID out-of-bound. id: " << id
                   << " max: " << mBuffers.size() - 1;
        return;
    }
    const std::size_t pos = mBufferIdToPos[id];

    if (!mBuffers[pos].inUse) {
        LOG(ERROR) << __func__ << ": Ignoring returning frame " << id << " which is already free.";
        return;
    }
    DCHECK_LT(pos, mFramesInUse);
    const std::size_t last_in_use_pos = --mFramesInUse;
    swapBufferFrames_unsafe(pos, last_in_use_pos);
    mBuffers[last_in_use_pos].inUse = false;
}

bool EvsCamera::increaseAvailableFrames_unsafe(const buffer_handle_t handle) {
    if (mAvailableFrames >= kMaxBuffersInFlight) {
        LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
                     << kMaxBuffersInFlight << "). Stop increasing.";
        return false;
    }
    const std::size_t pos = mAvailableFrames++;
    if (mAvailableFrames > mBuffers.size()) {
        const std::size_t oldBufferSize = mBuffers.size();
        mBuffers.resize(mAvailableFrames);
        mBufferPosToId.resize(mAvailableFrames);
        mBufferIdToPos.resize(mAvailableFrames);
        // Build position/ID mapping.
        for (std::size_t idx = oldBufferSize; idx < mBuffers.size(); ++idx) {
            mBufferPosToId[idx] = idx;
            mBufferIdToPos[idx] = idx;
        }
    }
    auto& buffer = mBuffers[pos];
    DCHECK(!buffer.inUse);
    DCHECK(!buffer.handle);
    buffer.handle = handle;
    return true;
}

bool EvsCamera::decreaseAvailableFrames_unsafe() {
    if (mFramesInUse >= mAvailableFrames) {
        DCHECK_EQ(mFramesInUse, mAvailableFrames);
        return false;
    }
    const std::size_t pos = --mAvailableFrames;
    auto& buffer = mBuffers[pos];
    DCHECK(!buffer.inUse);
    DCHECK(buffer.handle);
    freeOneFrame(buffer.handle);
    buffer.handle = nullptr;
    return true;
}

void EvsCamera::swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2) {
    if (pos1 == pos2) {
        return;
    }
    if (pos1 >= mBuffers.size() || pos2 >= mBuffers.size()) {
        LOG(ERROR) << __func__ << ": Index out-of-bound. pos1: " << pos1 << ", pos2: " << pos2
                   << ", buffer size: " << mBuffers.size();
        return;
    }
    const std::size_t id1 = mBufferPosToId[pos1];
    const std::size_t id2 = mBufferPosToId[pos2];
    std::swap(mBufferPosToId[pos1], mBufferPosToId[pos2]);
    std::swap(mBufferIdToPos[id1], mBufferIdToPos[id2]);
    std::swap(mBuffers[pos1], mBuffers[pos2]);
}

}  // namespace aidl::android::hardware::automotive::evs::implementation
+208 −0

File added.

Preview size limit exceeded, changes collapsed.