Merge "Add EvsCamera Class and Buffer Manipulation" into main

This commit is contained in:
Hao Chen
2023-09-22 17:58:53 +00:00
committed by Android (Google) Code Review
4 changed files with 620 additions and 22 deletions

View File

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

View File

@@ -0,0 +1,98 @@
/*
* 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

View File

@@ -0,0 +1,266 @@
/*
* 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

View File

@@ -0,0 +1,208 @@
/*
* 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 {
public:
using EvsCamera::increaseAvailableFrames_unsafe;
using EvsCamera::returnBuffer_unsafe;
using EvsCamera::useBuffer_unsafe;
~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.
}
void checkBufferOrder() {
for (std::size_t idx = 0; idx < mBuffers.size(); ++idx) {
const auto& buffer = mBuffers[idx];
EXPECT_EQ(idx < mFramesInUse, buffer.inUse);
EXPECT_EQ(idx < mAvailableFrames, buffer.handle != nullptr);
EXPECT_LE(mFramesInUse, mAvailableFrames);
}
}
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, 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));
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, 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));
};
TEST(EvsCameraBufferTest, ChangeBufferPoolSize) {
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
EXPECT_TRUE(evsCam->setMaxFramesInFlight(100).isOk());
evsCam->checkBufferOrder();
EXPECT_TRUE(evsCam->setMaxFramesInFlight(50).isOk());
evsCam->checkBufferOrder();
// 2 buffers in use.
const auto [id1, handle1] = evsCam->useBuffer_unsafe();
const auto [id2, handle2] = evsCam->useBuffer_unsafe();
std::ignore = evsCam->useBuffer_unsafe();
// It allows you to set the buffer pool size to 1, but it will keep the space for the in use
// buffers.
EXPECT_TRUE(evsCam->setMaxFramesInFlight(1).isOk());
evsCam->checkBufferOrder();
evsCam->returnBuffer_unsafe(id1);
evsCam->checkBufferOrder();
evsCam->returnBuffer_unsafe(id2);
evsCam->checkBufferOrder();
}
TEST(EvsCameraForTest, UseAndReturn) {
constexpr std::size_t kNumOfHandles = 20;
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
// Our "fake handles" of this test case is 1 to kNumOfHandles.
for (std::size_t i = 1; i <= kNumOfHandles; ++i) {
evsCam->increaseAvailableFrames_unsafe(reinterpret_cast<buffer_handle_t>(i));
}
evsCam->checkBufferOrder();
{
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
// Return buffers in the order of acquiring.
for (const auto [id, handleInt] : inUseIDHandlePairs) {
evsCam->returnBuffer_unsafe(id);
evsCam->checkBufferOrder();
}
}
{
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
// Return buffers in the reverse order of acquiring.
std::reverse(inUseIDHandlePairs.begin(), inUseIDHandlePairs.end());
for (const auto [id, handleInt] : inUseIDHandlePairs) {
evsCam->returnBuffer_unsafe(id);
evsCam->checkBufferOrder();
}
}
{
// Making sure the handles are still in [1, kNumOfHandles] and IDs are still [0,
// kNumOfHandles). The mapping may be different, though.
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
}
}
} // namespace aidl::android::hardware::automotive::evs::implementation