1 /*
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
26 namespace android {
27 namespace hardware {
28 namespace automotive {
29 namespace evs {
30 namespace V1_1 {
31 namespace 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
35 const unsigned int kMaximumDataFramesInFlight = 100;
36
37 const uint32_t kMaxReadingsPerSensor = 5;
38 const uint32_t kMaxReceiversCount = 3;
39
40 const unsigned int kSharedMemoryMaxSize =
41 kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float);
42
43 // Target frame rate in frames per second.
44 const int kTargetFrameRate = 10;
45
46 namespace {
47
fillMockArrayDesc(UltrasonicsArrayDesc & arrayDesc)48 void fillMockArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
49 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().
77 struct 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.
SerializeWaveformData(const std::vector<WaveformData> & waveformDataList,uint8_t * pData)84 void 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
102 // Fills dataFrameDesc with mock data.
fillMockDataFrame(UltrasonicsDataFrameDesc & dataFrameDesc,sp<IMemory> pIMemory)103 bool fillMockDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
104 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 = {
116 {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}}}};
119
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
EvsUltrasonicsArray(const char * deviceName)135 EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
136 : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
137 LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
138
139 // Set up mock data for description.
140 mArrayDesc.ultrasonicsArrayId = deviceName;
141 fillMockArrayDesc(mArrayDesc);
142
143 // Assign allocator.
144 mShmemAllocator = IAllocator::getService("ashmem");
145 if (mShmemAllocator.get() == nullptr) {
146 LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
147 }
148 }
149
Create(const char * deviceName)150 sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
151 return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
152 }
153
~EvsUltrasonicsArray()154 EvsUltrasonicsArray::~EvsUltrasonicsArray() {
155 LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
156 forceShutdown();
157 }
158
159 // This gets called if another caller "steals" ownership of the ultrasonic array.
forceShutdown()160 void 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
GetMockArrayDesc(const char * deviceName)184 UltrasonicsArrayDesc EvsUltrasonicsArray::GetMockArrayDesc(const char* deviceName) {
185 UltrasonicsArrayDesc ultrasonicsArrayDesc;
186 ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
187 fillMockArrayDesc(ultrasonicsArrayDesc);
188 return ultrasonicsArrayDesc;
189 }
190
getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb)191 Return<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
setMaxFramesInFlight(uint32_t bufferCount)200 Return<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
doneWithDataFrame(const UltrasonicsDataFrameDesc & dataFrameDesc)222 Return<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
startStream(const::android::sp<IEvsUltrasonicsArrayStream> & stream)259 Return<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
stopStream()289 Return<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
setAvailableFrames_Locked(unsigned bufferCount)319 bool 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
allocateAndMapSharedMemory()359 EvsUltrasonicsArray::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
increaseAvailableFrames_Locked(unsigned numToAdd)396 unsigned 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
decreaseAvailableFrames_Locked(unsigned numToRemove)431 unsigned 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.
generateDataFrames()454 void 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
499 UltrasonicsDataFrameDesc mockDataFrameDesc;
500 mockDataFrameDesc.dataFrameId = idx;
501 mockDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
502
503 // Fill mock waveform data.
504 fillMockDataFrame(mockDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
505
506 // Issue the (asynchronous) callback to the client -- can't be holding the lock
507 auto result = mStream->deliverDataFrame(mockDataFrameDesc);
508 if (result.isOk()) {
509 LOG(DEBUG) << "Delivered data frame id: " << mockDataFrameDesc.dataFrameId;
510 } 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
551