/* * Copyright 2022 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 "MockHidlEvsHal.h" #include "Constants.h" #include #include #include #include #include #include #include #include namespace { using ::android::hardware::automotive::evs::V1_0::DisplayDesc; using ::android::hardware::automotive::evs::V1_0::DisplayState; using ::android::hardware::automotive::evs::V1_0::EvsResult; using ::android::hardware::automotive::evs::V1_1::BufferDesc; using ::android::hardware::automotive::evs::V1_1::CameraDesc; using ::android::hardware::automotive::evs::V1_1::CameraParam; using ::android::hardware::automotive::evs::V1_1::EvsEventDesc; using ::android::hardware::automotive::evs::V1_1::EvsEventType; using ::android::hardware::automotive::evs::V1_1::IEvsCamera; using ::android::hardware::automotive::evs::V1_1::IEvsCameraStream; using ::android::hardware::automotive::evs::V1_1::IEvsDisplay; using ::android::hardware::automotive::evs::V1_1::IEvsEnumerator; using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray; using ::android::hardware::camera::device::V3_2::Stream; using ::std::chrono_literals::operator""ms; using ::std::chrono_literals::operator""s; inline constexpr char kMockCameraDeviceNamePrefix[] = "/dev/mockcamera"; inline constexpr int32_t kCameraParamDefaultMinValue = -255; inline constexpr int32_t kCameraParamDefaultMaxValue = 255; inline constexpr int32_t kCameraParamDefaultStepValue = 3; inline constexpr size_t kMinimumNumBuffers = 2; inline constexpr size_t kMaximumNumBuffers = 10; } // namespace namespace aidl::android::automotive::evs::implementation { MockHidlEvsHal::~MockHidlEvsHal() { std::lock_guard lock(mLock); for (auto& [id, state] : mStreamState) { auto it = mCameraFrameThread.find(id); if (it == mCameraFrameThread.end() || !it->second.joinable()) { continue; } state = StreamState::kStopping; it->second.join(); } deinitializeBufferPoolLocked(); mCameraClient.clear(); } ::android::sp MockHidlEvsHal::getEnumerator() { if (!mMockHidlEvsEnumerator) { LOG(ERROR) << "MockHidlEvsHal has not initialized yet."; return nullptr; } return mMockHidlEvsEnumerator; } void MockHidlEvsHal::initialize() { initializeBufferPool(kMaximumNumBuffers); configureCameras(mNumCameras); configureDisplays(mNumDisplays); configureEnumerator(); } bool MockHidlEvsHal::buildCameraMetadata(int32_t width, int32_t height, int32_t format, ::android::hardware::hidl_vec* out) { ::android::CameraMetadata metadata; const std::vector availableStreamConfigurations = {format, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}; metadata.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, availableStreamConfigurations.data(), availableStreamConfigurations.size()); camera_metadata_t* p = metadata.release(); if (validate_camera_metadata_structure(p, /* expected_size= */ nullptr) != ::android::OK) { LOG(ERROR) << "Failed to build a camera metadata."; return false; } size_t n = get_camera_metadata_size(p); out->resize(n); memcpy(out->data(), p, n); return true; } void MockHidlEvsHal::forwardFrames(size_t numberOfFramesToForward, const std::string& deviceId) { std::unique_lock l(mLock); ::android::base::ScopedLockAssertion lock_assertion(mLock); auto it = mStreamState.find(deviceId); if (it != mStreamState.end() && it->second != StreamState::kStopped) { LOG(WARNING) << "A mock video stream is already active."; return; } mStreamState.insert_or_assign(deviceId, StreamState::kRunning); for (size_t count = 0; mStreamState[deviceId] == StreamState::kRunning && count < numberOfFramesToForward; ++count) { if (mBufferPool.empty()) { if (!mBufferAvailableSignal.wait_for(l, /* rel_time= */ 10s, [this]() REQUIRES(mLock) { // Waiting for a buffer to use. return !mBufferPool.empty(); })) { LOG(ERROR) << "Buffer timeout; " << count << "/" << numberOfFramesToForward << " are sent."; break; } } auto it = mCameraClient.find(deviceId); if (it == mCameraClient.end() || it->second == nullptr) { LOG(ERROR) << "Failed to forward a frame as no active recipient exists; " << count << "/" << numberOfFramesToForward << " are sent."; break; } BufferDesc bufferToForward = mBufferPool.back(); mBufferPool.pop_back(); bufferToForward.timestamp = static_cast(::android::elapsedRealtimeNano() * 1e+3); bufferToForward.deviceId = deviceId; // Mark a buffer in-use. mBuffersInUse.push_back(bufferToForward); l.unlock(); // Forward a duplicated buffer. This must be done without a lock // because a shared data will be modified in doneWithFrame(). ::android::hardware::hidl_vec packet; packet.resize(1); packet[0] = bufferToForward; it->second->deliverFrame_1_1(packet); LOG(DEBUG) << deviceId << ": " << (count + 1) << "/" << numberOfFramesToForward << " frames are sent"; std::this_thread::sleep_for(33ms); // 30 frames per seconds l.lock(); } if (mStreamState.find(deviceId) != mStreamState.end()) { mStreamState[deviceId] = StreamState::kStopped; } } size_t MockHidlEvsHal::initializeBufferPool(size_t requested) { std::lock_guard lock(mLock); for (auto count = 0; count < requested; ++count) { AHardwareBuffer_Desc desc = { .width = 64, .height = 32, .layers = 1, .format = HAL_PIXEL_FORMAT_RGBA_8888, .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, }; AHardwareBuffer* ahwb; if (AHardwareBuffer_allocate(&desc, &ahwb) != ::android::NO_ERROR) { LOG(ERROR) << "Failed to allocate AHardwareBuffer"; return count; } buffer_handle_t memHandle = AHardwareBuffer_getNativeHandle(ahwb); BufferDesc aBuffer = { .buffer = { .nativeHandle = memHandle, }, .pixelSize = 4, .bufferId = static_cast(count), .deviceId = "Mock EvsCamera", }; *(reinterpret_cast(&aBuffer.buffer.description)) = desc; mBufferRecord.insert_or_assign(count, ahwb); mBufferPool.push_back(std::move(aBuffer)); } return mBufferPool.size(); } void MockHidlEvsHal::deinitializeBufferPoolLocked() { for (auto&& descriptor : mBuffersInUse) { auto it = mBufferRecord.find(descriptor.bufferId); if (it == mBufferRecord.end()) { LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId; } else { LOG(WARNING) << "Releasing buffer in use, id = " << descriptor.bufferId; AHardwareBuffer_release(it->second); mBufferRecord.erase(it); } } for (auto&& descriptor : mBufferPool) { auto it = mBufferRecord.find(descriptor.bufferId); if (it == mBufferRecord.end()) { LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId; } else { AHardwareBuffer_release(it->second); mBufferRecord.erase(it); } } mBuffersInUse.clear(); mBufferPool.clear(); } void MockHidlEvsHal::configureCameras(size_t n) { // Initializes a list of the camera parameters each mock camera // supports with their default values. mCameraParams = {{CameraParam::BRIGHTNESS, 80}, {CameraParam::CONTRAST, 60}, {CameraParam::AUTOGAIN, 3}, {CameraParam::AUTO_EXPOSURE, 1}}; for (auto i = 0; i < n; ++i) { (void)addMockCameraDevice(kMockCameraDeviceNamePrefix + std::to_string(i)); } } bool MockHidlEvsHal::addMockCameraDevice(const std::string& deviceId) { ::android::sp mockCamera = new (std::nothrow) NiceMockHidlEvsCamera(deviceId); // For the testing purpose, this method will return // EvsResult::INVALID_ARG if the client returns any buffer with // unknown identifier. ON_CALL(*mockCamera, doneWithFrame) .WillByDefault([this](const hidlevs::V1_0::BufferDesc& buffer) { std::lock_guard lock(mLock); auto it = std::find_if(mBuffersInUse.begin(), mBuffersInUse.end(), [id = buffer.bufferId](const BufferDesc& desc) { return id == desc.bufferId; }); if (it == mBuffersInUse.end()) { return ::android::hardware::Void(); } mBufferPool.push_back(std::move(*it)); mBuffersInUse.erase(it); return ::android::hardware::Void(); }); ON_CALL(*mockCamera, doneWithFrame_1_1) .WillByDefault([this](const ::android::hardware::hidl_vec& buffers) { size_t returned = 0; std::lock_guard lock(mLock); for (auto& b : buffers) { auto it = std::find_if(mBuffersInUse.begin(), mBuffersInUse.end(), [id = b.bufferId](const BufferDesc& desc) { return id == desc.bufferId; }); if (it == mBuffersInUse.end()) { continue; } ++returned; mBufferPool.push_back(std::move(*it)); mBuffersInUse.erase(it); } if (returned > 0) { mBufferAvailableSignal.notify_all(); return EvsResult::OK; } else { return EvsResult::INVALID_ARG; } }); // EVS HAL accepts only a single client; therefore, this method // returns a success always. ON_CALL(*mockCamera, forceMaster) .WillByDefault( [](const ::android::sp&) { return EvsResult::OK; }); // We return a mock camera descriptor with the metadata but empty vendor // flag. ON_CALL(*mockCamera, getCameraInfo) .WillByDefault([deviceId](MockHidlEvsCamera::getCameraInfo_cb callback) { hidlevs::V1_0::CameraDesc mockDesc = { .cameraId = deviceId, .vendorFlags = 0x0, }; callback(mockDesc); return ::android::hardware::Void(); }); ON_CALL(*mockCamera, getCameraInfo_1_1) .WillByDefault([deviceId, this](MockHidlEvsCamera::getCameraInfo_1_1_cb callback) { CameraDesc mockDesc = { .v1 = { .cameraId = deviceId, .vendorFlags = 0x0, }, .metadata = {}, }; if (!buildCameraMetadata(/* width= */ 640, /* height= */ 480, /* format= */ HAL_PIXEL_FORMAT_RGBA_8888, &mockDesc.metadata)) { return ::android::hardware::Void(); } callback(mockDesc); return ::android::hardware::Void(); }); // This method will return a value associated with a given // identifier if exists. ON_CALL(*mockCamera, getExtendedInfo).WillByDefault([this](uint32_t id) { auto it = mCameraExtendedInfo.find(id); if (it == mCameraExtendedInfo.end()) { // A requested information does not exist. return 0; } if (it->second.size() < 4) { // Stored information is in an invalid size. return 0; } int value = *(reinterpret_cast(it->second.data())); return value; }); ON_CALL(*mockCamera, getExtendedInfo_1_1) .WillByDefault([this](uint32_t id, MockHidlEvsCamera::getExtendedInfo_1_1_cb callback) { auto it = mCameraExtendedInfo.find(id); if (it == mCameraExtendedInfo.end()) { // A requested information does not exist. callback(EvsResult::INVALID_ARG, 0); return ::android::hardware::Void(); } ::android::hardware::hidl_vec value = it->second; callback(EvsResult::OK, value); return ::android::hardware::Void(); }); // This method will return a value of a requested camera parameter // if it is supported by a mock EVS camera. ON_CALL(*mockCamera, getIntParameter) .WillByDefault([this](CameraParam id, MockHidlEvsCamera::getIntParameter_cb callback) { auto it = mCameraParams.find(id); if (it == mCameraParams.end()) { LOG(ERROR) << "Ignore a request to read an unsupported parameter, " << (int)id; callback(EvsResult::INVALID_ARG, 0); return ::android::hardware::Void(); } // EVS HAL always returns a single integer value. ::android::hardware::hidl_vec values; values.resize(1); values[0] = it->second; callback(EvsResult::OK, values); return ::android::hardware::Void(); }); // This method returns the same range values if a requested camera // parameter is supported by a mock EVS camera. ON_CALL(*mockCamera, getIntParameterRange) .WillByDefault( [this](CameraParam id, MockHidlEvsCamera::getIntParameterRange_cb callback) { auto it = mCameraParams.find(id); if (it == mCameraParams.end()) { callback(0, 0, 0); return ::android::hardware::Void(); } // For the testing purpose, this mock EVS HAL always returns the // same values. callback(kCameraParamDefaultMinValue, kCameraParamDefaultMaxValue, kCameraParamDefaultStepValue); return ::android::hardware::Void(); }); // This method returns a list of camera parameters supported by a // mock EVS camera. ON_CALL(*mockCamera, getParameterList) .WillByDefault([this](MockHidlEvsCamera::getParameterList_cb callback) { ::android::hardware::hidl_vec list; list.resize(mCameraParams.size()); unsigned idx = 0; for (auto& [k, _] : mCameraParams) { list[idx++] = k; } callback(list); return ::android::hardware::Void(); }); // This method behaves exactly the same as getCameraInfo() because // the EVS HAL does not support a concept of the group (or logical) // camera. ON_CALL(*mockCamera, getPhysicalCameraInfo) .WillByDefault([deviceId]([[maybe_unused]] const ::android::hardware::hidl_string& id, MockHidlEvsCamera::getPhysicalCameraInfo_cb callback) { CameraDesc mockDesc = { .v1 = { .cameraId = deviceId, .vendorFlags = 0x0, }, .metadata = {}, }; callback(mockDesc); return ::android::hardware::Void(); }); // This method adds given buffer descriptors to the internal buffer // pool if their identifiers do not conflict to existing ones. ON_CALL(*mockCamera, importExternalBuffers) .WillByDefault([this](const ::android::hardware::hidl_vec& buffers, MockHidlEvsCamera::importExternalBuffers_cb callback) { std::lock_guard l(mLock); size_t count = 0; for (auto i = 0; i < buffers.size(); ++i) { auto it = std::find_if(mBufferPool.begin(), mBufferPool.end(), [&](const BufferDesc& b) { return b.bufferId == buffers[i].bufferId; }); if (it != mBufferPool.end()) { // Ignores external buffers with a conflicting // identifier. continue; } // TODO(b/235110887): Add external buffers to the pool. // // Temporarily, we count the number of buffers that // identifiers do not conflict with existing buffers. ++count; } callback(EvsResult::OK, count); return ::android::hardware::Void(); }); ON_CALL(*mockCamera, pauseVideoStream).WillByDefault([]() { return EvsResult::UNDERLYING_SERVICE_ERROR; }); ON_CALL(*mockCamera, resumeVideoStream).WillByDefault([]() { return EvsResult::UNDERLYING_SERVICE_ERROR; }); // This method stores a given vector with id. ON_CALL(*mockCamera, setExtendedInfo).WillByDefault([this](uint32_t id, int32_t v) { ::android::hardware::hidl_vec value; value.resize(sizeof(v)); *(reinterpret_cast(value.data())) = v; mCameraExtendedInfo.insert_or_assign(id, value); return EvsResult::OK; }); ON_CALL(*mockCamera, setExtendedInfo_1_1) .WillByDefault([this](uint32_t id, const ::android::hardware::hidl_vec& v) { mCameraExtendedInfo.insert_or_assign(id, v); return EvsResult::OK; }); // This method updates a parameter value if exists. ON_CALL(*mockCamera, setIntParameter) .WillByDefault([this](CameraParam id, int32_t in, MockHidlEvsCamera::setIntParameter_cb callback) { auto it = mCameraParams.find(id); if (it == mCameraParams.end()) { LOG(ERROR) << "Ignore a request to program an unsupported parameter, " << (int)id; callback(EvsResult::INVALID_ARG, {}); return ::android::hardware::Void(); } in = in > kCameraParamDefaultMaxValue ? kCameraParamDefaultMaxValue : in < kCameraParamDefaultMinValue ? kCameraParamDefaultMinValue : in; mCameraParams.insert_or_assign(id, in); ::android::hardware::hidl_vec values; values.resize(1); values[0] = in; callback(EvsResult::OK, values); return ::android::hardware::Void(); }); // We always return a success because EVS HAL does not allow // multiple camera clients exist. ON_CALL(*mockCamera, setMaster).WillByDefault([]() { return EvsResult::OK; }); // Because EVS HAL does allow multiple camera clients exist, we simply // set the size of the buffer pool. ON_CALL(*mockCamera, setMaxFramesInFlight) .WillByDefault([this, id = mockCamera->getId()](uint32_t bufferCount) { std::lock_guard l(mLock); if (bufferCount < kMinimumNumBuffers) { LOG(WARNING) << "Requested buffer pool size is too small to run a camera; " "adjusting the pool size to " << kMinimumNumBuffers; bufferCount = kMinimumNumBuffers; } int64_t delta = bufferCount; auto it = mCameraBufferPoolSize.find(id); if (it != mCameraBufferPoolSize.end()) { delta -= it->second; } if (!delta) { // No further action required. return EvsResult::OK; } size_t totalSize = mBufferPoolSize + delta; if (totalSize > kMaximumNumBuffers) { LOG(ERROR) << "Requested size, " << totalSize << ", exceeds the limitation."; return EvsResult::INVALID_ARG; } mBufferPoolSize = totalSize; mCameraBufferPoolSize.insert_or_assign(id, bufferCount); return EvsResult::OK; }); // We manage the camera ownership on recency-basis; therefore we simply // replace the client in this method. ON_CALL(*mockCamera, startVideoStream) .WillByDefault([this, id = mockCamera->getId()]( const ::android::sp& cb) { // TODO(b/235110887): Notifies a camera loss to the current // client. size_t n = 0; { std::lock_guard l(mLock); auto client = IEvsCameraStream::castFrom(cb).withDefault(nullptr); if (!client) { // This mock implementation supports v1.1 hidl // client only. return EvsResult::INVALID_ARG; } mCameraClient.insert_or_assign(id, client); n = mNumberOfFramesToSend; } std::packaged_task task( &MockHidlEvsHal::forwardFrames); std::thread t(std::move(task), this, /* numberOfFramesForward= */ n, id); std::lock_guard l(mLock); mCameraFrameThread.insert_or_assign(id, std::move(t)); return EvsResult::OK; }); // We simply drop a current client. ON_CALL(*mockCamera, stopVideoStream).WillByDefault([this, id = mockCamera->getId()]() { ::android::sp cb; std::thread threadToJoin; { std::lock_guard l(mLock); auto state = mStreamState.find(id); if (state == mStreamState.end() || state->second != StreamState::kRunning) { return ::android::hardware::Void(); } auto callback = mCameraClient.find(id); if (callback == mCameraClient.end()) { return ::android::hardware::Void(); } cb = callback->second; callback->second = nullptr; state->second = StreamState::kStopping; auto it = mCameraFrameThread.find(id); if (it == mCameraFrameThread.end() || !it->second.joinable()) { return ::android::hardware::Void(); } threadToJoin = std::move(it->second); mCameraFrameThread.erase(it); } if (cb) { EvsEventDesc e = { .aType = EvsEventType::STREAM_STOPPED, .deviceId = id, }; cb->notify(e); } // Join a frame-forward thread threadToJoin.join(); return ::android::hardware::Void(); }); // We don't take any action because EVS HAL allows only a single camera // client exists at a time. ON_CALL(*mockCamera, unsetMaster).WillByDefault([]() { return EvsResult::OK; }); std::lock_guard l(mLock); mMockHidlEvsCameras.push_back(std::move(mockCamera)); mMockDeviceStatus.insert_or_assign(deviceId, true); return true; } void MockHidlEvsHal::removeMockCameraDevice(const std::string& deviceId) { std::lock_guard l(mLock); auto it = mMockDeviceStatus.find(deviceId); if (it == mMockDeviceStatus.end()) { // Nothing to do. return; } mMockDeviceStatus[deviceId] = false; } void MockHidlEvsHal::configureDisplays(size_t n) { // Build mock IEvsDisplcy instances std::vector<::android::sp> displays(n); for (auto i = 0; i < n; ++i) { (void)addMockDisplayDevice(i); } } bool MockHidlEvsHal::addMockDisplayDevice(int id) { ::android::sp mockDisplay = new (std::nothrow) NiceMockHidlEvsDisplay(); ON_CALL(*mockDisplay, getDisplayInfo) .WillByDefault([id](MockHidlEvsDisplay::getDisplayInfo_cb callback) { DisplayDesc desc = { .displayId = "MockDisplay" + std::to_string(id), // For the testing purpose, we put a display id in the vendor // flag field. .vendorFlags = static_cast(id), }; callback(desc); return ::android::hardware::Void(); }); ON_CALL(*mockDisplay, getDisplayInfo_1_1) .WillByDefault([id, this](MockHidlEvsDisplay::getDisplayInfo_1_1_cb callback) { DisplayDesc desc = { .displayId = "MockDisplay" + std::to_string(id), // For the testing purpose, we put a display id in the vendor // flag field. .vendorFlags = static_cast(id), }; ::android::hardware::hidl_vec config; config.resize(sizeof(::android::ui::DisplayMode)); ::android::ui::DisplayMode* p = reinterpret_cast<::android::ui::DisplayMode*>(config.data()); p->resolution = ::android::ui::Size(64, 32); ::android::hardware::hidl_vec state; state.resize(sizeof(mCurrentDisplayState)); *(reinterpret_cast(state.data())) = mCurrentDisplayState; callback(config, state); return ::android::hardware::Void(); }); ON_CALL(*mockDisplay, getDisplayState).WillByDefault([this]() { return mCurrentDisplayState; }); ON_CALL(*mockDisplay, getTargetBuffer) .WillByDefault([](MockHidlEvsDisplay::getTargetBuffer_cb callback) { // TODO(b/263438927): implement this method. callback({}); return ::android::hardware::Void(); }); ON_CALL(*mockDisplay, returnTargetBufferForDisplay) .WillByDefault([](const hidlevs::V1_0::BufferDesc& in) { // TODO(b/263438927): implement this method. (void)in; return EvsResult::OK; }); ON_CALL(*mockDisplay, setDisplayState).WillByDefault([this](DisplayState in) { mCurrentDisplayState = in; return EvsResult::OK; }); std::lock_guard l(mLock); mMockHidlEvsDisplays.push_back(std::move(mockDisplay)); mMockDeviceStatus.insert_or_assign(std::to_string(id), true); return true; } void MockHidlEvsHal::removeMockDisplayDevice(int id) { std::lock_guard l(mLock); auto key = std::to_string(id); auto it = mMockDeviceStatus.find(key); if (it == mMockDeviceStatus.end()) { // Nothing to do. return; } mMockDeviceStatus[key] = false; } size_t MockHidlEvsHal::setNumberOfFramesToSend(size_t n) { std::lock_guard l(mLock); return mNumberOfFramesToSend = n; } void MockHidlEvsHal::configureEnumerator() { ::android::sp mockEnumerator = new (std::nothrow) NiceMockHidlEvsEnumerator(); ON_CALL(*mockEnumerator, closeCamera) .WillByDefault([this](const ::android::sp& handle) { ::android::sp c = IEvsCamera::castFrom(handle); CameraDesc desc; c->getCameraInfo_1_1([&desc](auto& read) { desc = read; }); std::lock_guard l(mLock); auto it = mCameraBufferPoolSize.find(desc.v1.cameraId); if (it == mCameraBufferPoolSize.end()) { // Safely ignore a request if we fail to find a corresponding mock // camera. return ::android::hardware::Void(); } mBufferPoolSize -= it->second; if (mBufferPoolSize < 0) { LOG(WARNING) << "mBuffeRPoolSize should not have a negative value, " << mBufferPoolSize; mBufferPoolSize = 0; } mCameraBufferPoolSize.insert_or_assign(desc.v1.cameraId, 0); return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, closeDisplay) .WillByDefault([this]([[maybe_unused]] const ::android::sp& displayObj) { auto pActiveDisplay = mActiveDisplay.promote(); if (!pActiveDisplay) { LOG(WARNING) << "Got a request to close a display already destroyed."; } // Nothing else to do. return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, closeUltrasonicsArray) .WillByDefault([](const ::android::sp&) { // Mock EVS HAL does not support IEvsUltrasonicsArray. return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, getCameraList) .WillByDefault([this](MockHidlEvsEnumerator::getCameraList_cb callback) { ::android::hardware::hidl_vec list( mMockHidlEvsCameras.size()); for (auto i = 0; i < mMockHidlEvsCameras.size(); ++i) { mMockHidlEvsCameras[i]->getCameraInfo([&](auto& desc) { list[i] = desc; }); // Inserts a camera record if it does not exist. if (mCameraList.find(list[i].cameraId) == mCameraList.end()) { CameraDesc newDevice = { .v1 = list[i], }; mCameraList.insert_or_assign(list[i].cameraId, newDevice); } } callback(list); return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, getCameraList_1_1) .WillByDefault([this](MockHidlEvsEnumerator::getCameraList_1_1_cb callback) { std::vector list(mMockHidlEvsCameras.size()); for (auto i = 0; i < mMockHidlEvsCameras.size(); ++i) { mMockHidlEvsCameras[i]->getCameraInfo_1_1([&](auto& desc) { list[i] = desc; }); // Inserts a camera record if it does not exist. if (mCameraList.find(list[i].v1.cameraId) == mCameraList.end()) { mCameraList.insert_or_assign(list[i].v1.cameraId, list[i]); } } callback(list); return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, getDisplayIdList) .WillByDefault([this](MockHidlEvsEnumerator::getDisplayIdList_cb callback) { ::android::hardware::hidl_vec list; list.resize(mMockHidlEvsDisplays.size()); for (auto i = 0; i < mMockHidlEvsDisplays.size(); ++i) { mMockHidlEvsDisplays[i]->getDisplayInfo([&](auto& desc) { // MockHidlEvsDisplay contains a display ID in its vendor flags. list[i] = desc.vendorFlags; }); } callback(list); return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, getDisplayState).WillByDefault([this]() { return mCurrentDisplayState; }); ON_CALL(*mockEnumerator, getUltrasonicsArrayList) .WillByDefault([]([[maybe_unused]] MockHidlEvsEnumerator::getUltrasonicsArrayList_cb callback) { // Mock EVS HAL does not support IEvsUltrasonicsArray yet. return ::android::hardware::Void(); }); ON_CALL(*mockEnumerator, isHardware).WillByDefault([]() { return false; }); ON_CALL(*mockEnumerator, openCamera) .WillByDefault([this](const ::android::hardware::hidl_string& id) -> ::android::sp { auto it = std::find_if(mMockHidlEvsCameras.begin(), mMockHidlEvsCameras.end(), [id](const ::android::sp& c) { hidlevs::V1_0::CameraDesc desc; c->getCameraInfo([&desc](auto& read) { desc = read; }); return desc.cameraId == id; }); if (it == mMockHidlEvsCameras.end()) { return nullptr; } auto instance = mCameraList.find(id); // Guaranteed to exist always. instance->second.activeInstance = *it; return *it; }); ON_CALL(*mockEnumerator, openCamera_1_1) .WillByDefault( [this](const ::android::hardware::hidl_string& id, [[maybe_unused]] const Stream& config) -> ::android::sp { auto it = std::find_if(mMockHidlEvsCameras.begin(), mMockHidlEvsCameras.end(), [id](const ::android::sp& c) { CameraDesc desc; c->getCameraInfo_1_1( [&desc](auto& read) { desc = read; }); return desc.v1.cameraId == id; }); if (it == mMockHidlEvsCameras.end()) { return nullptr; } auto instance = mCameraList.find(id); // Guaranteed to exist always. instance->second.activeInstance = *it; return *it; }); ON_CALL(*mockEnumerator, openDisplay).WillByDefault([]() { // TODO(b/263438927): implement this method. return nullptr; }); ON_CALL(*mockEnumerator, openDisplay_1_1) .WillByDefault([this](uint8_t id) -> ::android::sp { if (id == kExclusiveDisplayId) { if (mDisplayOwnedExclusively && !mActiveDisplay.promote()) { return nullptr; } DisplayDesc desc; mMockHidlEvsDisplays[0]->getDisplayInfo([&desc](auto& read) { desc = read; }); id = desc.vendorFlags; // the first display in the list is // the main display. mDisplayOwnedExclusively = true; } auto it = std::find_if(mMockHidlEvsDisplays.begin(), mMockHidlEvsDisplays.end(), [id](const ::android::sp& d) { DisplayDesc desc; d->getDisplayInfo([&desc](auto& read) { desc = read; }); return desc.vendorFlags == id; }); if (it == mMockHidlEvsDisplays.end()) { return nullptr; } mActiveDisplay = *it; mCurrentDisplayState = DisplayState::NOT_VISIBLE; return *it; }); ON_CALL(*mockEnumerator, openUltrasonicsArray) .WillByDefault([]([[maybe_unused]] const ::android::hardware::hidl_string& id) { // Mock EVS HAL does not support IEvsUltrasonicsArray yet. return nullptr; }); mMockHidlEvsEnumerator = std::move(mockEnumerator); } } // namespace aidl::android::automotive::evs::implementation