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

Commit 1052df38 authored by Hao Chen's avatar Hao Chen Committed by Android (Google) Code Review
Browse files

Merge "Add EvsCamera Class and Buffer Manipulation" into main

parents d0cea8be 5ed789cd
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.