| /* |
| * Copyright (C) 2016 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. |
| */ |
| |
| #define LOG_TAG "sensor_manager_hidl_hal_test" |
| #include <android-base/logging.h> |
| |
| #include <sys/mman.h> |
| |
| #include <chrono> |
| #include <thread> |
| |
| #include <android-base/result.h> |
| #include <android/frameworks/sensorservice/1.0/ISensorManager.h> |
| #include <android/frameworks/sensorservice/1.0/types.h> |
| #include <android/hardware/sensors/1.0/types.h> |
| #include <android/hidl/allocator/1.0/IAllocator.h> |
| #include <android/sensor.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <hidl/GtestPrinter.h> |
| #include <hidl/ServiceManagement.h> |
| #include <sensors/convert.h> |
| |
| using ::android::sp; |
| using ::android::frameworks::sensorservice::V1_0::ISensorManager; |
| using ::android::frameworks::sensorservice::V1_0::Result; |
| using ::android::hardware::hidl_memory; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::sensors::V1_0::Event; |
| using ::android::hardware::sensors::V1_0::RateLevel; |
| using ::android::hardware::sensors::V1_0::SensorFlagBits; |
| using ::android::hardware::sensors::V1_0::SensorFlagShift; |
| using ::android::hardware::sensors::V1_0::SensorInfo; |
| using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset; |
| using ::android::hardware::sensors::V1_0::SensorType; |
| using ::android::hardware::sensors::V1_0::toString; |
| using ::android::hidl::allocator::V1_0::IAllocator; |
| using ::testing::Contains; |
| |
| template <typename T> |
| static inline ::testing::AssertionResult isOk(const Return<T> &ret) { |
| return (ret.isOk() |
| ? ::testing::AssertionSuccess() |
| : ::testing::AssertionFailure()) << ret.description(); |
| } |
| |
| template <> |
| __attribute__((__unused__)) |
| inline ::testing::AssertionResult isOk(const Return<Result> &ret) { |
| return ((ret.isOk() && ret == Result::OK) |
| ? ::testing::AssertionSuccess() |
| : ::testing::AssertionFailure()) |
| << ret.description() << ", " |
| << (ret.isOk() ? toString(static_cast<Result>(ret)) : ""); |
| } |
| |
| static inline ::testing::AssertionResult isOk(Result result) { |
| using ::android::frameworks::sensorservice::V1_0::toString; |
| return (result == Result::OK |
| ? ::testing::AssertionSuccess() |
| : ::testing::AssertionFailure()) << toString(result); |
| } |
| |
| template<typename I, typename F> |
| static ::testing::AssertionResult isIncreasing(I begin, I end, F getField) { |
| typename std::iterator_traits<I>::pointer lastValue = nullptr; |
| I iter; |
| size_t pos; |
| for (iter = begin, pos = 0; iter != end; ++iter, ++pos) { |
| if (iter == begin) { |
| lastValue = &(*iter); |
| continue; |
| } |
| if (getField(*iter) < getField(*lastValue)) { |
| return ::testing::AssertionFailure() << "Not an increasing sequence, pos = " |
| << pos << ", " << getField(*iter) << " < " << getField(*lastValue); |
| } |
| } |
| return ::testing::AssertionSuccess(); |
| } |
| |
| #define EXPECT_OK(__ret__) EXPECT_TRUE(isOk(__ret__)) |
| #define ASSERT_OK(__ret__) ASSERT_TRUE(isOk(__ret__)) |
| |
| class SensorManagerTest : public ::testing::TestWithParam<std::string> { |
| public: |
| virtual void SetUp() override { |
| manager_ = ISensorManager::getService(GetParam()); |
| ASSERT_NE(manager_, nullptr); |
| ashmem_ = IAllocator::getService("ashmem"); |
| ASSERT_NE(ashmem_, nullptr); |
| } |
| |
| // Call getSensorList. Filter result based on |pred| if it is provided. |
| android::base::Result<std::vector<SensorInfo>> GetSensorList( |
| const std::function<bool(SensorInfo)> &pred = nullptr) { |
| Result out_result = Result::INVALID_OPERATION; |
| std::vector<SensorInfo> out_info; |
| |
| auto ret = manager_->getSensorList([&](const auto &list, auto result) { |
| out_result = result; |
| if (result == Result::OK) { |
| for (const auto &info : list) { |
| if (!pred || pred(info)) { |
| out_info.push_back(info); |
| } |
| } |
| } |
| }); |
| if (!ret.isOk()) { |
| return android::base::Error() << ret.description(); |
| } |
| if (out_result != Result::OK) { |
| return android::base::Error() << "getSensorList returns " << toString(out_result); |
| } |
| return out_info; |
| } |
| |
| sp<ISensorManager> manager_; |
| sp<IAllocator> ashmem_; |
| }; |
| |
| using map_region = std::unique_ptr<void, std::function<void(void*)>>; |
| |
| map_region map(const hidl_memory &mem) { |
| if (mem.handle() == nullptr || mem.handle()->numFds != 1) { |
| return nullptr; |
| } |
| size_t size = mem.size(); |
| void *buf = mmap(nullptr, size, PROT_READ, MAP_SHARED, mem.handle()->data[0], 0); |
| return map_region{buf, [size](void *localBuf) { |
| munmap(localBuf, size); |
| }}; |
| } |
| |
| TEST_P(SensorManagerTest, List) { |
| ASSERT_RESULT_OK(GetSensorList()); |
| } |
| |
| TEST_P(SensorManagerTest, Ashmem) { |
| auto ashmem_sensors = GetSensorList( |
| [](const auto &info) { return info.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM; }); |
| ASSERT_RESULT_OK(ashmem_sensors); |
| if (ashmem_sensors->empty()) { |
| GTEST_SKIP() << "DIRECT_CHANNEL_ASHMEM not supported by HAL, skipping"; |
| } |
| auto testOne = [this](uint64_t memSize, uint64_t intendedSize, |
| ISensorManager::createAshmemDirectChannel_cb callback) { |
| ASSERT_OK(ashmem_->allocate(memSize, [&](bool success, const auto &mem) { |
| ASSERT_TRUE(success); |
| ASSERT_NE(mem.handle(), nullptr); |
| ASSERT_OK(manager_->createAshmemDirectChannel(mem, intendedSize, callback)); |
| })); |
| }; |
| |
| testOne(16, 16, [](const auto &chan, Result result) { |
| EXPECT_EQ(result, Result::BAD_VALUE) << "unexpected result when memory size is too small"; |
| EXPECT_EQ(chan, nullptr); |
| }); |
| |
| testOne(1024, 1024, [](const auto &chan, Result result) { |
| EXPECT_OK(result); |
| EXPECT_NE(chan, nullptr); |
| }); |
| |
| testOne(1024, 2048, [](const auto &chan, Result result) { |
| EXPECT_EQ(result, Result::BAD_VALUE) << "unexpected result when intended size is too big"; |
| EXPECT_EQ(chan, nullptr); |
| }); |
| |
| testOne(1024, 16, [](const auto &chan, Result result) { |
| EXPECT_EQ(result, Result::BAD_VALUE) << "unexpected result when intended size is too small"; |
| EXPECT_EQ(chan, nullptr); |
| }); |
| } |
| |
| static std::vector<Event> parseEvents(uint8_t *buf, size_t memSize) { |
| using O = SensorsEventFormatOffset; |
| using ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent; |
| size_t offset = 0; |
| int64_t lastCounter = -1; |
| std::vector<Event> events; |
| Event event; |
| |
| while(offset + (size_t)O::TOTAL_LENGTH <= memSize) { |
| uint8_t *start = buf + offset; |
| int64_t atomicCounter = *reinterpret_cast<uint32_t *>(start + (size_t)O::ATOMIC_COUNTER); |
| if (atomicCounter <= lastCounter) { |
| break; |
| } |
| int32_t size = *reinterpret_cast<int32_t *>(start + (size_t)O::SIZE_FIELD); |
| if (size != (size_t)O::TOTAL_LENGTH) { |
| // unknown error, events parsed may be wrong, remove all |
| events.clear(); |
| break; |
| } |
| |
| convertFromSensorEvent(*reinterpret_cast<const sensors_event_t *>(start), &event); |
| events.push_back(event); |
| lastCounter = atomicCounter; |
| offset += (size_t)O::TOTAL_LENGTH; |
| } |
| return events; |
| } |
| |
| TEST_P(SensorManagerTest, GetDefaultAccelerometer) { |
| auto accelerometer_ashmem_sensors = |
| GetSensorList([](const auto &info) { return info.type == SensorType::ACCELEROMETER; }); |
| ASSERT_RESULT_OK(accelerometer_ashmem_sensors); |
| |
| ASSERT_OK( |
| manager_->getDefaultSensor(SensorType::ACCELEROMETER, [&](const auto &info, auto result) { |
| if (accelerometer_ashmem_sensors->empty()) { |
| ASSERT_EQ(Result::NOT_EXIST, result); |
| } else { |
| ASSERT_OK(result); |
| ASSERT_THAT(*accelerometer_ashmem_sensors, Contains(info)); |
| } |
| })); |
| } |
| |
| TEST_P(SensorManagerTest, Accelerometer) { |
| using std::literals::chrono_literals::operator""ms; |
| using ::android::hardware::sensors::V1_0::implementation::convertFromRateLevel; |
| |
| auto accelerometer_ashmem_sensors = GetSensorList([](const auto &info) { |
| if (info.type != SensorType::ACCELEROMETER) return false; |
| if (!(info.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM)) return false; |
| int maxLevel = |
| (info.flags & SensorFlagBits::MASK_DIRECT_REPORT) >> (int)SensorFlagShift::DIRECT_REPORT; |
| return maxLevel >= convertFromRateLevel(RateLevel::FAST); |
| }); |
| ASSERT_RESULT_OK(accelerometer_ashmem_sensors); |
| |
| if (accelerometer_ashmem_sensors->empty()) { |
| GTEST_SKIP() << "No accelerometer sensor that supports DIRECT_CHANNEL_ASHMEM and fast report " |
| << "rate, skipping"; |
| } |
| |
| for (const auto &info : *accelerometer_ashmem_sensors) { |
| int32_t handle = info.sensorHandle; |
| const size_t memSize = (size_t)SensorsEventFormatOffset::TOTAL_LENGTH * 300; |
| ASSERT_OK(ashmem_->allocate(memSize, [&](bool success, const auto &mem) { |
| ASSERT_TRUE(success); |
| map_region buf = map(mem); |
| ASSERT_NE(buf, nullptr); |
| ASSERT_OK( |
| manager_->createAshmemDirectChannel(mem, memSize, [&](const auto &chan, Result result) { |
| ASSERT_OK(result); |
| ASSERT_NE(chan, nullptr); |
| |
| int32_t returnedToken; |
| ASSERT_OK(chan->configure(handle, RateLevel::FAST, [&](auto token, auto res) { |
| ASSERT_OK(res); |
| ASSERT_GT(token, 0); |
| returnedToken = token; |
| })); // ~200Hz |
| std::this_thread::sleep_for(500ms); |
| ASSERT_OK(chan->configure(handle, RateLevel::STOP, [](auto token, auto res) { |
| ASSERT_OK(res); |
| ASSERT_EQ(token, 0); |
| })); |
| |
| auto events = parseEvents(static_cast<uint8_t *>(buf.get()), memSize); |
| |
| EXPECT_TRUE(isIncreasing(events.begin(), events.end(), [](const auto &event) { |
| return event.timestamp; |
| })) << "timestamp is not monotonically increasing"; |
| for (const auto &event : events) { |
| EXPECT_EQ(returnedToken, event.sensorHandle) |
| << "configure token and sensor handle don't match."; |
| } |
| })); |
| })); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| PerInstance, SensorManagerTest, |
| testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISensorManager::descriptor)), |
| android::hardware::PrintInstanceNameToString); |