blob: 951937a147f916ea0ad7855971027dd3ef6dbb6d [file] [log] [blame]
Tanmay Patilb97cceb2020-02-07 16:48:39 -08001/*
2 * Copyright 2020 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 "EvsUltrasonicsArray.h"
18
19#include <android-base/logging.h>
20#include <hidlmemory/mapping.h>
21#include <log/log.h>
22#include <time.h>
23#include <utils/SystemClock.h>
24#include <utils/Timers.h>
25
26namespace android {
27namespace hardware {
28namespace automotive {
29namespace evs {
30namespace V1_1 {
31namespace implementation {
32
33// Arbitrary limit on number of data frames allowed to be allocated
34// Safeguards against unreasonable resource consumption and provides a testable limit
35const unsigned int kMaximumDataFramesInFlight = 100;
36
37const uint32_t kMaxReadingsPerSensor = 5;
38const uint32_t kMaxReceiversCount = 3;
39
40const unsigned int kSharedMemoryMaxSize =
41 kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float);
42
43// Target frame rate in frames per second.
44const int kTargetFrameRate = 10;
45
46namespace {
47
Changyeon Jocb877f92020-08-06 13:14:43 -070048void fillMockArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
Tanmay Patilb97cceb2020-02-07 16:48:39 -080049 arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor;
50 arrayDesc.maxReceiversCount = kMaxReceiversCount;
51
52 const int kSensorCount = 3;
53 const float kMaxRange = 4000; // 4 metres.
54 const float kAngleOfMeasurement = 0.261799; // 15 degrees.
55
56 std::vector<UltrasonicSensor> sensors(kSensorCount);
57
58 // Sensor pointing forward on left side of front bumper.
59 sensors[0].maxRange = kMaxRange;
60 sensors[0].angleOfMeasurement = kAngleOfMeasurement;
61 sensors[0].pose = {{1, 0, 0, 0}, {-1000, 2000, 200}};
62
63 // Sensor pointing forward on center of front bumper.
64 sensors[1].maxRange = kMaxRange;
65 sensors[1].angleOfMeasurement = kAngleOfMeasurement;
66 sensors[1].pose = {{1, 0, 0, 0}, {0, 2000, 200}};
67
68 // Sensor pointing forward on right side of front bumper.
69 sensors[2].maxRange = kMaxRange;
70 sensors[2].angleOfMeasurement = kAngleOfMeasurement;
71 sensors[2].pose = {{1, 0, 0, 0}, {1000, 2000, 200}};
72
73 arrayDesc.sensors = sensors;
74}
75
76// Struct used by SerializeWaveformData().
77struct WaveformData {
78 uint8_t receiverId;
79 std::vector<std::pair<float, float>> readings;
80};
81
82// Serializes data provided in waveformDataList to a shared memory data pointer.
83// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
84void SerializeWaveformData(const std::vector<WaveformData>& waveformDataList, uint8_t* pData) {
85 for (auto& waveformData : waveformDataList) {
86 // Set Id
87 memcpy(pData, &waveformData.receiverId, sizeof(uint8_t));
88 pData += sizeof(uint8_t);
89
90 for (auto& reading : waveformData.readings) {
91 // Set the time of flight.
92 memcpy(pData, &reading.first, sizeof(float));
93 pData += sizeof(float);
94
95 // Set the resonance.
96 memcpy(pData, &reading.second, sizeof(float));
97 pData += sizeof(float);
98 }
99 }
100}
101
Changyeon Jocb877f92020-08-06 13:14:43 -0700102// Fills dataFrameDesc with mock data.
103bool fillMockDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800104 dataFrameDesc.timestampNs = elapsedRealtimeNano();
105
106 const std::vector<uint8_t> transmittersIdList = {0};
107 dataFrameDesc.transmittersIdList = transmittersIdList;
108
109 const std::vector<uint8_t> recvIdList = {0, 1, 2};
110 dataFrameDesc.receiversIdList = recvIdList;
111
112 const std::vector<uint32_t> receiversReadingsCountList = {2, 2, 4};
113 dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList;
114
115 const std::vector<WaveformData> waveformDataList = {
Changyeon Jo33ba66b2022-01-16 16:33:52 -0800116 {recvIdList[0], {{1000, 0.1f}, {2000, 0.8f}}},
117 {recvIdList[1], {{1000, 0.1f}, {2000, 1.0f}}},
118 {recvIdList[2], {{1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f}}}};
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800119
120 if (pIMemory.get() == nullptr) {
121 return false;
122 }
123
124 uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
125
126 pIMemory->update();
127 SerializeWaveformData(waveformDataList, pData);
128 pIMemory->commit();
129
130 return true;
131}
132
133} // namespace
134
135EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
136 : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
137 LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
138
Changyeon Jocb877f92020-08-06 13:14:43 -0700139 // Set up mock data for description.
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800140 mArrayDesc.ultrasonicsArrayId = deviceName;
Changyeon Jocb877f92020-08-06 13:14:43 -0700141 fillMockArrayDesc(mArrayDesc);
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800142
143 // Assign allocator.
144 mShmemAllocator = IAllocator::getService("ashmem");
145 if (mShmemAllocator.get() == nullptr) {
146 LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
147 }
148}
149
150sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
151 return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
152}
153
154EvsUltrasonicsArray::~EvsUltrasonicsArray() {
155 LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
156 forceShutdown();
157}
158
159// This gets called if another caller "steals" ownership of the ultrasonic array.
160void EvsUltrasonicsArray::forceShutdown() {
161 LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown";
162
163 // Make sure our output stream is cleaned up
164 // (It really should be already)
165 stopStream();
166
167 // Claim the lock while we work on internal state
168 std::lock_guard<std::mutex> lock(mAccessLock);
169
170 // Drop all the data frames we've been using
171 for (auto&& dataFrame : mDataFrames) {
172 if (dataFrame.inUse) {
173 LOG(ERROR) << "Error - releasing data frame despite remote ownership";
174 }
175 dataFrame.sharedMemory.clear();
176 }
177 mDataFrames.clear();
178
179 // Put this object into an unrecoverable error state since somebody else
180 // is going to own the underlying ultrasonic array now
181 mStreamState = DEAD;
182}
183
Changyeon Jocb877f92020-08-06 13:14:43 -0700184UltrasonicsArrayDesc EvsUltrasonicsArray::GetMockArrayDesc(const char* deviceName) {
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800185 UltrasonicsArrayDesc ultrasonicsArrayDesc;
186 ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
Changyeon Jocb877f92020-08-06 13:14:43 -0700187 fillMockArrayDesc(ultrasonicsArrayDesc);
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800188 return ultrasonicsArrayDesc;
189}
190
191Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) {
192 LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo";
193
194 // Return the description for the get info callback.
195 _get_info_cb(mArrayDesc);
196
197 return Void();
198}
199
200Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) {
201 LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight";
202
203 // Lock mutex for performing changes to available frames.
204 std::lock_guard<std::mutex> lock(mAccessLock);
205
206 // We cannot function without at least one buffer to send data.
207 if (bufferCount < 1) {
208 LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested";
209 return EvsResult::INVALID_ARG;
210 }
211
212 // Update our internal state of buffer count.
213 if (setAvailableFrames_Locked(bufferCount)) {
214 return EvsResult::OK;
215 } else {
216 return EvsResult::BUFFER_NOT_AVAILABLE;
217 }
218
219 return EvsResult::OK;
220}
221
222Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) {
223 LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame";
224
225 std::lock_guard<std::mutex> lock(mAccessLock);
226
227 if (dataFrameDesc.dataFrameId >= mDataFrames.size()) {
228 LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId "
229 << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")";
230 return Void();
231 }
232
233 if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) {
234 LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId
235 << "which is already free";
236 return Void();
237 }
238
239 // Mark the frame as available
240 mDataFrames[dataFrameDesc.dataFrameId].inUse = false;
241 mFramesInUse--;
242
243 // If this frame's index is high in the array, try to move it down
244 // to improve locality after mFramesAllowed has been reduced.
245 if (dataFrameDesc.dataFrameId >= mFramesAllowed) {
246 // Find an empty slot lower in the array (which should always exist in this case)
247 for (auto&& dataFrame : mDataFrames) {
248 if (!dataFrame.sharedMemory.IsValid()) {
249 dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory;
250 mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear();
251 return Void();
252 }
253 }
254 }
255
256 return Void();
257}
258
259Return<EvsResult> EvsUltrasonicsArray::startStream(
260 const ::android::sp<IEvsUltrasonicsArrayStream>& stream) {
261 LOG(DEBUG) << "EvsUltrasonicsArray startStream";
262
263 std::lock_guard<std::mutex> lock(mAccessLock);
264
265 if (mStreamState != STOPPED) {
266 LOG(ERROR) << "ignoring startStream call when a stream is already running.";
267 return EvsResult::STREAM_ALREADY_RUNNING;
268 }
269
270 // If the client never indicated otherwise, configure ourselves for a single streaming buffer
271 if (mFramesAllowed < 1) {
272 if (!setAvailableFrames_Locked(1)) {
273 LOG(ERROR)
274 << "Failed to start stream because we couldn't get shared memory data buffer";
275 return EvsResult::BUFFER_NOT_AVAILABLE;
276 }
277 }
278
279 // Record the user's callback for use when we have a frame ready
280 mStream = stream;
281
282 // Start the frame generation thread
283 mStreamState = RUNNING;
284 mCaptureThread = std::thread([this]() { generateDataFrames(); });
285
286 return EvsResult::OK;
287}
288
289Return<void> EvsUltrasonicsArray::stopStream() {
290 LOG(DEBUG) << "EvsUltrasonicsArray stopStream";
291
292 bool streamStateStopping = false;
293 {
294 std::lock_guard<std::mutex> lock(mAccessLock);
295 if (mStreamState == RUNNING) {
296 // Tell the GenerateFrames loop we want it to stop
297 mStreamState = STOPPING;
298 streamStateStopping = true;
299 }
300 }
301
302 if (streamStateStopping) {
303 // Block outside the mutex until the "stop" flag has been acknowledged
304 // We won't send any more frames, but the client might still get some already in flight
305 LOG(DEBUG) << "Waiting for stream thread to end...";
306 mCaptureThread.join();
307 }
308
309 {
310 std::lock_guard<std::mutex> lock(mAccessLock);
311 mStreamState = STOPPED;
312 mStream = nullptr;
313 LOG(DEBUG) << "Stream marked STOPPED.";
314 }
315
316 return Void();
317}
318
319bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) {
320 if (bufferCount < 1) {
321 LOG(ERROR) << "Ignoring request to set buffer count to zero";
322 return false;
323 }
324 if (bufferCount > kMaximumDataFramesInFlight) {
325 LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
326 return false;
327 }
328
329 // Is an increase required?
330 if (mFramesAllowed < bufferCount) {
331 // An increase is required
332 unsigned needed = bufferCount - mFramesAllowed;
333 LOG(INFO) << "Number of data frame buffers to add: " << needed;
334
335 unsigned added = increaseAvailableFrames_Locked(needed);
336 if (added != needed) {
337 // If we didn't add all the frames we needed, then roll back to the previous state
338 LOG(ERROR) << "Rolling back to previous frame queue size";
339 decreaseAvailableFrames_Locked(added);
340 return false;
341 }
342 } else if (mFramesAllowed > bufferCount) {
343 // A decrease is required
344 unsigned framesToRelease = mFramesAllowed - bufferCount;
345 LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease;
346
347 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
348 if (released != framesToRelease) {
349 // This shouldn't happen with a properly behaving client because the client
350 // should only make this call after returning sufficient outstanding buffers
351 // to allow a clean resize.
352 LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
353 }
354 }
355
356 return true;
357}
358
359EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() {
360 SharedMemory sharedMemory;
361
362 // Check shared memory allocator is valid.
363 if (mShmemAllocator.get() == nullptr) {
364 LOG(ERROR) << "Shared memory allocator not initialized.";
365 return SharedMemory();
366 }
367
368 // Allocate memory.
369 bool allocateSuccess = false;
370 Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize,
371 [&](bool success, const hidl_memory& hidlMem) {
372 if (!success) {
373 return;
374 }
375 allocateSuccess = success;
376 sharedMemory.hidlMemory = hidlMem;
377 });
378
379 // Check result of allocated memory.
380 if (!result.isOk() || !allocateSuccess) {
381 LOG(ERROR) << "Shared memory allocation failed.";
382 return SharedMemory();
383 }
384
385 // Map shared memory.
386 sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory);
387 if (sharedMemory.pIMemory.get() == nullptr) {
388 LOG(ERROR) << "Shared memory mapping failed.";
389 return SharedMemory();
390 }
391
392 // Return success.
393 return sharedMemory;
394}
395
396unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) {
397 unsigned added = 0;
398
399 while (added < numToAdd) {
400 SharedMemory sharedMemory = allocateAndMapSharedMemory();
401
402 // If allocate and map fails, break.
403 if (!sharedMemory.IsValid()) {
404 break;
405 }
406
407 // Find a place to store the new buffer
408 bool stored = false;
409 for (auto&& dataFrame : mDataFrames) {
410 if (!dataFrame.sharedMemory.IsValid()) {
411 // Use this existing entry
412 dataFrame.sharedMemory = sharedMemory;
413 dataFrame.inUse = false;
414 stored = true;
415 break;
416 }
417 }
418
419 if (!stored) {
420 // Add a BufferRecord wrapping this handle to our set of available buffers
421 mDataFrames.emplace_back(sharedMemory);
422 }
423
424 mFramesAllowed++;
425 added++;
426 }
427
428 return added;
429}
430
431unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) {
432 unsigned removed = 0;
433
434 for (auto&& dataFrame : mDataFrames) {
435 // Is this record not in use, but holding a buffer that we can free?
436 if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) {
437 // Release buffer and update the record so we can recognize it as "empty"
438 dataFrame.sharedMemory.clear();
439
440 mFramesAllowed--;
441 removed++;
442
443 if (removed == numToRemove) {
444 break;
445 }
446 }
447 }
448
449 return removed;
450}
451
452// This is the asynchronous data frame generation thread that runs in parallel with the
453// main serving thread. There is one for each active ultrasonic array instance.
454void EvsUltrasonicsArray::generateDataFrames() {
455 LOG(DEBUG) << "Data frame generation loop started";
456
457 unsigned idx = 0;
458
459 while (true) {
460 bool timeForFrame = false;
461
462 nsecs_t startTime = elapsedRealtimeNano();
463
464 // Lock scope for updating shared state
465 {
466 std::lock_guard<std::mutex> lock(mAccessLock);
467
468 if (mStreamState != RUNNING) {
469 // Break out of our main thread loop
470 break;
471 }
472
473 // Are we allowed to issue another buffer?
474 if (mFramesInUse >= mFramesAllowed) {
475 // Can't do anything right now -- skip this frame
476 LOG(WARNING) << "Skipped a frame because too many are in flight";
477 } else {
478 // Identify an available buffer to fill
479 for (idx = 0; idx < mDataFrames.size(); idx++) {
480 if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) {
481 // Found an available record, so stop looking
482 break;
483 }
484 }
485 if (idx >= mDataFrames.size()) {
486 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
487 LOG(ERROR) << "Failed to find an available buffer slot";
488 } else {
489 // We're going to make the frame busy
490 mDataFrames[idx].inUse = true;
491 mFramesInUse++;
492 timeForFrame = true;
493 }
494 }
495 }
496
497 if (timeForFrame) {
498 // Assemble the buffer description we'll transmit below
Changyeon Jocb877f92020-08-06 13:14:43 -0700499 UltrasonicsDataFrameDesc mockDataFrameDesc;
500 mockDataFrameDesc.dataFrameId = idx;
501 mockDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800502
Changyeon Jocb877f92020-08-06 13:14:43 -0700503 // Fill mock waveform data.
504 fillMockDataFrame(mockDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800505
506 // Issue the (asynchronous) callback to the client -- can't be holding the lock
Changyeon Jocb877f92020-08-06 13:14:43 -0700507 auto result = mStream->deliverDataFrame(mockDataFrameDesc);
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800508 if (result.isOk()) {
Changyeon Jocb877f92020-08-06 13:14:43 -0700509 LOG(DEBUG) << "Delivered data frame id: " << mockDataFrameDesc.dataFrameId;
Tanmay Patilb97cceb2020-02-07 16:48:39 -0800510 } else {
511 // This can happen if the client dies and is likely unrecoverable.
512 // To avoid consuming resources generating failing calls, we stop sending
513 // frames. Note, however, that the stream remains in the "STREAMING" state
514 // until cleaned up on the main thread.
515 LOG(ERROR) << "Frame delivery call failed in the transport layer.";
516
517 // Since we didn't actually deliver it, mark the frame as available
518 std::lock_guard<std::mutex> lock(mAccessLock);
519 mDataFrames[idx].inUse = false;
520 mFramesInUse--;
521
522 break;
523 }
524 }
525
526 // Sleep to generate frames at kTargetFrameRate.
527 static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate;
528 const nsecs_t now = elapsedRealtimeNano();
529 const nsecs_t workTimeUs = (now - startTime) / 1000;
530 const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
531 if (sleepDurationUs > 0) {
532 usleep(sleepDurationUs);
533 }
534 }
535
536 // If we've been asked to stop, send an event to signal the actual end of stream
537 EvsEventDesc event;
538 event.aType = EvsEventType::STREAM_STOPPED;
539 auto result = mStream->notify(event);
540 if (!result.isOk()) {
541 LOG(ERROR) << "Error delivering end of stream marker";
542 }
543}
544
545} // namespace implementation
546} // namespace V1_1
547} // namespace evs
548} // namespace automotive
549} // namespace hardware
550} // namespace android