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
121 if (pIMemory.get() == nullptr) {
122 return false;
123 }
124
125 uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
126
127 pIMemory->update();
128 SerializeWaveformData(waveformDataList, pData);
129 pIMemory->commit();
130
131 return true;
132 }
133
134 } // namespace
135
EvsUltrasonicsArray(const char * deviceName)136 EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
137 : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
138 LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
139
140 // Set up mock data for description.
141 mArrayDesc.ultrasonicsArrayId = deviceName;
142 fillMockArrayDesc(mArrayDesc);
143
144 // Assign allocator.
145 mShmemAllocator = IAllocator::getService("ashmem");
146 if (mShmemAllocator.get() == nullptr) {
147 LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
148 }
149 }
150
Create(const char * deviceName)151 sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
152 return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
153 }
154
~EvsUltrasonicsArray()155 EvsUltrasonicsArray::~EvsUltrasonicsArray() {
156 LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
157 forceShutdown();
158 }
159
160 // This gets called if another caller "steals" ownership of the ultrasonic array.
forceShutdown()161 void EvsUltrasonicsArray::forceShutdown() {
162 LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown";
163
164 // Make sure our output stream is cleaned up
165 // (It really should be already)
166 stopStream();
167
168 // Claim the lock while we work on internal state
169 std::lock_guard<std::mutex> lock(mAccessLock);
170
171 // Drop all the data frames we've been using
172 for (auto&& dataFrame : mDataFrames) {
173 if (dataFrame.inUse) {
174 LOG(ERROR) << "Error - releasing data frame despite remote ownership";
175 }
176 dataFrame.sharedMemory.clear();
177 }
178 mDataFrames.clear();
179
180 // Put this object into an unrecoverable error state since somebody else
181 // is going to own the underlying ultrasonic array now
182 mStreamState = DEAD;
183 }
184
GetMockArrayDesc(const char * deviceName)185 UltrasonicsArrayDesc EvsUltrasonicsArray::GetMockArrayDesc(const char* deviceName) {
186 UltrasonicsArrayDesc ultrasonicsArrayDesc;
187 ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
188 fillMockArrayDesc(ultrasonicsArrayDesc);
189 return ultrasonicsArrayDesc;
190 }
191
getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb)192 Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) {
193 LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo";
194
195 // Return the description for the get info callback.
196 _get_info_cb(mArrayDesc);
197
198 return Void();
199 }
200
setMaxFramesInFlight(uint32_t bufferCount)201 Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) {
202 LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight";
203
204 // Lock mutex for performing changes to available frames.
205 std::lock_guard<std::mutex> lock(mAccessLock);
206
207 // We cannot function without at least one buffer to send data.
208 if (bufferCount < 1) {
209 LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested";
210 return EvsResult::INVALID_ARG;
211 }
212
213 // Update our internal state of buffer count.
214 if (setAvailableFrames_Locked(bufferCount)) {
215 return EvsResult::OK;
216 } else {
217 return EvsResult::BUFFER_NOT_AVAILABLE;
218 }
219
220 return EvsResult::OK;
221 }
222
doneWithDataFrame(const UltrasonicsDataFrameDesc & dataFrameDesc)223 Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) {
224 LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame";
225
226 std::lock_guard<std::mutex> lock(mAccessLock);
227
228 if (dataFrameDesc.dataFrameId >= mDataFrames.size()) {
229 LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId "
230 << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")";
231 return Void();
232 }
233
234 if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) {
235 LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId
236 << "which is already free";
237 return Void();
238 }
239
240 // Mark the frame as available
241 mDataFrames[dataFrameDesc.dataFrameId].inUse = false;
242 mFramesInUse--;
243
244 // If this frame's index is high in the array, try to move it down
245 // to improve locality after mFramesAllowed has been reduced.
246 if (dataFrameDesc.dataFrameId >= mFramesAllowed) {
247 // Find an empty slot lower in the array (which should always exist in this case)
248 for (auto&& dataFrame : mDataFrames) {
249 if (!dataFrame.sharedMemory.IsValid()) {
250 dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory;
251 mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear();
252 return Void();
253 }
254 }
255 }
256
257 return Void();
258 }
259
startStream(const::android::sp<IEvsUltrasonicsArrayStream> & stream)260 Return<EvsResult> EvsUltrasonicsArray::startStream(
261 const ::android::sp<IEvsUltrasonicsArrayStream>& stream) {
262 LOG(DEBUG) << "EvsUltrasonicsArray startStream";
263
264 std::lock_guard<std::mutex> lock(mAccessLock);
265
266 if (mStreamState != STOPPED) {
267 LOG(ERROR) << "ignoring startStream call when a stream is already running.";
268 return EvsResult::STREAM_ALREADY_RUNNING;
269 }
270
271 // If the client never indicated otherwise, configure ourselves for a single streaming buffer
272 if (mFramesAllowed < 1) {
273 if (!setAvailableFrames_Locked(1)) {
274 LOG(ERROR)
275 << "Failed to start stream because we couldn't get shared memory data buffer";
276 return EvsResult::BUFFER_NOT_AVAILABLE;
277 }
278 }
279
280 // Record the user's callback for use when we have a frame ready
281 mStream = stream;
282
283 // Start the frame generation thread
284 mStreamState = RUNNING;
285 mCaptureThread = std::thread([this]() { generateDataFrames(); });
286
287 return EvsResult::OK;
288 }
289
stopStream()290 Return<void> EvsUltrasonicsArray::stopStream() {
291 LOG(DEBUG) << "EvsUltrasonicsArray stopStream";
292
293 bool streamStateStopping = false;
294 {
295 std::lock_guard<std::mutex> lock(mAccessLock);
296 if (mStreamState == RUNNING) {
297 // Tell the GenerateFrames loop we want it to stop
298 mStreamState = STOPPING;
299 streamStateStopping = true;
300 }
301 }
302
303 if (streamStateStopping) {
304 // Block outside the mutex until the "stop" flag has been acknowledged
305 // We won't send any more frames, but the client might still get some already in flight
306 LOG(DEBUG) << "Waiting for stream thread to end...";
307 mCaptureThread.join();
308 }
309
310 {
311 std::lock_guard<std::mutex> lock(mAccessLock);
312 mStreamState = STOPPED;
313 mStream = nullptr;
314 LOG(DEBUG) << "Stream marked STOPPED.";
315 }
316
317 return Void();
318 }
319
setAvailableFrames_Locked(unsigned bufferCount)320 bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) {
321 if (bufferCount < 1) {
322 LOG(ERROR) << "Ignoring request to set buffer count to zero";
323 return false;
324 }
325 if (bufferCount > kMaximumDataFramesInFlight) {
326 LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
327 return false;
328 }
329
330 // Is an increase required?
331 if (mFramesAllowed < bufferCount) {
332 // An increase is required
333 unsigned needed = bufferCount - mFramesAllowed;
334 LOG(INFO) << "Number of data frame buffers to add: " << needed;
335
336 unsigned added = increaseAvailableFrames_Locked(needed);
337 if (added != needed) {
338 // If we didn't add all the frames we needed, then roll back to the previous state
339 LOG(ERROR) << "Rolling back to previous frame queue size";
340 decreaseAvailableFrames_Locked(added);
341 return false;
342 }
343 } else if (mFramesAllowed > bufferCount) {
344 // A decrease is required
345 unsigned framesToRelease = mFramesAllowed - bufferCount;
346 LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease;
347
348 unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
349 if (released != framesToRelease) {
350 // This shouldn't happen with a properly behaving client because the client
351 // should only make this call after returning sufficient outstanding buffers
352 // to allow a clean resize.
353 LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
354 }
355 }
356
357 return true;
358 }
359
allocateAndMapSharedMemory()360 EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() {
361 SharedMemory sharedMemory;
362
363 // Check shared memory allocator is valid.
364 if (mShmemAllocator.get() == nullptr) {
365 LOG(ERROR) << "Shared memory allocator not initialized.";
366 return SharedMemory();
367 }
368
369 // Allocate memory.
370 bool allocateSuccess = false;
371 Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize,
372 [&](bool success, const hidl_memory& hidlMem) {
373 if (!success) {
374 return;
375 }
376 allocateSuccess = success;
377 sharedMemory.hidlMemory = hidlMem;
378 });
379
380 // Check result of allocated memory.
381 if (!result.isOk() || !allocateSuccess) {
382 LOG(ERROR) << "Shared memory allocation failed.";
383 return SharedMemory();
384 }
385
386 // Map shared memory.
387 sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory);
388 if (sharedMemory.pIMemory.get() == nullptr) {
389 LOG(ERROR) << "Shared memory mapping failed.";
390 return SharedMemory();
391 }
392
393 // Return success.
394 return sharedMemory;
395 }
396
increaseAvailableFrames_Locked(unsigned numToAdd)397 unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) {
398 unsigned added = 0;
399
400 while (added < numToAdd) {
401 SharedMemory sharedMemory = allocateAndMapSharedMemory();
402
403 // If allocate and map fails, break.
404 if (!sharedMemory.IsValid()) {
405 break;
406 }
407
408 // Find a place to store the new buffer
409 bool stored = false;
410 for (auto&& dataFrame : mDataFrames) {
411 if (!dataFrame.sharedMemory.IsValid()) {
412 // Use this existing entry
413 dataFrame.sharedMemory = sharedMemory;
414 dataFrame.inUse = false;
415 stored = true;
416 break;
417 }
418 }
419
420 if (!stored) {
421 // Add a BufferRecord wrapping this handle to our set of available buffers
422 mDataFrames.emplace_back(sharedMemory);
423 }
424
425 mFramesAllowed++;
426 added++;
427 }
428
429 return added;
430 }
431
decreaseAvailableFrames_Locked(unsigned numToRemove)432 unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) {
433 unsigned removed = 0;
434
435 for (auto&& dataFrame : mDataFrames) {
436 // Is this record not in use, but holding a buffer that we can free?
437 if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) {
438 // Release buffer and update the record so we can recognize it as "empty"
439 dataFrame.sharedMemory.clear();
440
441 mFramesAllowed--;
442 removed++;
443
444 if (removed == numToRemove) {
445 break;
446 }
447 }
448 }
449
450 return removed;
451 }
452
453 // This is the asynchronous data frame generation thread that runs in parallel with the
454 // main serving thread. There is one for each active ultrasonic array instance.
generateDataFrames()455 void EvsUltrasonicsArray::generateDataFrames() {
456 LOG(DEBUG) << "Data frame generation loop started";
457
458 unsigned idx = 0;
459
460 while (true) {
461 bool timeForFrame = false;
462
463 nsecs_t startTime = elapsedRealtimeNano();
464
465 // Lock scope for updating shared state
466 {
467 std::lock_guard<std::mutex> lock(mAccessLock);
468
469 if (mStreamState != RUNNING) {
470 // Break out of our main thread loop
471 break;
472 }
473
474 // Are we allowed to issue another buffer?
475 if (mFramesInUse >= mFramesAllowed) {
476 // Can't do anything right now -- skip this frame
477 LOG(WARNING) << "Skipped a frame because too many are in flight";
478 } else {
479 // Identify an available buffer to fill
480 for (idx = 0; idx < mDataFrames.size(); idx++) {
481 if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) {
482 // Found an available record, so stop looking
483 break;
484 }
485 }
486 if (idx >= mDataFrames.size()) {
487 // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
488 LOG(ERROR) << "Failed to find an available buffer slot";
489 } else {
490 // We're going to make the frame busy
491 mDataFrames[idx].inUse = true;
492 mFramesInUse++;
493 timeForFrame = true;
494 }
495 }
496 }
497
498 if (timeForFrame) {
499 // Assemble the buffer description we'll transmit below
500 UltrasonicsDataFrameDesc mockDataFrameDesc;
501 mockDataFrameDesc.dataFrameId = idx;
502 mockDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
503
504 // Fill mock waveform data.
505 fillMockDataFrame(mockDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
506
507 // Issue the (asynchronous) callback to the client -- can't be holding the lock
508 auto result = mStream->deliverDataFrame(mockDataFrameDesc);
509 if (result.isOk()) {
510 LOG(DEBUG) << "Delivered data frame id: " << mockDataFrameDesc.dataFrameId;
511 } else {
512 // This can happen if the client dies and is likely unrecoverable.
513 // To avoid consuming resources generating failing calls, we stop sending
514 // frames. Note, however, that the stream remains in the "STREAMING" state
515 // until cleaned up on the main thread.
516 LOG(ERROR) << "Frame delivery call failed in the transport layer.";
517
518 // Since we didn't actually deliver it, mark the frame as available
519 std::lock_guard<std::mutex> lock(mAccessLock);
520 mDataFrames[idx].inUse = false;
521 mFramesInUse--;
522
523 break;
524 }
525 }
526
527 // Sleep to generate frames at kTargetFrameRate.
528 static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate;
529 const nsecs_t now = elapsedRealtimeNano();
530 const nsecs_t workTimeUs = (now - startTime) / 1000;
531 const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
532 if (sleepDurationUs > 0) {
533 usleep(sleepDurationUs);
534 }
535 }
536
537 // If we've been asked to stop, send an event to signal the actual end of stream
538 EvsEventDesc event;
539 event.aType = EvsEventType::STREAM_STOPPED;
540 auto result = mStream->notify(event);
541 if (!result.isOk()) {
542 LOG(ERROR) << "Error delivering end of stream marker";
543 }
544 }
545
546 } // namespace implementation
547 } // namespace V1_1
548 } // namespace evs
549 } // namespace automotive
550 } // namespace hardware
551 } // namespace android
552