1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "MockEvsHal.h"
18
19 #include "Constants.h"
20
21 #include <aidl/android/hardware/automotive/evs/Rotation.h>
22 #include <aidl/android/hardware/automotive/evs/StreamType.h>
23 #include <aidl/android/hardware/common/NativeHandle.h>
24 #include <aidlcommonsupport/NativeHandle.h>
25 #include <android-base/logging.h>
26 #include <camera/CameraMetadata.h>
27 #include <hardware/gralloc.h>
28 #include <utils/SystemClock.h>
29
30 #include <functional>
31 #include <future>
32
33 #include <system/graphics-base.h>
34
35 namespace {
36
37 using ::aidl::android::hardware::automotive::evs::BufferDesc;
38 using ::aidl::android::hardware::automotive::evs::CameraDesc;
39 using ::aidl::android::hardware::automotive::evs::CameraParam;
40 using ::aidl::android::hardware::automotive::evs::DeviceStatus;
41 using ::aidl::android::hardware::automotive::evs::DeviceStatusType;
42 using ::aidl::android::hardware::automotive::evs::DisplayDesc;
43 using ::aidl::android::hardware::automotive::evs::DisplayState;
44 using ::aidl::android::hardware::automotive::evs::EvsEventDesc;
45 using ::aidl::android::hardware::automotive::evs::EvsEventType;
46 using ::aidl::android::hardware::automotive::evs::EvsResult;
47 using ::aidl::android::hardware::automotive::evs::IEvsCamera;
48 using ::aidl::android::hardware::automotive::evs::IEvsCameraStream;
49 using ::aidl::android::hardware::automotive::evs::IEvsDisplay;
50 using ::aidl::android::hardware::automotive::evs::IEvsEnumerator;
51 using ::aidl::android::hardware::automotive::evs::IEvsEnumeratorStatusCallback;
52 using ::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray;
53 using ::aidl::android::hardware::automotive::evs::ParameterRange;
54 using ::aidl::android::hardware::automotive::evs::Rotation;
55 using ::aidl::android::hardware::automotive::evs::Stream;
56 using ::aidl::android::hardware::automotive::evs::StreamType;
57 using ::aidl::android::hardware::common::NativeHandle;
58 using ::aidl::android::hardware::graphics::common::BufferUsage;
59 using ::aidl::android::hardware::graphics::common::HardwareBuffer;
60 using ::aidl::android::hardware::graphics::common::PixelFormat;
61 using ::std::chrono_literals::operator""ms;
62 using ::std::chrono_literals::operator""s;
63
64 inline constexpr char kMockCameraDeviceNamePrefix[] = "/dev/mockcamera";
65 inline constexpr int32_t kCameraParamDefaultMinValue = -255;
66 inline constexpr int32_t kCameraParamDefaultMaxValue = 255;
67 inline constexpr int32_t kCameraParamDefaultStepValue = 3;
68 inline constexpr size_t kMinimumNumBuffers = 2;
69 inline constexpr size_t kMaximumNumBuffers = 10;
70
copyNativeHandle(const NativeHandle & handle,bool doDup)71 NativeHandle copyNativeHandle(const NativeHandle& handle, bool doDup) {
72 NativeHandle dup;
73
74 dup.fds = std::vector<ndk::ScopedFileDescriptor>(handle.fds.size());
75 if (!doDup) {
76 for (auto i = 0; i < handle.fds.size(); ++i) {
77 dup.fds.at(i).set(handle.fds[i].get());
78 }
79 } else {
80 for (auto i = 0; i < handle.fds.size(); ++i) {
81 dup.fds[i] = std::move(handle.fds[i].dup());
82 }
83 }
84 dup.ints = handle.ints;
85
86 return std::move(dup);
87 }
88
copyHardwareBuffer(const HardwareBuffer & buffer,bool doDup)89 HardwareBuffer copyHardwareBuffer(const HardwareBuffer& buffer, bool doDup) {
90 HardwareBuffer copied = {
91 .description = buffer.description,
92 .handle = copyNativeHandle(buffer.handle, doDup),
93 };
94
95 return std::move(copied);
96 }
97
copyBufferDesc(const BufferDesc & src,bool doDup)98 BufferDesc copyBufferDesc(const BufferDesc& src, bool doDup) {
99 BufferDesc copied = {
100 .buffer = copyHardwareBuffer(src.buffer, doDup),
101 .pixelSizeBytes = src.pixelSizeBytes,
102 .bufferId = src.bufferId,
103 .deviceId = src.deviceId,
104 .timestamp = src.timestamp,
105 .metadata = src.metadata,
106 };
107
108 return std::move(copied);
109 }
110
111 } // namespace
112
113 namespace aidl::android::automotive::evs::implementation {
114
~MockEvsHal()115 MockEvsHal::~MockEvsHal() {
116 std::lock_guard lock(mLock);
117 for (auto& [id, state] : mStreamState) {
118 auto it = mCameraFrameThread.find(id);
119 if (it == mCameraFrameThread.end() || !it->second.joinable()) {
120 continue;
121 }
122
123 state = StreamState::kStopping;
124 it->second.join();
125 }
126
127 deinitializeBufferPoolLocked();
128 mCameraClient.clear();
129 }
130
getEnumerator()131 std::shared_ptr<IEvsEnumerator> MockEvsHal::getEnumerator() {
132 if (!mMockEvsEnumerator) {
133 LOG(ERROR) << "MockEvsHal has not initialized yet.";
134 return nullptr;
135 }
136
137 return IEvsEnumerator::fromBinder(mMockEvsEnumerator->asBinder());
138 }
139
initialize()140 void MockEvsHal::initialize() {
141 initializeBufferPool(kMaximumNumBuffers);
142 configureCameras(mNumCameras);
143 configureDisplays(mNumDisplays);
144 configureEnumerator();
145 }
146
buildCameraMetadata(int32_t width,int32_t height,int32_t format,std::vector<uint8_t> * out)147 bool MockEvsHal::buildCameraMetadata(int32_t width, int32_t height, int32_t format,
148 std::vector<uint8_t>* out) {
149 ::android::CameraMetadata metadata;
150
151 const std::vector<int32_t> availableStreamConfigurations =
152 {format, width, height, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT};
153
154 metadata.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
155 availableStreamConfigurations.data(), availableStreamConfigurations.size());
156
157 camera_metadata_t* p = metadata.release();
158 if (validate_camera_metadata_structure(p, /* expected_size= */ nullptr) != ::android::OK) {
159 LOG(ERROR) << "Failed to build a camera metadata.";
160 return false;
161 }
162
163 size_t n = get_camera_metadata_size(p);
164 out->resize(n);
165 memcpy(out->data(), p, n);
166
167 return true;
168 }
169
forwardFrames(size_t numberOfFramesToForward,const std::string & deviceId)170 void MockEvsHal::forwardFrames(size_t numberOfFramesToForward, const std::string& deviceId) {
171 std::unique_lock l(mLock);
172 ::android::base::ScopedLockAssertion lock_assertion(mLock);
173 auto it = mStreamState.find(deviceId);
174 if (it != mStreamState.end() && it->second != StreamState::kStopped) {
175 LOG(WARNING) << "A mock video stream is already active.";
176 return;
177 }
178 mStreamState.insert_or_assign(deviceId, StreamState::kRunning);
179
180 for (size_t count = 0;
181 mStreamState[deviceId] == StreamState::kRunning && count < numberOfFramesToForward;
182 ++count) {
183 if (mBufferPool.empty()) {
184 if (!mBufferAvailableSignal.wait_for(l, /* rel_time= */ 10s, [this]() REQUIRES(mLock) {
185 // Waiting for a buffer to use.
186 return !mBufferPool.empty();
187 })) {
188 LOG(ERROR) << "Buffer timeout; " << count << "/" << numberOfFramesToForward
189 << " are sent.";
190 break;
191 }
192 }
193
194 auto it = mCameraClient.find(deviceId);
195 if (it == mCameraClient.end() || it->second == nullptr) {
196 LOG(ERROR) << "Failed to forward a frame as no active recipient exists; " << count
197 << "/" << numberOfFramesToForward << " are sent.";
198 break;
199 }
200
201 // Duplicate a buffer.
202 BufferDesc bufferToUse = std::move(mBufferPool.back());
203 mBufferPool.pop_back();
204
205 BufferDesc bufferToForward = copyBufferDesc(bufferToUse, /* doDup= */ true);
206 bufferToForward.timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() * 1e+3);
207 bufferToForward.deviceId = deviceId;
208
209 // Mark a buffer in-use.
210 mBuffersInUse.push_back(std::move(bufferToUse));
211 l.unlock();
212
213 // Forward a duplicated buffer. This must be done without a lock
214 // because a shared data will be modified in doneWithFrame().
215 std::vector<BufferDesc> packet;
216 packet.push_back(std::move(bufferToForward));
217 it->second->deliverFrame(packet);
218
219 LOG(DEBUG) << deviceId << ": " << (count + 1) << "/" << numberOfFramesToForward
220 << " frames are sent";
221 std::this_thread::sleep_for(33ms); // 30 frames per seconds
222 l.lock();
223 }
224
225 if (mStreamState.find(deviceId) != mStreamState.end()) {
226 mStreamState[deviceId] = StreamState::kStopped;
227 }
228 }
229
initializeBufferPool(size_t requested)230 size_t MockEvsHal::initializeBufferPool(size_t requested) {
231 std::lock_guard lock(mLock);
232 for (auto count = 0; count < requested; ++count) {
233 AHardwareBuffer_Desc desc = {
234 .width = 64,
235 .height = 32,
236 .layers = 1,
237 .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
238 .format = HAL_PIXEL_FORMAT_RGBA_8888,
239 };
240 AHardwareBuffer* ahwb;
241 if (AHardwareBuffer_allocate(&desc, &ahwb) != ::android::NO_ERROR) {
242 LOG(ERROR) << "Failed to allocate AHardwareBuffer";
243 return count;
244 }
245 buffer_handle_t memHandle = AHardwareBuffer_getNativeHandle(ahwb);
246 BufferDesc aBuffer = {
247 .buffer =
248 {
249 .description =
250 {
251 .width = 64,
252 .height = 32,
253 .layers = 1,
254 .usage = BufferUsage::CPU_READ_OFTEN,
255 .format = PixelFormat::RGBA_8888,
256 .stride = 64,
257 },
258 .handle = ::android::dupToAidl(memHandle),
259 },
260 .pixelSizeBytes = 1,
261 .bufferId = count,
262 .deviceId = "Mock EvsCamera",
263 };
264
265 mBufferRecord.insert_or_assign(count, ahwb);
266 mBufferPool.push_back(std::move(aBuffer));
267 }
268
269 return mBufferPool.size();
270 }
271
deinitializeBufferPoolLocked()272 void MockEvsHal::deinitializeBufferPoolLocked() {
273 for (auto&& descriptor : mBuffersInUse) {
274 auto it = mBufferRecord.find(descriptor.bufferId);
275 if (it == mBufferRecord.end()) {
276 LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId;
277 } else {
278 LOG(WARNING) << "Releasing buffer in use, id = " << descriptor.bufferId;
279 AHardwareBuffer_release(it->second);
280 mBufferRecord.erase(it);
281 }
282 }
283 for (auto&& descriptor : mBufferPool) {
284 auto it = mBufferRecord.find(descriptor.bufferId);
285 if (it == mBufferRecord.end()) {
286 LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId;
287 } else {
288 AHardwareBuffer_release(it->second);
289 mBufferRecord.erase(it);
290 }
291 }
292
293 mBuffersInUse.clear();
294 mBufferPool.clear();
295 }
296
configureCameras(size_t n)297 void MockEvsHal::configureCameras(size_t n) {
298 // Initializes a list of the camera parameters each mock camera
299 // supports with their default values.
300 mCameraParams = {{CameraParam::BRIGHTNESS, 80},
301 {CameraParam::CONTRAST, 60},
302 {CameraParam::AUTOGAIN, 3},
303 {CameraParam::AUTO_EXPOSURE, 1}};
304
305 for (auto i = 0; i < n; ++i) {
306 (void)addMockCameraDevice(kMockCameraDeviceNamePrefix + std::to_string(i));
307 }
308 }
309
addMockCameraDevice(const std::string & deviceId)310 bool MockEvsHal::addMockCameraDevice(const std::string& deviceId) {
311 std::shared_ptr<NiceMockEvsCamera> mockCamera =
312 ndk::SharedRefBase::make<NiceMockEvsCamera>(deviceId);
313
314 // For the testing purpose, this method will return
315 // EvsResult::INVALID_ARG if the client returns any buffer with
316 // unknown identifier.
317 ON_CALL(*mockCamera, doneWithFrame)
318 .WillByDefault([this](const std::vector<BufferDesc>& buffers) {
319 size_t returned = 0;
320 std::lock_guard lock(mLock);
321 for (auto& b : buffers) {
322 auto it = std::find_if(mBuffersInUse.begin(), mBuffersInUse.end(),
323 [id = b.bufferId](const BufferDesc& desc) {
324 return id == desc.bufferId;
325 });
326 if (it == mBuffersInUse.end()) {
327 continue;
328 }
329
330 ++returned;
331 mBufferPool.push_back(std::move(*it));
332 mBuffersInUse.erase(it);
333 }
334
335 if (returned > 0) {
336 mBufferAvailableSignal.notify_all();
337 return ndk::ScopedAStatus::ok();
338 } else {
339 return ndk::ScopedAStatus::fromServiceSpecificError(
340 static_cast<int>(EvsResult::INVALID_ARG));
341 }
342 });
343
344 // EVS HAL accepts only a single client; therefore, this method
345 // returns a success always.
346 ON_CALL(*mockCamera, forcePrimaryClient).WillByDefault([](const std::shared_ptr<IEvsDisplay>&) {
347 return ndk::ScopedAStatus::ok();
348 });
349
350 // We return a mock camera descriptor with the metadata but empty vendor
351 // flag.
352 ON_CALL(*mockCamera, getCameraInfo).WillByDefault([deviceId, this](CameraDesc* desc) {
353 CameraDesc mockDesc = {
354 .id = deviceId,
355 .vendorFlags = 0x0,
356 };
357
358 if (!buildCameraMetadata(/* width= */ 640, /* height= */ 480,
359 /* format= */ HAL_PIXEL_FORMAT_RGBA_8888, &mockDesc.metadata)) {
360 return ndk::ScopedAStatus::fromServiceSpecificError(
361 static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
362 }
363
364 *desc = std::move(mockDesc);
365 return ndk::ScopedAStatus::ok();
366 });
367
368 // This method will return a value associated with a given
369 // identifier if exists.
370 ON_CALL(*mockCamera, getExtendedInfo)
371 .WillByDefault([this](int32_t id, std::vector<uint8_t>* v) {
372 auto it = mCameraExtendedInfo.find(id);
373 if (it == mCameraExtendedInfo.end()) {
374 // A requested information does not exist.
375 return ndk::ScopedAStatus::fromServiceSpecificError(
376 static_cast<int>(EvsResult::INVALID_ARG));
377 }
378
379 *v = it->second;
380 return ndk::ScopedAStatus::ok();
381 });
382
383 // This method will return a value of a requested camera parameter
384 // if it is supported by a mock EVS camera.
385 ON_CALL(*mockCamera, getIntParameter)
386 .WillByDefault([this](CameraParam id, std::vector<int32_t>* v) {
387 auto it = mCameraParams.find(id);
388 if (it == mCameraParams.end()) {
389 LOG(ERROR) << "Ignore a request to read an unsupported parameter, " << (int)id;
390 return ndk::ScopedAStatus::fromServiceSpecificError(
391 static_cast<int>(EvsResult::INVALID_ARG));
392 }
393
394 // EVS HAL always returns a single integer value.
395 v->push_back(it->second);
396 return ndk::ScopedAStatus::ok();
397 });
398
399 // This method returns the same range values if a requested camera
400 // parameter is supported by a mock EVS camera.
401 ON_CALL(*mockCamera, getIntParameterRange)
402 .WillByDefault([this](CameraParam id, ParameterRange* range) {
403 auto it = mCameraParams.find(id);
404 if (it == mCameraParams.end()) {
405 return ndk::ScopedAStatus::fromServiceSpecificError(
406 static_cast<int>(EvsResult::INVALID_ARG));
407 }
408
409 // For the testing purpose, this mock EVS HAL always returns the
410 // same values.
411 range->min = kCameraParamDefaultMinValue;
412 range->max = kCameraParamDefaultMaxValue;
413 range->step = kCameraParamDefaultStepValue;
414 return ndk::ScopedAStatus::ok();
415 });
416
417 // This method returns a list of camera parameters supported by a
418 // mock EVS camera.
419 ON_CALL(*mockCamera, getParameterList).WillByDefault([this](std::vector<CameraParam>* list) {
420 for (auto& [k, _] : mCameraParams) {
421 list->push_back(k);
422 }
423 return ndk::ScopedAStatus::ok();
424 });
425
426 // This method behaves exactly the same as getCameraInfo() because
427 // the EVS HAL does not support a concept of the group (or logical)
428 // camera.
429 ON_CALL(*mockCamera, getPhysicalCameraInfo)
430 .WillByDefault([deviceId](const std::string&, CameraDesc* desc) {
431 CameraDesc mockDesc = {
432 .id = deviceId,
433 .vendorFlags = 0x0,
434 .metadata = {},
435 };
436
437 *desc = std::move(mockDesc);
438 return ndk::ScopedAStatus::ok();
439 });
440
441 // This method adds given buffer descriptors to the internal buffer
442 // pool if their identifiers do not conflict to existing ones.
443 ON_CALL(*mockCamera, importExternalBuffers)
444 .WillByDefault([this](const std::vector<BufferDesc>& buffers, int32_t* num) {
445 std::lock_guard l(mLock);
446 size_t count = 0;
447 for (auto i = 0; i < buffers.size(); ++i) {
448 auto it = std::find_if(mBufferPool.begin(), mBufferPool.end(),
449 [&](const BufferDesc& b) {
450 return b.bufferId == buffers[i].bufferId;
451 });
452 if (it != mBufferPool.end()) {
453 // Ignores external buffers with a conflicting
454 // identifier.
455 continue;
456 }
457
458 // TODO(b/235110887): Explicitly copies external buffers
459 // stores them in mBufferPool.
460 //
461 // Temporarily, we count the number of buffers that
462 // identifiers do not conflict with existing buffers.
463 ++count;
464 }
465
466 *num = count;
467 return ndk::ScopedAStatus::ok();
468 });
469
470 ON_CALL(*mockCamera, pauseVideoStream).WillByDefault([]() { return ndk::ScopedAStatus::ok(); });
471
472 ON_CALL(*mockCamera, resumeVideoStream).WillByDefault([]() {
473 return ndk::ScopedAStatus::ok();
474 });
475
476 // This method stores a given vector with id.
477 ON_CALL(*mockCamera, setExtendedInfo)
478 .WillByDefault([this](int32_t id, const std::vector<uint8_t>& v) {
479 mCameraExtendedInfo.insert_or_assign(id, v);
480 return ndk::ScopedAStatus::ok();
481 });
482
483 // This method updates a parameter value if exists.
484 ON_CALL(*mockCamera, setIntParameter)
485 .WillByDefault([this](CameraParam id, int32_t in, std::vector<int32_t>* out) {
486 auto it = mCameraParams.find(id);
487 if (it == mCameraParams.end()) {
488 LOG(ERROR) << "Ignore a request to program an unsupported parameter, "
489 << (int)id;
490 return ndk::ScopedAStatus::fromServiceSpecificError(
491 static_cast<int>(EvsResult::INVALID_ARG));
492 }
493
494 in = in > kCameraParamDefaultMaxValue ? kCameraParamDefaultMaxValue
495 : in < kCameraParamDefaultMinValue ? kCameraParamDefaultMinValue
496 : in;
497 mCameraParams.insert_or_assign(id, in);
498 out->push_back(in);
499
500 return ndk::ScopedAStatus::ok();
501 });
502
503 // We always return a success because EVS HAL does not allow
504 // multiple camera clients exist.
505 ON_CALL(*mockCamera, setPrimaryClient).WillByDefault([]() { return ndk::ScopedAStatus::ok(); });
506
507 // Because EVS HAL does allow multiple camera clients exist, we simply
508 // set the size of the buffer pool.
509 ON_CALL(*mockCamera, setMaxFramesInFlight)
510 .WillByDefault([this, id = mockCamera->getId()](int32_t bufferCount) {
511 std::lock_guard l(mLock);
512 if (bufferCount < kMinimumNumBuffers) {
513 LOG(WARNING) << "Requested buffer pool size is too small to run a camera; "
514 "adjusting the pool size to "
515 << kMinimumNumBuffers;
516 bufferCount = kMinimumNumBuffers;
517 }
518
519 int64_t delta = bufferCount;
520 auto it = mCameraBufferPoolSize.find(id);
521 if (it != mCameraBufferPoolSize.end()) {
522 delta -= it->second;
523 }
524
525 if (!delta) {
526 // No further action required.
527 return ndk::ScopedAStatus::ok();
528 }
529
530 size_t totalSize = mBufferPoolSize + delta;
531 if (totalSize > kMaximumNumBuffers) {
532 LOG(ERROR) << "Requested size, " << totalSize << ", exceeds the limitation.";
533 return ndk::ScopedAStatus::fromServiceSpecificError(
534 static_cast<int>(EvsResult::INVALID_ARG));
535 }
536
537 mBufferPoolSize = totalSize;
538 mCameraBufferPoolSize.insert_or_assign(id, bufferCount);
539 return ndk::ScopedAStatus::ok();
540 });
541
542 // We manage the camera ownership on recency-basis; therefore we simply
543 // replace the client in this method.
544 ON_CALL(*mockCamera, startVideoStream)
545 .WillByDefault(
546 [this, id = mockCamera->getId()](const std::shared_ptr<IEvsCameraStream>& cb) {
547 // TODO(b/235110887): Notifies a camera loss to the current
548 // client.
549 size_t n = 0;
550 {
551 std::lock_guard l(mLock);
552 mCameraClient.insert_or_assign(id, cb);
553 n = mNumberOfFramesToSend;
554 }
555
556 std::lock_guard l(mLock);
557 std::packaged_task<void(MockEvsHal*, size_t, const std::string&)> task(
558 &MockEvsHal::forwardFrames);
559 std::thread t(std::move(task), this, /* numberOfFramesForward= */ n, id);
560 mCameraFrameThread.insert_or_assign(id, std::move(t));
561
562 return ndk::ScopedAStatus::ok();
563 });
564
565 // We simply drop a current client.
566 ON_CALL(*mockCamera, stopVideoStream).WillByDefault([this, id = mockCamera->getId()]() {
567 std::shared_ptr<aidlevs::IEvsCameraStream> cb;
568 std::thread threadToJoin;
569 {
570 std::lock_guard l(mLock);
571 auto state = mStreamState.find(id);
572 if (state == mStreamState.end() || state->second != StreamState::kRunning) {
573 return ndk::ScopedAStatus::ok();
574 }
575
576 auto callback = mCameraClient.find(id);
577 if (callback == mCameraClient.end()) {
578 return ndk::ScopedAStatus::ok();
579 }
580
581 cb = callback->second;
582 callback->second = nullptr;
583 state->second = StreamState::kStopping;
584
585 auto it = mCameraFrameThread.find(id);
586 if (it == mCameraFrameThread.end() || !it->second.joinable()) {
587 return ndk::ScopedAStatus::ok();
588 }
589
590 threadToJoin = std::move(it->second);
591 mCameraFrameThread.erase(it);
592 }
593
594 if (cb) {
595 EvsEventDesc e = {
596 .deviceId = id,
597 .aType = EvsEventType::STREAM_STOPPED,
598 };
599 cb->notify(e);
600 }
601
602 // Join a frame-forward thread
603 threadToJoin.join();
604 return ndk::ScopedAStatus::ok();
605 });
606
607 // We don't take any action because EVS HAL allows only a single camera
608 // client exists at a time.
609 ON_CALL(*mockCamera, unsetPrimaryClient).WillByDefault([]() {
610 return ndk::ScopedAStatus::ok();
611 });
612
613 std::lock_guard l(mLock);
614 mMockEvsCameras.push_back(std::move(mockCamera));
615 mMockDeviceStatus.insert_or_assign(deviceId, DeviceStatusType::CAMERA_AVAILABLE);
616
617 std::vector<DeviceStatus> msg(1);
618 msg[0] = {
619 .id = deviceId,
620 .status = DeviceStatusType::CAMERA_AVAILABLE,
621 };
622 for (auto callback : mDeviceStatusCallbacks) {
623 callback->deviceStatusChanged(msg);
624 }
625
626 return true;
627 }
628
removeMockCameraDevice(const std::string & deviceId)629 void MockEvsHal::removeMockCameraDevice(const std::string& deviceId) {
630 std::lock_guard l(mLock);
631 auto it = mMockDeviceStatus.find(deviceId);
632 if (it == mMockDeviceStatus.end()) {
633 // Nothing to do.
634 return;
635 }
636
637 mMockDeviceStatus[deviceId] = DeviceStatusType::CAMERA_NOT_AVAILABLE;
638
639 std::vector<DeviceStatus> msg(1);
640 msg[0] = {
641 .id = deviceId,
642 .status = DeviceStatusType::CAMERA_NOT_AVAILABLE,
643 };
644 for (auto callback : mDeviceStatusCallbacks) {
645 callback->deviceStatusChanged(msg);
646 }
647 }
648
configureDisplays(size_t n)649 void MockEvsHal::configureDisplays(size_t n) {
650 // Build mock IEvsDisplcy instances
651 std::vector<std::shared_ptr<NiceMockEvsDisplay>> displays(n);
652
653 for (auto i = 0; i < n; ++i) {
654 (void)addMockDisplayDevice(i);
655 }
656 }
657
addMockDisplayDevice(int id)658 bool MockEvsHal::addMockDisplayDevice(int id) {
659 std::shared_ptr<NiceMockEvsDisplay> mockDisplay =
660 ndk::SharedRefBase::make<NiceMockEvsDisplay>();
661
662 ON_CALL(*mockDisplay, getDisplayInfo).WillByDefault([id](DisplayDesc* out) {
663 DisplayDesc desc = {
664 .width = 1920,
665 .height = 1080,
666 .orientation = Rotation::ROTATION_0,
667 .id = "MockDisplay" + std::to_string(id),
668 .vendorFlags = id, // For the testing purpose, we put a display id in the vendor
669 // flag field.
670 };
671 *out = std::move(desc);
672 return ndk::ScopedAStatus::ok();
673 });
674
675 ON_CALL(*mockDisplay, getDisplayState).WillByDefault([this](DisplayState* out) {
676 *out = mCurrentDisplayState;
677 return ndk::ScopedAStatus::ok();
678 });
679
680 ON_CALL(*mockDisplay, getTargetBuffer).WillByDefault([](BufferDesc* out) {
681 (void)out;
682 return ndk::ScopedAStatus::ok();
683 });
684
685 ON_CALL(*mockDisplay, returnTargetBufferForDisplay).WillByDefault([](const BufferDesc& in) {
686 (void)in;
687 return ndk::ScopedAStatus::ok();
688 });
689
690 ON_CALL(*mockDisplay, setDisplayState).WillByDefault([this](DisplayState in) {
691 mCurrentDisplayState = in;
692 return ndk::ScopedAStatus::ok();
693 });
694
695 std::lock_guard l(mLock);
696 mMockEvsDisplays.push_back(std::move(mockDisplay));
697 mMockDeviceStatus.insert_or_assign(std::to_string(id), DeviceStatusType::DISPLAY_AVAILABLE);
698
699 std::vector<DeviceStatus> msg(1);
700 msg[0] = {
701 .id = std::to_string(id),
702 .status = DeviceStatusType::DISPLAY_AVAILABLE,
703 };
704 for (auto callback : mDeviceStatusCallbacks) {
705 callback->deviceStatusChanged(msg);
706 }
707
708 return true;
709 }
710
removeMockDisplayDevice(int id)711 void MockEvsHal::removeMockDisplayDevice(int id) {
712 std::lock_guard l(mLock);
713 auto key = std::to_string(id);
714 auto it = mMockDeviceStatus.find(key);
715 if (it == mMockDeviceStatus.end()) {
716 // Nothing to do.
717 return;
718 }
719
720 mMockDeviceStatus[key] = DeviceStatusType::DISPLAY_NOT_AVAILABLE;
721
722 std::vector<DeviceStatus> msg(1);
723 msg[0] = {
724 .id = key,
725 .status = DeviceStatusType::DISPLAY_NOT_AVAILABLE,
726 };
727 for (auto callback : mDeviceStatusCallbacks) {
728 callback->deviceStatusChanged(msg);
729 }
730 }
731
setNumberOfFramesToSend(size_t n)732 size_t MockEvsHal::setNumberOfFramesToSend(size_t n) {
733 std::lock_guard l(mLock);
734 return mNumberOfFramesToSend = n;
735 }
736
configureEnumerator()737 void MockEvsHal::configureEnumerator() {
738 std::shared_ptr<NiceMockEvsEnumerator> mockEnumerator =
739 ndk::SharedRefBase::make<NiceMockEvsEnumerator>();
740
741 ON_CALL(*mockEnumerator, closeCamera)
742 .WillByDefault([this](const std::shared_ptr<IEvsCamera>& c) {
743 CameraDesc desc;
744 if (!c->getCameraInfo(&desc).isOk()) {
745 // Safely ignore a request to close a camera if we fail to read a
746 // camera descriptor.
747 return ndk::ScopedAStatus::ok();
748 }
749
750 std::lock_guard l(mLock);
751 auto it = mCameraBufferPoolSize.find(desc.id);
752 if (it == mCameraBufferPoolSize.end()) {
753 // Safely ignore a request if we fail to find a corresponding mock
754 // camera.
755 return ndk::ScopedAStatus::ok();
756 }
757
758 mBufferPoolSize -= it->second;
759 if (mBufferPoolSize < 0) {
760 LOG(WARNING) << "mBuffeRPoolSize should not have a negative value, "
761 << mBufferPoolSize;
762 mBufferPoolSize = 0;
763 }
764 mCameraBufferPoolSize.insert_or_assign(desc.id, 0);
765 return ndk::ScopedAStatus::ok();
766 });
767
768 ON_CALL(*mockEnumerator, closeDisplay)
769 .WillByDefault([this]([[maybe_unused]] const std::shared_ptr<IEvsDisplay>& displayObj) {
770 auto pActiveDisplay = mActiveDisplay.lock();
771 if (!pActiveDisplay) {
772 return ndk::ScopedAStatus::fromServiceSpecificError(
773 static_cast<int>(EvsResult::OWNERSHIP_LOST));
774 }
775
776 // Nothing else to do.
777
778 return ndk::ScopedAStatus::ok();
779 });
780
781 ON_CALL(*mockEnumerator, closeUltrasonicsArray)
782 .WillByDefault([](const std::shared_ptr<IEvsUltrasonicsArray>&) {
783 // Mock EVS HAL does not support IEvsUltrasonicsArray.
784 return ndk::ScopedAStatus::ok();
785 });
786
787 ON_CALL(*mockEnumerator, getCameraList).WillByDefault([this](std::vector<CameraDesc>* out) {
788 out->resize(mMockEvsCameras.size());
789
790 for (auto i = 0; i < mMockEvsCameras.size(); ++i) {
791 CameraDesc desc;
792 if (!mMockEvsCameras[i]->getCameraInfo(&desc).isOk()) {
793 LOG(ERROR) << "Failed to retrieve a camera desc";
794 continue;
795 }
796
797 // Inserts a camera record if it does not exist.
798 if (mCameraList.find(desc.id) == mCameraList.end()) {
799 mCameraList.insert_or_assign(desc.id, desc);
800 }
801
802 (*out)[i] = std::move(desc);
803 }
804
805 return ndk::ScopedAStatus::ok();
806 });
807
808 ON_CALL(*mockEnumerator, getDisplayIdList).WillByDefault([this](std::vector<uint8_t>* out) {
809 out->resize(mMockEvsDisplays.size());
810
811 for (auto i = 0; i < mMockEvsDisplays.size(); ++i) {
812 DisplayDesc desc;
813 if (!mMockEvsDisplays[i]->getDisplayInfo(&desc).isOk()) {
814 continue;
815 }
816
817 // MockEvsDisplay contains a display ID in its vendor flags.
818 (*out)[i] = static_cast<uint8_t>(desc.vendorFlags);
819 }
820
821 return ndk::ScopedAStatus::ok();
822 });
823
824 ON_CALL(*mockEnumerator, getDisplayState).WillByDefault([this](DisplayState* out) {
825 *out = mCurrentDisplayState;
826 return ndk::ScopedAStatus::ok();
827 });
828
829 ON_CALL(*mockEnumerator, getStreamList)
830 .WillByDefault([](const CameraDesc& desc, std::vector<Stream>* out) {
831 if (desc.metadata.empty()) {
832 return ndk::ScopedAStatus::ok();
833 }
834
835 camera_metadata_t* p = const_cast<camera_metadata_t*>(
836 reinterpret_cast<const camera_metadata_t*>(desc.metadata.data()));
837 camera_metadata_entry_t entry;
838 if (find_camera_metadata_entry(p, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
839 &entry)) {
840 return ndk::ScopedAStatus::ok();
841 }
842
843 const auto n = calculate_camera_metadata_entry_data_size(
844 get_camera_metadata_tag_type(
845 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
846 entry.count);
847 out->resize(n);
848
849 for (auto i = 0; i < n; ++i) {
850 // ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS is a set of 5
851 // int32_t words.
852 Stream s = {
853 .id = i,
854 .streamType = entry.data.i32[3] ==
855 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
856 ? StreamType::OUTPUT
857 : StreamType::INPUT,
858 .width = entry.data.i32[1],
859 .height = entry.data.i32[2],
860 .format = static_cast<PixelFormat>(entry.data.i32[0]),
861 .usage = BufferUsage::CAMERA_INPUT,
862 .rotation = Rotation::ROTATION_0,
863 };
864
865 (*out)[i] = s;
866 }
867
868 return ndk::ScopedAStatus::ok();
869 });
870
871 ON_CALL(*mockEnumerator, getUltrasonicsArrayList)
872 .WillByDefault([](std::vector<aidlevs::UltrasonicsArrayDesc>*) {
873 // Mock EVS HAL does not support IEvsUltrasonicsArray yet.
874 return ndk::ScopedAStatus::ok();
875 });
876
877 ON_CALL(*mockEnumerator, isHardware).WillByDefault([](bool* flag) {
878 *flag = false;
879 return ndk::ScopedAStatus::ok();
880 });
881
882 ON_CALL(*mockEnumerator, openCamera)
883 .WillByDefault([this](const std::string& id, [[maybe_unused]] const Stream& config,
884 std::shared_ptr<IEvsCamera>* out) {
885 auto it = std::find_if(mMockEvsCameras.begin(), mMockEvsCameras.end(),
886 [id](const std::shared_ptr<NiceMockEvsCamera>& c) {
887 CameraDesc desc;
888 return c->getCameraInfo(&desc).isOk() && desc.id == id;
889 });
890
891 if (it == mMockEvsCameras.end()) {
892 return ndk::ScopedAStatus::fromServiceSpecificError(
893 static_cast<int>(EvsResult::INVALID_ARG));
894 }
895
896 auto instance = mCameraList.find(id); // Guaranteed to exist always.
897 instance->second.activeInstance = *it;
898 *out = IEvsCamera::fromBinder((*it)->asBinder());
899 return ndk::ScopedAStatus::ok();
900 });
901
902 ON_CALL(*mockEnumerator, openDisplay)
903 .WillByDefault([this](int32_t id, std::shared_ptr<IEvsDisplay>* out) {
904 if (id == kExclusiveDisplayId) {
905 if (mDisplayOwnedExclusively && !mActiveDisplay.expired()) {
906 return ndk::ScopedAStatus::fromServiceSpecificError(
907 static_cast<int>(EvsResult::RESOURCE_BUSY));
908 }
909
910 DisplayDesc desc;
911 if (!mMockEvsDisplays[0]->getDisplayInfo(&desc).isOk()) {
912 return ndk::ScopedAStatus::fromServiceSpecificError(
913 static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
914 }
915 id = desc.vendorFlags; // the first display in the list is
916 // the main display.
917 mDisplayOwnedExclusively = true;
918 }
919
920 auto it = std::find_if(mMockEvsDisplays.begin(), mMockEvsDisplays.end(),
921 [id](const std::shared_ptr<NiceMockEvsDisplay>& d) {
922 DisplayDesc desc;
923 return d->getDisplayInfo(&desc).isOk() &&
924 desc.vendorFlags == id;
925 });
926
927 if (it == mMockEvsDisplays.end()) {
928 return ndk::ScopedAStatus::fromServiceSpecificError(
929 static_cast<int>(EvsResult::INVALID_ARG));
930 }
931
932 mActiveDisplay = *it;
933 mCurrentDisplayState = DisplayState::NOT_VISIBLE;
934 *out = IEvsDisplay::fromBinder((*it)->asBinder());
935 return ndk::ScopedAStatus::ok();
936 });
937
938 ON_CALL(*mockEnumerator, openUltrasonicsArray)
939 .WillByDefault([](const std::string&, std::shared_ptr<IEvsUltrasonicsArray>*) {
940 // Mock EVS HAL does not support IEvsUltrasonicsArray yet.
941 return ndk::ScopedAStatus::ok();
942 });
943
944 ON_CALL(*mockEnumerator, registerStatusCallback)
945 .WillByDefault([this](const std::shared_ptr<IEvsEnumeratorStatusCallback>& cb) {
946 if (!cb) {
947 return ndk::ScopedAStatus::ok();
948 }
949
950 std::lock_guard l(mLock);
951 mDeviceStatusCallbacks.insert(cb);
952 return ndk::ScopedAStatus::ok();
953 });
954
955 mMockEvsEnumerator = std::move(mockEnumerator);
956 }
957
958 } // namespace aidl::android::automotive::evs::implementation
959