diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp new file mode 100644 index 0000000000..c13eaf2e92 --- /dev/null +++ b/sensors/2.0/multihal/Android.bp @@ -0,0 +1,73 @@ +// +// Copyright (C) 2019 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. + +cc_defaults { + name: "android.hardware.sensors@2.0-multihal-defaults", + header_libs: [ + "android.hardware.sensors@2.0-multihal.header", + ], + shared_libs: [ + "android.hardware.sensors@1.0", + "android.hardware.sensors@2.0", + "libbase", + "libcutils", + "libfmq", + "libhidlbase", + "liblog", + "libpower", + "libutils", + ], + cflags: ["-DLOG_TAG=\"SensorsMultiHal\""], +} + +cc_binary { + name: "android.hardware.sensors@2.0-service.multihal", + defaults: [ + "hidl_defaults", + "android.hardware.sensors@2.0-multihal-defaults", + ], + vendor: true, + relative_install_path: "hw", + srcs: [ + "service.cpp", + "HalProxy.cpp", + "ScopedWakelock.cpp", + ], + init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"], + vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"], +} + +cc_library_headers { + name: "android.hardware.sensors@2.0-multihal.header", + vendor_available: true, + export_include_dirs: ["include"], +} + +// The below targets should only be used for testing. +cc_test_library { + name: "android.hardware.sensors@2.0-HalProxy", + defaults: ["android.hardware.sensors@2.0-multihal-defaults"], + vendor_available: true, + srcs: [ + "HalProxy.cpp", + "ScopedWakelock.cpp", + ], + export_header_lib_headers: [ + "android.hardware.sensors@2.0-multihal.header", + ], + shared_libs: [ + "libutils", + ], +} diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp new file mode 100644 index 0000000000..49c5a0d412 --- /dev/null +++ b/sensors/2.0/multihal/HalProxy.cpp @@ -0,0 +1,686 @@ +/* + * Copyright (C) 2019 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 "HalProxy.h" + +#include "SubHal.h" + +#include + +#include +#include "hardware_legacy/power.h" + +#include + +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::sensors::V2_0::EventQueueFlagBits; +using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits; +using ::android::hardware::sensors::V2_0::implementation::getTimeNow; +using ::android::hardware::sensors::V2_0::implementation::kWakelockTimeoutNs; + +typedef ISensorsSubHal*(SensorsHalGetSubHalFunc)(uint32_t*); + +static constexpr int32_t kBitsAfterSubHalIndex = 24; + +/** + * Set the subhal index as first byte of sensor handle and return this modified version. + * + * @param sensorHandle The sensor handle to modify. + * @param subHalIndex The index in the hal proxy of the sub hal this sensor belongs to. + * + * @return The modified sensor handle. + */ +int32_t setSubHalIndex(int32_t sensorHandle, size_t subHalIndex) { + return sensorHandle | (static_cast(subHalIndex) << kBitsAfterSubHalIndex); +} + +/** + * Extract the subHalIndex from sensorHandle. + * + * @param sensorHandle The sensorHandle to extract from. + * + * @return The subhal index. + */ +size_t extractSubHalIndex(int32_t sensorHandle) { + return static_cast(sensorHandle >> kBitsAfterSubHalIndex); +} + +/** + * Convert nanoseconds to milliseconds. + * + * @param nanos The nanoseconds input. + * + * @return The milliseconds count. + */ +int64_t msFromNs(int64_t nanos) { + constexpr int64_t nanosecondsInAMillsecond = 1000000; + return nanos / nanosecondsInAMillsecond; +} + +HalProxy::HalProxy() { + const char* kMultiHalConfigFile = "/vendor/etc/sensors/hals.conf"; + initializeSubHalListFromConfigFile(kMultiHalConfigFile); + init(); +} + +HalProxy::HalProxy(std::vector& subHalList) : mSubHalList(subHalList) { + init(); +} + +HalProxy::~HalProxy() { + stopThreads(); +} + +Return HalProxy::getSensorsList(getSensorsList_cb _hidl_cb) { + std::vector sensors; + for (const auto& iter : mSensors) { + sensors.push_back(iter.second); + } + _hidl_cb(sensors); + return Void(); +} + +Return HalProxy::setOperationMode(OperationMode mode) { + Result result = Result::OK; + size_t subHalIndex; + for (subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) { + ISensorsSubHal* subHal = mSubHalList[subHalIndex]; + result = subHal->setOperationMode(mode); + if (result != Result::OK) { + ALOGE("setOperationMode failed for SubHal: %s", subHal->getName().c_str()); + break; + } + } + if (result != Result::OK) { + // Reset the subhal operation modes that have been flipped + for (size_t i = 0; i < subHalIndex; i++) { + ISensorsSubHal* subHal = mSubHalList[i]; + subHal->setOperationMode(mCurrentOperationMode); + } + } else { + mCurrentOperationMode = mode; + } + return result; +} + +Return HalProxy::activate(int32_t sensorHandle, bool enabled) { + if (!isSubHalIndexValid(sensorHandle)) { + return Result::BAD_VALUE; + } + return getSubHalForSensorHandle(sensorHandle) + ->activate(clearSubHalIndex(sensorHandle), enabled); +} + +Return HalProxy::initialize( + const ::android::hardware::MQDescriptorSync& eventQueueDescriptor, + const ::android::hardware::MQDescriptorSync& wakeLockDescriptor, + const sp& sensorsCallback) { + Result result = Result::OK; + + stopThreads(); + resetSharedWakelock(); + + // So that the pending write events queue can be cleared safely and when we start threads + // again we do not get new events until after initialize resets the subhals. + disableAllSensors(); + + // Clears the queue if any events were pending write before. + mPendingWriteEventsQueue = std::queue, size_t>>(); + + // Clears previously connected dynamic sensors + mDynamicSensors.clear(); + + mDynamicSensorsCallback = sensorsCallback; + + // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions. + mEventQueue = + std::make_unique(eventQueueDescriptor, true /* resetPointers */); + + // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP + // events have been successfully read and handled by the framework. + mWakeLockQueue = + std::make_unique(wakeLockDescriptor, true /* resetPointers */); + + if (mEventQueueFlag != nullptr) { + EventFlag::deleteEventFlag(&mEventQueueFlag); + } + if (mWakelockQueueFlag != nullptr) { + EventFlag::deleteEventFlag(&mWakelockQueueFlag); + } + if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) { + result = Result::BAD_VALUE; + } + if (EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakelockQueueFlag) != OK) { + result = Result::BAD_VALUE; + } + if (!mDynamicSensorsCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) { + result = Result::BAD_VALUE; + } + + mThreadsRun.store(true); + + mPendingWritesThread = std::thread(startPendingWritesThread, this); + mWakelockThread = std::thread(startWakelockThread, this); + + for (size_t i = 0; i < mSubHalList.size(); i++) { + auto subHal = mSubHalList[i]; + const auto& subHalCallback = mSubHalCallbacks[i]; + Result currRes = subHal->initialize(subHalCallback); + if (currRes != Result::OK) { + result = currRes; + ALOGE("Subhal '%s' failed to initialize.", subHal->getName().c_str()); + break; + } + } + + mCurrentOperationMode = OperationMode::NORMAL; + + return result; +} + +Return HalProxy::batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) { + if (!isSubHalIndexValid(sensorHandle)) { + return Result::BAD_VALUE; + } + return getSubHalForSensorHandle(sensorHandle) + ->batch(clearSubHalIndex(sensorHandle), samplingPeriodNs, maxReportLatencyNs); +} + +Return HalProxy::flush(int32_t sensorHandle) { + if (!isSubHalIndexValid(sensorHandle)) { + return Result::BAD_VALUE; + } + return getSubHalForSensorHandle(sensorHandle)->flush(clearSubHalIndex(sensorHandle)); +} + +Return HalProxy::injectSensorData(const Event& event) { + Result result = Result::OK; + if (mCurrentOperationMode == OperationMode::NORMAL && + event.sensorType != V1_0::SensorType::ADDITIONAL_INFO) { + ALOGE("An event with type != ADDITIONAL_INFO passed to injectSensorData while operation" + " mode was NORMAL."); + result = Result::BAD_VALUE; + } + if (result == Result::OK) { + Event subHalEvent = event; + if (!isSubHalIndexValid(event.sensorHandle)) { + return Result::BAD_VALUE; + } + subHalEvent.sensorHandle = clearSubHalIndex(event.sensorHandle); + result = getSubHalForSensorHandle(event.sensorHandle)->injectSensorData(subHalEvent); + } + return result; +} + +Return HalProxy::registerDirectChannel(const SharedMemInfo& mem, + registerDirectChannel_cb _hidl_cb) { + if (mDirectChannelSubHal == nullptr) { + _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */); + } else { + mDirectChannelSubHal->registerDirectChannel(mem, _hidl_cb); + } + return Return(); +} + +Return HalProxy::unregisterDirectChannel(int32_t channelHandle) { + Result result; + if (mDirectChannelSubHal == nullptr) { + result = Result::INVALID_OPERATION; + } else { + result = mDirectChannelSubHal->unregisterDirectChannel(channelHandle); + } + return result; +} + +Return HalProxy::configDirectReport(int32_t sensorHandle, int32_t channelHandle, + RateLevel rate, configDirectReport_cb _hidl_cb) { + if (mDirectChannelSubHal == nullptr) { + _hidl_cb(Result::INVALID_OPERATION, -1 /* reportToken */); + } else { + mDirectChannelSubHal->configDirectReport(clearSubHalIndex(sensorHandle), channelHandle, + rate, _hidl_cb); + } + return Return(); +} + +Return HalProxy::debug(const hidl_handle& fd, const hidl_vec& /*args*/) { + if (fd.getNativeHandle() == nullptr || fd->numFds < 1) { + ALOGE("%s: missing fd for writing", __FUNCTION__); + return Void(); + } + + android::base::borrowed_fd writeFd = dup(fd->data[0]); + + std::ostringstream stream; + stream << "===HalProxy===" << std::endl; + stream << "Internal values:" << std::endl; + stream << " Threads are running: " << (mThreadsRun.load() ? "true" : "false") << std::endl; + int64_t now = getTimeNow(); + stream << " Wakelock timeout start time: " << msFromNs(now - mWakelockTimeoutStartTime) + << " ms ago" << std::endl; + stream << " Wakelock timeout reset time: " << msFromNs(now - mWakelockTimeoutResetTime) + << " ms ago" << std::endl; + // TODO(b/142969448): Add logging for history of wakelock acquisition per subhal. + stream << " Wakelock ref count: " << mWakelockRefCount << std::endl; + stream << " Size of pending write events queue: " << mPendingWriteEventsQueue.size() + << std::endl; + if (!mPendingWriteEventsQueue.empty()) { + stream << " Size of events list on front of pending writes queue: " + << mPendingWriteEventsQueue.front().first.size() << std::endl; + } + stream << " # of non-dynamic sensors across all subhals: " << mSensors.size() << std::endl; + stream << " # of dynamic sensors across all subhals: " << mDynamicSensors.size() << std::endl; + stream << "SubHals (" << mSubHalList.size() << "):" << std::endl; + for (ISensorsSubHal* subHal : mSubHalList) { + stream << " Name: " << subHal->getName() << std::endl; + stream << " Debug dump: " << std::endl; + android::base::WriteStringToFd(stream.str(), writeFd); + subHal->debug(fd, {}); + stream.str(""); + stream << std::endl; + } + android::base::WriteStringToFd(stream.str(), writeFd); + return Return(); +} + +Return HalProxy::onDynamicSensorsConnected(const hidl_vec& dynamicSensorsAdded, + int32_t subHalIndex) { + std::vector sensors; + { + std::lock_guard lock(mDynamicSensorsMutex); + for (SensorInfo sensor : dynamicSensorsAdded) { + if (!subHalIndexIsClear(sensor.sensorHandle)) { + ALOGE("Dynamic sensor added %s had sensorHandle with first byte not 0.", + sensor.name.c_str()); + } else { + sensor.sensorHandle = setSubHalIndex(sensor.sensorHandle, subHalIndex); + mDynamicSensors[sensor.sensorHandle] = sensor; + sensors.push_back(sensor); + } + } + } + mDynamicSensorsCallback->onDynamicSensorsConnected(sensors); + return Return(); +} + +Return HalProxy::onDynamicSensorsDisconnected( + const hidl_vec& dynamicSensorHandlesRemoved, int32_t subHalIndex) { + // TODO(b/143302327): Block this call until all pending events are flushed from queue + std::vector sensorHandles; + { + std::lock_guard lock(mDynamicSensorsMutex); + for (int32_t sensorHandle : dynamicSensorHandlesRemoved) { + if (!subHalIndexIsClear(sensorHandle)) { + ALOGE("Dynamic sensorHandle removed had first byte not 0."); + } else { + sensorHandle = setSubHalIndex(sensorHandle, subHalIndex); + if (mDynamicSensors.find(sensorHandle) != mDynamicSensors.end()) { + mDynamicSensors.erase(sensorHandle); + sensorHandles.push_back(sensorHandle); + } + } + } + } + mDynamicSensorsCallback->onDynamicSensorsDisconnected(sensorHandles); + return Return(); +} + +void HalProxy::initializeSubHalListFromConfigFile(const char* configFileName) { + std::ifstream subHalConfigStream(configFileName); + if (!subHalConfigStream) { + ALOGE("Failed to load subHal config file: %s", configFileName); + } else { + std::string subHalLibraryFile; + while (subHalConfigStream >> subHalLibraryFile) { + void* handle = dlopen(subHalLibraryFile.c_str(), RTLD_NOW); + if (handle == nullptr) { + ALOGE("dlopen failed for library: %s", subHalLibraryFile.c_str()); + } else { + SensorsHalGetSubHalFunc* sensorsHalGetSubHalPtr = + (SensorsHalGetSubHalFunc*)dlsym(handle, "sensorsHalGetSubHal"); + if (sensorsHalGetSubHalPtr == nullptr) { + ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s", + subHalLibraryFile.c_str()); + } else { + std::function sensorsHalGetSubHal = + *sensorsHalGetSubHalPtr; + uint32_t version; + ISensorsSubHal* subHal = sensorsHalGetSubHal(&version); + if (version != SUB_HAL_2_0_VERSION) { + ALOGE("SubHal version was not 2.0 for library: %s", + subHalLibraryFile.c_str()); + } else { + ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str()); + mSubHalList.push_back(subHal); + } + } + } + } + } +} + +void HalProxy::initializeSubHalCallbacks() { + for (size_t subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) { + sp callback = new HalProxyCallback(this, subHalIndex); + mSubHalCallbacks.push_back(callback); + } +} + +void HalProxy::initializeSensorList() { + for (size_t subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) { + ISensorsSubHal* subHal = mSubHalList[subHalIndex]; + auto result = subHal->getSensorsList([&](const auto& list) { + for (SensorInfo sensor : list) { + if (!subHalIndexIsClear(sensor.sensorHandle)) { + ALOGE("SubHal sensorHandle's first byte was not 0"); + } else { + ALOGV("Loaded sensor: %s", sensor.name.c_str()); + sensor.sensorHandle = setSubHalIndex(sensor.sensorHandle, subHalIndex); + setDirectChannelFlags(&sensor, subHal); + mSensors[sensor.sensorHandle] = sensor; + } + } + }); + if (!result.isOk()) { + ALOGE("getSensorsList call failed for SubHal: %s", subHal->getName().c_str()); + } + } +} + +void HalProxy::init() { + initializeSubHalCallbacks(); + initializeSensorList(); +} + +void HalProxy::stopThreads() { + mThreadsRun.store(false); + if (mEventQueueFlag != nullptr && mEventQueue != nullptr) { + size_t numToRead = mEventQueue->availableToRead(); + std::vector events(numToRead); + mEventQueue->read(events.data(), numToRead); + mEventQueueFlag->wake(static_cast(EventQueueFlagBits::EVENTS_READ)); + } + if (mWakelockQueueFlag != nullptr && mWakeLockQueue != nullptr) { + uint32_t kZero = 0; + mWakeLockQueue->write(&kZero); + mWakelockQueueFlag->wake(static_cast(WakeLockQueueFlagBits::DATA_WRITTEN)); + } + mWakelockCV.notify_one(); + mEventQueueWriteCV.notify_one(); + if (mPendingWritesThread.joinable()) { + mPendingWritesThread.join(); + } + if (mWakelockThread.joinable()) { + mWakelockThread.join(); + } +} + +void HalProxy::disableAllSensors() { + for (const auto& sensorEntry : mSensors) { + int32_t sensorHandle = sensorEntry.first; + activate(sensorHandle, false /* enabled */); + } + std::lock_guard dynamicSensorsLock(mDynamicSensorsMutex); + for (const auto& sensorEntry : mDynamicSensors) { + int32_t sensorHandle = sensorEntry.first; + activate(sensorHandle, false /* enabled */); + } +} + +void HalProxy::startPendingWritesThread(HalProxy* halProxy) { + halProxy->handlePendingWrites(); +} + +void HalProxy::handlePendingWrites() { + // TODO(b/143302327): Find a way to optimize locking strategy maybe using two mutexes instead of + // one. + std::unique_lock lock(mEventQueueWriteMutex); + while (mThreadsRun.load()) { + mEventQueueWriteCV.wait( + lock, [&] { return !mPendingWriteEventsQueue.empty() || !mThreadsRun.load(); }); + if (mThreadsRun.load()) { + std::vector& pendingWriteEvents = mPendingWriteEventsQueue.front().first; + size_t numWakeupEvents = mPendingWriteEventsQueue.front().second; + size_t eventQueueSize = mEventQueue->getQuantumCount(); + size_t numToWrite = std::min(pendingWriteEvents.size(), eventQueueSize); + lock.unlock(); + if (!mEventQueue->writeBlocking( + pendingWriteEvents.data(), numToWrite, + static_cast(EventQueueFlagBits::EVENTS_READ), + static_cast(EventQueueFlagBits::READ_AND_PROCESS), + kPendingWriteTimeoutNs, mEventQueueFlag)) { + ALOGE("Dropping %zu events after blockingWrite failed.", numToWrite); + if (numWakeupEvents > 0) { + if (pendingWriteEvents.size() > eventQueueSize) { + decrementRefCountAndMaybeReleaseWakelock( + countNumWakeupEvents(pendingWriteEvents, eventQueueSize)); + } else { + decrementRefCountAndMaybeReleaseWakelock(numWakeupEvents); + } + } + } + lock.lock(); + if (pendingWriteEvents.size() > eventQueueSize) { + // TODO(b/143302327): Check if this erase operation is too inefficient. It will copy + // all the events ahead of it down to fill gap off array at front after the erase. + pendingWriteEvents.erase(pendingWriteEvents.begin(), + pendingWriteEvents.begin() + eventQueueSize); + } else { + mPendingWriteEventsQueue.pop(); + } + } + } +} + +void HalProxy::startWakelockThread(HalProxy* halProxy) { + halProxy->handleWakelocks(); +} + +void HalProxy::handleWakelocks() { + std::unique_lock lock(mWakelockMutex); + while (mThreadsRun.load()) { + mWakelockCV.wait(lock, [&] { return mWakelockRefCount > 0 || !mThreadsRun.load(); }); + if (mThreadsRun.load()) { + int64_t timeLeft; + if (sharedWakelockDidTimeout(&timeLeft)) { + resetSharedWakelock(); + } else { + uint32_t numWakeLocksProcessed; + lock.unlock(); + bool success = mWakeLockQueue->readBlocking( + &numWakeLocksProcessed, 1, 0, + static_cast(WakeLockQueueFlagBits::DATA_WRITTEN), timeLeft); + lock.lock(); + if (success) { + decrementRefCountAndMaybeReleaseWakelock( + static_cast(numWakeLocksProcessed)); + } + } + } + } + resetSharedWakelock(); +} + +bool HalProxy::sharedWakelockDidTimeout(int64_t* timeLeft) { + bool didTimeout; + int64_t duration = getTimeNow() - mWakelockTimeoutStartTime; + if (duration > kWakelockTimeoutNs) { + didTimeout = true; + } else { + didTimeout = false; + *timeLeft = kWakelockTimeoutNs - duration; + } + return didTimeout; +} + +void HalProxy::resetSharedWakelock() { + std::lock_guard lockGuard(mWakelockMutex); + decrementRefCountAndMaybeReleaseWakelock(mWakelockRefCount); + mWakelockTimeoutResetTime = getTimeNow(); +} + +void HalProxy::postEventsToMessageQueue(const std::vector& events, size_t numWakeupEvents, + ScopedWakelock wakelock) { + size_t numToWrite = 0; + std::lock_guard lock(mEventQueueWriteMutex); + if (wakelock.isLocked()) { + incrementRefCountAndMaybeAcquireWakelock(numWakeupEvents); + } + if (mPendingWriteEventsQueue.empty()) { + numToWrite = std::min(events.size(), mEventQueue->availableToWrite()); + if (numToWrite > 0) { + if (mEventQueue->write(events.data(), numToWrite)) { + // TODO(b/143302327): While loop if mEventQueue->avaiableToWrite > 0 to possibly fit + // in more writes immediately + mEventQueueFlag->wake(static_cast(EventQueueFlagBits::READ_AND_PROCESS)); + } else { + numToWrite = 0; + } + } + } + if (numToWrite < events.size()) { + // TODO(b/143302327): Bound the mPendingWriteEventsQueue so that we do not trigger OOMs if + // framework stalls + std::vector eventsLeft(events.begin() + numToWrite, events.end()); + mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents}); + mEventQueueWriteCV.notify_one(); + } +} + +bool HalProxy::incrementRefCountAndMaybeAcquireWakelock(size_t delta, + int64_t* timeoutStart /* = nullptr */) { + if (!mThreadsRun.load()) return false; + std::lock_guard lockGuard(mWakelockMutex); + if (mWakelockRefCount == 0) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakelockName); + mWakelockCV.notify_one(); + } + mWakelockTimeoutStartTime = getTimeNow(); + mWakelockRefCount += delta; + if (timeoutStart != nullptr) { + *timeoutStart = mWakelockTimeoutStartTime; + } + return true; +} + +void HalProxy::decrementRefCountAndMaybeReleaseWakelock(size_t delta, + int64_t timeoutStart /* = -1 */) { + if (!mThreadsRun.load()) return; + std::lock_guard lockGuard(mWakelockMutex); + if (timeoutStart == -1) timeoutStart = mWakelockTimeoutResetTime; + if (mWakelockRefCount == 0 || timeoutStart < mWakelockTimeoutResetTime) return; + mWakelockRefCount -= std::min(mWakelockRefCount, delta); + if (mWakelockRefCount == 0) { + release_wake_lock(kWakelockName); + } +} + +void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal) { + bool sensorSupportsDirectChannel = + (sensorInfo->flags & (V1_0::SensorFlagBits::MASK_DIRECT_REPORT | + V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL)) != 0; + if (mDirectChannelSubHal == nullptr && sensorSupportsDirectChannel) { + mDirectChannelSubHal = subHal; + } else if (mDirectChannelSubHal != nullptr && subHal != mDirectChannelSubHal) { + // disable direct channel capability for sensors in subHals that are not + // the only one we will enable + sensorInfo->flags &= ~(V1_0::SensorFlagBits::MASK_DIRECT_REPORT | + V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL); + } +} + +ISensorsSubHal* HalProxy::getSubHalForSensorHandle(int32_t sensorHandle) { + return mSubHalList[extractSubHalIndex(sensorHandle)]; +} + +bool HalProxy::isSubHalIndexValid(int32_t sensorHandle) { + return extractSubHalIndex(sensorHandle) < mSubHalList.size(); +} + +size_t HalProxy::countNumWakeupEvents(const std::vector& events, size_t n) { + size_t numWakeupEvents = 0; + for (size_t i = 0; i < n; i++) { + int32_t sensorHandle = events[i].sensorHandle; + if (mSensors[sensorHandle].flags & static_cast(V1_0::SensorFlagBits::WAKE_UP)) { + numWakeupEvents++; + } + } + return numWakeupEvents; +} + +int32_t HalProxy::clearSubHalIndex(int32_t sensorHandle) { + return sensorHandle & (~kSensorHandleSubHalIndexMask); +} + +bool HalProxy::subHalIndexIsClear(int32_t sensorHandle) { + return (sensorHandle & kSensorHandleSubHalIndexMask) == 0; +} + +void HalProxyCallback::postEvents(const std::vector& events, ScopedWakelock wakelock) { + if (events.empty() || !mHalProxy->areThreadsRunning()) return; + size_t numWakeupEvents; + std::vector processedEvents = processEvents(events, &numWakeupEvents); + if (numWakeupEvents > 0) { + ALOG_ASSERT(wakelock.isLocked(), + "Wakeup events posted while wakelock unlocked for subhal" + " w/ index %zu.", + mSubHalIndex); + } else { + ALOG_ASSERT(!wakelock.isLocked(), + "No Wakeup events posted but wakelock locked for subhal" + " w/ index %zu.", + mSubHalIndex); + } + mHalProxy->postEventsToMessageQueue(processedEvents, numWakeupEvents, std::move(wakelock)); +} + +ScopedWakelock HalProxyCallback::createScopedWakelock(bool lock) { + ScopedWakelock wakelock(mHalProxy, lock); + return wakelock; +} + +std::vector HalProxyCallback::processEvents(const std::vector& events, + size_t* numWakeupEvents) const { + *numWakeupEvents = 0; + std::vector eventsOut; + for (Event event : events) { + event.sensorHandle = setSubHalIndex(event.sensorHandle, mSubHalIndex); + eventsOut.push_back(event); + const SensorInfo& sensor = mHalProxy->getSensorInfo(event.sensorHandle); + if ((sensor.flags & V1_0::SensorFlagBits::WAKE_UP) != 0) { + (*numWakeupEvents)++; + } + } + return eventsOut; +} + +} // namespace implementation +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android diff --git a/sensors/2.0/multihal/OWNERS b/sensors/2.0/multihal/OWNERS new file mode 100644 index 0000000000..e9556700d6 --- /dev/null +++ b/sensors/2.0/multihal/OWNERS @@ -0,0 +1,3 @@ +arthuri@google.com +bduddie@google.com +stange@google.com \ No newline at end of file diff --git a/sensors/2.0/multihal/ScopedWakelock.cpp b/sensors/2.0/multihal/ScopedWakelock.cpp new file mode 100644 index 0000000000..d85d4a7891 --- /dev/null +++ b/sensors/2.0/multihal/ScopedWakelock.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 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 "ScopedWakelock.h" + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace implementation { + +int64_t getTimeNow() { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); +} + +ScopedWakelock::ScopedWakelock(IScopedWakelockRefCounter* refCounter, bool locked) + : mRefCounter(refCounter), mLocked(locked) { + if (mLocked) { + mLocked = mRefCounter->incrementRefCountAndMaybeAcquireWakelock(1, &mCreatedAtTimeNs); + } +} + +ScopedWakelock::~ScopedWakelock() { + if (mLocked) { + mRefCounter->decrementRefCountAndMaybeReleaseWakelock(1, mCreatedAtTimeNs); + } +} + +} // namespace implementation +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android \ No newline at end of file diff --git a/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml b/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml new file mode 100644 index 0000000000..a771100ecc --- /dev/null +++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml @@ -0,0 +1,11 @@ + + + android.hardware.sensors + hwbinder + 2.0 + + ISensors + multihal + + + diff --git a/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc new file mode 100644 index 0000000000..a4da3b084b --- /dev/null +++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc @@ -0,0 +1,7 @@ +service vendor.sensors-hal-2-0-multihal /vendor/bin/hw/android.hardware.sensors@2.0-service.multihal + class hal + user system + group system wakelock + writepid /dev/cpuset/system-background/tasks + capabilities BLOCK_SUSPEND + rlimit rtprio 10 10 diff --git a/sensors/2.0/multihal/include/HalProxy.h b/sensors/2.0/multihal/include/HalProxy.h new file mode 100644 index 0000000000..b1dd737025 --- /dev/null +++ b/sensors/2.0/multihal/include/HalProxy.h @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "ScopedWakelock.h" +#include "SubHal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::EventFlag; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::MessageQueue; +using ::android::hardware::MQDescriptor; +using ::android::hardware::Return; +using ::android::hardware::Void; + +class HalProxy : public ISensors, public IScopedWakelockRefCounter { + public: + using Event = ::android::hardware::sensors::V1_0::Event; + using OperationMode = ::android::hardware::sensors::V1_0::OperationMode; + using RateLevel = ::android::hardware::sensors::V1_0::RateLevel; + using Result = ::android::hardware::sensors::V1_0::Result; + using SensorInfo = ::android::hardware::sensors::V1_0::SensorInfo; + using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo; + using ISensorsSubHal = ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal; + + explicit HalProxy(); + // Test only constructor. + explicit HalProxy(std::vector& subHalList); + ~HalProxy(); + + // Methods from ::android::hardware::sensors::V2_0::ISensors follow. + Return getSensorsList(getSensorsList_cb _hidl_cb) override; + + Return setOperationMode(OperationMode mode) override; + + Return activate(int32_t sensorHandle, bool enabled) override; + + Return initialize( + const ::android::hardware::MQDescriptorSync& eventQueueDescriptor, + const ::android::hardware::MQDescriptorSync& wakeLockDescriptor, + const sp& sensorsCallback) override; + + Return batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) override; + + Return flush(int32_t sensorHandle) override; + + Return injectSensorData(const Event& event) override; + + Return registerDirectChannel(const SharedMemInfo& mem, + registerDirectChannel_cb _hidl_cb) override; + + Return unregisterDirectChannel(int32_t channelHandle) override; + + Return configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate, + configDirectReport_cb _hidl_cb) override; + + Return debug(const hidl_handle& fd, const hidl_vec& args) override; + + // Below methods from ::android::hardware::sensors::V2_0::ISensorsCallback with a minor change + // to pass in the sub-HAL index. While the above methods are invoked from the sensors framework + // via the binder, these methods are invoked from a callback provided to sub-HALs inside the + // same process as the HalProxy, but potentially running on different threads. + Return onDynamicSensorsConnected(const hidl_vec& dynamicSensorsAdded, + int32_t subHalIndex); + + Return onDynamicSensorsDisconnected(const hidl_vec& dynamicSensorHandlesRemoved, + int32_t subHalIndex); + + // Below methods are for HalProxyCallback + + /** + * Post events to the event message queue if there is room to write them. Otherwise post the + * remaining events to a background thread for a blocking write with a kPendingWriteTimeoutNs + * timeout. + * + * @param events The list of events to post to the message queue. + * @param numWakeupEvents The number of wakeup events in events. + * @param wakelock The wakelock associated with this post of events. + */ + void postEventsToMessageQueue(const std::vector& events, size_t numWakeupEvents, + ScopedWakelock wakelock); + + /** + * Get the sensor info associated with that sensorHandle. + * + * @param sensorHandle The sensor handle. + * + * @return The sensor info object in the mapping. + */ + const SensorInfo& getSensorInfo(int32_t sensorHandle) { return mSensors[sensorHandle]; } + + bool areThreadsRunning() { return mThreadsRun.load(); } + + // Below methods are from IScopedWakelockRefCounter interface + bool incrementRefCountAndMaybeAcquireWakelock(size_t delta, + int64_t* timeoutStart = nullptr) override; + + void decrementRefCountAndMaybeReleaseWakelock(size_t delta, int64_t timeoutStart = -1) override; + + private: + using EventMessageQueue = MessageQueue; + using WakeLockMessageQueue = MessageQueue; + + /** + * The Event FMQ where sensor events are written + */ + std::unique_ptr mEventQueue; + + /** + * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events + */ + std::unique_ptr mWakeLockQueue; + + /** + * Event Flag to signal to the framework when sensor events are available to be read and to + * interrupt event queue blocking write. + */ + EventFlag* mEventQueueFlag = nullptr; + + //! Event Flag to signal internally that the wakelock queue should stop its blocking read. + EventFlag* mWakelockQueueFlag = nullptr; + + /** + * Callback to the sensors framework to inform it that new sensors have been added or removed. + */ + sp mDynamicSensorsCallback; + + /** + * SubHal object pointers that have been saved from vendor dynamic libraries. + */ + std::vector mSubHalList; + + //! The list of subhal callbacks for each subhal where the indices correlate with mSubHalList + std::vector> mSubHalCallbacks; + + /** + * Map of sensor handles to SensorInfo objects that contains the sensor info from subhals as + * well as the modified sensor handle for the framework. + * + * The subhal index is encoded in the first byte of the sensor handle and the remaining + * bytes are generated by the subhal to identify the sensor. + */ + std::map mSensors; + + //! Map of the dynamic sensors that have been added to halproxy. + std::map mDynamicSensors; + + //! The current operation mode for all subhals. + OperationMode mCurrentOperationMode = OperationMode::NORMAL; + + //! The single subHal that supports directChannel reporting. + ISensorsSubHal* mDirectChannelSubHal = nullptr; + + //! The timeout for each pending write on background thread for events. + static const int64_t kPendingWriteTimeoutNs = 5 * INT64_C(1000000000) /* 5 seconds */; + + //! The bit mask used to get the subhal index from a sensor handle. + static constexpr int32_t kSensorHandleSubHalIndexMask = 0xFF000000; + + /** + * A FIFO queue of pairs of vector of events and the number of wakeup events in that vector + * which are waiting to be written to the events fmq in the background thread. + */ + std::queue, size_t>> mPendingWriteEventsQueue; + + //! The mutex protecting writing to the fmq and the pending events queue + std::mutex mEventQueueWriteMutex; + + //! The condition variable waiting on pending write events to stack up + std::condition_variable mEventQueueWriteCV; + + //! The thread object ptr that handles pending writes + std::thread mPendingWritesThread; + + //! The thread object that handles wakelocks + std::thread mWakelockThread; + + //! The bool indicating whether to end the threads started in initialize + std::atomic_bool mThreadsRun = true; + + //! The mutex protecting access to the dynamic sensors added and removed methods. + std::mutex mDynamicSensorsMutex; + + // WakelockRefCount membar vars below + + //! The mutex protecting the wakelock refcount and subsequent wakelock releases and + //! acquisitions + std::recursive_mutex mWakelockMutex; + + std::condition_variable_any mWakelockCV; + + //! The refcount of how many ScopedWakelocks and pending wakeup events are active + size_t mWakelockRefCount = 0; + + int64_t mWakelockTimeoutStartTime = getTimeNow(); + + int64_t mWakelockTimeoutResetTime = getTimeNow(); + + const char* kWakelockName = "SensorsHAL_WAKEUP"; + + /** + * Initialize the list of SubHal objects in mSubHalList by reading from dynamic libraries + * listed in a config file. + */ + void initializeSubHalListFromConfigFile(const char* configFileName); + + /** + * Initialize the HalProxyCallback vector using the list of subhals. + */ + void initializeSubHalCallbacks(); + + /** + * Initialize the list of SensorInfo objects in mSensorList by getting sensors from each + * subhal. + */ + void initializeSensorList(); + + /** + * Calls the helper methods that all ctors use. + */ + void init(); + + /** + * Stops all threads by setting the threads running flag to false and joining to them. + */ + void stopThreads(); + + /** + * Disable all the sensors observed by the HalProxy. + */ + void disableAllSensors(); + + /** + * Starts the thread that handles pending writes to event fmq. + * + * @param halProxy The HalProxy object pointer. + */ + static void startPendingWritesThread(HalProxy* halProxy); + + //! Handles the pending writes on events to eventqueue. + void handlePendingWrites(); + + /** + * Starts the thread that handles decrementing the ref count on wakeup events processed by the + * framework and timing out wakelocks. + * + * @param halProxy The HalProxy object pointer. + */ + static void startWakelockThread(HalProxy* halProxy); + + //! Handles the wakelocks. + void handleWakelocks(); + + /** + * @param timeLeft The variable that should be set to the timeleft before timeout will occur or + * unmodified if timeout occurred. + * + * @return true if the shared wakelock has been held passed the timeout and should be released + */ + bool sharedWakelockDidTimeout(int64_t* timeLeft); + + /** + * Reset all the member variables associated with the wakelock ref count and maybe release + * the shared wakelock. + */ + void resetSharedWakelock(); + + /** + * Clear direct channel flags if the HalProxy has already chosen a subhal as its direct channel + * subhal. Set the directChannelSubHal pointer to the subHal passed in if this is the first + * direct channel enabled sensor seen. + * + * @param sensorInfo The SensorInfo object that may be altered to have direct channel support + * disabled. + * @param subHal The subhal pointer that the current sensorInfo object came from. + */ + void setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal); + + /* + * Get the subhal pointer which can be found by indexing into the mSubHalList vector + * using the index from the first byte of sensorHandle. + * + * @param sensorHandle The handle used to identify a sensor in one of the subhals. + */ + ISensorsSubHal* getSubHalForSensorHandle(int32_t sensorHandle); + + /** + * Checks that sensorHandle's subhal index byte is within bounds of mSubHalList. + * + * @param sensorHandle The sensor handle to check. + * + * @return true if sensorHandles's subhal index byte is valid. + */ + bool isSubHalIndexValid(int32_t sensorHandle); + + /** + * Count the number of wakeup events in the first n events of the vector. + * + * @param events The vector of Event objects. + * @param n The end index not inclusive of events to consider. + * + * @return The number of wakeup events of the considered events. + */ + size_t countNumWakeupEvents(const std::vector& events, size_t n); + + /* + * Clear out the subhal index bytes from a sensorHandle. + * + * @param sensorHandle The sensor handle to modify. + * + * @return The modified version of the sensor handle. + */ + static int32_t clearSubHalIndex(int32_t sensorHandle); + + /** + * @param sensorHandle The sensor handle to modify. + * + * @return true if subHalIndex byte of sensorHandle is zeroed. + */ + static bool subHalIndexIsClear(int32_t sensorHandle); +}; + +/** + * Callback class used to provide the HalProxy with the index of which subHal is invoking + */ +class HalProxyCallback : public IHalProxyCallback { + using SensorInfo = ::android::hardware::sensors::V1_0::SensorInfo; + + public: + HalProxyCallback(HalProxy* halProxy, int32_t subHalIndex) + : mHalProxy(halProxy), mSubHalIndex(subHalIndex) {} + + Return onDynamicSensorsConnected( + const hidl_vec& dynamicSensorsAdded) override { + return mHalProxy->onDynamicSensorsConnected(dynamicSensorsAdded, mSubHalIndex); + } + + Return onDynamicSensorsDisconnected( + const hidl_vec& dynamicSensorHandlesRemoved) override { + return mHalProxy->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved, mSubHalIndex); + } + + void postEvents(const std::vector& events, ScopedWakelock wakelock); + + ScopedWakelock createScopedWakelock(bool lock); + + private: + HalProxy* mHalProxy; + int32_t mSubHalIndex; + + std::vector processEvents(const std::vector& events, + size_t* numWakeupEvents) const; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android diff --git a/sensors/2.0/multihal/include/ScopedWakelock.h b/sensors/2.0/multihal/include/ScopedWakelock.h new file mode 100644 index 0000000000..aa6d9db3d4 --- /dev/null +++ b/sensors/2.0/multihal/include/ScopedWakelock.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::sensors::V2_0::SensorTimeout; + +const int64_t kWakelockTimeoutNs = + static_cast(SensorTimeout::WAKE_LOCK_SECONDS) * INT64_C(1000000000); + +int64_t getTimeNow(); + +class IScopedWakelockRefCounter : public RefBase { + public: + /** + * Increment the wakelock ref count and maybe acquire the shared wakelock if incrementing + * from 0 then return the time of incrementing back to caller. + * + * @param delta The amount to change ref count by. + * @param timeoutStart The ptr to the timestamp in ns that the increment occurred which will be + * set in the function or nullptr if not specified. + * + * @return true if successfully incremented the wakelock ref count. + */ + virtual bool incrementRefCountAndMaybeAcquireWakelock(size_t delta, + int64_t* timeoutStart = nullptr) = 0; + /** + * Decrement the wakelock ref count and maybe release wakelock if ref count ends up 0. + * + * @param delta The amount to change ref count by. + * @param timeoutStart The timestamp in ns that the calling context kept track of when + * incrementing the ref count or -1 by default + */ + virtual void decrementRefCountAndMaybeReleaseWakelock(size_t delta, + int64_t timeoutStart = -1) = 0; + // Virtual dtor needed for compilation success + virtual ~IScopedWakelockRefCounter(){}; +}; + +/** + * Wrapper around wake lock acquisition functions (acquire/release_wake_lock) that provides a + * RAII-style mechanism for keeping a wake lock held for the duration of a scoped block. + * When a ScopedWakelock is created, it increments the reference count stored in the HalProxy + * for the sub-HALs specific wake lock, acquiring the wake lock if necessary. When the object goes + * out of scope, the ref count is decremented, potentially releasing the wake lock if no other + * references to the wake lock exist. + * + * This class is allocated through the createScopedWakelock callback inside the IHalProxyCallback + * provided to sub-HALs during initialization and should be used for all wake lock acquisition + * inside of the sub-HAL to ensure wake locks are not held indefinitely. + * + * The most prevalent use case for this class will be for posting events to the framework through + * the postEvents HalProxy callback. The expectation is that sub-HALs will create this + * ScopedWakelock through the createScopedWakelock upon receiving a sensor events. The lock boolean + * provided to createScopedWakelock will be set the according to whether the sensor events are + * from wakeup sensors. Then, the sub-HAL will perform any processing necessary before invoking the + * postEvents callback passing in the previously created ScopedWakelock. At this point, ownership + * of the object will be passed to the HalProxy that will then be responsible for ensuring any + * wake locks continue to be held, if necessary. + */ +class ScopedWakelock { + public: + ScopedWakelock(ScopedWakelock&&) = default; + ScopedWakelock& operator=(ScopedWakelock&&) = default; + virtual ~ScopedWakelock(); + + bool isLocked() const { return mLocked; } + + private: + friend class HalProxyCallback; + IScopedWakelockRefCounter* mRefCounter; + int64_t mCreatedAtTimeNs; + bool mLocked; + ScopedWakelock(IScopedWakelockRefCounter* refCounter, bool locked); + ScopedWakelock(const ScopedWakelock&) = delete; + ScopedWakelock& operator=(const ScopedWakelock&) = delete; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android \ No newline at end of file diff --git a/sensors/2.0/multihal/include/SubHal.h b/sensors/2.0/multihal/include/SubHal.h new file mode 100644 index 0000000000..92ae3a61df --- /dev/null +++ b/sensors/2.0/multihal/include/SubHal.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "ScopedWakelock.h" + +#include +#include + +#include + +// Indicates the current version of the multiHAL interface formatted as (HAL major version) << 24 | +// (HAL minor version) << 16 | (multiHAL version) +#define SUB_HAL_2_0_VERSION 0x02000000 + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::sensors::V1_0::Event; +using ::android::hardware::sensors::V1_0::Result; +using ::android::hardware::sensors::V1_0::SensorInfo; + +/** + * Interface that contains several callbacks into the HalProxy class to communicate dynamic sensor + * changes and sensor events to the framework and acquire wake locks. The HalProxy will ensure + * callbacks occurring at the same time from multiple sub-HALs are synchronized in a safe, efficient + * manner. + */ +class IHalProxyCallback : public ISensorsCallback { + public: + /** + * Thread-safe callback used to post events to the HalProxy. Sub-HALs should invoke this + * whenever new sensor events need to be delivered to the sensors framework. Once invoked, the + * HalProxy will attempt to send events to the sensors framework using a blocking write with a + * 5 second timeout. This write may be done asynchronously if the queue used to communicate + * with the framework is full to avoid blocking sub-HALs for the length of the timeout. If the + * write fails, the events will be dropped and any wake locks held will be released. + * + * The provided ScopedWakelock must be locked if the events are from wakeup sensors. If it's + * not locked accordingly, the HalProxy will crash as this indicates the sub-HAL isn't compliant + * with the sensors HAL 2.0 specification. Additionally, since ScopedWakelock isn't copyable, + * the HalProxy will take ownership of the wake lock given when this method is invoked. Once the + * method returns, the HalProxy will handle holding the wake lock, if necessary, until the + * framework has successfully processed any wakeup events. + * + * No return type is used for this callback to avoid sub-HALs trying to resend events when + * writes fail. Writes should only fail when the framework is under inordinate stress which will + * likely result in a framework restart so retrying will likely only result in overloading the + * HalProxy. Sub-HALs should always assume that the write was a success and perform any + * necessary cleanup. Additionally, the HalProxy will ensure it logs any errors (through ADB and + * bug reports) it encounters during delivery to ensure it's obvious that a failure occurred. + * + * @param events the events that should be sent to the sensors framework + * @param wakelock ScopedWakelock that should be locked to send events from wake sensors and + * unlocked otherwise. + */ + virtual void postEvents(const std::vector& events, ScopedWakelock wakelock) = 0; + + /** + * Initializes a ScopedWakelock on the stack that, when locked, will increment the reference + * count for the sub-HAL's wake lock managed inside the HalProxy. See the ScopedWakelock class + * definition for how it should be used. + * + * @param lock whether the ScopedWakelock should be locked before it's returned. + * @return the created ScopedWakelock + */ + virtual ScopedWakelock createScopedWakelock(bool lock) = 0; +}; + +/** + * ISensorsSubHal is an interface that sub-HALs must implement in order to be compliant with + * multihal 2.0 and in order for the HalProxy to successfully load and communicate with the sub-HAL. + * + * Any vendor wishing to implement this interface and support multihal 2.0 will need to create a + * dynamic library that exposes sensorsHalGetSubHal (defined below). This library will be loaded by + * the HalProxy when the sensors HAL is initialized and then the HalProxy will retrieve the vendor's + * implementation of sensorsHalGetSubHal. + * + * With the exception of the initialize method, ISensorsSubHal will implement the ISensors.hal spec. + * Any sensor handles given to the HalProxy, either through getSensorsList() or the + * onDynamicSensors(Dis)Connected callbacks, will be translated to avoid clashing with other sub-HAL + * handles. To achieve this, the HalProxy will use the upper byte to store the sub-HAL index and + * sub-HALs can continue to use the lower 3 bytes of the handle. + */ +class ISensorsSubHal : public ISensors { + public: + // The ISensors version of initialize isn't used for multihal. Instead, sub-HALs must implement + // the version below to allow communciation logic to centralized in the HalProxy + Return initialize( + const ::android::hardware::MQDescriptorSync& /* eventQueueDescriptor */, + const ::android::hardware::MQDescriptorSync& /* wakeLockDescriptor */, + const sp& /* sensorsCallback */) final { + return Result::INVALID_OPERATION; + } + + /** + * Method defined in ::android::hidl::base::V1_0::IBase. + * + * This method should write debug information to hidl_handle that is useful for debugging + * issues. Suggestions include: + * - Sensor info including handle values and any other state available in the SensorInfo class + * - List of active sensors and their current sampling period and reporting latency + * - Information about pending flush requests + * - Current operating mode + * - Currently registered direct channel info + * - A history of any of the above + */ + virtual Return debug(const hidl_handle& fd, const hidl_vec& args) = 0; + + /** + * @return A human-readable name for use in wake locks and logging. + */ + virtual const std::string getName() = 0; + + /** + * This is the first method invoked on the sub-HAL after it's allocated through + * sensorsHalGetSubHal() by the HalProxy. Sub-HALs should use this to initialize any state and + * retain the callback given in order to communicate with the HalProxy. Method will be called + * anytime the sensors framework restarts. Therefore, this method will be responsible for + * reseting the state of the subhal and cleaning up and reallocating any previously allocated + * data. Initialize should ensure that the subhal has reset its operation mode to NORMAL state + * as well. + * + * @param halProxyCallback callback used to inform the HalProxy when a dynamic sensor's state + * changes, new sensor events should be sent to the framework, and when a new ScopedWakelock + * should be created. + * @return result OK on success + */ + virtual Return initialize(const sp& halProxyCallback) = 0; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android + +using ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal; + +/** + * Function that must be exported so the HalProxy class can invoke it on the sub-HAL dynamic + * library. This function will only be invoked once at initialization time. + * + * NOTE: The supported sensors HAL version must match SUB_HAL_2_0_VERSION exactly or the HalProxy + * will fail to initialize. + * + * @param uint32_t when this function returns, this parameter must contain the HAL version that + * this sub-HAL supports. To support this version of multi-HAL, this must be set to + * SUB_HAL_2_0_VERSION. + * @return A statically allocated, valid ISensorsSubHal implementation. + */ +__attribute__((visibility("default"))) extern "C" ISensorsSubHal* sensorsHalGetSubHal( + uint32_t* version); diff --git a/sensors/2.0/multihal/service.cpp b/sensors/2.0/multihal/service.cpp new file mode 100644 index 0000000000..ef77048020 --- /dev/null +++ b/sensors/2.0/multihal/service.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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 +#include +#include +#include +#include "HalProxy.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; +using android::hardware::sensors::V2_0::ISensors; +using android::hardware::sensors::V2_0::implementation::HalProxy; + +int main(int /* argc */, char** /* argv */) { + configureRpcThreadpool(1, true); + + android::sp halProxy = new HalProxy(); + if (halProxy->registerAsService() != ::android::OK) { + ALOGE("Failed to register Sensors HAL instance"); + return -1; + } + + joinRpcThreadpool(); + return 1; // joinRpcThreadpool shouldn't exit +} diff --git a/sensors/2.0/multihal/tests/Android.bp b/sensors/2.0/multihal/tests/Android.bp new file mode 100644 index 0000000000..e7f9499db0 --- /dev/null +++ b/sensors/2.0/multihal/tests/Android.bp @@ -0,0 +1,99 @@ +// +// Copyright (C) 2019 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. + +cc_defaults { + name: "android.hardware.sensors@2.0-fakesubhal-defaults", + srcs: [ + "fake_subhal/*.cpp", + ], + header_libs: [ + "android.hardware.sensors@2.0-multihal.header", + ], + export_include_dirs: ["fake_subhal"], + shared_libs: [ + "android.hardware.sensors@1.0", + "android.hardware.sensors@2.0", + "libcutils", + "libfmq", + "libhardware", + "libhidlbase", + "liblog", + "libpower", + "libutils", + ], + static_libs: [ + "android.hardware.sensors@2.0-HalProxy", + ], + cflags: [ + "-DLOG_TAG=\"FakeSubHal\"" + ], +} + +cc_library { + name: "android.hardware.sensors@2.0-fakesubhal-config1", + vendor: true, + defaults: ["android.hardware.sensors@2.0-fakesubhal-defaults"], + cflags: [ + "-DSUPPORT_CONTINUOUS_SENSORS", + "-DSUB_HAL_NAME=\"FakeSubHal-Continuous\"", + ], +} + +cc_library { + name: "android.hardware.sensors@2.0-fakesubhal-config2", + vendor: true, + defaults: ["android.hardware.sensors@2.0-fakesubhal-defaults"], + cflags: [ + "-DSUPPORT_ON_CHANGE_SENSORS", + "-DSUB_HAL_NAME=\"FakeSubHal-OnChange\"", + ], +} + +cc_test_library { + name: "android.hardware.sensors@2.0-fakesubhal-unittest", + vendor_available: true, + defaults: ["android.hardware.sensors@2.0-fakesubhal-defaults"], + cflags: [ + "-DSUPPORT_ON_CHANGE_SENSORS", + "-DSUPPORT_CONTINUOUS_SENSORS", + "-DSUB_HAL_NAME=\"FakeSubHal-Test\"", + ], +} + +cc_test { + name: "android.hardware.sensors@2.0-halproxy-unit-tests", + srcs: ["HalProxy_test.cpp"], + vendor: true, + static_libs: [ + "android.hardware.sensors@2.0-HalProxy", + "android.hardware.sensors@2.0-fakesubhal-unittest", + ], + shared_libs: [ + "android.hardware.sensors@1.0", + "android.hardware.sensors@2.0", + "libbase", + "libcutils", + "libfmq", + "libhardware", + "libhidlbase", + "liblog", + "libpower", + "libutils", + ], + test_suites: ["device-tests"], + cflags: [ + "-DLOG_TAG=\"HalProxyUnitTests\"", + ], +} diff --git a/sensors/2.0/multihal/tests/HalProxy_test.cpp b/sensors/2.0/multihal/tests/HalProxy_test.cpp new file mode 100644 index 0000000000..1fd35d1afb --- /dev/null +++ b/sensors/2.0/multihal/tests/HalProxy_test.cpp @@ -0,0 +1,833 @@ +// +// Copyright (C) 2019 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 + +#include +#include + +#include "HalProxy.h" +#include "ScopedWakelock.h" +#include "SensorsSubHal.h" + +#include +#include +#include +#include + +namespace { + +using ::android::hardware::EventFlag; +using ::android::hardware::hidl_vec; +using ::android::hardware::MessageQueue; +using ::android::hardware::Return; +using ::android::hardware::sensors::V1_0::EventPayload; +using ::android::hardware::sensors::V1_0::SensorFlagBits; +using ::android::hardware::sensors::V1_0::SensorInfo; +using ::android::hardware::sensors::V1_0::SensorType; +using ::android::hardware::sensors::V2_0::EventQueueFlagBits; +using ::android::hardware::sensors::V2_0::ISensorsCallback; +using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits; +using ::android::hardware::sensors::V2_0::implementation::HalProxy; +using ::android::hardware::sensors::V2_0::implementation::HalProxyCallback; +using ::android::hardware::sensors::V2_0::subhal::implementation::AddAndRemoveDynamicSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation:: + AllSupportDirectChannelSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation:: + DoesNotSupportDirectChannelSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal; +using ::android::hardware::sensors::V2_0::subhal::implementation:: + SetOperationModeFailingSensorsSubHal; + +using EventMessageQueue = MessageQueue; +using WakeupMessageQueue = MessageQueue; + +// The barebones sensors callback class passed into halproxy initialize calls +class SensorsCallback : public ISensorsCallback { + public: + Return onDynamicSensorsConnected( + const hidl_vec& /*dynamicSensorsAdded*/) override { + // Nothing yet + return Return(); + } + + Return onDynamicSensorsDisconnected( + const hidl_vec& /*dynamicSensorHandlesRemoved*/) override { + // Nothing yet + return Return(); + } +}; + +// The sensors callback that expects a variable list of sensors to be added +class TestSensorsCallback : public ISensorsCallback { + public: + Return onDynamicSensorsConnected( + const hidl_vec& dynamicSensorsAdded) override { + mSensorsConnected.insert(mSensorsConnected.end(), dynamicSensorsAdded.begin(), + dynamicSensorsAdded.end()); + return Return(); + } + + Return onDynamicSensorsDisconnected( + const hidl_vec& dynamicSensorHandlesRemoved) override { + mSensorHandlesDisconnected.insert(mSensorHandlesDisconnected.end(), + dynamicSensorHandlesRemoved.begin(), + dynamicSensorHandlesRemoved.end()); + return Return(); + } + + const std::vector& getSensorsConnected() const { return mSensorsConnected; } + const std::vector& getSensorHandlesDisconnected() const { + return mSensorHandlesDisconnected; + } + + private: + std::vector mSensorsConnected; + std::vector mSensorHandlesDisconnected; +}; + +// Helper declarations follow + +/** + * Tests that for each SensorInfo object from a proxy getSensorsList call each corresponding + * object from a subhal getSensorsList call has the same type and its last 3 bytes are the + * same for sensorHandle field. + * + * @param proxySensorsList The list of SensorInfo objects from the proxy.getSensorsList callback. + * @param subHalSenosrsList The list of SensorInfo objects from the subHal.getSensorsList callback. + */ +void testSensorsListFromProxyAndSubHal(const std::vector& proxySensorsList, + const std::vector& subHalSensorsList); + +/** + * Tests that there is exactly one subhal that allows its sensors to have direct channel enabled. + * Therefore, all SensorInfo objects that are not from the enabled subhal should be disabled for + * direct channel. + * + * @param sensorsList The SensorInfo object list from proxy.getSensorsList call. + * @param enabledSubHalIndex The index of the subhal in the halproxy that is expected to be + * enabled. + */ +void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector& sensorsList, + size_t enabledSubHalIndex); + +void ackWakeupEventsToHalProxy(size_t numEvents, std::unique_ptr& wakelockQueue, + EventFlag* wakelockQueueFlag); + +bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr& eventQueue, + EventFlag* eventQueueFlag); + +std::unique_ptr makeEventFMQ(size_t size); + +std::unique_ptr makeWakelockFMQ(size_t size); + +/** + * Construct and return a HIDL Event type thats sensorHandle refers to a proximity sensor + * which is a wakeup type sensor. + * + * @return A proximity event. + */ +Event makeProximityEvent(); + +/** + * Construct and return a HIDL Event type thats sensorHandle refers to a proximity sensor + * which is a wakeup type sensor. + * + * @return A proximity event. + */ +Event makeAccelerometerEvent(); + +/** + * Make a certain number of proximity type events with the sensorHandle field set to + * the proper number for AllSensorsSubHal subhal type. + * + * @param numEvents The number of events to make. + * + * @return The created list of events. + */ +std::vector makeMultipleProximityEvents(size_t numEvents); + +/** + * Make a certain number of accelerometer type events with the sensorHandle field set to + * the proper number for AllSensorsSubHal subhal type. + * + * @param numEvents The number of events to make. + * + * @return The created list of events. + */ +std::vector makeMultipleAccelerometerEvents(size_t numEvents); + +/** + * Given a SensorInfo vector and a sensor handles vector populate 'sensors' with SensorInfo + * objects that have the sensorHandle property set to int32_ts from start to start + size + * (exclusive) and push those sensorHandles also onto 'sensorHandles'. + * + * @param start The starting sensorHandle value. + * @param size The ending (not included) sensorHandle value. + * @param sensors The SensorInfo object vector reference to push_back to. + * @param sensorHandles The sensor handles int32_t vector reference to push_back to. + */ +void makeSensorsAndSensorHandlesStartingAndOfSize(int32_t start, size_t size, + std::vector& sensors, + std::vector& sensorHandles); + +// Tests follow +TEST(HalProxyTest, GetSensorsListOneSubHalTest) { + AllSensorsSubHal subHal; + std::vector fakeSubHals{&subHal}; + HalProxy proxy(fakeSubHals); + + proxy.getSensorsList([&](const auto& proxySensorsList) { + subHal.getSensorsList([&](const auto& subHalSensorsList) { + testSensorsListFromProxyAndSubHal(proxySensorsList, subHalSensorsList); + }); + }); +} + +TEST(HalProxyTest, GetSensorsListTwoSubHalTest) { + ContinuousSensorsSubHal continuousSubHal; + OnChangeSensorsSubHal onChangeSubHal; + std::vector fakeSubHals; + fakeSubHals.push_back(&continuousSubHal); + fakeSubHals.push_back(&onChangeSubHal); + HalProxy proxy(fakeSubHals); + + std::vector proxySensorsList, combinedSubHalSensorsList; + + proxy.getSensorsList([&](const auto& list) { proxySensorsList = list; }); + continuousSubHal.getSensorsList([&](const auto& list) { + combinedSubHalSensorsList.insert(combinedSubHalSensorsList.end(), list.begin(), list.end()); + }); + onChangeSubHal.getSensorsList([&](const auto& list) { + combinedSubHalSensorsList.insert(combinedSubHalSensorsList.end(), list.begin(), list.end()); + }); + + testSensorsListFromProxyAndSubHal(proxySensorsList, combinedSubHalSensorsList); +} + +TEST(HalProxyTest, SetOperationModeTwoSubHalSuccessTest) { + ContinuousSensorsSubHal subHal1; + OnChangeSensorsSubHal subHal2; + + std::vector fakeSubHals{&subHal1, &subHal2}; + HalProxy proxy(fakeSubHals); + + EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL); + EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL); + + Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION); + + EXPECT_EQ(result, Result::OK); + EXPECT_EQ(subHal1.getOperationMode(), OperationMode::DATA_INJECTION); + EXPECT_EQ(subHal2.getOperationMode(), OperationMode::DATA_INJECTION); +} + +TEST(HalProxyTest, SetOperationModeTwoSubHalFailTest) { + AllSensorsSubHal subHal1; + SetOperationModeFailingSensorsSubHal subHal2; + + std::vector fakeSubHals{&subHal1, &subHal2}; + HalProxy proxy(fakeSubHals); + + EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL); + EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL); + + Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION); + + EXPECT_NE(result, Result::OK); + EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL); + EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL); +} + +TEST(HalProxyTest, InitDirectChannelTwoSubHalsUnitTest) { + AllSupportDirectChannelSensorsSubHal subHal1; + AllSupportDirectChannelSensorsSubHal subHal2; + + std::vector fakeSubHals{&subHal1, &subHal2}; + HalProxy proxy(fakeSubHals); + + proxy.getSensorsList([&](const auto& sensorsList) { + testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 0); + }); +} + +TEST(HalProxyTest, InitDirectChannelThreeSubHalsUnitTest) { + DoesNotSupportDirectChannelSensorsSubHal subHal1; + AllSupportDirectChannelSensorsSubHal subHal2, subHal3; + std::vector fakeSubHals{&subHal1, &subHal2, &subHal3}; + HalProxy proxy(fakeSubHals); + + proxy.getSensorsList([&](const auto& sensorsList) { + testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 1); + }); +} + +TEST(HalProxyTest, PostSingleNonWakeupEvent) { + constexpr size_t kQueueSize = 5; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events{makeAccelerometerEvent()}; + subHal.postEvents(events, false /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), 1); +} + +TEST(HalProxyTest, PostMultipleNonWakeupEvent) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 3; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + subHal.postEvents(events, false /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), kNumEvents); +} + +TEST(HalProxyTest, PostSingleWakeupEvent) { + constexpr size_t kQueueSize = 5; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + EventFlag* eventQueueFlag; + EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag); + + EventFlag* wakelockQueueFlag; + EventFlag::createEventFlag(wakeLockQueue->getEventFlagWord(), &wakelockQueueFlag); + + std::vector events{makeProximityEvent()}; + subHal.postEvents(events, true /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), 1); + + readEventsOutOfQueue(1, eventQueue, eventQueueFlag); + ackWakeupEventsToHalProxy(1, wakeLockQueue, wakelockQueueFlag); +} + +TEST(HalProxyTest, PostMultipleWakeupEvents) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 3; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + EventFlag* eventQueueFlag; + EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag); + + EventFlag* wakelockQueueFlag; + EventFlag::createEventFlag(wakeLockQueue->getEventFlagWord(), &wakelockQueueFlag); + + std::vector events = makeMultipleProximityEvents(kNumEvents); + subHal.postEvents(events, true /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), kNumEvents); + + readEventsOutOfQueue(kNumEvents, eventQueue, eventQueueFlag); + ackWakeupEventsToHalProxy(kNumEvents, wakeLockQueue, wakelockQueueFlag); +} + +TEST(HalProxyTest, PostEventsMultipleSubhals) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 2; + AllSensorsSubHal subHal1, subHal2; + std::vector subHals{&subHal1, &subHal2}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + subHal1.postEvents(events, false /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), kNumEvents); + + subHal2.postEvents(events, false /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), kNumEvents * 2); +} + +TEST(HalProxyTest, PostEventsDelayedWrite) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 6; + AllSensorsSubHal subHal1, subHal2; + std::vector subHals{&subHal1, &subHal2}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + EventFlag* eventQueueFlag; + EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + subHal1.postEvents(events, false /* wakeup */); + + EXPECT_EQ(eventQueue->availableToRead(), kQueueSize); + + // readblock a full queue size worth of events out of queue, timeout for half a second + EXPECT_TRUE(readEventsOutOfQueue(kQueueSize, eventQueue, eventQueueFlag)); + + // proxy background thread should have wrote remaining events when it saw space + EXPECT_TRUE(readEventsOutOfQueue(kNumEvents - kQueueSize, eventQueue, eventQueueFlag)); + + EXPECT_EQ(eventQueue->availableToRead(), 0); +} + +TEST(HalProxyTest, PostEventsMultipleSubhalsThreaded) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 2; + AllSensorsSubHal subHal1, subHal2; + std::vector subHals{&subHal1, &subHal2}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + + std::thread t1(&AllSensorsSubHal::postEvents, &subHal1, events, false); + std::thread t2(&AllSensorsSubHal::postEvents, &subHal2, events, false); + + t1.join(); + t2.join(); + + EXPECT_EQ(eventQueue->availableToRead(), kNumEvents * 2); +} + +TEST(HalProxyTest, DestructingWithEventsPendingOnBackgroundThread) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 6; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + subHal.postEvents(events, false /* wakeup */); + + // Destructing HalProxy object with events on the background thread +} + +TEST(HalProxyTest, DestructingWithUnackedWakeupEventsPosted) { + constexpr size_t kQueueSize = 5; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events{makeProximityEvent()}; + subHal.postEvents(events, true /* wakeup */); + + // Not sending any acks back through wakeLockQueue + + // Destructing HalProxy object with unacked wakeup events posted +} + +TEST(HalProxyTest, ReinitializeWithEventsPendingOnBackgroundThread) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumEvents = 10; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events = makeMultipleAccelerometerEvents(kNumEvents); + subHal.postEvents(events, false /* wakeup */); + + eventQueue = makeEventFMQ(kQueueSize); + wakeLockQueue = makeWakelockFMQ(kQueueSize); + + Result secondInitResult = + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + EXPECT_EQ(secondInitResult, Result::OK); + // Small sleep so that pending writes thread has a change to hit writeBlocking call. + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + Event eventOut; + EXPECT_FALSE(eventQueue->read(&eventOut)); +} + +TEST(HalProxyTest, ReinitializingWithUnackedWakeupEventsPosted) { + constexpr size_t kQueueSize = 5; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + std::vector events{makeProximityEvent()}; + subHal.postEvents(events, true /* wakeup */); + + // Not sending any acks back through wakeLockQueue + + eventQueue = makeEventFMQ(kQueueSize); + wakeLockQueue = makeWakelockFMQ(kQueueSize); + + Result secondInitResult = + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + EXPECT_EQ(secondInitResult, Result::OK); +} + +TEST(HalProxyTest, InitializeManyTimesInARow) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumTimesToInit = 100; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + + for (size_t i = 0; i < kNumTimesToInit; i++) { + Result secondInitResult = + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + EXPECT_EQ(secondInitResult, Result::OK); + } +} + +TEST(HalProxyTest, OperationModeResetOnInitialize) { + constexpr size_t kQueueSize = 5; + AllSensorsSubHal subHal; + std::vector subHals{&subHal}; + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.setOperationMode(OperationMode::DATA_INJECTION); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + Event event = makeAccelerometerEvent(); + // Should not be able to inject a non AdditionInfo type event because operation mode should + // have been reset to NORMAL + EXPECT_EQ(proxy.injectSensorData(event), Result::BAD_VALUE); +} + +TEST(HalProxyTest, DynamicSensorsDiscardedOnInitialize) { + constexpr size_t kQueueSize = 5; + constexpr size_t kNumSensors = 5; + AddAndRemoveDynamicSensorsSubHal subHal; + std::vector subHals{&subHal}; + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + HalProxy proxy(subHals); + + std::vector sensorsToConnect; + std::vector sensorHandlesToAttemptToRemove; + makeSensorsAndSensorHandlesStartingAndOfSize(1, kNumSensors, sensorsToConnect, + sensorHandlesToAttemptToRemove); + + std::vector nonDynamicSensorHandles; + for (int32_t sensorHandle = 1; sensorHandle < 10; sensorHandle++) { + nonDynamicSensorHandles.push_back(sensorHandle); + } + + TestSensorsCallback* callback = new TestSensorsCallback(); + ::android::sp callbackPtr = callback; + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr); + subHal.addDynamicSensors(sensorsToConnect); + + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr); + subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove); + + std::vector sensorHandlesActuallyRemoved = callback->getSensorHandlesDisconnected(); + + // Should not have received the sensorHandles for any dynamic sensors that were removed since + // all of them should have been removed in the second initialize call. + EXPECT_TRUE(sensorHandlesActuallyRemoved.empty()); +} + +TEST(HalProxyTest, DynamicSensorsConnectedTest) { + constexpr size_t kNumSensors = 3; + AddAndRemoveDynamicSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(0); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(0); + + std::vector sensorsToConnect; + std::vector sensorHandlesToExpect; + makeSensorsAndSensorHandlesStartingAndOfSize(1, kNumSensors, sensorsToConnect, + sensorHandlesToExpect); + + TestSensorsCallback* callback = new TestSensorsCallback(); + ::android::sp callbackPtr = callback; + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr); + subHal.addDynamicSensors(sensorsToConnect); + + std::vector sensorsSeen = callback->getSensorsConnected(); + EXPECT_EQ(kNumSensors, sensorsSeen.size()); + for (size_t i = 0; i < kNumSensors; i++) { + auto sensorHandleSeen = sensorsSeen[i].sensorHandle; + // Note since only one subhal we do not need to change first byte for expected + auto sensorHandleExpected = sensorHandlesToExpect[i]; + EXPECT_EQ(sensorHandleSeen, sensorHandleExpected); + } +} + +TEST(HalProxyTest, DynamicSensorsDisconnectedTest) { + constexpr size_t kNumSensors = 3; + AddAndRemoveDynamicSensorsSubHal subHal; + std::vector subHals{&subHal}; + HalProxy proxy(subHals); + std::unique_ptr eventQueue = makeEventFMQ(0); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(0); + + std::vector sensorsToConnect; + std::vector sensorHandlesToExpect; + makeSensorsAndSensorHandlesStartingAndOfSize(20, kNumSensors, sensorsToConnect, + sensorHandlesToExpect); + + std::vector nonDynamicSensorHandles; + for (int32_t sensorHandle = 1; sensorHandle < 10; sensorHandle++) { + nonDynamicSensorHandles.push_back(sensorHandle); + } + + std::set nonDynamicSensorHandlesSet(nonDynamicSensorHandles.begin(), + nonDynamicSensorHandles.end()); + + std::vector sensorHandlesToAttemptToRemove; + sensorHandlesToAttemptToRemove.insert(sensorHandlesToAttemptToRemove.end(), + sensorHandlesToExpect.begin(), + sensorHandlesToExpect.end()); + sensorHandlesToAttemptToRemove.insert(sensorHandlesToAttemptToRemove.end(), + nonDynamicSensorHandles.begin(), + nonDynamicSensorHandles.end()); + + TestSensorsCallback* callback = new TestSensorsCallback(); + ::android::sp callbackPtr = callback; + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr); + subHal.addDynamicSensors(sensorsToConnect); + subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove); + + std::vector sensorHandlesSeen = callback->getSensorHandlesDisconnected(); + EXPECT_EQ(kNumSensors, sensorHandlesSeen.size()); + for (size_t i = 0; i < kNumSensors; i++) { + auto sensorHandleSeen = sensorHandlesSeen[i]; + // Note since only one subhal we do not need to change first byte for expected + auto sensorHandleExpected = sensorHandlesToExpect[i]; + EXPECT_EQ(sensorHandleSeen, sensorHandleExpected); + EXPECT_TRUE(nonDynamicSensorHandlesSet.find(sensorHandleSeen) == + nonDynamicSensorHandlesSet.end()); + } +} + +TEST(HalProxyTest, InvalidSensorHandleSubHalIndexProxyCalls) { + constexpr size_t kNumSubHals = 3; + constexpr size_t kQueueSize = 5; + int32_t kNumSubHalsInt32 = static_cast(kNumSubHals); + std::vector subHalObjs(kNumSubHals); + std::vector subHals; + for (const auto& subHal : subHalObjs) { + subHals.push_back((ISensorsSubHal*)(&subHal)); + } + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + // Initialize for the injectSensorData call so callback postEvents is valid + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + // For testing proxy.injectSensorData properly + proxy.setOperationMode(OperationMode::DATA_INJECTION); + + // kNumSubHalsInt32 index is one off the end of mSubHalList in proxy object + EXPECT_EQ(proxy.activate(0x00000001 | (kNumSubHalsInt32 << 24), true), Result::BAD_VALUE); + EXPECT_EQ(proxy.batch(0x00000001 | (kNumSubHalsInt32 << 24), 0, 0), Result::BAD_VALUE); + EXPECT_EQ(proxy.flush(0x00000001 | (kNumSubHalsInt32 << 24)), Result::BAD_VALUE); + Event event; + event.sensorHandle = 0x00000001 | (kNumSubHalsInt32 << 24); + EXPECT_EQ(proxy.injectSensorData(event), Result::BAD_VALUE); +} + +TEST(HalProxyTest, PostedEventSensorHandleSubHalIndexValid) { + constexpr size_t kQueueSize = 5; + constexpr int32_t subhal1Index = 0; + constexpr int32_t subhal2Index = 1; + AllSensorsSubHal subhal1; + AllSensorsSubHal subhal2; + std::vector subHals{&subhal1, &subhal2}; + + std::unique_ptr eventQueue = makeEventFMQ(kQueueSize); + std::unique_ptr wakeLockQueue = makeWakelockFMQ(kQueueSize); + ::android::sp callback = new SensorsCallback(); + HalProxy proxy(subHals); + proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback); + + int32_t sensorHandleToPost = 0x00000001; + Event eventIn = makeAccelerometerEvent(); + eventIn.sensorHandle = sensorHandleToPost; + std::vector eventsToPost{eventIn}; + subhal1.postEvents(eventsToPost, false); + + Event eventOut; + EXPECT_TRUE(eventQueue->read(&eventOut)); + + EXPECT_EQ(eventOut.sensorHandle, (subhal1Index << 24) | sensorHandleToPost); + + subhal2.postEvents(eventsToPost, false); + + EXPECT_TRUE(eventQueue->read(&eventOut)); + + EXPECT_EQ(eventOut.sensorHandle, (subhal2Index << 24) | sensorHandleToPost); +} + +// Helper implementations follow +void testSensorsListFromProxyAndSubHal(const std::vector& proxySensorsList, + const std::vector& subHalSensorsList) { + EXPECT_EQ(proxySensorsList.size(), subHalSensorsList.size()); + + for (size_t i = 0; i < proxySensorsList.size(); i++) { + const SensorInfo& proxySensor = proxySensorsList[i]; + const SensorInfo& subHalSensor = subHalSensorsList[i]; + + EXPECT_EQ(proxySensor.type, subHalSensor.type); + EXPECT_EQ(proxySensor.sensorHandle & 0x00FFFFFF, subHalSensor.sensorHandle); + } +} + +void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector& sensorsList, + size_t enabledSubHalIndex) { + for (const SensorInfo& sensor : sensorsList) { + size_t subHalIndex = static_cast(sensor.sensorHandle >> 24); + if (subHalIndex == enabledSubHalIndex) { + // First subhal should have been picked as the direct channel subhal + // and so have direct channel enabled on all of its sensors + EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0); + EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0); + } else { + // All other subhals should have direct channel disabled for all sensors + EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0); + EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0); + } + } +} + +void ackWakeupEventsToHalProxy(size_t numEvents, std::unique_ptr& wakelockQueue, + EventFlag* wakelockQueueFlag) { + uint32_t numEventsUInt32 = static_cast(numEvents); + wakelockQueue->write(&numEventsUInt32); + wakelockQueueFlag->wake(static_cast(WakeLockQueueFlagBits::DATA_WRITTEN)); +} + +bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr& eventQueue, + EventFlag* eventQueueFlag) { + constexpr int64_t kReadBlockingTimeout = INT64_C(500000000); + std::vector events(numEvents); + return eventQueue->readBlocking(events.data(), numEvents, + static_cast(EventQueueFlagBits::EVENTS_READ), + static_cast(EventQueueFlagBits::READ_AND_PROCESS), + kReadBlockingTimeout, eventQueueFlag); +} + +std::unique_ptr makeEventFMQ(size_t size) { + return std::make_unique(size, true); +} + +std::unique_ptr makeWakelockFMQ(size_t size) { + return std::make_unique(size, true); +} + +Event makeProximityEvent() { + Event event; + event.timestamp = 0xFF00FF00; + // This is the sensorhandle of proximity, which is wakeup type + event.sensorHandle = 0x00000008; + event.sensorType = SensorType::PROXIMITY; + event.u = EventPayload(); + return event; +} + +Event makeAccelerometerEvent() { + Event event; + event.timestamp = 0xFF00FF00; + // This is the sensorhandle of proximity, which is wakeup type + event.sensorHandle = 0x00000001; + event.sensorType = SensorType::ACCELEROMETER; + event.u = EventPayload(); + return event; +} + +std::vector makeMultipleProximityEvents(size_t numEvents) { + std::vector events; + for (size_t i = 0; i < numEvents; i++) { + events.push_back(makeProximityEvent()); + } + return events; +} + +std::vector makeMultipleAccelerometerEvents(size_t numEvents) { + std::vector events; + for (size_t i = 0; i < numEvents; i++) { + events.push_back(makeAccelerometerEvent()); + } + return events; +} + +void makeSensorsAndSensorHandlesStartingAndOfSize(int32_t start, size_t size, + std::vector& sensors, + std::vector& sensorHandles) { + for (int32_t sensorHandle = start; sensorHandle < start + static_cast(size); + sensorHandle++) { + SensorInfo sensor; + // Just set the sensorHandle field to the correct value so as to not have + // to compare every field + sensor.sensorHandle = sensorHandle; + sensors.push_back(sensor); + sensorHandles.push_back(sensorHandle); + } +} + +} // namespace diff --git a/sensors/2.0/multihal/tests/fake_subhal/README b/sensors/2.0/multihal/tests/fake_subhal/README new file mode 100644 index 0000000000..ddcc58452f --- /dev/null +++ b/sensors/2.0/multihal/tests/fake_subhal/README @@ -0,0 +1,19 @@ +This directory contains a modified version of the default implementation +provided for sensors HAL 2.0 to support multi-HAL 2.0. It should be used as a +means to verify the multi-HAL 2.0 implementation can successfully load and +interact with sub-HALs. + +This sub-HAL implementation has two macros that can be used to configure support +for different sets of sensors. One "SUPPORT_CONTINUOUS_SENSORS", enables +support for continuous sensors like accel, and gyro whereas the other +"SUPPORT_ON_CHANGE_SENSORS" enables support for on change sensors like the +light and proximity sensor. A build target is defined for each of these macros, +but more targets could be added to support both in one sub-HAL or none at all, +if necessary. + +When built, the library will be written to +out/target/product//vendor/lib64/android.hardware.sensors@2.0-fakesubhal.so + +Take this .so and place it where the multi-HAL config will cause the HalProxy to +look and then restart the system server with adb shell stop / adb shell start +to cause the multi-HAL to restart and attempt to load in the sub-HAL. diff --git a/sensors/2.0/multihal/tests/fake_subhal/Sensor.cpp b/sensors/2.0/multihal/tests/fake_subhal/Sensor.cpp new file mode 100644 index 0000000000..de89a00ef9 --- /dev/null +++ b/sensors/2.0/multihal/tests/fake_subhal/Sensor.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2019 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 "Sensor.h" + +#include +#include + +#include + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace subhal { +namespace implementation { + +using ::android::hardware::sensors::V1_0::MetaDataEventType; +using ::android::hardware::sensors::V1_0::SensorFlagBits; +using ::android::hardware::sensors::V1_0::SensorStatus; + +Sensor::Sensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : mIsEnabled(false), + mSamplingPeriodNs(0), + mLastSampleTimeNs(0), + mCallback(callback), + mMode(OperationMode::NORMAL) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + constexpr float kDefaultMaxDelayUs = 1000 * 1000; + mSensorInfo.maxDelay = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; + mRunThread = std::thread(startThread, this); +} + +Sensor::~Sensor() { + // Ensure that lock is unlocked before calling mRunThread.join() or a + // deadlock will occur. + { + std::unique_lock lock(mRunMutex); + mStopThread = true; + mIsEnabled = false; + mWaitCV.notify_all(); + } + mRunThread.join(); +} + +const SensorInfo& Sensor::getSensorInfo() const { + return mSensorInfo; +} + +void Sensor::batch(int32_t samplingPeriodNs) { + samplingPeriodNs = + std::clamp(samplingPeriodNs, mSensorInfo.minDelay * 1000, mSensorInfo.maxDelay * 1000); + + if (mSamplingPeriodNs != samplingPeriodNs) { + mSamplingPeriodNs = samplingPeriodNs; + // Wake up the 'run' thread to check if a new event should be generated now + mWaitCV.notify_all(); + } +} + +void Sensor::activate(bool enable) { + if (mIsEnabled != enable) { + std::unique_lock lock(mRunMutex); + mIsEnabled = enable; + mWaitCV.notify_all(); + } +} + +Result Sensor::flush() { + // Only generate a flush complete event if the sensor is enabled and if the sensor is not a + // one-shot sensor. + if (!mIsEnabled || (mSensorInfo.flags & static_cast(SensorFlagBits::ONE_SHOT_MODE))) { + return Result::BAD_VALUE; + } + + // Note: If a sensor supports batching, write all of the currently batched events for the sensor + // to the Event FMQ prior to writing the flush complete event. + Event ev; + ev.sensorHandle = mSensorInfo.sensorHandle; + ev.sensorType = SensorType::META_DATA; + ev.u.meta.what = MetaDataEventType::META_DATA_FLUSH_COMPLETE; + std::vector evs{ev}; + mCallback->postEvents(evs, isWakeUpSensor()); + + return Result::OK; +} + +void Sensor::startThread(Sensor* sensor) { + sensor->run(); +} + +void Sensor::run() { + std::unique_lock runLock(mRunMutex); + constexpr int64_t kNanosecondsInSeconds = 1000 * 1000 * 1000; + + while (!mStopThread) { + if (!mIsEnabled || mMode == OperationMode::DATA_INJECTION) { + mWaitCV.wait(runLock, [&] { + return ((mIsEnabled && mMode == OperationMode::NORMAL) || mStopThread); + }); + } else { + timespec curTime; + clock_gettime(CLOCK_REALTIME, &curTime); + int64_t now = (curTime.tv_sec * kNanosecondsInSeconds) + curTime.tv_nsec; + int64_t nextSampleTime = mLastSampleTimeNs + mSamplingPeriodNs; + + if (now >= nextSampleTime) { + mLastSampleTimeNs = now; + nextSampleTime = mLastSampleTimeNs + mSamplingPeriodNs; + mCallback->postEvents(readEvents(), isWakeUpSensor()); + } + + mWaitCV.wait_for(runLock, std::chrono::nanoseconds(nextSampleTime - now)); + } + } +} + +bool Sensor::isWakeUpSensor() { + return mSensorInfo.flags & static_cast(SensorFlagBits::WAKE_UP); +} + +std::vector Sensor::readEvents() { + std::vector events; + Event event; + event.sensorHandle = mSensorInfo.sensorHandle; + event.sensorType = mSensorInfo.type; + event.timestamp = ::android::elapsedRealtimeNano(); + event.u.vec3.x = 0; + event.u.vec3.y = 0; + event.u.vec3.z = 0; + event.u.vec3.status = SensorStatus::ACCURACY_HIGH; + events.push_back(event); + return events; +} + +void Sensor::setOperationMode(OperationMode mode) { + if (mMode != mode) { + std::unique_lock lock(mRunMutex); + mMode = mode; + mWaitCV.notify_all(); + } +} + +bool Sensor::supportsDataInjection() const { + return mSensorInfo.flags & static_cast(SensorFlagBits::DATA_INJECTION); +} + +Result Sensor::injectEvent(const Event& event) { + Result result = Result::OK; + if (event.sensorType == SensorType::ADDITIONAL_INFO) { + // When in OperationMode::NORMAL, SensorType::ADDITIONAL_INFO is used to push operation + // environment data into the device. + } else if (!supportsDataInjection()) { + result = Result::INVALID_OPERATION; + } else if (mMode == OperationMode::DATA_INJECTION) { + mCallback->postEvents(std::vector{event}, isWakeUpSensor()); + } else { + result = Result::BAD_VALUE; + } + return result; +} + +OnChangeSensor::OnChangeSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(sensorHandle, callback), mPreviousEventSet(false) { + mSensorInfo.flags |= SensorFlagBits::ON_CHANGE_MODE; +} + +void OnChangeSensor::activate(bool enable) { + Sensor::activate(enable); + if (!enable) { + mPreviousEventSet = false; + } +} + +std::vector OnChangeSensor::readEvents() { + std::vector events = Sensor::readEvents(); + std::vector outputEvents; + + for (auto iter = events.begin(); iter != events.end(); ++iter) { + Event ev = *iter; + if (ev.u.vec3 != mPreviousEvent.u.vec3 || !mPreviousEventSet) { + outputEvents.push_back(ev); + mPreviousEvent = ev; + mPreviousEventSet = true; + } + } + return outputEvents; +} + +ContinuousSensor::ContinuousSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(sensorHandle, callback) { + mSensorInfo.flags |= SensorFlagBits::CONTINUOUS_MODE; +} + +AccelSensor::AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : ContinuousSensor(sensorHandle, callback) { + mSensorInfo.name = "Accel Sensor"; + mSensorInfo.type = SensorType::ACCELEROMETER; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_ACCELEROMETER; + mSensorInfo.maxRange = 78.4f; // +/- 8g + mSensorInfo.resolution = 1.52e-5; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelay = 20 * 1000; // microseconds + mSensorInfo.flags |= SensorFlagBits::DATA_INJECTION; +} + +std::vector AccelSensor::readEvents() { + std::vector events; + Event event; + event.sensorHandle = mSensorInfo.sensorHandle; + event.sensorType = mSensorInfo.type; + event.timestamp = ::android::elapsedRealtimeNano(); + event.u.vec3.x = 0; + event.u.vec3.y = 0; + event.u.vec3.z = -9.815; + event.u.vec3.status = SensorStatus::ACCURACY_HIGH; + events.push_back(event); + return events; +} + +PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : ContinuousSensor(sensorHandle, callback) { + mSensorInfo.name = "Pressure Sensor"; + mSensorInfo.type = SensorType::PRESSURE; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_PRESSURE; + mSensorInfo.maxRange = 1100.0f; // hPa + mSensorInfo.resolution = 0.005f; // hPa + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelay = 100 * 1000; // microseconds +} + +MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : ContinuousSensor(sensorHandle, callback) { + mSensorInfo.name = "Magnetic Field Sensor"; + mSensorInfo.type = SensorType::MAGNETIC_FIELD; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_MAGNETIC_FIELD; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelay = 20 * 1000; // microseconds +} + +LightSensor::LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(sensorHandle, callback) { + mSensorInfo.name = "Light Sensor"; + mSensorInfo.type = SensorType::LIGHT; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_LIGHT; + mSensorInfo.maxRange = 43000.0f; + mSensorInfo.resolution = 10.0f; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelay = 200 * 1000; // microseconds +} + +ProximitySensor::ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(sensorHandle, callback) { + mSensorInfo.name = "Proximity Sensor"; + mSensorInfo.type = SensorType::PROXIMITY; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_PROXIMITY; + mSensorInfo.maxRange = 5.0f; + mSensorInfo.resolution = 1.0f; + mSensorInfo.power = 0.012f; // mA + mSensorInfo.minDelay = 200 * 1000; // microseconds + mSensorInfo.flags |= SensorFlagBits::WAKE_UP; +} + +GyroSensor::GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : ContinuousSensor(sensorHandle, callback) { + mSensorInfo.name = "Gyro Sensor"; + mSensorInfo.type = SensorType::GYROSCOPE; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_GYROSCOPE; + mSensorInfo.maxRange = 1000.0f * M_PI / 180.0f; + mSensorInfo.resolution = 1000.0f * M_PI / (180.0f * 32768.0f); + mSensorInfo.power = 0.001f; + mSensorInfo.minDelay = 2.5f * 1000; // microseconds +} + +std::vector GyroSensor::readEvents() { + std::vector events; + Event event; + event.sensorHandle = mSensorInfo.sensorHandle; + event.sensorType = mSensorInfo.type; + event.timestamp = ::android::elapsedRealtimeNano(); + event.u.vec3.x = 0; + event.u.vec3.y = 0; + event.u.vec3.z = 0; + event.u.vec3.status = SensorStatus::ACCURACY_HIGH; + events.push_back(event); + return events; +} + +AmbientTempSensor::AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(sensorHandle, callback) { + mSensorInfo.name = "Ambient Temp Sensor"; + mSensorInfo.type = SensorType::AMBIENT_TEMPERATURE; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE; + mSensorInfo.maxRange = 80.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelay = 40 * 1000; // microseconds +} + +DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : ContinuousSensor(sensorHandle, callback) { + mSensorInfo.name = "Device Temp Sensor"; + mSensorInfo.type = SensorType::TEMPERATURE; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_TEMPERATURE; + mSensorInfo.maxRange = 80.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelay = 40 * 1000; // microseconds +} + +RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle, + ISensorsEventCallback* callback) + : OnChangeSensor(sensorHandle, callback) { + mSensorInfo.name = "Relative Humidity Sensor"; + mSensorInfo.type = SensorType::RELATIVE_HUMIDITY; + mSensorInfo.typeAsString = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY; + mSensorInfo.maxRange = 100.0f; + mSensorInfo.resolution = 0.1f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelay = 40 * 1000; // microseconds +} + +} // namespace implementation +} // namespace subhal +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android diff --git a/sensors/2.0/multihal/tests/fake_subhal/Sensor.h b/sensors/2.0/multihal/tests/fake_subhal/Sensor.h new file mode 100644 index 0000000000..60f5d3d40a --- /dev/null +++ b/sensors/2.0/multihal/tests/fake_subhal/Sensor.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +using ::android::hardware::sensors::V1_0::Event; +using ::android::hardware::sensors::V1_0::OperationMode; +using ::android::hardware::sensors::V1_0::Result; +using ::android::hardware::sensors::V1_0::SensorInfo; +using ::android::hardware::sensors::V1_0::SensorType; + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace subhal { +namespace implementation { + +class ISensorsEventCallback { + public: + virtual ~ISensorsEventCallback(){}; + virtual void postEvents(const std::vector& events, bool wakeup) = 0; +}; + +class Sensor { + public: + Sensor(int32_t sensorHandle, ISensorsEventCallback* callback); + virtual ~Sensor(); + + const SensorInfo& getSensorInfo() const; + void batch(int32_t samplingPeriodNs); + virtual void activate(bool enable); + Result flush(); + + void setOperationMode(OperationMode mode); + bool supportsDataInjection() const; + Result injectEvent(const Event& event); + + protected: + void run(); + virtual std::vector readEvents(); + static void startThread(Sensor* sensor); + + bool isWakeUpSensor(); + + bool mIsEnabled; + int64_t mSamplingPeriodNs; + int64_t mLastSampleTimeNs; + SensorInfo mSensorInfo; + + std::atomic_bool mStopThread; + std::condition_variable mWaitCV; + std::mutex mRunMutex; + std::thread mRunThread; + + ISensorsEventCallback* mCallback; + + OperationMode mMode; +}; + +class OnChangeSensor : public Sensor { + public: + OnChangeSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + virtual void activate(bool enable) override; + + protected: + virtual std::vector readEvents() override; + + protected: + Event mPreviousEvent; + bool mPreviousEventSet; +}; + +class ContinuousSensor : public Sensor { + public: + ContinuousSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class AccelSensor : public ContinuousSensor { + public: + AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + std::vector readEvents() override; +}; + +class GyroSensor : public ContinuousSensor { + public: + GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + std::vector readEvents() override; +}; + +class DeviceTempSensor : public ContinuousSensor { + public: + DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class PressureSensor : public ContinuousSensor { + public: + PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class MagnetometerSensor : public ContinuousSensor { + public: + MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class AmbientTempSensor : public OnChangeSensor { + public: + AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class LightSensor : public OnChangeSensor { + public: + LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class ProximitySensor : public OnChangeSensor { + public: + ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class RelativeHumiditySensor : public OnChangeSensor { + public: + RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +} // namespace implementation +} // namespace subhal +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android diff --git a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp new file mode 100644 index 0000000000..ff5ff38541 --- /dev/null +++ b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2019 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 "SensorsSubHal.h" + +#include +#include + +ISensorsSubHal* sensorsHalGetSubHal(uint32_t* version) { +#if defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS + static ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal subHal; +#elif defined SUPPORT_CONTINUOUS_SENSORS + static ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal + subHal; +#elif defined SUPPORT_ON_CHANGE_SENSORS + static ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal subHal; +#else + static ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal subHal; +#endif // defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS + *version = SUB_HAL_2_0_VERSION; + return &subHal; +} + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace subhal { +namespace implementation { + +using ::android::hardware::Void; +using ::android::hardware::sensors::V1_0::Event; +using ::android::hardware::sensors::V1_0::OperationMode; +using ::android::hardware::sensors::V1_0::RateLevel; +using ::android::hardware::sensors::V1_0::Result; +using ::android::hardware::sensors::V1_0::SharedMemInfo; +using ::android::hardware::sensors::V2_0::SensorTimeout; +using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits; +using ::android::hardware::sensors::V2_0::implementation::ScopedWakelock; + +SensorsSubHal::SensorsSubHal() : mCallback(nullptr), mNextHandle(1) {} + +// Methods from ::android::hardware::sensors::V2_0::ISensors follow. +Return SensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) { + std::vector sensors; + for (const auto& sensor : mSensors) { + sensors.push_back(sensor.second->getSensorInfo()); + } + + _hidl_cb(sensors); + return Void(); +} + +Return SensorsSubHal::setOperationMode(OperationMode mode) { + for (auto sensor : mSensors) { + sensor.second->setOperationMode(mode); + } + mCurrentOperationMode = mode; + return Result::OK; +} + +Return SensorsSubHal::activate(int32_t sensorHandle, bool enabled) { + auto sensor = mSensors.find(sensorHandle); + if (sensor != mSensors.end()) { + sensor->second->activate(enabled); + return Result::OK; + } + return Result::BAD_VALUE; +} + +Return SensorsSubHal::batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t /* maxReportLatencyNs */) { + auto sensor = mSensors.find(sensorHandle); + if (sensor != mSensors.end()) { + sensor->second->batch(samplingPeriodNs); + return Result::OK; + } + return Result::BAD_VALUE; +} + +Return SensorsSubHal::flush(int32_t sensorHandle) { + auto sensor = mSensors.find(sensorHandle); + if (sensor != mSensors.end()) { + return sensor->second->flush(); + } + return Result::BAD_VALUE; +} + +Return SensorsSubHal::injectSensorData(const Event& event) { + auto sensor = mSensors.find(event.sensorHandle); + if (sensor != mSensors.end()) { + return sensor->second->injectEvent(event); + } + + return Result::BAD_VALUE; +} + +Return SensorsSubHal::registerDirectChannel(const SharedMemInfo& /* mem */, + registerDirectChannel_cb _hidl_cb) { + _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */); + return Return(); +} + +Return SensorsSubHal::unregisterDirectChannel(int32_t /* channelHandle */) { + return Result::INVALID_OPERATION; +} + +Return SensorsSubHal::configDirectReport(int32_t /* sensorHandle */, + int32_t /* channelHandle */, RateLevel /* rate */, + configDirectReport_cb _hidl_cb) { + _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */); + return Return(); +} + +Return SensorsSubHal::debug(const hidl_handle& fd, const hidl_vec& args) { + if (fd.getNativeHandle() == nullptr || fd->numFds < 1) { + ALOGE("%s: missing fd for writing", __FUNCTION__); + return Void(); + } + + FILE* out = fdopen(dup(fd->data[0]), "w"); + + if (args.size() != 0) { + fprintf(out, + "Note: sub-HAL %s currently does not support args. Input arguments are " + "ignored.\n", + getName().c_str()); + } + + std::ostringstream stream; + stream << "Available sensors:" << std::endl; + for (auto sensor : mSensors) { + SensorInfo info = sensor.second->getSensorInfo(); + stream << "Name: " << info.name << std::endl; + stream << "Min delay: " << info.minDelay << std::endl; + stream << "Flags: " << info.flags << std::endl; + } + stream << std::endl; + + fprintf(out, "%s", stream.str().c_str()); + + fclose(out); + return Return(); +} + +Return SensorsSubHal::initialize(const sp& halProxyCallback) { + mCallback = halProxyCallback; + setOperationMode(OperationMode::NORMAL); + return Result::OK; +} + +void SensorsSubHal::postEvents(const std::vector& events, bool wakeup) { + ScopedWakelock wakelock = mCallback->createScopedWakelock(wakeup); + mCallback->postEvents(events, std::move(wakelock)); +} + +ContinuousSensorsSubHal::ContinuousSensorsSubHal() { + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); +} + +OnChangeSensorsSubHal::OnChangeSensorsSubHal() { + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); +} + +AllSensorsSubHal::AllSensorsSubHal() { + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); +} + +Return SetOperationModeFailingSensorsSubHal::setOperationMode(OperationMode /*mode*/) { + return Result::BAD_VALUE; +} + +Return AllSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) { + std::vector sensors; + for (const auto& sensor : mSensors) { + SensorInfo sensorInfo = sensor.second->getSensorInfo(); + sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL; + sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_REPORT; + sensors.push_back(sensorInfo); + } + _hidl_cb(sensors); + return Void(); +} + +Return DoesNotSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) { + std::vector sensors; + for (const auto& sensor : mSensors) { + SensorInfo sensorInfo = sensor.second->getSensorInfo(); + sensorInfo.flags &= ~static_cast(V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL); + sensorInfo.flags &= ~static_cast(V1_0::SensorFlagBits::MASK_DIRECT_REPORT); + sensors.push_back(sensorInfo); + } + _hidl_cb(sensors); + return Void(); +} + +void AddAndRemoveDynamicSensorsSubHal::addDynamicSensors( + const std::vector& sensorsAdded) { + mCallback->onDynamicSensorsConnected(sensorsAdded); +} + +void AddAndRemoveDynamicSensorsSubHal::removeDynamicSensors( + const std::vector& sensorHandlesRemoved) { + mCallback->onDynamicSensorsDisconnected(sensorHandlesRemoved); +} + +} // namespace implementation +} // namespace subhal +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android diff --git a/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h new file mode 100644 index 0000000000..c1e36472cf --- /dev/null +++ b/sensors/2.0/multihal/tests/fake_subhal/SensorsSubHal.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "SubHal.h" + +#include "Sensor.h" + +#include + +namespace android { +namespace hardware { +namespace sensors { +namespace V2_0 { +namespace subhal { +namespace implementation { + +using ::android::hardware::sensors::V1_0::OperationMode; +using ::android::hardware::sensors::V1_0::Result; +using ::android::hardware::sensors::V2_0::implementation::IHalProxyCallback; + +/** + * Implementation of a ISensorsSubHal that can be used to test the implementation of multihal 2.0. + * See the README file for more details on how this class can be used for testing. + */ +class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback { + using Event = ::android::hardware::sensors::V1_0::Event; + using RateLevel = ::android::hardware::sensors::V1_0::RateLevel; + using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo; + + public: + SensorsSubHal(); + + // Methods from ::android::hardware::sensors::V2_0::ISensors follow. + virtual Return getSensorsList(getSensorsList_cb _hidl_cb) override; + + virtual Return setOperationMode(OperationMode mode) override; + + OperationMode getOperationMode() const { return mCurrentOperationMode; } + + Return activate(int32_t sensorHandle, bool enabled) override; + + Return batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) override; + + Return flush(int32_t sensorHandle) override; + + Return injectSensorData(const Event& event) override; + + Return registerDirectChannel(const SharedMemInfo& mem, + registerDirectChannel_cb _hidl_cb) override; + + Return unregisterDirectChannel(int32_t channelHandle) override; + + Return configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate, + configDirectReport_cb _hidl_cb) override; + + Return debug(const hidl_handle& fd, const hidl_vec& args) override; + + // Methods from ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal follow. + const std::string getName() override { +#ifdef SUB_HAL_NAME + return SUB_HAL_NAME; +#else // SUB_HAL_NAME + return "FakeSubHal"; +#endif // SUB_HAL_NAME + } + + Return initialize(const sp& halProxyCallback) override; + + // Method from ISensorsEventCallback. + void postEvents(const std::vector& events, bool wakeup) override; + + protected: + template + void AddSensor() { + std::shared_ptr sensor = + std::make_shared(mNextHandle++ /* sensorHandle */, this /* callback */); + mSensors[sensor->getSensorInfo().sensorHandle] = sensor; + } + + /** + * A map of the available sensors + */ + std::map> mSensors; + + /** + * Callback used to communicate to the HalProxy when dynamic sensors are connected / + * disconnected, sensor events need to be sent to the framework, and when a wakelock should be + * acquired. + */ + sp mCallback; + + private: + /** + * The current operation mode of the multihal framework. Ensures that all subhals are set to + * the same operation mode. + */ + OperationMode mCurrentOperationMode = OperationMode::NORMAL; + + /** + * The next available sensor handle + */ + int32_t mNextHandle; +}; + +// SubHal that has continuous sensors for testing purposes. +class ContinuousSensorsSubHal : public SensorsSubHal { + public: + ContinuousSensorsSubHal(); +}; + +// SubHal that has on-change sensors for testing purposes. +class OnChangeSensorsSubHal : public SensorsSubHal { + public: + OnChangeSensorsSubHal(); +}; + +// SubHal that has both continuous and on-change sensors for testing purposes. +class AllSensorsSubHal : public SensorsSubHal { + public: + AllSensorsSubHal(); +}; + +class SetOperationModeFailingSensorsSubHal : public AllSensorsSubHal { + public: + Return setOperationMode(OperationMode mode) override; +}; + +class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal { + public: + Return getSensorsList(getSensorsList_cb _hidl_cb) override; +}; + +class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal { + public: + Return getSensorsList(getSensorsList_cb _hidl_cb) override; +}; + +class AddAndRemoveDynamicSensorsSubHal : public AllSensorsSubHal { + public: + void addDynamicSensors(const std::vector& sensorsAdded); + void removeDynamicSensors(const std::vector& sensorHandlesAdded); +}; + +} // namespace implementation +} // namespace subhal +} // namespace V2_0 +} // namespace sensors +} // namespace hardware +} // namespace android