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 "MockHidlEvsHal_1_0.h"
18
19 #include <android-base/logging.h>
20 #include <hardware/gralloc.h>
21 #include <ui/DisplayMode.h>
22 #include <utils/SystemClock.h>
23
24 #include <functional>
25 #include <future>
26
27 #include <system/graphics-base.h>
28
29 namespace {
30
31 using ::android::hardware::automotive::evs::V1_0::BufferDesc;
32 using ::android::hardware::automotive::evs::V1_0::CameraDesc;
33 using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
34 using ::android::hardware::automotive::evs::V1_0::DisplayState;
35 using ::android::hardware::automotive::evs::V1_0::EvsResult;
36 using ::android::hardware::automotive::evs::V1_0::IEvsCamera;
37 using ::android::hardware::automotive::evs::V1_0::IEvsCameraStream;
38 using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
39 using ::android::hardware::automotive::evs::V1_0::IEvsEnumerator;
40 using ::std::chrono_literals::operator""ms;
41 using ::std::chrono_literals::operator""s;
42
43 inline constexpr char kMockCameraDeviceNamePrefix[] = "/dev/mockcamera";
44 inline constexpr size_t kMinimumNumBuffers = 2;
45 inline constexpr size_t kMaximumNumBuffers = 10;
46
47 } // namespace
48
49 namespace aidl::android::automotive::evs::implementation {
50
~MockHidlEvsHal_1_0()51 MockHidlEvsHal_1_0::~MockHidlEvsHal_1_0() {
52 std::lock_guard lock(mLock);
53 for (auto& [id, state] : mStreamState) {
54 auto it = mCameraFrameThread.find(id);
55 if (it == mCameraFrameThread.end() || !it->second.joinable()) {
56 continue;
57 }
58
59 state = StreamState::kStopping;
60 it->second.join();
61 }
62
63 deinitializeBufferPoolLocked();
64 mCameraClient.clear();
65 }
66
getEnumerator()67 ::android::sp<IEvsEnumerator> MockHidlEvsHal_1_0::getEnumerator() {
68 if (!mMockHidlEvsEnumerator) {
69 LOG(ERROR) << "MockHidlEvsHal_1_0 has not initialized yet.";
70 return nullptr;
71 }
72
73 return mMockHidlEvsEnumerator;
74 }
75
initialize()76 void MockHidlEvsHal_1_0::initialize() {
77 initializeBufferPool(kMaximumNumBuffers);
78 configureCameras(mNumCameras);
79 configureDisplays(mNumDisplays);
80 configureEnumerator();
81 }
82
forwardFrames(size_t numberOfFramesToForward,const std::string & deviceId)83 void MockHidlEvsHal_1_0::forwardFrames(size_t numberOfFramesToForward,
84 const std::string& deviceId) {
85 std::unique_lock l(mLock);
86 ::android::base::ScopedLockAssertion lock_assertion(mLock);
87 auto it = mStreamState.find(deviceId);
88 if (it != mStreamState.end() && it->second != StreamState::kStopped) {
89 LOG(WARNING) << "A mock video stream is already active.";
90 return;
91 }
92 mStreamState.insert_or_assign(deviceId, StreamState::kRunning);
93
94 for (size_t count = 0;
95 mStreamState[deviceId] == StreamState::kRunning && count < numberOfFramesToForward;
96 ++count) {
97 if (mBufferPool.empty()) {
98 if (!mBufferAvailableSignal.wait_for(l, /* rel_time= */ 10s, [this]() REQUIRES(mLock) {
99 // Waiting for a buffer to use.
100 return !mBufferPool.empty();
101 })) {
102 LOG(ERROR) << "Buffer timeout; " << count << "/" << numberOfFramesToForward
103 << " are sent.";
104 break;
105 }
106 }
107
108 auto it = mCameraClient.find(deviceId);
109 if (it == mCameraClient.end() || it->second == nullptr) {
110 LOG(ERROR) << "Failed to forward a frame as no active recipient exists; " << count
111 << "/" << numberOfFramesToForward << " are sent.";
112 break;
113 }
114
115 BufferDesc bufferToForward = mBufferPool.back();
116 mBufferPool.pop_back();
117
118 // Mark a buffer in-use.
119 mBuffersInUse.push_back(bufferToForward);
120 l.unlock();
121
122 // Forward a duplicated buffer. This must be done without a lock
123 // because a shared data will be modified in doneWithFrame().
124 it->second->deliverFrame(bufferToForward);
125
126 LOG(DEBUG) << deviceId << ": " << (count + 1) << "/" << numberOfFramesToForward
127 << " frames are sent";
128 std::this_thread::sleep_for(33ms); // 30 frames per seconds
129 l.lock();
130 }
131
132 if (mStreamState.find(deviceId) != mStreamState.end()) {
133 mStreamState[deviceId] = StreamState::kStopped;
134 }
135 }
136
initializeBufferPool(size_t requested)137 size_t MockHidlEvsHal_1_0::initializeBufferPool(size_t requested) {
138 std::lock_guard lock(mLock);
139 for (auto count = 0; count < requested; ++count) {
140 AHardwareBuffer_Desc desc = {
141 .width = 64,
142 .height = 32,
143 .layers = 1,
144 .format = HAL_PIXEL_FORMAT_RGBA_8888,
145 .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
146 };
147 AHardwareBuffer* ahwb;
148 if (AHardwareBuffer_allocate(&desc, &ahwb) != ::android::NO_ERROR) {
149 LOG(ERROR) << "Failed to allocate AHardwareBuffer";
150 return count;
151 }
152
153 BufferDesc aBuffer = {
154 .width = desc.width,
155 .height = desc.height,
156 .stride = 256, // 4 bytes per pixel.
157 .pixelSize = 4,
158 .format = desc.format,
159 .usage = static_cast<uint32_t>(desc.usage),
160 .bufferId = static_cast<uint32_t>(count),
161 .memHandle = AHardwareBuffer_getNativeHandle(ahwb),
162 };
163 mBufferRecord.insert_or_assign(count, ahwb);
164 mBufferPool.push_back(std::move(aBuffer));
165 }
166
167 return mBufferPool.size();
168 }
169
deinitializeBufferPoolLocked()170 void MockHidlEvsHal_1_0::deinitializeBufferPoolLocked() {
171 for (auto&& descriptor : mBuffersInUse) {
172 auto it = mBufferRecord.find(descriptor.bufferId);
173 if (it == mBufferRecord.end()) {
174 LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId;
175 } else {
176 LOG(WARNING) << "Releasing buffer in use, id = " << descriptor.bufferId;
177 AHardwareBuffer_release(it->second);
178 mBufferRecord.erase(it);
179 }
180 }
181
182 for (auto&& descriptor : mBufferPool) {
183 auto it = mBufferRecord.find(descriptor.bufferId);
184 if (it == mBufferRecord.end()) {
185 LOG(WARNING) << "Ignoring unknown buffer id, " << descriptor.bufferId;
186 } else {
187 AHardwareBuffer_release(it->second);
188 mBufferRecord.erase(it);
189 }
190 }
191
192 mBuffersInUse.clear();
193 mBufferPool.clear();
194 }
195
configureCameras(size_t n)196 void MockHidlEvsHal_1_0::configureCameras(size_t n) {
197 // Initializes a list of the camera parameters each mock camera
198 // supports with their default values.
199 for (auto i = 0; i < n; ++i) {
200 (void)addMockCameraDevice(kMockCameraDeviceNamePrefix + std::to_string(i));
201 }
202 }
203
addMockCameraDevice(const std::string & deviceId)204 bool MockHidlEvsHal_1_0::addMockCameraDevice(const std::string& deviceId) {
205 ::android::sp<NiceMockHidlEvsCamera> mockCamera =
206 new (std::nothrow) NiceMockHidlEvsCamera(deviceId);
207
208 // For the testing purpose, this method will return
209 // EvsResult::INVALID_ARG if the client returns any buffer with
210 // unknown identifier.
211 ON_CALL(*mockCamera, doneWithFrame)
212 .WillByDefault([this](const hidlevs::V1_0::BufferDesc& buffer) {
213 std::lock_guard lock(mLock);
214 auto it = std::find_if(mBuffersInUse.begin(), mBuffersInUse.end(),
215 [id = buffer.bufferId](const BufferDesc& desc) {
216 return id == desc.bufferId;
217 });
218 if (it == mBuffersInUse.end()) {
219 return ::android::hardware::Void();
220 }
221
222 mBufferPool.push_back(std::move(*it));
223 mBuffersInUse.erase(it);
224
225 return ::android::hardware::Void();
226 });
227
228 // We return a mock camera descriptor with the metadata but empty vendor
229 // flag.
230 ON_CALL(*mockCamera, getCameraInfo)
231 .WillByDefault([deviceId](MockHidlEvsCamera::getCameraInfo_cb callback) {
232 hidlevs::V1_0::CameraDesc mockDesc = {
233 .cameraId = deviceId,
234 .vendorFlags = 0x0,
235 };
236
237 callback(mockDesc);
238 return ::android::hardware::Void();
239 });
240
241 // This method will return a value associated with a given
242 // identifier if exists.
243 ON_CALL(*mockCamera, getExtendedInfo).WillByDefault([this](uint32_t id) {
244 auto it = mCameraExtendedInfo.find(id);
245 if (it == mCameraExtendedInfo.end()) {
246 // A requested information does not exist.
247 return 0;
248 }
249
250 if (it->second.size() < 4) {
251 // Stored information is in an invalid size.
252 return 0;
253 }
254
255 int value = *(reinterpret_cast<int32_t*>(it->second.data()));
256 return value;
257 });
258
259 // This method stores a given vector with id.
260 ON_CALL(*mockCamera, setExtendedInfo).WillByDefault([this](uint32_t id, int32_t v) {
261 ::android::hardware::hidl_vec<uint8_t> value;
262 value.resize(sizeof(v));
263 *(reinterpret_cast<int32_t*>(value.data())) = v;
264 mCameraExtendedInfo.insert_or_assign(id, value);
265 return EvsResult::OK;
266 });
267
268 // Because EVS HAL does allow multiple camera clients exist, we simply
269 // set the size of the buffer pool.
270 ON_CALL(*mockCamera, setMaxFramesInFlight)
271 .WillByDefault([this, id = mockCamera->getId()](uint32_t bufferCount) {
272 std::lock_guard l(mLock);
273 if (bufferCount < kMinimumNumBuffers) {
274 LOG(WARNING) << "Requested buffer pool size is too small to run a camera; "
275 "adjusting the pool size to "
276 << kMinimumNumBuffers;
277 bufferCount = kMinimumNumBuffers;
278 }
279
280 int64_t delta = bufferCount;
281 auto it = mCameraBufferPoolSize.find(id);
282 if (it != mCameraBufferPoolSize.end()) {
283 delta -= it->second;
284 }
285
286 if (delta == 0) {
287 // No further action required.
288 return EvsResult::OK;
289 }
290
291 size_t totalSize = mBufferPoolSize + delta;
292 if (totalSize > kMaximumNumBuffers) {
293 LOG(ERROR) << "Requested size, " << totalSize << ", exceeds the limitation.";
294 return EvsResult::INVALID_ARG;
295 }
296
297 mBufferPoolSize = totalSize;
298 mCameraBufferPoolSize.insert_or_assign(id, bufferCount);
299 return EvsResult::OK;
300 });
301
302 // We manage the camera ownership on recency-basis; therefore we simply
303 // replace the client in this method.
304 ON_CALL(*mockCamera, startVideoStream)
305 .WillByDefault([this, id = mockCamera->getId()](
306 const ::android::sp<hidlevs::V1_0::IEvsCameraStream>& cb) {
307 // TODO(b/235110887): Notifies a camera loss to the current
308 // client.
309 size_t n = 0;
310 {
311 std::lock_guard l(mLock);
312 mCameraClient.insert_or_assign(id, cb);
313 n = mNumberOfFramesToSend;
314 }
315
316 std::packaged_task<void(MockHidlEvsHal_1_0*, size_t, const std::string&)> task(
317 &MockHidlEvsHal_1_0::forwardFrames);
318 std::thread t(std::move(task), this, /* numberOfFramesForward= */ n, id);
319 std::lock_guard l(mLock);
320 mCameraFrameThread.insert_or_assign(id, std::move(t));
321
322 return EvsResult::OK;
323 });
324
325 // We simply drop a current client.
326 ON_CALL(*mockCamera, stopVideoStream).WillByDefault([this, id = mockCamera->getId()]() {
327 ::android::sp<IEvsCameraStream> cb;
328 std::thread threadToJoin;
329 {
330 std::lock_guard l(mLock);
331 auto state = mStreamState.find(id);
332 if (state == mStreamState.end() || state->second != StreamState::kRunning) {
333 return ::android::hardware::Void();
334 }
335
336 auto callback = mCameraClient.find(id);
337 if (callback == mCameraClient.end()) {
338 return ::android::hardware::Void();
339 }
340
341 cb = callback->second;
342 callback->second = nullptr;
343 state->second = StreamState::kStopping;
344
345 auto it = mCameraFrameThread.find(id);
346 if (it == mCameraFrameThread.end() || !it->second.joinable()) {
347 return ::android::hardware::Void();
348 }
349
350 threadToJoin = std::move(it->second);
351 mCameraFrameThread.erase(it);
352 }
353
354 if (cb) {
355 cb->deliverFrame({});
356 }
357
358 // Join a frame-forward thread
359 threadToJoin.join();
360 return ::android::hardware::Void();
361 });
362
363 std::lock_guard l(mLock);
364 mMockHidlEvsCameras.push_back(std::move(mockCamera));
365 mMockDeviceStatus.insert_or_assign(deviceId, true);
366
367 return true;
368 }
369
removeMockCameraDevice(const std::string & deviceId)370 void MockHidlEvsHal_1_0::removeMockCameraDevice(const std::string& deviceId) {
371 std::lock_guard l(mLock);
372 auto it = mMockDeviceStatus.find(deviceId);
373 if (it == mMockDeviceStatus.end()) {
374 // Nothing to do.
375 return;
376 }
377
378 mMockDeviceStatus[deviceId] = false;
379 }
380
configureDisplays(size_t n)381 void MockHidlEvsHal_1_0::configureDisplays(size_t n) {
382 // Build mock IEvsDisplcy instances
383 std::vector<::android::sp<NiceMockHidlEvsDisplay>> displays(n);
384
385 for (auto i = 0; i < n; ++i) {
386 (void)addMockDisplayDevice(i);
387 }
388 }
389
addMockDisplayDevice(int id)390 bool MockHidlEvsHal_1_0::addMockDisplayDevice(int id) {
391 ::android::sp<NiceMockHidlEvsDisplay> mockDisplay = new (std::nothrow) NiceMockHidlEvsDisplay();
392
393 ON_CALL(*mockDisplay, getDisplayInfo)
394 .WillByDefault([id](MockHidlEvsDisplay::getDisplayInfo_cb callback) {
395 DisplayDesc desc = {
396 .displayId = "MockDisplay" + std::to_string(id),
397 // For the testing purpose, we put a display id in the vendor
398 // flag field.
399 .vendorFlags = static_cast<uint32_t>(id),
400 };
401
402 callback(desc);
403 return ::android::hardware::Void();
404 });
405
406 ON_CALL(*mockDisplay, getDisplayState).WillByDefault([this]() { return mCurrentDisplayState; });
407
408 ON_CALL(*mockDisplay, getTargetBuffer)
409 .WillByDefault([](MockHidlEvsDisplay::getTargetBuffer_cb callback) {
410 // TODO(b/263438927): implement this method.
411 callback({});
412 return ::android::hardware::Void();
413 });
414
415 ON_CALL(*mockDisplay, returnTargetBufferForDisplay)
416 .WillByDefault([](const hidlevs::V1_0::BufferDesc& in) {
417 // TODO(b/263438927): implement this method.
418 (void)in;
419 return EvsResult::OK;
420 });
421
422 ON_CALL(*mockDisplay, setDisplayState).WillByDefault([this](DisplayState in) {
423 mCurrentDisplayState = in;
424 return EvsResult::OK;
425 });
426
427 std::lock_guard l(mLock);
428 mMockHidlEvsDisplays.push_back(std::move(mockDisplay));
429 mMockDeviceStatus.insert_or_assign(std::to_string(id), true);
430
431 return true;
432 }
433
removeMockDisplayDevice(int id)434 void MockHidlEvsHal_1_0::removeMockDisplayDevice(int id) {
435 std::lock_guard l(mLock);
436 auto key = std::to_string(id);
437 auto it = mMockDeviceStatus.find(key);
438 if (it == mMockDeviceStatus.end()) {
439 // Nothing to do.
440 return;
441 }
442
443 mMockDeviceStatus[key] = false;
444 }
445
setNumberOfFramesToSend(size_t n)446 size_t MockHidlEvsHal_1_0::setNumberOfFramesToSend(size_t n) {
447 std::lock_guard l(mLock);
448 return mNumberOfFramesToSend = n;
449 }
450
configureEnumerator()451 void MockHidlEvsHal_1_0::configureEnumerator() {
452 ::android::sp<NiceMockHidlEvsEnumerator_1_0> mockEnumerator =
453 new (std::nothrow) NiceMockHidlEvsEnumerator_1_0();
454
455 ON_CALL(*mockEnumerator, closeCamera)
456 .WillByDefault([this](const ::android::sp<hidlevs::V1_0::IEvsCamera>& handle) {
457 ::android::sp<IEvsCamera> c = IEvsCamera::castFrom(handle);
458 CameraDesc desc;
459 c->getCameraInfo([&desc](auto& read) { desc = read; });
460
461 std::lock_guard l(mLock);
462 auto it = mCameraBufferPoolSize.find(desc.cameraId);
463 if (it == mCameraBufferPoolSize.end()) {
464 // Safely ignore a request if we fail to find a corresponding mock
465 // camera.
466 return ::android::hardware::Void();
467 }
468
469 mBufferPoolSize -= it->second;
470 if (mBufferPoolSize < 0) {
471 LOG(WARNING) << "mBuffeRPoolSize should not have a negative value, "
472 << mBufferPoolSize;
473 mBufferPoolSize = 0;
474 }
475 mCameraBufferPoolSize.insert_or_assign(desc.cameraId, 0);
476 return ::android::hardware::Void();
477 });
478
479 ON_CALL(*mockEnumerator, closeDisplay)
480 .WillByDefault([this]([[maybe_unused]] const ::android::sp<hidlevs::V1_0::IEvsDisplay>&
481 displayObj) {
482 auto pActiveDisplay = mActiveDisplay.promote();
483 if (!pActiveDisplay) {
484 LOG(WARNING) << "Got a request to close a display already destroyed.";
485 }
486
487 // Nothing else to do.
488 return ::android::hardware::Void();
489 });
490
491 ON_CALL(*mockEnumerator, getCameraList)
492 .WillByDefault([this](MockHidlEvsEnumerator_1_0::getCameraList_cb callback) {
493 ::android::hardware::hidl_vec<hidlevs::V1_0::CameraDesc> list(
494 mMockHidlEvsCameras.size());
495
496 for (auto i = 0; i < mMockHidlEvsCameras.size(); ++i) {
497 mMockHidlEvsCameras[i]->getCameraInfo([&](auto& desc) { list[i] = desc; });
498
499 // Inserts a camera record if it does not exist.
500 if (mCameraList.find(list[i].cameraId) == mCameraList.end()) {
501 mCameraList.insert_or_assign(list[i].cameraId, list[i]);
502 }
503 }
504
505 callback(list);
506 return ::android::hardware::Void();
507 });
508
509 ON_CALL(*mockEnumerator, getDisplayState).WillByDefault([this]() {
510 return mCurrentDisplayState;
511 });
512
513 ON_CALL(*mockEnumerator, openCamera)
514 .WillByDefault([this](const ::android::hardware::hidl_string& id)
515 -> ::android::sp<hidlevs::V1_0::IEvsCamera> {
516 auto it = std::find_if(mMockHidlEvsCameras.begin(), mMockHidlEvsCameras.end(),
517 [id](const ::android::sp<NiceMockHidlEvsCamera>& c) {
518 hidlevs::V1_0::CameraDesc desc;
519 c->getCameraInfo([&desc](auto& read) { desc = read; });
520 return desc.cameraId == id;
521 });
522
523 if (it == mMockHidlEvsCameras.end()) {
524 return nullptr;
525 }
526
527 auto instance = mCameraList.find(id); // Guaranteed to exist always.
528 instance->second.activeInstance = *it;
529 return *it;
530 });
531
532 ON_CALL(*mockEnumerator, openDisplay).WillByDefault([this]() -> ::android::sp<IEvsDisplay> {
533 // This method returns the first display always.
534 if (mMockHidlEvsDisplays.empty()) {
535 return nullptr;
536 }
537
538 mActiveDisplay = mMockHidlEvsDisplays[0];
539 mCurrentDisplayState = DisplayState::NOT_VISIBLE;
540 return mMockHidlEvsDisplays[0];
541 });
542
543 mMockHidlEvsEnumerator = std::move(mockEnumerator);
544 }
545
546 } // namespace aidl::android::automotive::evs::implementation
547