Merge changes from topic "UltrasonicsEVS"
* changes:
Add VTS for Ultrasonics to EVS 1.1
Adds default implementation for ultrasonics HAL
Adds HAL for ultrasonics to EVS 1.1
diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp
index 17f31e4..f9bccef 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/1.1/Android.bp
@@ -12,6 +12,8 @@
"IEvsCameraStream.hal",
"IEvsDisplay.hal",
"IEvsEnumerator.hal",
+ "IEvsUltrasonicsArray.hal",
+ "IEvsUltrasonicsArrayStream.hal",
],
interfaces: [
"android.frameworks.automotive.display@1.0",
diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal
index 84dd21f..d604e4f 100644
--- a/automotive/evs/1.1/IEvsEnumerator.hal
+++ b/automotive/evs/1.1/IEvsEnumerator.hal
@@ -18,12 +18,13 @@
import IEvsCamera;
import IEvsDisplay;
+import IEvsUltrasonicsArray;
import @1.0::IEvsEnumerator;
import @1.0::EvsResult;
import android.hardware.camera.device@3.2::Stream;
/**
- * Provides the mechanism for EVS camera discovery
+ * Provides the mechanism for EVS camera and ultrasonics array discovery
*/
interface IEvsEnumerator extends @1.0::IEvsEnumerator {
/**
@@ -76,4 +77,33 @@
* @return display EvsDisplay object to be used.
*/
openDisplay_1_1(uint8_t id) generates (IEvsDisplay display);
+
+ /**
+ * Returns a list of all ultrasonics array available to the system.
+ * Will return an empty vector if ultrasonics is not supported.
+ *
+ * @return ultrasonicsArrays A list of ultrasonics available for EVS service.
+ */
+ getUltrasonicsArrayList() generates (vec<UltrasonicsArrayDesc> ultrasonicsArrays);
+
+ /**
+ * Gets the IEvsUltrasonicsArray associated with a ultrasonicsArrayId from a
+ * UltrasonicsDataDesc
+ *
+ * @param ultrasonicsArrayId A unique identifier of the ultrasonic array.
+ * @return evsUltrasonicsArray IEvsUltrasonicsArray object associated with a
+ * given ultrasonicsArrayId.
+ */
+ openUltrasonicsArray(string ultrasonicsArrayId) generates (
+ IEvsUltrasonicsArray evsUltrasonicsArray);
+
+ /**
+ * Return the specified IEvsUltrasonicsArray interface as no longer in use
+ *
+ * When the IEvsUltrasonicsArray object is no longer required, it must be released.
+ * NOTE: Data streaming must be cleanly stopped before making this call.
+ *
+ * @param evsUltrasonicsArray EvsUltrasonics array object to be closed.
+ */
+ closeUltrasonicsArray(IEvsUltrasonicsArray evsUltrasonicsArray);
};
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArray.hal b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
new file mode 100644
index 0000000..ae4f941
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import @1.0::EvsResult;
+import UltrasonicsArrayDesc;
+import UltrasonicsDataFrameDesc;
+import IEvsUltrasonicsArrayStream;
+
+/**
+ * HAL interface for ultrasonics sensor array.
+ */
+interface IEvsUltrasonicsArray {
+ /**
+ * Returns the ultrasonic sensor array information.
+ *
+ * @return info The description of this ultrasonic array. This must be the
+ * same value as reported by IEvsEnumerator::getUltrasonicsArrayList().
+ */
+ getUltrasonicArrayInfo() generates (UltrasonicsArrayDesc info);
+
+ /**
+ * Specifies the depth of the buffer chain the ultrasonic sensors is
+ * asked to support.
+ *
+ * Up to this many data frames may be held concurrently by the client of IEvsUltrasonicsArray.
+ * If this many frames have been delivered to the receiver without being returned
+ * by doneWithFrame, the stream must skip frames until a buffer is returned for reuse.
+ * It is legal for this call to come at any time, even while streams are already running,
+ * in which case buffers should be added or removed from the chain as appropriate.
+ * If no call is made to this entry point, the IEvsUltrasonicsArray must support at least one
+ * data frame by default. More is acceptable.
+ *
+ * @param bufferCount Number of buffers the client of
+ * IEvsUltrasonicsArray may hold concurrently.
+ * @return result EvsResult::OK is returned if this call is successful.
+ * Will return EvsResult::INVALID_ARG on invalid bufferCount.
+ */
+ setMaxFramesInFlight(uint32_t bufferCount) generates (EvsResult result);
+
+ /**
+ * Requests to start the stream.
+ *
+ * @param stream Implementation of IEvsUltrasonicsArrayStream.
+ * @return result EvsResult::OK is returned if this call is successful. Returns
+ * EvsResult::STREAM_ALREADY_RUNNING if stream is already running.
+ */
+ startStream(IEvsUltrasonicsArrayStream stream) generates (EvsResult result);
+
+ /**
+ * Requests to stop the delivery of the ultrasonic array data frames.
+ *
+ * Because delivery is asynchronous, frames may continue to arrive for
+ * some time after this call returns. Each must be returned until the
+ * closure of the stream is signaled to the IEvsCameraStream.
+ * This function cannot fail and is ignored if the stream isn't running.
+ */
+ stopStream();
+
+ /**
+ * Notifies the UltrasonicsDataDesc is consumed that was received from
+ * IEvsUltrasonicsArrayStream.
+ *
+ * @param dataFrameDesc Ultrasonics data descriptor.
+ */
+ doneWithDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+};
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
new file mode 100644
index 0000000..f95209f
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import UltrasonicsDataFrameDesc;
+import @1.1::EvsEventDesc;
+
+/**
+ * Implemented on client side to receive asynchronous ultrasonic data
+ * deliveries.
+ */
+interface IEvsUltrasonicsArrayStream {
+ /**
+ * Receives calls from the HAL each time a data frame is ready.
+ *
+ * @param dataFrameDesc Ultrasonic array data frame descriptor.
+ */
+ oneway deliverDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+
+ /**
+ * Receives calls from the HAL each time an event happens.
+ *
+ * @param event Event EVS event with possible event information.
+ */
+ oneway notify(EvsEventDesc event);
+};
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index a35c9db..d843167 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -10,6 +10,7 @@
"EvsDisplay.cpp",
"ConfigManager.cpp",
"ConfigManagerUtil.cpp",
+ "EvsUltrasonicsArray.cpp",
],
init_rc: ["android.hardware.automotive.evs@1.1-service.rc"],
@@ -17,11 +18,14 @@
"android.hardware.automotive.evs@1.0",
"android.hardware.automotive.evs@1.1",
"android.hardware.camera.device@3.3",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
"libbase",
"libbinder",
"liblog",
"libhardware",
"libhidlbase",
+ "libhidlmemory",
"liblog",
"libui",
"libutils",
diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp
index 0319560..117ee7a 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.1/default/EvsEnumerator.cpp
@@ -19,6 +19,7 @@
#include "EvsEnumerator.h"
#include "EvsCamera.h"
#include "EvsDisplay.h"
+#include "EvsUltrasonicsArray.h"
namespace android {
namespace hardware {
@@ -31,12 +32,12 @@
// NOTE: All members values are static so that all clients operate on the same state
// That is to say, this is effectively a singleton despite the fact that HIDL
// constructs a new instance for each client.
-std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
-wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
-unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
-sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
-std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
-
+std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
+unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
+sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
+std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
+std::list<EvsEnumerator::UltrasonicsArrayRecord> EvsEnumerator::sUltrasonicsArrayRecordList;
EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
ALOGD("EvsEnumerator created");
@@ -66,6 +67,10 @@
}
});
}
+
+ // Add ultrasonics array desc.
+ sUltrasonicsArrayRecordList.emplace_back(
+ EvsUltrasonicsArray::GetDummyArrayDesc("front_array"));
}
@@ -355,6 +360,102 @@
return pRecord;
}
+EvsEnumerator::UltrasonicsArrayRecord* EvsEnumerator::findUltrasonicsArrayById(
+ const std::string& ultrasonicsArrayId) {
+ auto recordIt = std::find_if(
+ sUltrasonicsArrayRecordList.begin(), sUltrasonicsArrayRecordList.end(),
+ [&ultrasonicsArrayId](const UltrasonicsArrayRecord& record) {
+ return ultrasonicsArrayId == record.desc.ultrasonicsArrayId;});
+
+ return (recordIt != sUltrasonicsArrayRecordList.end()) ? &*recordIt : nullptr;
+}
+
+Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
+ hidl_vec<UltrasonicsArrayDesc> desc;
+ desc.resize(sUltrasonicsArrayRecordList.size());
+
+ // Copy over desc from sUltrasonicsArrayRecordList.
+ for (auto p = std::make_pair(sUltrasonicsArrayRecordList.begin(), desc.begin());
+ p.first != sUltrasonicsArrayRecordList.end(); p.first++, p.second++) {
+ *p.second = p.first->desc;
+ }
+
+ // Send back the results
+ ALOGD("reporting %zu ultrasonics arrays available", desc.size());
+ _hidl_cb(desc);
+
+ // HIDL convention says we return Void if we sent our result back via callback
+ return Void();
+}
+
+Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
+ const hidl_string& ultrasonicsArrayId) {
+ // Find the named ultrasonic array.
+ UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+
+ // Is this a recognized ultrasonic array id?
+ if (!pRecord) {
+ ALOGE("Requested ultrasonics array %s not found", ultrasonicsArrayId.c_str());
+ return nullptr;
+ }
+
+ // Has this ultrasonic array already been instantiated by another caller?
+ sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+ if (pActiveUltrasonicsArray != nullptr) {
+ ALOGW("Killing previous ultrasonics array because of new caller");
+ closeUltrasonicsArray(pActiveUltrasonicsArray);
+ }
+
+ // Construct a ultrasonic array instance for the caller
+ pActiveUltrasonicsArray = EvsUltrasonicsArray::Create(ultrasonicsArrayId.c_str());
+ pRecord->activeInstance = pActiveUltrasonicsArray;
+ if (pActiveUltrasonicsArray == nullptr) {
+ ALOGE("Failed to allocate new EvsUltrasonicsArray object for %s\n",
+ ultrasonicsArrayId.c_str());
+ }
+
+ return pActiveUltrasonicsArray;
+}
+
+Return<void> EvsEnumerator::closeUltrasonicsArray(
+ const sp<IEvsUltrasonicsArray>& pEvsUltrasonicsArray) {
+
+ if (pEvsUltrasonicsArray.get() == nullptr) {
+ ALOGE("Ignoring call to closeUltrasonicsArray with null ultrasonics array");
+ return Void();
+ }
+
+ // Get the ultrasonics array id so we can find it in our list.
+ std::string ultrasonicsArrayId;
+ pEvsUltrasonicsArray->getUltrasonicArrayInfo([&ultrasonicsArrayId](UltrasonicsArrayDesc desc) {
+ ultrasonicsArrayId.assign(desc.ultrasonicsArrayId);
+ });
+
+ // Find the named ultrasonics array
+ UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+ if (!pRecord) {
+ ALOGE("Asked to close a ultrasonics array whose name isnt not found");
+ return Void();
+ }
+
+ sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+
+ if (pActiveUltrasonicsArray.get() == nullptr) {
+ ALOGE("Somehow a ultrasonics array is being destroyed when the enumerator didn't know "
+ "one existed");
+ } else if (pActiveUltrasonicsArray != pEvsUltrasonicsArray) {
+ // This can happen if the ultrasonics array was aggressively reopened,
+ // orphaning this previous instance
+ ALOGW("Ignoring close of previously orphaned ultrasonics array - why did a client steal?");
+ } else {
+ // Drop the active ultrasonics array
+ pActiveUltrasonicsArray->forceShutdown();
+ pRecord->activeInstance = nullptr;
+ }
+
+ return Void();
+}
+
} // namespace implementation
} // namespace V1_1
} // namespace evs
diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h
index 9415953..d80124b 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.h
+++ b/automotive/evs/1.1/default/EvsEnumerator.h
@@ -21,6 +21,7 @@
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
#include <list>
@@ -46,6 +47,7 @@
class EvsCamera; // from EvsCamera.h
class EvsDisplay; // from EvsDisplay.h
+class EvsUltrasonicsArray; // from EvsUltrasonicsArray.h
class EvsEnumerator : public IEvsEnumerator {
@@ -65,6 +67,11 @@
Return<bool> isHardware() override { return true; }
Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
+ Return<void> getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) override;
+ Return<sp<IEvsUltrasonicsArray>> openUltrasonicsArray(
+ const hidl_string& ultrasonicsArrayId) override;
+ Return<void> closeUltrasonicsArray(
+ const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) override;
// Implementation details
EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
@@ -80,10 +87,21 @@
CameraRecord(const char *cameraId) : desc() { desc.v1.cameraId = cameraId; }
};
+ struct UltrasonicsArrayRecord {
+ UltrasonicsArrayDesc desc;
+ wp<EvsUltrasonicsArray> activeInstance;
+
+ UltrasonicsArrayRecord(const UltrasonicsArrayDesc& arrayDesc) : desc(arrayDesc) {};
+ };
+
static CameraRecord* findCameraById(const std::string& cameraId);
static std::list<CameraRecord> sCameraList;
+ static UltrasonicsArrayRecord* findUltrasonicsArrayById(const std::string& ultrasonicsArrayId);
+
+ static std::list<UltrasonicsArrayRecord> sUltrasonicsArrayRecordList;
+
// Weak pointer. Object destructs if client dies.
static wp<EvsDisplay> sActiveDisplay;
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
new file mode 100644
index 0000000..bc69aa4
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsUltrasonicsArray.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+#include <time.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+// Arbitrary limit on number of data frames allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+const unsigned int kMaximumDataFramesInFlight = 100;
+
+const uint32_t kMaxReadingsPerSensor = 5;
+const uint32_t kMaxReceiversCount = 3;
+
+const unsigned int kSharedMemoryMaxSize =
+ kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float);
+
+// Target frame rate in frames per second.
+const int kTargetFrameRate = 10;
+
+namespace {
+
+void fillDummyArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
+ arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor;
+ arrayDesc.maxReceiversCount = kMaxReceiversCount;
+
+ const int kSensorCount = 3;
+ const float kMaxRange = 4000; // 4 metres.
+ const float kAngleOfMeasurement = 0.261799; // 15 degrees.
+
+ std::vector<UltrasonicSensor> sensors(kSensorCount);
+
+ // Sensor pointing forward on left side of front bumper.
+ sensors[0].maxRange = kMaxRange;
+ sensors[0].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[0].pose = {{1, 0, 0, 0}, {-1000, 2000, 200}};
+
+ // Sensor pointing forward on center of front bumper.
+ sensors[1].maxRange = kMaxRange;
+ sensors[1].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[1].pose = {{1, 0, 0, 0}, {0, 2000, 200}};
+
+ // Sensor pointing forward on right side of front bumper.
+ sensors[2].maxRange = kMaxRange;
+ sensors[2].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[2].pose = {{1, 0, 0, 0}, {1000, 2000, 200}};
+
+ arrayDesc.sensors = sensors;
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+ uint8_t receiverId;
+ std::vector<std::pair<float, float>> readings;
+};
+
+// Serializes data provided in waveformDataList to a shared memory data pointer.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+void SerializeWaveformData(const std::vector<WaveformData>& waveformDataList, uint8_t* pData) {
+ for (auto& waveformData : waveformDataList) {
+ // Set Id
+ memcpy(pData, &waveformData.receiverId, sizeof(uint8_t));
+ pData += sizeof(uint8_t);
+
+ for (auto& reading : waveformData.readings) {
+ // Set the time of flight.
+ memcpy(pData, &reading.first, sizeof(float));
+ pData += sizeof(float);
+
+ // Set the resonance.
+ memcpy(pData, &reading.second, sizeof(float));
+ pData += sizeof(float);
+ }
+ }
+}
+
+// Fills dataFrameDesc with dummy data.
+bool fillDummyDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
+ dataFrameDesc.timestampNs = elapsedRealtimeNano();
+
+ const std::vector<uint8_t> transmittersIdList = {0};
+ dataFrameDesc.transmittersIdList = transmittersIdList;
+
+ const std::vector<uint8_t> recvIdList = {0, 1, 2};
+ dataFrameDesc.receiversIdList = recvIdList;
+
+ const std::vector<uint32_t> receiversReadingsCountList = {2, 2, 4};
+ dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList;
+
+ const std::vector<WaveformData> waveformDataList = {
+ {recvIdList[0], { {1000, 0.1f}, {2000, 0.8f} }},
+ {recvIdList[1], { {1000, 0.1f}, {2000, 1.0f} }},
+ {recvIdList[2], { {1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f} }}
+ };
+
+ if (pIMemory.get() == nullptr) {
+ return false;
+ }
+
+ uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+
+ pIMemory->update();
+ SerializeWaveformData(waveformDataList, pData);
+ pIMemory->commit();
+
+ return true;
+}
+
+} // namespace
+
+EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
+ : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
+ LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
+
+ // Set up dummy data for description.
+ mArrayDesc.ultrasonicsArrayId = deviceName;
+ fillDummyArrayDesc(mArrayDesc);
+
+ // Assign allocator.
+ mShmemAllocator = IAllocator::getService("ashmem");
+ if (mShmemAllocator.get() == nullptr) {
+ LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
+ }
+}
+
+sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
+ return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
+}
+
+EvsUltrasonicsArray::~EvsUltrasonicsArray() {
+ LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
+ forceShutdown();
+}
+
+// This gets called if another caller "steals" ownership of the ultrasonic array.
+void EvsUltrasonicsArray::forceShutdown() {
+ LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown";
+
+ // Make sure our output stream is cleaned up
+ // (It really should be already)
+ stopStream();
+
+ // Claim the lock while we work on internal state
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // Drop all the data frames we've been using
+ for (auto&& dataFrame : mDataFrames) {
+ if (dataFrame.inUse) {
+ LOG(ERROR) << "Error - releasing data frame despite remote ownership";
+ }
+ dataFrame.sharedMemory.clear();
+ }
+ mDataFrames.clear();
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the underlying ultrasonic array now
+ mStreamState = DEAD;
+}
+
+UltrasonicsArrayDesc EvsUltrasonicsArray::GetDummyArrayDesc(const char* deviceName) {
+ UltrasonicsArrayDesc ultrasonicsArrayDesc;
+ ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
+ fillDummyArrayDesc(ultrasonicsArrayDesc);
+ return ultrasonicsArrayDesc;
+}
+
+Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) {
+ LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo";
+
+ // Return the description for the get info callback.
+ _get_info_cb(mArrayDesc);
+
+ return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) {
+ LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight";
+
+ // Lock mutex for performing changes to available frames.
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // We cannot function without at least one buffer to send data.
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested";
+ return EvsResult::INVALID_ARG;
+ }
+
+ // Update our internal state of buffer count.
+ if (setAvailableFrames_Locked(bufferCount)) {
+ return EvsResult::OK;
+ } else {
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+
+ return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+ LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame";
+
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (dataFrameDesc.dataFrameId >= mDataFrames.size()) {
+ LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId "
+ << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")";
+ return Void();
+ }
+
+ if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) {
+ LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId
+ << "which is already free";
+ return Void();
+ }
+
+ // Mark the frame as available
+ mDataFrames[dataFrameDesc.dataFrameId].inUse = false;
+ mFramesInUse--;
+
+ // If this frame's index is high in the array, try to move it down
+ // to improve locality after mFramesAllowed has been reduced.
+ if (dataFrameDesc.dataFrameId >= mFramesAllowed) {
+ // Find an empty slot lower in the array (which should always exist in this case)
+ for (auto&& dataFrame : mDataFrames) {
+ if (!dataFrame.sharedMemory.IsValid()) {
+ dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory;
+ mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear();
+ return Void();
+ }
+ }
+ }
+
+ return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::startStream(
+ const ::android::sp<IEvsUltrasonicsArrayStream>& stream) {
+ LOG(DEBUG) << "EvsUltrasonicsArray startStream";
+
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != STOPPED) {
+ LOG(ERROR) << "ignoring startStream call when a stream is already running.";
+ return EvsResult::STREAM_ALREADY_RUNNING;
+ }
+
+ // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+ if (mFramesAllowed < 1) {
+ if (!setAvailableFrames_Locked(1)) {
+ LOG(ERROR)
+ << "Failed to start stream because we couldn't get shared memory data buffer";
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+ }
+
+ // Record the user's callback for use when we have a frame ready
+ mStream = stream;
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = std::thread([this]() { generateDataFrames(); });
+
+ return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::stopStream() {
+ LOG(DEBUG) << "EvsUltrasonicsArray stopStream";
+
+ bool streamStateStopping = false;
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+ streamStateStopping = true;
+ }
+ }
+
+ if (streamStateStopping) {
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some already in flight
+ LOG(DEBUG) << "Waiting for stream thread to end...";
+ mCaptureThread.join();
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ LOG(DEBUG) << "Stream marked STOPPED.";
+ }
+
+ return Void();
+}
+
+bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) {
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring request to set buffer count to zero";
+ return false;
+ }
+ if (bufferCount > kMaximumDataFramesInFlight) {
+ LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
+ return false;
+ }
+
+ // Is an increase required?
+ if (mFramesAllowed < bufferCount) {
+ // An increase is required
+ unsigned needed = bufferCount - mFramesAllowed;
+ LOG(INFO) << "Number of data frame buffers to add: " << needed;
+
+ unsigned added = increaseAvailableFrames_Locked(needed);
+ if (added != needed) {
+ // If we didn't add all the frames we needed, then roll back to the previous state
+ LOG(ERROR) << "Rolling back to previous frame queue size";
+ decreaseAvailableFrames_Locked(added);
+ return false;
+ }
+ } else if (mFramesAllowed > bufferCount) {
+ // A decrease is required
+ unsigned framesToRelease = mFramesAllowed - bufferCount;
+ LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease;
+
+ unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
+ if (released != framesToRelease) {
+ // This shouldn't happen with a properly behaving client because the client
+ // should only make this call after returning sufficient outstanding buffers
+ // to allow a clean resize.
+ LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
+ }
+ }
+
+ return true;
+}
+
+EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() {
+ SharedMemory sharedMemory;
+
+ // Check shared memory allocator is valid.
+ if (mShmemAllocator.get() == nullptr) {
+ LOG(ERROR) << "Shared memory allocator not initialized.";
+ return SharedMemory();
+ }
+
+ // Allocate memory.
+ bool allocateSuccess = false;
+ Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize,
+ [&](bool success, const hidl_memory& hidlMem) {
+ if (!success) {
+ return;
+ }
+ allocateSuccess = success;
+ sharedMemory.hidlMemory = hidlMem;
+ });
+
+ // Check result of allocated memory.
+ if (!result.isOk() || !allocateSuccess) {
+ LOG(ERROR) << "Shared memory allocation failed.";
+ return SharedMemory();
+ }
+
+ // Map shared memory.
+ sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory);
+ if (sharedMemory.pIMemory.get() == nullptr) {
+ LOG(ERROR) << "Shared memory mapping failed.";
+ return SharedMemory();
+ }
+
+ // Return success.
+ return sharedMemory;
+}
+
+unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) {
+ unsigned added = 0;
+
+ while (added < numToAdd) {
+ SharedMemory sharedMemory = allocateAndMapSharedMemory();
+
+ // If allocate and map fails, break.
+ if (!sharedMemory.IsValid()) {
+ break;
+ }
+
+ // Find a place to store the new buffer
+ bool stored = false;
+ for (auto&& dataFrame : mDataFrames) {
+ if (!dataFrame.sharedMemory.IsValid()) {
+ // Use this existing entry
+ dataFrame.sharedMemory = sharedMemory;
+ dataFrame.inUse = false;
+ stored = true;
+ break;
+ }
+ }
+
+ if (!stored) {
+ // Add a BufferRecord wrapping this handle to our set of available buffers
+ mDataFrames.emplace_back(sharedMemory);
+ }
+
+ mFramesAllowed++;
+ added++;
+ }
+
+ return added;
+}
+
+unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+ unsigned removed = 0;
+
+ for (auto&& dataFrame : mDataFrames) {
+ // Is this record not in use, but holding a buffer that we can free?
+ if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) {
+ // Release buffer and update the record so we can recognize it as "empty"
+ dataFrame.sharedMemory.clear();
+
+ mFramesAllowed--;
+ removed++;
+
+ if (removed == numToRemove) {
+ break;
+ }
+ }
+ }
+
+ return removed;
+}
+
+// This is the asynchronous data frame generation thread that runs in parallel with the
+// main serving thread. There is one for each active ultrasonic array instance.
+void EvsUltrasonicsArray::generateDataFrames() {
+ LOG(DEBUG) << "Data frame generation loop started";
+
+ unsigned idx = 0;
+
+ while (true) {
+ bool timeForFrame = false;
+
+ nsecs_t startTime = elapsedRealtimeNano();
+
+ // Lock scope for updating shared state
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ break;
+ }
+
+ // Are we allowed to issue another buffer?
+ if (mFramesInUse >= mFramesAllowed) {
+ // Can't do anything right now -- skip this frame
+ LOG(WARNING) << "Skipped a frame because too many are in flight";
+ } else {
+ // Identify an available buffer to fill
+ for (idx = 0; idx < mDataFrames.size(); idx++) {
+ if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) {
+ // Found an available record, so stop looking
+ break;
+ }
+ }
+ if (idx >= mDataFrames.size()) {
+ // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+ LOG(ERROR) << "Failed to find an available buffer slot";
+ } else {
+ // We're going to make the frame busy
+ mDataFrames[idx].inUse = true;
+ mFramesInUse++;
+ timeForFrame = true;
+ }
+ }
+ }
+
+ if (timeForFrame) {
+ // Assemble the buffer description we'll transmit below
+ UltrasonicsDataFrameDesc dummyDataFrameDesc;
+ dummyDataFrameDesc.dataFrameId = idx;
+ dummyDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
+
+ // Fill dummy waveform data.
+ fillDummyDataFrame(dummyDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
+
+ // Issue the (asynchronous) callback to the client -- can't be holding the lock
+ auto result = mStream->deliverDataFrame(dummyDataFrameDesc);
+ if (result.isOk()) {
+ LOG(DEBUG) << "Delivered data frame id: " << dummyDataFrameDesc.dataFrameId;
+ } else {
+ // This can happen if the client dies and is likely unrecoverable.
+ // To avoid consuming resources generating failing calls, we stop sending
+ // frames. Note, however, that the stream remains in the "STREAMING" state
+ // until cleaned up on the main thread.
+ LOG(ERROR) << "Frame delivery call failed in the transport layer.";
+
+ // Since we didn't actually deliver it, mark the frame as available
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mDataFrames[idx].inUse = false;
+ mFramesInUse--;
+
+ break;
+ }
+ }
+
+ // Sleep to generate frames at kTargetFrameRate.
+ static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate;
+ const nsecs_t now = elapsedRealtimeNano();
+ const nsecs_t workTimeUs = (now - startTime) / 1000;
+ const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
+ if (sleepDurationUs > 0) {
+ usleep(sleepDurationUs);
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual end of stream
+ EvsEventDesc event;
+ event.aType = EvsEventType::STREAM_STOPPED;
+ auto result = mStream->notify(event);
+ if (!result.isOk()) {
+ LOG(ERROR) << "Error delivering end of stream marker";
+ }
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.h b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
new file mode 100644
index 0000000..7a41012
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+
+#include <thread>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <utils/threads.h>
+
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/types.h>
+
+using ::android::hardware::hidl_memory;
+using ::android::hardware::automotive::evs::V1_0::EvsResult;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsArrayDesc;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+class EvsUltrasonicsArray : public IEvsUltrasonicsArray {
+ public:
+ // Methods from ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray follow.
+ Return<void> getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) override;
+ Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
+ Return<void> doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) override;
+ Return<EvsResult> startStream(const ::android::sp<IEvsUltrasonicsArrayStream>& stream) override;
+ Return<void> stopStream() override;
+
+ // Factory function to create a array.
+ static sp<EvsUltrasonicsArray> Create(const char* deviceName);
+
+ // Returns a ultrasonics array descriptor filled with sample data.
+ static UltrasonicsArrayDesc GetDummyArrayDesc(const char* id);
+
+ DISALLOW_COPY_AND_ASSIGN(EvsUltrasonicsArray);
+ virtual ~EvsUltrasonicsArray() override;
+ void forceShutdown(); // This gets called if another caller "steals" ownership
+
+ private:
+ // Structure holding the hidl memory struct and the interface to a shared memory.
+ struct SharedMemory {
+ hidl_memory hidlMemory;
+ sp<IMemory> pIMemory;
+
+ SharedMemory() : hidlMemory(hidl_memory()), pIMemory(nullptr){};
+
+ SharedMemory(hidl_memory hidlMem, sp<IMemory> pIMem)
+ : hidlMemory(hidlMem), pIMemory(pIMem) {}
+
+ bool IsValid() { return (pIMemory.get() != nullptr && hidlMemory.valid()); }
+
+ void clear() {
+ hidlMemory = hidl_memory();
+ pIMemory.clear();
+ }
+ };
+
+ // Struct for a data frame record.
+ struct DataFrameRecord {
+ SharedMemory sharedMemory;
+ bool inUse;
+ explicit DataFrameRecord(SharedMemory shMem) : sharedMemory(shMem), inUse(false){};
+ };
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+
+ EvsUltrasonicsArray(const char* deviceName);
+
+ // These three functions are expected to be called while mAccessLock is held
+ bool setAvailableFrames_Locked(unsigned bufferCount);
+ unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+ unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+ void generateDataFrames();
+
+ SharedMemory allocateAndMapSharedMemory();
+
+ UltrasonicsArrayDesc mArrayDesc = {}; // The properties of this ultrasonic array.
+
+ std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ sp<IEvsUltrasonicsArrayStream> mStream = nullptr; // The callback used to deliver each frame
+
+ sp<IAllocator> mShmemAllocator = nullptr; // Shared memory allocator.
+
+ std::mutex mAccessLock;
+ std::vector<DataFrameRecord> mDataFrames GUARDED_BY(mAccessLock); // Shared memory buffers.
+ unsigned mFramesAllowed GUARDED_BY(mAccessLock); // How many buffers are we currently using.
+ unsigned mFramesInUse GUARDED_BY(mAccessLock); // How many buffers are currently outstanding.
+
+ StreamStateValues mStreamState GUARDED_BY(mAccessLock);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index aafdb70..b34e7e7 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -177,3 +177,194 @@
*/
ABSOLUTE_ZOOM,
};
+
+/**
+ * Structure identifies and describes an ultrasonics array in the car.
+ *
+ * A ultrasonics array represents a group of ultrasonic sensors within the
+ * car. These may be sensors that are physically connected to the same hardware
+ * control unit or represent a logical group of sensors like front and back.
+ * The HAL is responsible for filling out this structure for each Ultrasonics
+ * Array.
+ */
+struct UltrasonicsArrayDesc {
+ /**
+ * Unique identifier for the ultrasonic array. This may be a path or name of the
+ * physical control device or a string identifying a logical group of sensors forming an array
+ * such as "front_array" and "back_array".
+ */
+ string ultrasonicsArrayId;
+
+ /**
+ * Maximum number of readings (points on waveform) provided per sensor in
+ * each data frame. Used by client to pre-allocate required memory buffer for
+ * incoming data.
+ */
+ uint32_t maxReadingsPerSensorCount;
+
+ /**
+ * Maximum number of receiver sensors in a data frame. Must be between 1
+ * and sensorCount. Used by client to pre-allocate required memory buffer for
+ * incoming data.
+ */
+ uint32_t maxReceiversCount;
+
+ /**
+ * The order of sensors specified should preferably be in clockwise order
+ * around the car, starting from front left-most sensor.
+ */
+ vec<UltrasonicSensor> sensors;
+};
+
+/**
+ * Structure for rotation expressed as quaternions.
+ * Convention used: Unit quaternion with hamilton convention.
+ */
+struct RotationQuat {
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+/** Structure for translation with x, y and z units. */
+struct Translation {
+ float x;
+ float y;
+ float z;
+};
+
+/**
+ * Provides the orientation and location of a car sensor relative to the android automotive
+ * coordinate system:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ * The sensor pose defines the transformation to be applied to the android automotive axes to
+ * obtain the sensor local axes.
+ * The pose consists of rotation, (specified as a quaternions) and translation
+ * (vector with x, y, z).
+ * This rotation and translation applied to the sensor data in the sensor's local coordinate
+ * system transform the data to the automotive coordinate system.
+ * i.e Pcar = ( Rot * Psensor ) + Trans
+ * Here Pcar is a point in automotive coordinate system and Psensor is a point in the sensor's
+ * coordinate system.
+ * Example:
+ * For a sensor on the front bumper and on the left corner of the car with its X axis pointing to
+ * the front, the sensor is located at (-2, 4, 0) meters w.r.t android automotive axes and the
+ * sensor local axes has a rotation of 90 degrees counter-clockwise w.r.t android automotive axes
+ * when viewing the car from top on the +Z axis side:
+ *
+ * ↑X sensor
+ * Y←∘______
+ * | | front
+ * | car |
+ * | ↑Y |
+ * | ∘→X | rear
+ * |______|
+ *
+ * For this example the rotation and translation will be:
+ * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion.
+ * Translation = (-2, 4, 0) in meters = (-2000, 4000, 0) in milli-meters.
+ * Note: Every sensor type must specify its own pose.
+ */
+struct SensorPose {
+ /**
+ * Rotation part of the sensor pose, expressed as a unit quaternion.
+ */
+ RotationQuat rotation;
+
+ /**
+ * Translation part of the sensor pose, in (x, y, z) format with milli-meter units.
+ */
+ Translation translation;
+};
+
+/**
+ * Structure that contains all information of an ultrasonic sensor.
+ */
+struct UltrasonicSensor {
+ /**
+ * Pose provides the orientation and location of the ultrasonic sensor within the car.
+ * The +Y axis points along the center of the beam spread the X axis to the right and the Z
+ * axis in the up direction.
+ */
+ SensorPose pose;
+
+ /**
+ * Maximum range of the sensor in milli-metres.
+ */
+ float maxRange;
+
+ /**
+ * Half-angle of the angle of measurement of the sensor, relative to the
+ * sensor’s x axis, in radians.
+ */
+ float angleOfMeasurement;
+};
+
+/**
+ * Structure that describes the data frame received from an ultrasonics array.
+ *
+ * Each data frame returned consists of received waveform signals from a subset
+ * of sensors in an array as indicated by the receiversIdList. The signal is
+ * transmitted at a particular time instant indicated by timestampNs from a
+ * subset of sensors in the array as provided in the transmittersIdList.
+ */
+struct UltrasonicsDataFrameDesc {
+ /**
+ * Timestamp of the start of the transmit signal for this data frame.
+ * Timestamp unit is nanoseconds and is obtained from android elapsed realtime clock which is
+ * the time since system was booted and includes deep sleep.
+ * timeOfFlight readings are future-deltas to this timestamp.
+ */
+ uint64_t timestampNs;
+
+ /**
+ * Identifier of data frame. Used by implementation for managing multiple frames in flight.
+ */
+ uint32_t dataFrameId;
+
+ /**
+ * List of indexes of sensors in range [0, sensorCount - 1] that
+ * transmitted the signal for this data frame.
+ */
+ vec<uint8_t> transmittersIdList;
+
+ /**
+ * List of indexes of sensors in range [0, sensorCount - 1] that received
+ * the signal. The order of ids must match the order of the waveforms in the
+ * waveformsData.
+ * Size of list is upper bound by maxReceiversCount.
+ */
+ vec<uint8_t> receiversIdList;
+
+ /**
+ * List of the number of readings corresponding to each ultrasonics sensor in
+ * the receiversIdList. Order of the readings count must match the order in
+ * receiversIdList.
+ * Size of list is upper bound by maxReadingsPerSensorCount.
+ */
+ vec<uint32_t> receiversReadingsCountList;
+
+ /**
+ * Shared memory object containing the waveforms data. Contains one waveform
+ * for each sensor specified in receiversIdList, in order.
+ * Each waveform is represented by a number of readings, which are sample
+ * points on the waveform. The number of readings for each waveform is as
+ * specified in the receiversReadingsCountList.
+ * Each reading is a pair of time Of flight and resonance.
+ * Time of flight (float): Time between transmit and receive signal in nanoseconds.
+ * Resonance (float): Resonance at time on waveform in range [0.0, 1.0].
+ *
+ * The structure of shared memory (example with 2 waveforms, each with 2 readings):
+ *
+ * Byte: | 0 | 1-4 | 5-8 | 9-12 | 13-16 || 17 | 18-21 | 22-25 | 26-29 | 30-33 |
+ * Data: | RecId1 | TOF1 | RES1 | TOF2 | RES2 || RecId2 | TOF1 | RES1 | TOF2 | RES2 |
+ * | Waveform1 || Waveform2 |
+ * Here:
+ * RecId : Receiver's Id. Order matches the receiversIdList, type uint8_t
+ * TOF : Time of flight, type float (4 bytes)
+ * RES : Resonance, type float (4 bytes)
+ * Note: All readings and waveforms are contigious with no padding.
+ */
+ memory waveformsData;
+};
diff --git a/automotive/evs/1.1/vts/functional/Android.bp b/automotive/evs/1.1/vts/functional/Android.bp
index 4753933..086a199 100644
--- a/automotive/evs/1.1/vts/functional/Android.bp
+++ b/automotive/evs/1.1/vts/functional/Android.bp
@@ -18,12 +18,16 @@
name: "VtsHalEvsV1_1TargetTest",
srcs: [
"FrameHandler.cpp",
+ "FrameHandlerUltrasonics.cpp",
"VtsHalEvsV1_1TargetTest.cpp",
],
defaults: ["VtsHalTargetTestDefaults"],
shared_libs: [
"libui",
"libcamera_metadata",
+ "libhidlmemory",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
],
static_libs: [
"android.hardware.automotive.evs@1.0",
@@ -34,7 +38,7 @@
"android.hardware.graphics.common@1.2",
"android.hardware.camera.device@3.2",
],
- test_suites: ["general-tests"],
+ test_suites: ["vts-core"],
cflags: [
"-O0",
"-g",
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
new file mode 100644
index 0000000..22522ce
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameHandlerUltrasonics.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::Return;
+using ::android::sp;
+
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventType;
+
+FrameHandlerUltrasonics::FrameHandlerUltrasonics(sp<IEvsUltrasonicsArray> pEvsUltrasonicsArray) :
+ mEvsUltrasonicsArray(pEvsUltrasonicsArray), mReceiveFramesCount(0) {
+ // Nothing but member initialization
+}
+
+Return<void> FrameHandlerUltrasonics::notify(const EvsEventDesc& evsEvent) {
+ switch (evsEvent.aType) {
+ case EvsEventType::STREAM_STARTED:
+ case EvsEventType::STREAM_STOPPED:
+ case EvsEventType::FRAME_DROPPED:
+ case EvsEventType::TIMEOUT:
+ mReceivedEvents.emplace_back(evsEvent);
+ break;
+ default:
+ LOG(ERROR) << "Received unexpected event";
+ }
+
+ return android::hardware::Void();
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+ uint8_t receiverId;
+ std::vector<std::pair<float, float>> readings;
+};
+
+// De-serializes shared memory to vector of WaveformData.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+std::vector<WaveformData> DeSerializeWaveformData(std::vector<uint32_t> recvReadingsCountList,
+ uint8_t* pData) {
+ std::vector<WaveformData> waveformDataList(recvReadingsCountList.size());
+
+ for (int i = 0; i < waveformDataList.size(); i++) {
+ // Set Id
+ memcpy(&waveformDataList[i].receiverId, pData, sizeof(uint8_t));
+ pData += sizeof(uint8_t);
+
+ waveformDataList[i].readings.resize(recvReadingsCountList[i]);
+
+ for (auto& reading : waveformDataList[i].readings) {
+ // Set the time of flight.
+ memcpy(&reading.first, pData, sizeof(float));
+ pData += sizeof(float);
+
+ // Set the resonance.
+ memcpy(&reading.second, pData, sizeof(float));
+ pData += sizeof(float);
+ }
+ }
+ return waveformDataList;
+}
+
+bool DataFrameValidator(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+
+ if (dataFrameDesc.receiversIdList.size() != dataFrameDesc.receiversReadingsCountList.size()) {
+ LOG(ERROR) << "Size mismatch of receiversIdList and receiversReadingsCountList";
+ return false;
+ }
+
+ if(!dataFrameDesc.waveformsData.valid()) {
+ LOG(ERROR) << "Data frame does not valid hidl memory";
+ return false;
+ }
+
+ // Check total bytes from dataFrameDesc are within the shared memory size.
+ int totalWaveformDataBytesSize = 0;
+ for (int i = 0; i < dataFrameDesc.receiversIdList.size(); i++) {
+ totalWaveformDataBytesSize = 1 + (4 * 2 * dataFrameDesc.receiversReadingsCountList[i]);
+ }
+ if (totalWaveformDataBytesSize > dataFrameDesc.waveformsData.size()) {
+ LOG(ERROR) << "Total waveform data bytes in desc exceed shared memory size";
+ return false;
+ }
+
+ sp<IMemory> pIMemory = mapMemory(dataFrameDesc.waveformsData);
+ if(pIMemory.get() == nullptr) {
+ LOG(ERROR) << "Failed to map hidl memory";
+ return false;
+ }
+
+ uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+ if(pData == nullptr) {
+ LOG(ERROR) << "Failed getPointer from mapped shared memory";
+ return false;
+ }
+
+ const std::vector<WaveformData> waveformDataList = DeSerializeWaveformData(
+ dataFrameDesc.receiversReadingsCountList, pData);
+
+ // Verify the waveforms data.
+ for(int i = 0; i < waveformDataList.size(); i++) {
+ if (waveformDataList[i].receiverId != dataFrameDesc.receiversIdList[i]) {
+ LOG(ERROR) << "Receiver Id mismatch";
+ return false;
+ }
+ for(auto& reading : waveformDataList[i].readings) {
+ if (reading.second < 0.0f || reading.second > 1.0f) {
+ LOG(ERROR) << "Resonance reading is not in range [0, 1]";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+Return<void> FrameHandlerUltrasonics::deliverDataFrame(
+ const UltrasonicsDataFrameDesc& dataFrameDesc) {
+ LOG(DEBUG) << "FrameHandlerUltrasonics::receiveFrames";
+
+ mReceiveFramesCount++;
+ mLastReceivedFrames = dataFrameDesc;
+
+ if(!DataFrameValidator(dataFrameDesc)) {
+ mAllFramesValid = false;
+ }
+
+ // Send done with data frame.
+ mEvsUltrasonicsArray->doneWithDataFrame(dataFrameDesc);
+
+ return android::hardware::Void();
+}
+
+bool FrameHandlerUltrasonics::checkEventReceived(EvsEventDesc evsEvent) {
+ LOG(DEBUG) << "FrameHandlerUltrasonics::checkEventReceived";
+ int size = mReceivedEvents.size(); // work around
+ LOG(DEBUG) << "Received event number: " << size;
+ auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), evsEvent);
+ return iter != mReceivedEvents.end();
+}
+
+int FrameHandlerUltrasonics::getReceiveFramesCount() {
+ return mReceiveFramesCount;
+}
+
+bool FrameHandlerUltrasonics::areAllFramesValid() {
+ return mAllFramesValid;
+}
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
new file mode 100644
index 0000000..1fc2143
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAME_HANDLER_ULTRASONICS_H
+#define FRAME_HANDLER_ULTRASONICS_H
+
+#include <android/hardware/automotive/evs/1.1/types.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+
+#include <vector>
+
+class FrameHandlerUltrasonics : public
+ android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream {
+public:
+ FrameHandlerUltrasonics(
+ android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+ pEvsUltrasonicsArray);
+
+ // Implementation for ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream
+ android::hardware::Return<void> notify(
+ const android::hardware::automotive::evs::V1_1::EvsEventDesc& evsEvent) override;
+ android::hardware::Return<void> deliverDataFrame(
+ const android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc&
+ dataFrameDesc) override;
+
+ bool checkEventReceived(android::hardware::automotive::evs::V1_1::EvsEventDesc evsEvent);
+ int getReceiveFramesCount();
+ bool areAllFramesValid();
+
+private:
+ android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+ mEvsUltrasonicsArray;
+ android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc mLastReceivedFrames;
+ std::vector<android::hardware::automotive::evs::V1_1::EvsEventDesc> mReceivedEvents;
+ int mReceiveFramesCount;
+ bool mAllFramesValid = true;
+};
+
+#endif //FRAME_HANDLER_ULTRASONICS_H
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 2c8c1e1..8580500 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -37,6 +37,7 @@
#include "FrameHandler.h"
+#include "FrameHandlerUltrasonics.h"
#include <cstdio>
#include <cstring>
@@ -151,9 +152,21 @@
}
}
);
+ }
- // We insist on at least one camera for EVS to pass any camera tests
- ASSERT_GE(cameraInfo.size(), 1u);
+ void loadUltrasonicsArrayList() {
+ // SetUp() must run first!
+ assert(pEnumerator != nullptr);
+
+ // Get the ultrasonics array list
+ pEnumerator->getUltrasonicsArrayList([this](hidl_vec<UltrasonicsArrayDesc> ultraList) {
+ ALOGI("Ultrasonics array list callback received %zu arrays", ultraList.size());
+ ultrasonicsArraysInfo.reserve(ultraList.size());
+ for (auto&& ultraArray : ultraList) {
+ ALOGI("Found ultrasonics array %s", ultraArray.ultrasonicsArrayId.c_str());
+ ultrasonicsArraysInfo.push_back(ultraArray);
+ }
+ });
}
bool isLogicalCamera(const camera_metadata_t *metadata) {
@@ -240,6 +253,11 @@
// is HW module implementation.
std::deque<wp<IEvsCamera_1_1>> activeCameras; // A list of active camera handles that are
// needed to be cleaned up.
+ std::vector<UltrasonicsArrayDesc>
+ ultrasonicsArraysInfo; // Empty unless/until
+ // loadUltrasonicsArrayList() is called
+ std::deque<wp<IEvsCamera_1_1>> activeUltrasonicsArrays; // A list of active ultrasonic array
+ // handles that are to be cleaned up.
};
@@ -2220,6 +2238,110 @@
}
+/*
+ * UltrasonicsArrayOpenClean:
+ * Opens each ultrasonics arrays reported by the enumerator and then explicitly closes it via a
+ * call to closeUltrasonicsArray. Then repeats the test to ensure all ultrasonics arrays
+ * can be reopened.
+ */
+TEST_F(EvsHidlTest, UltrasonicsArrayOpenClean) {
+ ALOGI("Starting UltrasonicsArrayOpenClean test");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // Open and close each ultrasonics array twice
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ for (int pass = 0; pass < 2; pass++) {
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ // Verify that this ultrasonics array self-identifies correctly
+ pUltrasonicsArray->getUltrasonicArrayInfo([&ultraInfo](UltrasonicsArrayDesc desc) {
+ ALOGD("Found ultrasonics array %s", ultraInfo.ultrasonicsArrayId.c_str());
+ EXPECT_EQ(ultraInfo.ultrasonicsArrayId, desc.ultrasonicsArrayId);
+ });
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+ }
+}
+
+
+// Starts a stream and verifies all data received is valid.
+TEST_F(EvsHidlTest, UltrasonicsVerifyStreamData) {
+ ALOGI("Starting UltrasonicsVerifyStreamData");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // For each ultrasonics array.
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+ // Start stream.
+ EvsResult result = pUltrasonicsArray->startStream(frameHandler);
+ ASSERT_EQ(result, EvsResult::OK);
+
+ // Wait 5 seconds to receive frames.
+ sleep(5);
+
+ // Stop stream.
+ pUltrasonicsArray->stopStream();
+
+ EXPECT_GT(frameHandler->getReceiveFramesCount(), 0);
+ EXPECT_TRUE(frameHandler->areAllFramesValid());
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+}
+
+
+// Sets frames in flight before and after start of stream and verfies success.
+TEST_F(EvsHidlTest, UltrasonicsSetFramesInFlight) {
+ ALOGI("Starting UltrasonicsSetFramesInFlight");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // For each ultrasonics array.
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ EvsResult result = pUltrasonicsArray->setMaxFramesInFlight(10);
+ EXPECT_EQ(result, EvsResult::OK);
+
+ sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+ // Start stream.
+ result = pUltrasonicsArray->startStream(frameHandler);
+ ASSERT_EQ(result, EvsResult::OK);
+
+ result = pUltrasonicsArray->setMaxFramesInFlight(5);
+ EXPECT_EQ(result, EvsResult::OK);
+
+ // Stop stream.
+ pUltrasonicsArray->stopStream();
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+}
+
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);