Merge "Vibrator: Stable AIDL interface."
diff --git a/atrace/1.0/vts/functional/Android.bp b/atrace/1.0/vts/functional/Android.bp
index d3f4276..ae24968 100644
--- a/atrace/1.0/vts/functional/Android.bp
+++ b/atrace/1.0/vts/functional/Android.bp
@@ -19,5 +19,5 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalAtraceV1_0TargetTest.cpp"],
static_libs: ["android.hardware.atrace@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp b/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
index c62c2f0..2eaa03e 100644
--- a/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
+++ b/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
@@ -18,8 +18,9 @@
#include <android/hardware/atrace/1.0/IAtraceDevice.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
using ::android::sp;
using ::android::hardware::hidl_string;
@@ -28,29 +29,13 @@
using ::android::hardware::atrace::V1_0::IAtraceDevice;
using ::android::hardware::atrace::V1_0::Status;
-// Test environment for Boot HIDL HAL.
-class AtraceHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static AtraceHidlEnvironment* Instance() {
- static AtraceHidlEnvironment* instance = new AtraceHidlEnvironment;
- return instance;
- }
-
- virtual void registerTestServices() override { registerTestService<IAtraceDevice>(); }
-
- private:
- AtraceHidlEnvironment() {}
-};
-
/**
* There is no expected behaviour that can be tested so these tests check the
* HAL doesn't crash with different execution orders.
*/
-struct AtraceHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+struct AtraceHidlTest : public ::testing::TestWithParam<std::string> {
virtual void SetUp() override {
- atrace = ::testing::VtsHalHidlTargetTestBase::getService<IAtraceDevice>(
- AtraceHidlEnvironment::Instance()->getServiceName<IAtraceDevice>());
+ atrace = IAtraceDevice::getService(GetParam());
ASSERT_NE(atrace, nullptr);
}
@@ -82,13 +67,13 @@
}
/* list categories from vendors. */
-TEST_F(AtraceHidlTest, listCategories) {
+TEST_P(AtraceHidlTest, listCategories) {
hidl_vec<hidl_string> vnd_categories = getVendorCategoryName(atrace);
EXPECT_NE(0, vnd_categories.size());
}
/* enable categories. */
-TEST_F(AtraceHidlTest, enableCategories) {
+TEST_P(AtraceHidlTest, enableCategories) {
hidl_vec<hidl_string> vnd_categories = getVendorCategoryName(atrace);
// empty Category with ERROR_INVALID_ARGUMENT
hidl_vec<hidl_string> empty_categories;
@@ -102,17 +87,13 @@
}
/* enable categories. */
-TEST_F(AtraceHidlTest, disableAllCategories) {
+TEST_P(AtraceHidlTest, disableAllCategories) {
auto ret = atrace->disableAllCategories();
ASSERT_TRUE(ret.isOk());
EXPECT_EQ(Status::SUCCESS, ret);
}
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(AtraceHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- AtraceHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, AtraceHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAtraceDevice::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/current.txt b/current.txt
index 877bcb4..8953d51 100644
--- a/current.txt
+++ b/current.txt
@@ -586,6 +586,9 @@
# HALs released in Android R
07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
+ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
+26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
+db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types
34515afa2bb792d3c6d8495a5f5d907d179c8507ca5e55c10050d02ae1d516ef android.hardware.neuralnetworks@1.3::IDevice
b74fe72cfe438f50e772e6a307657ff449d5bde83c15dd1f140ff2edbe73499c android.hardware.neuralnetworks@1.3::types
544049dcda3f943ad67d83d5277f06681a3782982a9af5a78b5d4e8d295d061a android.hardware.vibrator@1.4::IVibrator
diff --git a/health/1.0/vts/functional/Android.bp b/health/1.0/vts/functional/Android.bp
index c14dcee..be2d206 100644
--- a/health/1.0/vts/functional/Android.bp
+++ b/health/1.0/vts/functional/Android.bp
@@ -19,5 +19,5 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalHealthV1_0TargetTest.cpp"],
static_libs: ["android.hardware.health@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp b/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
index 335d15d..b985a9f 100644
--- a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
+++ b/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
@@ -18,11 +18,11 @@
#include <android/hardware/health/1.0/IHealth.h>
#include <android/hardware/health/1.0/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <log/log.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
-
using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
using IHealth = ::android::hardware::health::V1_0::IHealth;
@@ -30,25 +30,10 @@
using ::android::sp;
-// Test environment for Health HIDL HAL.
-class HealthHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static HealthHidlEnvironment* Instance() {
- static HealthHidlEnvironment* instance = new HealthHidlEnvironment;
- return instance;
- }
-
- virtual void registerTestServices() override { registerTestService<IHealth>(); }
- private:
- HealthHidlEnvironment() {}
-};
-
-class HealthHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class HealthHidlTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
- health = ::testing::VtsHalHidlTargetTestBase::getService<IHealth>(
- HealthHidlEnvironment::Instance()->getServiceName<IHealth>());
+ health = IHealth::getService(GetParam());
ASSERT_NE(health, nullptr);
health->init(config,
[&](const auto& halConfigOut) { config = halConfigOut; });
@@ -61,7 +46,7 @@
/**
* Ensure EnergyCounter call returns positive energy counter or NOT_SUPPORTED
*/
-TEST_F(HealthHidlTest, TestEnergyCounter) {
+TEST_P(HealthHidlTest, TestEnergyCounter) {
Result result;
int64_t energy = 0;
health->energyCounter([&](Result ret, int64_t energyOut) {
@@ -73,11 +58,7 @@
ASSERT_TRUE(result != Result::SUCCESS || energy > 0);
}
-int main(int argc, char **argv) {
- ::testing::AddGlobalTestEnvironment(HealthHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- HealthHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, HealthHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/health/2.0/README.md b/health/2.0/README.md
index 4ecfb9a..8a7c922 100644
--- a/health/2.0/README.md
+++ b/health/2.0/README.md
@@ -1,3 +1,8 @@
+# Implement the 2.1 HAL instead!
+
+It is strongly recommended that you implement the 2.1 HAL directly. See
+`hardware/interfaces/health/2.1/README.md` for more details.
+
# Upgrading from Health 1.0 HAL
1. Remove `android.hardware.health@1.0*` from `PRODUCT_PACKAGES`
diff --git a/health/2.1/README.md b/health/2.1/README.md
new file mode 100644
index 0000000..5a19d7b
--- /dev/null
+++ b/health/2.1/README.md
@@ -0,0 +1,245 @@
+# Implementing Health 2.1 HAL
+
+1. Install common binderized service. The binderized service `dlopen()`s
+ passthrough implementations on the device, so there is no need to write
+ your own.
+
+ ```mk
+ # Install default binderized implementation to vendor.
+ PRODUCT_PACKAGES += android.hardware.health@2.1-service
+ ```
+
+1. Delete existing VINTF manifest entry. Search for `android.hardware.health` in
+ your device manifest, and delete the whole `<hal>` entry for older versions
+ of the HAL. Instead, when `android.hardware.health@2.1-service` is installed,
+ a VINTF manifest fragment is installed to `/vendor/etc/vintf`, so there is
+ no need to manually specify it in your device manifest. See
+ [Manifest fragments](https://source.android.com/devices/architecture/vintf/objects#manifest-fragments)
+ for details.
+
+1. Install the proper passthrough implemetation.
+
+ 1. If you want to use default implementation:
+
+ ```mk
+ # Install default passthrough implementation to vendor.
+ PRODUCT_PACKAGES += android.hardware.health@2.1-impl
+ ```
+
+ You are done. Otherwise, go to the next step.
+
+ 1. If you want to write your own implementation,
+
+ 1. Copy skeleton implementation from the [appendix](#impl).
+
+ 1. Modify the implementation to suit your needs.
+
+ * If you have a board or device specific `libhealthd`, see
+ [Upgrading with a customized libhealthd](#update-from-1-0).
+ * If you are upgrading from 1.0 health HAL, see
+ [Upgrading from Health HAL 1.0](#update-from-1-0).
+ * If you are upgrading from a customized 2.0 health HAL
+ implementation, See
+ [Upgrading from Health HAL 2.0](#update-from-2-0).
+
+ 1. [Update necessary SELinux permissions](#selinux).
+
+ 1. [Fix `/charger` symlink](#charger-symlink).
+
+# Upgrading with a customized libhealthd or from Health HAL 1.0 {#update-from-1-0}
+
+`libhealthd` contains two functions: `healthd_board_init()` and
+`healthd_board_battery_update()`. Similarly, Health HAL 1.0 contains `init()`
+and `update()`, with an additional `energyCounter()` function.
+
+* `healthd_board_init()` / `@1.0::IHealth.init()` should be called before
+ passing the `healthd_config` struct to your `HealthImpl` class. See
+ `HIDL_FETCH_IHealth` in [`HealthImpl.cpp`](#health_impl_cpp).
+
+* `healthd_board_battery_update()` / `@1.0::IHealth.update()` should be called
+ in `HealthImpl::UpdateHealthInfo()`. Example:
+
+ ```c++
+ void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) {
+ struct BatteryProperties props;
+ convertFromHealthInfo(health_info->legacy.legacy, &props);
+ healthd_board_battery_update(&props);
+ convertToHealthInfo(&props, health_info->legacy.legacy);
+ }
+ ```
+ For efficiency, you should move code in `healthd_board_battery_update` to
+ `HealthImpl::UpdateHealthInfo` and modify `health_info` directly to avoid
+ conversion to `BatteryProperties`.
+
+* Code for `@1.0::IHealth.energyCounter()` should be moved to
+ `HealthImpl::getEnergyCounter()`. Example:
+
+ ```c++
+ Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
+ int64_t energy = /* ... */;
+ _hidl_cb(Result::SUCCESS, energy);
+ return Void();
+ }
+ ```
+
+# Upgrading from Health HAL 2.0 {#update-from-2-0}
+
+* If you have implemented `healthd_board_init()` and/or
+ `healthd_board_battery_update()` (instead of using `libhealthd.default`),
+ see [the section above](#update-from-1-0)
+ for instructions to convert them.
+
+* If you have implemented `get_storage_info()` and/or `get_disk_stats()`
+ (instead of using libhealthstoragedefault), implement `HealthImpl::getDiskStats`
+ and/or `HealthImpl::getStorageInfo` directly. There is no need to override
+ `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call
+ `getDiskStats` and `getStorageInfo` to retrieve storage information.
+
+# Update necessary SELinux permissions {#selinux}
+
+For example (replace `<device>` with the device name):
+```
+# device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
+# Add device specific permissions to hal_health_default domain, especially
+# if a device-specific libhealthd is used and/or device-specific storage related
+# APIs are implemented.
+```
+
+# Fix `/charger` symlink {#charger-symlink}
+If you are using `/charger` in your `init.rc` scripts, it is recommended
+(required for devices running in Android R) that the path is changed to
+`/system/bin/charger` instead.
+
+Search for `service charger` in your device configuration directory to see if
+this change applies to your device. Below is an example of how the script should
+look like:
+
+```
+service charger /system/bin/charger
+ class charger
+ user system
+ group system wakelock input
+ capabilities SYS_BOOT
+ file /dev/kmsg w
+ file /sys/fs/pstore/console-ramoops-0 r
+ file /sys/fs/pstore/console-ramoops r
+ file /proc/last_kmsg r
+```
+
+# Appendix: sample code for the implementation {#impl}
+
+## `device/<manufacturer>/<device>/health/Android.bp` {#android_bp}
+
+```bp
+cc_library_shared {
+ name: "android.hardware.health@2.1-impl-<device>",
+ stem: "android.hardware.health@2.0-impl-2.1-<device>",
+
+ // Install to vendor and recovery.
+ proprietary: true,
+ recovery_available: true,
+
+ relative_install_path: "hw",
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.health@2.1",
+ "android.hardware.health@2.0",
+ ],
+
+ static_libs: [
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ "libhealthloop",
+ "libhealth2impl",
+ // "libhealthd.<device>"
+ ],
+
+ srcs: [
+ "HealthImpl.cpp",
+ ],
+
+ // No vintf_fragments because both -impl and -service should have been
+ // installed.
+}
+```
+
+## `device/<manufacturer>/<device>/health/HealthImpl.cpp` {#health_impl_cpp}
+
+```c++
+#include <memory>
+#include <string_view>
+
+#include <health/utils.h>
+#include <health2impl/Health.h>
+#include <hidl/Status.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::health::InitHealthdConfig;
+using ::android::hardware::health::V2_1::IHealth;
+using ::android::hidl::base::V1_0::IBase;
+
+using namespace std::literals;
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+// android::hardware::health::V2_1::implementation::Health implements most
+// defaults. Uncomment functions that you need to override.
+class HealthImpl : public Health {
+ public:
+ HealthImpl(std::unique_ptr<healthd_config>&& config)
+ : Health(std::move(config)) {}
+
+ // A subclass can override this if these information should be retrieved
+ // differently.
+ // Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
+ // Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
+ // Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
+ // Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
+ // Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
+ // Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
+ // Return<void> getStorageInfo(getStorageInfo_cb _hidl_cb) override;
+ // Return<void> getDiskStats(getDiskStats_cb _hidl_cb) override;
+ // Return<void> getHealthInfo(getHealthInfo_cb _hidl_cb) override;
+
+ // Functions introduced in Health HAL 2.1.
+ // Return<void> getHealthConfig(getHealthConfig_cb _hidl_cb) override;
+ // Return<void> getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override;
+ // Return<void> shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override;
+
+ protected:
+ // A subclass can override this to modify any health info object before
+ // returning to clients. This is similar to healthd_board_battery_update().
+ // By default, it does nothing.
+ // void UpdateHealthInfo(HealthInfo* health_info) override;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
+
+extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) {
+ using ::android::hardware::health::V2_1::implementation::HealthImpl;
+ if (instance != "default"sv) {
+ return nullptr;
+ }
+ auto config = std::make_unique<healthd_config>();
+ InitHealthdConfig(config.get());
+
+ // healthd_board_init(config.get());
+
+ return new HealthImpl(std::move(config));
+}
+```
diff --git a/health/2.1/default/Android.bp b/health/2.1/default/Android.bp
new file mode 100644
index 0000000..3649853
--- /dev/null
+++ b/health/2.1/default/Android.bp
@@ -0,0 +1,81 @@
+// 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.health@2.1-impl-defaults",
+ relative_install_path: "hw",
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.health@2.1",
+ "android.hardware.health@2.0",
+ ],
+
+ static_libs: [
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ "libhealthloop",
+ "libhealth2impl",
+ ],
+}
+
+// Default passthrough implementation of the health@2.1 HAL.
+// Passhtrough implementations of the health@2.1 HAL must be installed in
+// vendor in order to support charger.
+// Passhtrough implementations of the health@2.1 HAL must be installed in
+// recovery in order to allow recovery to check battery status.
+// See README.md for details.
+cc_library_shared {
+ name: "android.hardware.health@2.1-impl",
+ stem: "android.hardware.health@2.0-impl-2.1",
+
+ // Only vendor and recovery variants are allowed, not core.
+ vendor: true,
+ recovery_available: true,
+
+ defaults: ["android.hardware.health@2.1-impl-defaults"],
+
+ srcs: [
+ "impl.cpp",
+ ],
+
+ // No vintf_fragments because both -impl and -service should have been
+ // installed.
+}
+
+// Default binderized service of the health@2.1 HAL.
+// This binderized implementation dlopen()s the passthrough implementation,
+// so there is no need to implement your own.
+cc_binary {
+ name: "android.hardware.health@2.1-service",
+ vendor: true,
+ defaults: ["android.hardware.health@2.1-impl-defaults"],
+ init_rc: ["android.hardware.health@2.1-service.rc"],
+
+ srcs: [
+ "service.cpp",
+ ],
+
+ vintf_fragments: [
+ "android.hardware.health@2.1.xml"
+ ],
+
+ overrides: [
+ "healthd",
+ ],
+}
diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc
new file mode 100644
index 0000000..917f1c2
--- /dev/null
+++ b/health/2.1/default/android.hardware.health@2.1-service.rc
@@ -0,0 +1,6 @@
+service health-hal-2-1 /vendor/bin/hw/android.hardware.health@2.1-service
+ class hal
+ user system
+ group system
+ capabilities WAKE_ALARM
+ file /dev/kmsg w
diff --git a/health/2.1/default/android.hardware.health@2.1.xml b/health/2.1/default/android.hardware.health@2.1.xml
new file mode 100644
index 0000000..34fdca6
--- /dev/null
+++ b/health/2.1/default/android.hardware.health@2.1.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.health</name>
+ <transport>hwbinder</transport>
+ <fqname>@2.1::IHealth/default</fqname>
+ </hal>
+</manifest>
diff --git a/health/2.1/default/impl.cpp b/health/2.1/default/impl.cpp
new file mode 100644
index 0000000..7389a21
--- /dev/null
+++ b/health/2.1/default/impl.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <memory>
+#include <string_view>
+
+#include <health/utils.h>
+#include <health2impl/Health.h>
+
+using ::android::sp;
+using ::android::hardware::health::InitHealthdConfig;
+using ::android::hardware::health::V2_1::IHealth;
+using ::android::hardware::health::V2_1::implementation::Health;
+
+using namespace std::literals;
+
+// Passthrough implementation of the health service. Use default configuration.
+// It does not invoke callbacks unless update() is called explicitly. No
+// background thread is spawned to handle callbacks.
+//
+// The passthrough implementation is only allowed in recovery mode, charger, and
+// opened by the hwbinder service.
+// If Android is booted normally, the hwbinder service is used instead.
+//
+// This implementation only implements the "default" instance. It rejects
+// other instance names.
+// Note that the Android framework only reads values from the "default"
+// health HAL 2.1 instance.
+extern "C" IHealth* HIDL_FETCH_IHealth(const char* instance) {
+ if (instance != "default"sv) {
+ return nullptr;
+ }
+ auto config = std::make_unique<healthd_config>();
+ InitHealthdConfig(config.get());
+
+ // This implementation uses default config. If you want to customize it
+ // (e.g. with healthd_board_init), do it here.
+
+ return new Health(std::move(config));
+}
diff --git a/health/2.1/default/service.cpp b/health/2.1/default/service.cpp
new file mode 100644
index 0000000..f8334c5
--- /dev/null
+++ b/health/2.1/default/service.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "android.hardware.health@2.1-service"
+
+#include <android-base/logging.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <health2impl/BinderHealth.h>
+
+using ::android::sp;
+using ::android::hardware::health::V2_1::IHealth;
+using ::android::hardware::health::V2_1::implementation::BinderHealth;
+using IHealth_2_0 = ::android::hardware::health::V2_0::IHealth;
+
+static constexpr const char* gInstanceName = "default";
+
+int main(int /* argc */, char* /* argv */[]) {
+ sp<IHealth> passthrough =
+ IHealth::castFrom(IHealth_2_0::getService(gInstanceName, true /* getStub */));
+ CHECK(passthrough != nullptr)
+ << "Cannot find passthrough implementation of health 2.1 HAL for instance "
+ << gInstanceName;
+ sp<BinderHealth> binder = new BinderHealth(gInstanceName, passthrough);
+ return binder->StartLoop();
+}
diff --git a/health/utils/libhealth2impl/Android.bp b/health/utils/libhealth2impl/Android.bp
new file mode 100644
index 0000000..14374a2
--- /dev/null
+++ b/health/utils/libhealth2impl/Android.bp
@@ -0,0 +1,51 @@
+// 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.
+
+// A helper library for health@2.x HAL implementation.
+// HAL implementations can link to this library and extend the Health class.
+cc_library_static {
+ name: "libhealth2impl",
+ vendor_available: true,
+ recovery_available: true,
+ srcs: [
+ "BinderHealth.cpp",
+ "HalHealthLoop.cpp",
+ "Health.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.health@2.1",
+ "android.hardware.health@2.0",
+ ],
+ static_libs: [
+ "libbatterymonitor",
+ "libhealthloop",
+ "android.hardware.health@1.0-convert",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_static_lib_headers: [
+ "libbatterymonitor",
+ "libhealthloop",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/health/utils/libhealth2impl/BinderHealth.cpp b/health/utils/libhealth2impl/BinderHealth.cpp
new file mode 100644
index 0000000..625d0e0
--- /dev/null
+++ b/health/utils/libhealth2impl/BinderHealth.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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 <health2impl/BinderHealth.h>
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+
+#include <health2impl/Callback.h>
+#include <health2impl/Health.h>
+
+using android::hardware::handleTransportPoll;
+using android::hardware::IPCThreadState;
+using android::hardware::setupTransportPolling;
+
+using android::hardware::health::V2_0::Result;
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+bool IsDeadObjectLogged(const Return<void>& ret) {
+ if (ret.isOk()) return false;
+ if (ret.isDeadObject()) return true;
+ LOG(ERROR) << "Cannot call healthInfoChanged* on callback: " << ret.description();
+ return false;
+}
+
+BinderHealth::BinderHealth(const std::string& name, const sp<IHealth>& impl)
+ : HalHealthLoop(name, impl) {
+ CHECK_NE(this, impl.get());
+ CHECK(!impl->isRemote());
+}
+
+//
+// Methods that handle callbacks.
+//
+
+Return<Result> BinderHealth::registerCallback(const sp<V2_0::IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ return Result::SUCCESS;
+ }
+
+ Callback* wrapped = nullptr;
+ {
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ wrapped = callbacks_.emplace_back(Wrap(callback)).get();
+ // unlock
+ }
+
+ auto linkRet = callback->linkToDeath(this, 0u /* cookie */);
+ if (!linkRet.withDefault(false)) {
+ LOG(WARNING) << __func__ << "Cannot link to death: "
+ << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());
+ // ignore the error
+ }
+
+ getHealthInfo_2_1([&](auto res, const auto& health_info) {
+ if (res != Result::SUCCESS) {
+ LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res);
+ return;
+ }
+ auto ret = wrapped->Notify(health_info);
+ if (IsDeadObjectLogged(ret)) {
+ // Remove callback reference.
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ auto it = std::find_if(callbacks_.begin(), callbacks_.end(),
+ [wrapped](const auto& cb) { return cb.get() == wrapped; });
+ if (it != callbacks_.end()) {
+ callbacks_.erase(it);
+ }
+ // unlock
+ }
+ });
+
+ return Result::SUCCESS;
+}
+
+bool BinderHealth::unregisterCallbackInternal(const sp<IBase>& callback) {
+ if (callback == nullptr) {
+ return false;
+ }
+
+ bool removed = false;
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ if (interfacesEqual((*it)->Get(), callback)) {
+ it = callbacks_.erase(it);
+ removed = true;
+ } else {
+ ++it;
+ }
+ }
+ (void)callback->unlinkToDeath(this).isOk(); // ignore errors
+ return removed;
+}
+
+Return<Result> BinderHealth::update() {
+ Result result = service()->update();
+ if (result != Result::SUCCESS) return result;
+ getHealthInfo_2_1([&](auto res, const auto& health_info) {
+ if (res != Result::SUCCESS) {
+ result = res;
+ return;
+ }
+ OnHealthInfoChanged(health_info);
+ });
+ return result;
+}
+
+Return<Result> BinderHealth::unregisterCallback(const sp<V2_0::IHealthInfoCallback>& callback) {
+ return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
+}
+
+void BinderHealth::OnHealthInfoChanged(const HealthInfo& health_info) {
+ // Notify all callbacks
+ std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ auto ret = (*it)->Notify(health_info);
+ if (IsDeadObjectLogged(ret)) {
+ it = callbacks_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ lock.unlock();
+
+ // adjusts uevent / wakealarm periods
+ HalHealthLoop::OnHealthInfoChanged(health_info);
+}
+
+void BinderHealth::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {
+ (void)unregisterCallbackInternal(who.promote());
+}
+
+void BinderHealth::BinderEvent(uint32_t /*epevents*/) {
+ if (binder_fd_ >= 0) {
+ handleTransportPoll(binder_fd_);
+ }
+}
+
+void BinderHealth::Init(struct healthd_config* config) {
+ // Set up epoll and get uevent / wake alarm periods
+ HalHealthLoop::Init(config);
+
+ LOG(INFO) << instance_name() << " instance initializing with healthd_config...";
+
+ binder_fd_ = setupTransportPolling();
+
+ if (binder_fd_ >= 0) {
+ auto binder_event = [](auto* health_loop, uint32_t epevents) {
+ static_cast<BinderHealth*>(health_loop)->BinderEvent(epevents);
+ };
+ if (RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
+ PLOG(ERROR) << instance_name() << " instance: Register for binder events failed";
+ }
+ }
+
+ CHECK_EQ(registerAsService(instance_name()), android::OK)
+ << instance_name() << ": Failed to register HAL";
+
+ LOG(INFO) << instance_name() << ": Hal init done";
+}
+
+int BinderHealth::PrepareToWait(void) {
+ IPCThreadState::self()->flushCommands();
+ return HalHealthLoop::PrepareToWait();
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/HalHealthLoop.cpp b/health/utils/libhealth2impl/HalHealthLoop.cpp
new file mode 100644
index 0000000..3901a76
--- /dev/null
+++ b/health/utils/libhealth2impl/HalHealthLoop.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 <health2impl/HalHealthLoop.h>
+
+#include <android-base/logging.h>
+#include <hal_conversion.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+
+#include <health2impl/Health.h>
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::handleTransportPoll;
+using android::hardware::IPCThreadState;
+using android::hardware::setupTransportPolling;
+
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V2_0::Result;
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+void HalHealthLoop::Init(struct healthd_config* config) {
+ // Retrieve healthd_config from the HAL.
+ service_->getHealthConfig([config](auto res, const auto& health_config) {
+ CHECK(res == Result::SUCCESS);
+
+ convertFromHealthConfig(health_config.battery, config);
+ config->boot_min_cap = health_config.bootMinCap;
+
+ // Leave screen_on empty because it is handled in GetScreenOn below.
+
+ // Leave ignorePowerSupplyNames empty because it isn't
+ // used by clients of health HAL.
+ });
+}
+
+void HalHealthLoop::Heartbeat(void) {
+ // noop
+}
+
+void HalHealthLoop::ScheduleBatteryUpdate() {
+ // ignore errors. impl may not be able to handle any callbacks, so
+ // update() may return errors.
+ Result res = service_->update();
+ if (res != Result::SUCCESS) {
+ LOG(WARNING) << "update() on the health HAL implementation failed with " << toString(res);
+ }
+
+ service_->getHealthInfo_2_1([this](auto res, const auto& health_info) {
+ CHECK(res == Result::SUCCESS)
+ << "getHealthInfo_2_1() on the health HAL implementation failed with "
+ << toString(res);
+ this->OnHealthInfoChanged(health_info);
+ });
+}
+
+int HalHealthLoop::PrepareToWait() {
+ return -1;
+}
+
+void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
+ set_charger_online(health_info);
+ AdjustWakealarmPeriods(charger_online());
+}
+
+void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
+ const auto& props = health_info.legacy.legacy;
+ charger_online_ =
+ props.chargerAcOnline || props.chargerUsbOnline || props.chargerWirelessOnline;
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/Health.cpp b/health/utils/libhealth2impl/Health.cpp
new file mode 100644
index 0000000..f4684ae
--- /dev/null
+++ b/health/utils/libhealth2impl/Health.cpp
@@ -0,0 +1,267 @@
+/*
+ * 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 <health2impl/Health.h>
+
+#include <functional>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android/hardware/health/1.0/types.h>
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <android/hardware/health/2.0/types.h>
+#include <android/hardware/health/2.1/IHealthInfoCallback.h>
+#include <hal_conversion.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+
+#include <health2impl/Callback.h>
+#include <health2impl/HalHealthLoop.h>
+
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_1::IHealth;
+
+using ScreenOn = decltype(healthd_config::screen_on);
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+/*
+// If you need to call healthd_board_init, construct the Health instance with
+// the healthd_config after calling healthd_board_init:
+struct healthd_config* init_config(struct healthd_config* config) {
+ healthd_board_init(config);
+ return config;
+}
+class MyHealth : public Health {
+ MyHealth(struct healthd_config* config) :
+ Health(init_config(config)) {}
+};
+*/
+
+Health::Health(std::unique_ptr<healthd_config>&& config) : healthd_config_(std::move(config)) {
+ battery_monitor_.init(healthd_config_.get());
+}
+
+//
+// Callbacks are not supported by the passthrough implementation.
+//
+
+Return<Result> Health::registerCallback(const sp<V2_0::IHealthInfoCallback>&) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> Health::unregisterCallback(const sp<V2_0::IHealthInfoCallback>&) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> Health::update() {
+ Result result = Result::UNKNOWN;
+ getHealthInfo_2_1([&](auto res, const auto& /* health_info */) {
+ result = res;
+ if (res != Result::SUCCESS) {
+ LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res);
+ return;
+ }
+
+ battery_monitor_.logValues();
+ });
+ return result;
+}
+
+//
+// Getters.
+//
+
+template <typename T>
+static Return<void> GetProperty(BatteryMonitor* monitor, int id, T defaultValue,
+ const std::function<void(Result, T)>& callback) {
+ struct BatteryProperty prop;
+ T ret = defaultValue;
+ Result result = Result::SUCCESS;
+ status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+ if (err != OK) {
+ LOG(DEBUG) << "getProperty(" << id << ")"
+ << " fails: (" << err << ") " << strerror(-err);
+ } else {
+ ret = static_cast<T>(prop.valueInt64);
+ }
+ switch (err) {
+ case OK:
+ result = Result::SUCCESS;
+ break;
+ case NAME_NOT_FOUND:
+ result = Result::NOT_SUPPORTED;
+ break;
+ default:
+ result = Result::UNKNOWN;
+ break;
+ }
+ callback(result, static_cast<T>(ret));
+ return Void();
+}
+
+Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
+ return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, 0, _hidl_cb);
+}
+
+Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
+ return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CURRENT_NOW, 0, _hidl_cb);
+}
+
+Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
+ return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CURRENT_AVG, 0, _hidl_cb);
+}
+
+Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
+ return GetProperty<int32_t>(&battery_monitor_, BATTERY_PROP_CAPACITY, 0, _hidl_cb);
+}
+
+Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
+ return GetProperty<int64_t>(&battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, 0, _hidl_cb);
+}
+
+Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
+ return GetProperty(&battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN,
+ _hidl_cb);
+}
+
+Return<void> Health::getStorageInfo(getStorageInfo_cb _hidl_cb) {
+ // This implementation does not support StorageInfo. An implementation may extend this
+ // class and override this function to support storage info.
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<void> Health::getDiskStats(getDiskStats_cb _hidl_cb) {
+ // This implementation does not support DiskStats. An implementation may extend this
+ // class and override this function to support disk stats.
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+template <typename T, typename Method>
+static inline void GetHealthInfoField(Health* service, Method func, T* out) {
+ *out = T{};
+ std::invoke(func, service, [out](Result result, const T& value) {
+ if (result == Result::SUCCESS) *out = value;
+ });
+}
+
+Return<void> Health::getHealthInfo(getHealthInfo_cb _hidl_cb) {
+ return getHealthInfo_2_1(
+ [&](auto res, const auto& health_info) { _hidl_cb(res, health_info.legacy); });
+}
+
+Return<void> Health::getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) {
+ battery_monitor_.updateValues();
+
+ HealthInfo health_info = battery_monitor_.getHealthInfo_2_1();
+
+ // Fill in storage infos; these aren't retrieved by BatteryMonitor.
+ GetHealthInfoField(this, &Health::getStorageInfo, &health_info.legacy.storageInfos);
+ GetHealthInfoField(this, &Health::getDiskStats, &health_info.legacy.diskStats);
+
+ // A subclass may want to update health info struct before returning it.
+ UpdateHealthInfo(&health_info);
+
+ _hidl_cb(Result::SUCCESS, health_info);
+ return Void();
+}
+
+Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
+ if (handle == nullptr || handle->numFds == 0) {
+ return Void();
+ }
+
+ int fd = handle->data[0];
+ battery_monitor_.dumpState(fd);
+ getHealthInfo_2_1([fd](auto res, const auto& info) {
+ android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
+ if (res == Result::SUCCESS) {
+ android::base::WriteStringToFd(toString(info), fd);
+ } else {
+ android::base::WriteStringToFd(toString(res), fd);
+ }
+ android::base::WriteStringToFd("\n", fd);
+ });
+
+ fsync(fd);
+ return Void();
+}
+
+Return<void> Health::getHealthConfig(getHealthConfig_cb _hidl_cb) {
+ HealthConfig config = {};
+ convertToHealthConfig(healthd_config_.get(), config.battery);
+ config.bootMinCap = static_cast<int32_t>(healthd_config_->boot_min_cap);
+
+ _hidl_cb(Result::SUCCESS, config);
+ return Void();
+}
+
+Return<void> Health::shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) {
+ if (!healthd_config_->screen_on) {
+ _hidl_cb(Result::NOT_SUPPORTED, true);
+ return Void();
+ }
+
+ Result returned_result = Result::UNKNOWN;
+ bool screen_on = true;
+ getHealthInfo_2_1([&](auto res, const auto& health_info) {
+ returned_result = res;
+ if (returned_result != Result::SUCCESS) return;
+
+ struct BatteryProperties props = {};
+ V1_0::hal_conversion::convertFromHealthInfo(health_info.legacy.legacy, &props);
+ screen_on = healthd_config_->screen_on(&props);
+ });
+ _hidl_cb(returned_result, screen_on);
+ return Void();
+}
+
+//
+// Subclass helpers / overrides
+//
+
+void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
+ /*
+ // Sample code for a subclass to implement this:
+ // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
+ health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
+
+ // If you need to call healthd_board_battery_update:
+ struct BatteryProperties props;
+ convertFromHealthInfo(health_info.legacy.legacy, &props);
+ healthd_board_battery_update(&props);
+ convertToHealthInfo(&props, health_info.legacy.legacy);
+ */
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/include/health2impl/BinderHealth.h b/health/utils/libhealth2impl/include/health2impl/BinderHealth.h
new file mode 100644
index 0000000..1da5bd1
--- /dev/null
+++ b/health/utils/libhealth2impl/include/health2impl/BinderHealth.h
@@ -0,0 +1,111 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <healthd/healthd.h>
+
+#include <health2impl/Callback.h>
+#include <health2impl/HalHealthLoop.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+// binderized health HAL implementation.
+class BinderHealth : public HalHealthLoop, public IHealth, public hidl_death_recipient {
+ public:
+ // |impl| should be the passthrough implementation.
+ BinderHealth(const std::string& name, const sp<IHealth>& impl);
+
+ // Methods from ::android::hardware::health::V2_0::IHealth follow.
+ Return<::android::hardware::health::V2_0::Result> registerCallback(
+ const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override;
+ Return<::android::hardware::health::V2_0::Result> unregisterCallback(
+ const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override;
+ Return<::android::hardware::health::V2_0::Result> update() override;
+ Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override {
+ return service()->getChargeCounter(_hidl_cb);
+ }
+ Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override {
+ return service()->getCurrentNow(_hidl_cb);
+ }
+ Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override {
+ return service()->getCurrentAverage(_hidl_cb);
+ }
+ Return<void> getCapacity(getCapacity_cb _hidl_cb) override {
+ return service()->getCapacity(_hidl_cb);
+ }
+ Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override {
+ return service()->getEnergyCounter(_hidl_cb);
+ }
+ Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override {
+ return service()->getChargeStatus(_hidl_cb);
+ }
+ Return<void> getStorageInfo(getStorageInfo_cb _hidl_cb) override {
+ return service()->getStorageInfo(_hidl_cb);
+ }
+ Return<void> getDiskStats(getDiskStats_cb _hidl_cb) override {
+ return service()->getDiskStats(_hidl_cb);
+ }
+ Return<void> getHealthInfo(getHealthInfo_cb _hidl_cb) override {
+ return service()->getHealthInfo(_hidl_cb);
+ }
+
+ // Methods from ::android::hardware::health::V2_1::IHealth follow.
+ Return<void> getHealthConfig(getHealthConfig_cb _hidl_cb) override {
+ return service()->getHealthConfig(_hidl_cb);
+ }
+ Return<void> getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override {
+ return service()->getHealthInfo_2_1(_hidl_cb);
+ }
+ Return<void> shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override {
+ return service()->shouldKeepScreenOn(_hidl_cb);
+ }
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override {
+ return service()->debug(fd, args);
+ }
+
+ // hidl_death_recipient implementation.
+ void serviceDied(uint64_t cookie, const wp<IBase>& who) override;
+
+ // Called by BinderHealthCallback.
+ void OnHealthInfoChanged(const HealthInfo& health_info) override;
+
+ protected:
+ void Init(struct healthd_config* config) override;
+ int PrepareToWait() override;
+ // A subclass may override this if it wants to handle binder events differently.
+ virtual void BinderEvent(uint32_t epevents);
+
+ private:
+ bool unregisterCallbackInternal(const sp<IBase>& callback);
+ int binder_fd_ = -1;
+ std::mutex callbacks_lock_;
+ std::vector<std::unique_ptr<Callback>> callbacks_;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/include/health2impl/Callback.h b/health/utils/libhealth2impl/include/health2impl/Callback.h
new file mode 100644
index 0000000..a30480b
--- /dev/null
+++ b/health/utils/libhealth2impl/include/health2impl/Callback.h
@@ -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.
+ */
+#pragma once
+
+#include <android/hardware/health/2.1/IHealth.h>
+#include <android/hardware/health/2.1/IHealthInfoCallback.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hidl::base::V1_0::IBase;
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+// Wraps an IHealthInfoCallback.
+class Callback {
+ public:
+ virtual ~Callback() {}
+ virtual Return<void> Notify(const HealthInfo&) = 0;
+ virtual sp<IBase> Get() = 0;
+};
+
+class Callback_2_0 : public Callback {
+ public:
+ Callback_2_0(const sp<V2_0::IHealthInfoCallback>& callback) : callback_(callback) {}
+ Return<void> Notify(const HealthInfo& info) override {
+ return callback_->healthInfoChanged(info.legacy);
+ }
+ sp<IBase> Get() override { return callback_; }
+
+ private:
+ sp<V2_0::IHealthInfoCallback> callback_;
+};
+
+class Callback_2_1 : public Callback {
+ public:
+ Callback_2_1(const sp<IHealthInfoCallback>& callback) : callback_(callback) {}
+ Return<void> Notify(const HealthInfo& info) override {
+ return callback_->healthInfoChanged_2_1(info);
+ }
+ sp<IBase> Get() override { return callback_; }
+
+ private:
+ sp<IHealthInfoCallback> callback_;
+};
+
+inline std::unique_ptr<Callback> Wrap(const sp<V2_0::IHealthInfoCallback>& callback_2_0) {
+ auto callback_2_1 = IHealthInfoCallback::castFrom(callback_2_0).withDefault(nullptr);
+ if (callback_2_1) return std::make_unique<Callback_2_1>(callback_2_1);
+ return std::make_unique<Callback_2_0>(callback_2_0);
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h
new file mode 100644
index 0000000..d9b5580
--- /dev/null
+++ b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <optional>
+
+#include <android/hardware/health/2.1/IHealth.h>
+#include <health/HealthLoop.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+// An implementation of HealthLoop for using a given health HAL. This is useful
+// for services that opens the passthrough implementation and starts the HealthLoop
+// to periodically poll data from the implementation.
+class HalHealthLoop : public HealthLoop {
+ public:
+ HalHealthLoop(const std::string& name, const sp<IHealth>& service)
+ : instance_name_(name), service_(service) {}
+
+ protected:
+ virtual void Init(struct healthd_config* config) override;
+ virtual void Heartbeat() override;
+ virtual int PrepareToWait() override;
+ virtual void ScheduleBatteryUpdate() override;
+
+ // HealthLoop periodically calls ScheduleBatteryUpdate, which calls
+ // OnHealthInfoChanged callback. A client can override this function to
+ // broadcast the health_info to interested listeners. By default, this
+ // adjust uevents / wakealarm periods.
+ virtual void OnHealthInfoChanged(const HealthInfo& health_info);
+
+ const std::string& instance_name() const { return instance_name_; }
+ const sp<IHealth>& service() const { return service_; }
+ bool charger_online() const { return charger_online_; }
+
+ // Helpers for subclasses to implement OnHealthInfoChanged.
+ void set_charger_online(const HealthInfo& health_info);
+
+ private:
+ const std::string& instance_name_;
+ sp<IHealth> service_;
+ bool charger_online_ = false;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealth2impl/include/health2impl/Health.h b/health/utils/libhealth2impl/include/health2impl/Health.h
new file mode 100644
index 0000000..853b0cd
--- /dev/null
+++ b/health/utils/libhealth2impl/include/health2impl/Health.h
@@ -0,0 +1,90 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <healthd/BatteryMonitor.h>
+#include <hidl/Status.h>
+
+#include <health2impl/Callback.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hidl::base::V1_0::IBase;
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_1 {
+namespace implementation {
+
+class Health : public IHealth {
+ public:
+ Health(std::unique_ptr<healthd_config>&& config);
+
+ // Methods from ::android::hardware::health::V2_0::IHealth follow.
+ Return<::android::hardware::health::V2_0::Result> registerCallback(
+ const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override;
+ Return<::android::hardware::health::V2_0::Result> unregisterCallback(
+ const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback) override;
+ Return<::android::hardware::health::V2_0::Result> update() override;
+ Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
+ Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
+ Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
+ Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
+ Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
+ Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
+ Return<void> getStorageInfo(getStorageInfo_cb _hidl_cb) override;
+ Return<void> getDiskStats(getDiskStats_cb _hidl_cb) override;
+ Return<void> getHealthInfo(getHealthInfo_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::health::V2_1::IHealth follow.
+ Return<void> getHealthConfig(getHealthConfig_cb _hidl_cb) override;
+ Return<void> getHealthInfo_2_1(getHealthInfo_2_1_cb _hidl_cb) override;
+ Return<void> shouldKeepScreenOn(shouldKeepScreenOn_cb _hidl_cb) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+ protected:
+ // A subclass can override this to modify any health info object before
+ // returning to clients. This is similar to healthd_board_battery_update().
+ // By default, it does nothing.
+ virtual void UpdateHealthInfo(HealthInfo* health_info);
+
+ private:
+ bool unregisterCallbackInternal(const sp<IBase>& callback);
+
+ BatteryMonitor battery_monitor_;
+ std::unique_ptr<healthd_config> healthd_config_;
+
+ std::mutex callbacks_lock_;
+ std::vector<std::unique_ptr<Callback>> callbacks_;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/ir/1.0/vts/functional/Android.bp b/ir/1.0/vts/functional/Android.bp
index f5c9d61..f9edebd 100644
--- a/ir/1.0/vts/functional/Android.bp
+++ b/ir/1.0/vts/functional/Android.bp
@@ -21,5 +21,5 @@
static_libs: [
"android.hardware.ir@1.0",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp b/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
index 5fd2dd4..a5dbdcc 100644
--- a/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
+++ b/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
@@ -21,8 +21,9 @@
#include <android/hardware/ir/1.0/IConsumerIr.h>
#include <android/hardware/ir/1.0/types.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <algorithm>
using ::android::hardware::ir::V1_0::IConsumerIr;
@@ -31,26 +32,10 @@
using ::android::hardware::Return;
using ::android::sp;
-// Test environment for Ir
-class ConsumerIrHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static ConsumerIrHidlEnvironment* Instance() {
- static ConsumerIrHidlEnvironment* instance = new ConsumerIrHidlEnvironment;
- return instance;
- }
-
- virtual void registerTestServices() override { registerTestService<IConsumerIr>(); }
- private:
- ConsumerIrHidlEnvironment() {}
-};
-
-// The main test class for IR HIDL HAL.
-class ConsumerIrHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class ConsumerIrHidlTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
- ir = ::testing::VtsHalHidlTargetTestBase::getService<IConsumerIr>(
- ConsumerIrHidlEnvironment::Instance()->getServiceName<IConsumerIr>());
+ ir = IConsumerIr::getService(GetParam());
ASSERT_NE(ir, nullptr);
}
@@ -60,7 +45,7 @@
};
// Test transmit() for the min and max frequency of every available range
-TEST_F(ConsumerIrHidlTest, TransmitTest) {
+TEST_P(ConsumerIrHidlTest, TransmitTest) {
bool success;
hidl_vec<ConsumerIrFreqRange> ranges;
auto cb = [&](bool s, hidl_vec<ConsumerIrFreqRange> v) {
@@ -84,7 +69,7 @@
}
// Test transmit() when called with invalid frequencies
-TEST_F(ConsumerIrHidlTest, BadFreqTest) {
+TEST_P(ConsumerIrHidlTest, BadFreqTest) {
uint32_t len = 16;
hidl_vec<int32_t> vec;
vec.resize(len);
@@ -92,11 +77,7 @@
EXPECT_FALSE(ir->transmit(-1, vec));
}
-int main(int argc, char **argv) {
- ::testing::AddGlobalTestEnvironment(ConsumerIrHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- ConsumerIrHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, ConsumerIrHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IConsumerIr::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 90ce852..0f2720e 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -15,7 +15,7 @@
//
cc_test {
- name: "VtsHalNeuralNetworksV1_3TargetTest",
+ name: "VtsHalNeuralnetworksV1_3TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"BasicTests.cpp",
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 421922a..0cefffa 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -4,10 +4,10 @@
"name": "VtsHalNeuralnetworksV1_0TargetTest",
"options": [
{
- // Just use sample-all driver for presubmit tests for faster results.
- // The other sample drivers (fast-float, quant, etc.) are subsets of
- // sample-all.
- "native-test-flag": "--gtest_filter=*sample_all*"
+ // Do not use any sample driver except sample-all in order to reduce
+ // testing time. The other sample drivers (fast-float, quant, etc.)
+ // are subsets of sample-all.
+ "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
}
]
},
@@ -15,10 +15,10 @@
"name": "VtsHalNeuralnetworksV1_1TargetTest",
"options": [
{
- // Just use sample-all driver for presubmit tests for faster results.
- // The other sample drivers (fast-float, quant, etc.) are subsets of
- // sample-all.
- "native-test-flag": "--gtest_filter=*sample_all*"
+ // Do not use any sample driver except sample-all in order to reduce
+ // testing time. The other sample drivers (fast-float, quant, etc.)
+ // are subsets of sample-all.
+ "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
}
]
},
@@ -26,10 +26,21 @@
"name": "VtsHalNeuralnetworksV1_2TargetTest",
"options": [
{
- // Just use sample-all driver for presubmit tests for faster results.
- // The other sample drivers (fast-float, quant, etc.) are subsets of
- // sample-all.
- "native-test-flag": "--gtest_filter=*sample_all*"
+ // Do not use any sample driver except sample-all in order to reduce
+ // testing time. The other sample drivers (fast-float, quant, etc.)
+ // are subsets of sample-all.
+ "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
+ }
+ ]
+ },
+ {
+ "name": "VtsHalNeuralnetworksV1_3TargetTest",
+ "options": [
+ {
+ // Do not use any sample driver except sample-all in order to reduce
+ // testing time. The other sample drivers (fast-float, quant, etc.)
+ // are subsets of sample-all.
+ "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
}
]
}
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
new file mode 100644
index 0000000..c13eaf2
--- /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 0000000..49c5a0d
--- /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 <android/hardware/sensors/2.0/types.h>
+
+#include <android-base/file.h>
+#include "hardware_legacy/power.h"
+
+#include <dlfcn.h>
+
+#include <cinttypes>
+#include <cmath>
+#include <fstream>
+#include <functional>
+#include <thread>
+
+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<int32_t>(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<size_t>(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<ISensorsSubHal*>& subHalList) : mSubHalList(subHalList) {
+ init();
+}
+
+HalProxy::~HalProxy() {
+ stopThreads();
+}
+
+Return<void> HalProxy::getSensorsList(getSensorsList_cb _hidl_cb) {
+ std::vector<SensorInfo> sensors;
+ for (const auto& iter : mSensors) {
+ sensors.push_back(iter.second);
+ }
+ _hidl_cb(sensors);
+ return Void();
+}
+
+Return<Result> 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<Result> HalProxy::activate(int32_t sensorHandle, bool enabled) {
+ if (!isSubHalIndexValid(sensorHandle)) {
+ return Result::BAD_VALUE;
+ }
+ return getSubHalForSensorHandle(sensorHandle)
+ ->activate(clearSubHalIndex(sensorHandle), enabled);
+}
+
+Return<Result> HalProxy::initialize(
+ const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& 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<std::pair<std::vector<Event>, 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<EventMessageQueue>(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<WakeLockMessageQueue>(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<Result> 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<Result> HalProxy::flush(int32_t sensorHandle) {
+ if (!isSubHalIndexValid(sensorHandle)) {
+ return Result::BAD_VALUE;
+ }
+ return getSubHalForSensorHandle(sensorHandle)->flush(clearSubHalIndex(sensorHandle));
+}
+
+Return<Result> 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<void> 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<void>();
+}
+
+Return<Result> HalProxy::unregisterDirectChannel(int32_t channelHandle) {
+ Result result;
+ if (mDirectChannelSubHal == nullptr) {
+ result = Result::INVALID_OPERATION;
+ } else {
+ result = mDirectChannelSubHal->unregisterDirectChannel(channelHandle);
+ }
+ return result;
+}
+
+Return<void> 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<void>();
+}
+
+Return<void> HalProxy::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /*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<void>();
+}
+
+Return<void> HalProxy::onDynamicSensorsConnected(const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ int32_t subHalIndex) {
+ std::vector<SensorInfo> sensors;
+ {
+ std::lock_guard<std::mutex> 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<void>();
+}
+
+Return<void> HalProxy::onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved, int32_t subHalIndex) {
+ // TODO(b/143302327): Block this call until all pending events are flushed from queue
+ std::vector<int32_t> sensorHandles;
+ {
+ std::lock_guard<std::mutex> 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>();
+}
+
+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<SensorsHalGetSubHalFunc> 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<IHalProxyCallback> 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<Event> events(numToRead);
+ mEventQueue->read(events.data(), numToRead);
+ mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ));
+ }
+ if (mWakelockQueueFlag != nullptr && mWakeLockQueue != nullptr) {
+ uint32_t kZero = 0;
+ mWakeLockQueue->write(&kZero);
+ mWakelockQueueFlag->wake(static_cast<uint32_t>(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<std::mutex> 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<std::mutex> lock(mEventQueueWriteMutex);
+ while (mThreadsRun.load()) {
+ mEventQueueWriteCV.wait(
+ lock, [&] { return !mPendingWriteEventsQueue.empty() || !mThreadsRun.load(); });
+ if (mThreadsRun.load()) {
+ std::vector<Event>& 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<uint32_t>(EventQueueFlagBits::EVENTS_READ),
+ static_cast<uint32_t>(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<std::recursive_mutex> 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<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN), timeLeft);
+ lock.lock();
+ if (success) {
+ decrementRefCountAndMaybeReleaseWakelock(
+ static_cast<size_t>(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<std::recursive_mutex> lockGuard(mWakelockMutex);
+ decrementRefCountAndMaybeReleaseWakelock(mWakelockRefCount);
+ mWakelockTimeoutResetTime = getTimeNow();
+}
+
+void HalProxy::postEventsToMessageQueue(const std::vector<Event>& events, size_t numWakeupEvents,
+ ScopedWakelock wakelock) {
+ size_t numToWrite = 0;
+ std::lock_guard<std::mutex> 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<uint32_t>(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<Event> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<Event>& 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<uint32_t>(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<Event>& events, ScopedWakelock wakelock) {
+ if (events.empty() || !mHalProxy->areThreadsRunning()) return;
+ size_t numWakeupEvents;
+ std::vector<Event> 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<Event> HalProxyCallback::processEvents(const std::vector<Event>& events,
+ size_t* numWakeupEvents) const {
+ *numWakeupEvents = 0;
+ std::vector<Event> 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 0000000..e955670
--- /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 0000000..d85d4a7
--- /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::nanoseconds>(
+ 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 0000000..a771100
--- /dev/null
+++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-multihal.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.sensors</name>
+ <transport>hwbinder</transport>
+ <version>2.0</version>
+ <interface>
+ <name>ISensors</name>
+ <instance>multihal</instance>
+ </interface>
+ </hal>
+</manifest>
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 0000000..a4da3b0
--- /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 0000000..b1dd737
--- /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 <android/hardware/sensors/2.0/ISensors.h>
+#include <android/hardware/sensors/2.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hardware_legacy/power.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <utility>
+
+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<ISensorsSubHal*>& subHalList);
+ ~HalProxy();
+
+ // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
+ Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+
+ Return<Result> setOperationMode(OperationMode mode) override;
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+
+ Return<Result> initialize(
+ const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& sensorsCallback) override;
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override;
+
+ Return<Result> flush(int32_t sensorHandle) override;
+
+ Return<Result> injectSensorData(const Event& event) override;
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ registerDirectChannel_cb _hidl_cb) override;
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ configDirectReport_cb _hidl_cb) override;
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& 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<void> onDynamicSensorsConnected(const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ int32_t subHalIndex);
+
+ Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& 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<Event>& 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<Event, kSynchronizedReadWrite>;
+ using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+ /**
+ * The Event FMQ where sensor events are written
+ */
+ std::unique_ptr<EventMessageQueue> mEventQueue;
+
+ /**
+ * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
+ */
+ std::unique_ptr<WakeLockMessageQueue> 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<ISensorsCallback> mDynamicSensorsCallback;
+
+ /**
+ * SubHal object pointers that have been saved from vendor dynamic libraries.
+ */
+ std::vector<ISensorsSubHal*> mSubHalList;
+
+ //! The list of subhal callbacks for each subhal where the indices correlate with mSubHalList
+ std::vector<const sp<IHalProxyCallback>> 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<int32_t, SensorInfo> mSensors;
+
+ //! Map of the dynamic sensors that have been added to halproxy.
+ std::map<int32_t, SensorInfo> 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<std::pair<std::vector<Event>, 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<Event>& 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<void> onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+ return mHalProxy->onDynamicSensorsConnected(dynamicSensorsAdded, mSubHalIndex);
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ return mHalProxy->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved, mSubHalIndex);
+ }
+
+ void postEvents(const std::vector<Event>& events, ScopedWakelock wakelock);
+
+ ScopedWakelock createScopedWakelock(bool lock);
+
+ private:
+ HalProxy* mHalProxy;
+ int32_t mSubHalIndex;
+
+ std::vector<Event> processEvents(const std::vector<Event>& 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 0000000..aa6d9db
--- /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 <android/hardware/sensors/2.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::sensors::V2_0::SensorTimeout;
+
+const int64_t kWakelockTimeoutNs =
+ static_cast<int64_t>(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 0000000..92ae3a6
--- /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 <android/hardware/sensors/1.0/types.h>
+#include <android/hardware/sensors/2.0/ISensors.h>
+
+#include <vector>
+
+// 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<Event>& 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<Result> initialize(
+ const ::android::hardware::MQDescriptorSync<Event>& /* eventQueueDescriptor */,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& /* wakeLockDescriptor */,
+ const sp<ISensorsCallback>& /* 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<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& 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<Result> initialize(const sp<IHalProxyCallback>& 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 0000000..ef77048
--- /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 <android/hardware/sensors/2.0/ISensors.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#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<ISensors> 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 0000000..e7f9499
--- /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 0000000..1fd35d1
--- /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 <gtest/gtest.h>
+
+#include <android/hardware/sensors/2.0/types.h>
+#include <fmq/MessageQueue.h>
+
+#include "HalProxy.h"
+#include "ScopedWakelock.h"
+#include "SensorsSubHal.h"
+
+#include <chrono>
+#include <set>
+#include <thread>
+#include <vector>
+
+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<Event, ::android::hardware::kSynchronizedReadWrite>;
+using WakeupMessageQueue = MessageQueue<uint32_t, ::android::hardware::kSynchronizedReadWrite>;
+
+// The barebones sensors callback class passed into halproxy initialize calls
+class SensorsCallback : public ISensorsCallback {
+ public:
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo>& /*dynamicSensorsAdded*/) override {
+ // Nothing yet
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& /*dynamicSensorHandlesRemoved*/) override {
+ // Nothing yet
+ return Return<void>();
+ }
+};
+
+// The sensors callback that expects a variable list of sensors to be added
+class TestSensorsCallback : public ISensorsCallback {
+ public:
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+ mSensorsConnected.insert(mSensorsConnected.end(), dynamicSensorsAdded.begin(),
+ dynamicSensorsAdded.end());
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ mSensorHandlesDisconnected.insert(mSensorHandlesDisconnected.end(),
+ dynamicSensorHandlesRemoved.begin(),
+ dynamicSensorHandlesRemoved.end());
+ return Return<void>();
+ }
+
+ const std::vector<SensorInfo>& getSensorsConnected() const { return mSensorsConnected; }
+ const std::vector<int32_t>& getSensorHandlesDisconnected() const {
+ return mSensorHandlesDisconnected;
+ }
+
+ private:
+ std::vector<SensorInfo> mSensorsConnected;
+ std::vector<int32_t> 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<SensorInfo>& proxySensorsList,
+ const std::vector<SensorInfo>& 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<SensorInfo>& sensorsList,
+ size_t enabledSubHalIndex);
+
+void ackWakeupEventsToHalProxy(size_t numEvents, std::unique_ptr<WakeupMessageQueue>& wakelockQueue,
+ EventFlag* wakelockQueueFlag);
+
+bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueue>& eventQueue,
+ EventFlag* eventQueueFlag);
+
+std::unique_ptr<EventMessageQueue> makeEventFMQ(size_t size);
+
+std::unique_ptr<WakeupMessageQueue> 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<Event> 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<Event> 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<SensorInfo>& sensors,
+ std::vector<int32_t>& sensorHandles);
+
+// Tests follow
+TEST(HalProxyTest, GetSensorsListOneSubHalTest) {
+ AllSensorsSubHal subHal;
+ std::vector<ISensorsSubHal*> 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<ISensorsSubHal*> fakeSubHals;
+ fakeSubHals.push_back(&continuousSubHal);
+ fakeSubHals.push_back(&onChangeSubHal);
+ HalProxy proxy(fakeSubHals);
+
+ std::vector<SensorInfo> 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<ISensorsSubHal*> 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<ISensorsSubHal*> 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<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
+ HalProxy proxy(fakeSubHals);
+
+ proxy.getSensorsList([&](const auto& sensorsList) {
+ testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 0);
+ });
+}
+
+TEST(HalProxyTest, InitDirectChannelThreeSubHalsUnitTest) {
+ DoesNotSupportDirectChannelSensorsSubHal subHal1;
+ AllSupportDirectChannelSensorsSubHal subHal2, subHal3;
+ std::vector<ISensorsSubHal*> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<Event> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<Event> 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<ISensorsSubHal*> subHals{&subHal1, &subHal2};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal1, &subHal2};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ EventFlag* eventQueueFlag;
+ EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal1, &subHal2};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ HalProxy proxy(subHals);
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ HalProxy proxy(subHals);
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ HalProxy proxy(subHals);
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ HalProxy proxy(subHals);
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<Event> 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<ISensorsSubHal*> subHals{&subHal};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<ISensorsSubHal*> subHals{&subHal};
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<ISensorsSubHal*> subHals{&subHal};
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ HalProxy proxy(subHals);
+
+ std::vector<SensorInfo> sensorsToConnect;
+ std::vector<int32_t> sensorHandlesToAttemptToRemove;
+ makeSensorsAndSensorHandlesStartingAndOfSize(1, kNumSensors, sensorsToConnect,
+ sensorHandlesToAttemptToRemove);
+
+ std::vector<int32_t> nonDynamicSensorHandles;
+ for (int32_t sensorHandle = 1; sensorHandle < 10; sensorHandle++) {
+ nonDynamicSensorHandles.push_back(sensorHandle);
+ }
+
+ TestSensorsCallback* callback = new TestSensorsCallback();
+ ::android::sp<ISensorsCallback> callbackPtr = callback;
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
+ subHal.addDynamicSensors(sensorsToConnect);
+
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
+ subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove);
+
+ std::vector<int32_t> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(0);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(0);
+
+ std::vector<SensorInfo> sensorsToConnect;
+ std::vector<int32_t> sensorHandlesToExpect;
+ makeSensorsAndSensorHandlesStartingAndOfSize(1, kNumSensors, sensorsToConnect,
+ sensorHandlesToExpect);
+
+ TestSensorsCallback* callback = new TestSensorsCallback();
+ ::android::sp<ISensorsCallback> callbackPtr = callback;
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
+ subHal.addDynamicSensors(sensorsToConnect);
+
+ std::vector<SensorInfo> 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<ISensorsSubHal*> subHals{&subHal};
+ HalProxy proxy(subHals);
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(0);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(0);
+
+ std::vector<SensorInfo> sensorsToConnect;
+ std::vector<int32_t> sensorHandlesToExpect;
+ makeSensorsAndSensorHandlesStartingAndOfSize(20, kNumSensors, sensorsToConnect,
+ sensorHandlesToExpect);
+
+ std::vector<int32_t> nonDynamicSensorHandles;
+ for (int32_t sensorHandle = 1; sensorHandle < 10; sensorHandle++) {
+ nonDynamicSensorHandles.push_back(sensorHandle);
+ }
+
+ std::set<int32_t> nonDynamicSensorHandlesSet(nonDynamicSensorHandles.begin(),
+ nonDynamicSensorHandles.end());
+
+ std::vector<int32_t> sensorHandlesToAttemptToRemove;
+ sensorHandlesToAttemptToRemove.insert(sensorHandlesToAttemptToRemove.end(),
+ sensorHandlesToExpect.begin(),
+ sensorHandlesToExpect.end());
+ sensorHandlesToAttemptToRemove.insert(sensorHandlesToAttemptToRemove.end(),
+ nonDynamicSensorHandles.begin(),
+ nonDynamicSensorHandles.end());
+
+ TestSensorsCallback* callback = new TestSensorsCallback();
+ ::android::sp<ISensorsCallback> callbackPtr = callback;
+ proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
+ subHal.addDynamicSensors(sensorsToConnect);
+ subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove);
+
+ std::vector<int32_t> 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<int32_t>(kNumSubHals);
+ std::vector<AllSensorsSubHal> subHalObjs(kNumSubHals);
+ std::vector<ISensorsSubHal*> subHals;
+ for (const auto& subHal : subHalObjs) {
+ subHals.push_back((ISensorsSubHal*)(&subHal));
+ }
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<ISensorsSubHal*> subHals{&subhal1, &subhal2};
+
+ std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallback> 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<Event> 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<SensorInfo>& proxySensorsList,
+ const std::vector<SensorInfo>& 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<SensorInfo>& sensorsList,
+ size_t enabledSubHalIndex) {
+ for (const SensorInfo& sensor : sensorsList) {
+ size_t subHalIndex = static_cast<size_t>(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<WakeupMessageQueue>& wakelockQueue,
+ EventFlag* wakelockQueueFlag) {
+ uint32_t numEventsUInt32 = static_cast<uint32_t>(numEvents);
+ wakelockQueue->write(&numEventsUInt32);
+ wakelockQueueFlag->wake(static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN));
+}
+
+bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueue>& eventQueue,
+ EventFlag* eventQueueFlag) {
+ constexpr int64_t kReadBlockingTimeout = INT64_C(500000000);
+ std::vector<Event> events(numEvents);
+ return eventQueue->readBlocking(events.data(), numEvents,
+ static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ),
+ static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS),
+ kReadBlockingTimeout, eventQueueFlag);
+}
+
+std::unique_ptr<EventMessageQueue> makeEventFMQ(size_t size) {
+ return std::make_unique<EventMessageQueue>(size, true);
+}
+
+std::unique_ptr<WakeupMessageQueue> makeWakelockFMQ(size_t size) {
+ return std::make_unique<WakeupMessageQueue>(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<Event> makeMultipleProximityEvents(size_t numEvents) {
+ std::vector<Event> events;
+ for (size_t i = 0; i < numEvents; i++) {
+ events.push_back(makeProximityEvent());
+ }
+ return events;
+}
+
+std::vector<Event> makeMultipleAccelerometerEvents(size_t numEvents) {
+ std::vector<Event> 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<SensorInfo>& sensors,
+ std::vector<int32_t>& sensorHandles) {
+ for (int32_t sensorHandle = start; sensorHandle < start + static_cast<int32_t>(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 0000000..ddcc584
--- /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/<device>/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 0000000..de89a00
--- /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 <hardware/sensors.h>
+#include <utils/SystemClock.h>
+
+#include <cmath>
+
+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<std::mutex> 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<std::mutex> 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<uint32_t>(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<Event> evs{ev};
+ mCallback->postEvents(evs, isWakeUpSensor());
+
+ return Result::OK;
+}
+
+void Sensor::startThread(Sensor* sensor) {
+ sensor->run();
+}
+
+void Sensor::run() {
+ std::unique_lock<std::mutex> 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<uint32_t>(SensorFlagBits::WAKE_UP);
+}
+
+std::vector<Event> Sensor::readEvents() {
+ std::vector<Event> 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<std::mutex> lock(mRunMutex);
+ mMode = mode;
+ mWaitCV.notify_all();
+ }
+}
+
+bool Sensor::supportsDataInjection() const {
+ return mSensorInfo.flags & static_cast<uint32_t>(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>{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<Event> OnChangeSensor::readEvents() {
+ std::vector<Event> events = Sensor::readEvents();
+ std::vector<Event> 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<Event> AccelSensor::readEvents() {
+ std::vector<Event> 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<Event> GyroSensor::readEvents() {
+ std::vector<Event> 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 0000000..60f5d3d
--- /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 <android/hardware/sensors/1.0/types.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+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<Event>& 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<Event> 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<Event> 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<Event> readEvents() override;
+};
+
+class GyroSensor : public ContinuousSensor {
+ public:
+ GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+ protected:
+ std::vector<Event> 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 0000000..ff5ff38
--- /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 <android/hardware/sensors/2.0/types.h>
+#include <log/log.h>
+
+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<void> SensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+ std::vector<SensorInfo> sensors;
+ for (const auto& sensor : mSensors) {
+ sensors.push_back(sensor.second->getSensorInfo());
+ }
+
+ _hidl_cb(sensors);
+ return Void();
+}
+
+Return<Result> SensorsSubHal::setOperationMode(OperationMode mode) {
+ for (auto sensor : mSensors) {
+ sensor.second->setOperationMode(mode);
+ }
+ mCurrentOperationMode = mode;
+ return Result::OK;
+}
+
+Return<Result> 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<Result> 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<Result> SensorsSubHal::flush(int32_t sensorHandle) {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ return sensor->second->flush();
+ }
+ return Result::BAD_VALUE;
+}
+
+Return<Result> 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<void> SensorsSubHal::registerDirectChannel(const SharedMemInfo& /* mem */,
+ registerDirectChannel_cb _hidl_cb) {
+ _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
+ return Return<void>();
+}
+
+Return<Result> SensorsSubHal::unregisterDirectChannel(int32_t /* channelHandle */) {
+ return Result::INVALID_OPERATION;
+}
+
+Return<void> SensorsSubHal::configDirectReport(int32_t /* sensorHandle */,
+ int32_t /* channelHandle */, RateLevel /* rate */,
+ configDirectReport_cb _hidl_cb) {
+ _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
+ return Return<void>();
+}
+
+Return<void> SensorsSubHal::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& 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<void>();
+}
+
+Return<Result> SensorsSubHal::initialize(const sp<IHalProxyCallback>& halProxyCallback) {
+ mCallback = halProxyCallback;
+ setOperationMode(OperationMode::NORMAL);
+ return Result::OK;
+}
+
+void SensorsSubHal::postEvents(const std::vector<Event>& events, bool wakeup) {
+ ScopedWakelock wakelock = mCallback->createScopedWakelock(wakeup);
+ mCallback->postEvents(events, std::move(wakelock));
+}
+
+ContinuousSensorsSubHal::ContinuousSensorsSubHal() {
+ AddSensor<AccelSensor>();
+ AddSensor<GyroSensor>();
+ AddSensor<MagnetometerSensor>();
+ AddSensor<PressureSensor>();
+ AddSensor<DeviceTempSensor>();
+}
+
+OnChangeSensorsSubHal::OnChangeSensorsSubHal() {
+ AddSensor<AmbientTempSensor>();
+ AddSensor<LightSensor>();
+ AddSensor<ProximitySensor>();
+ AddSensor<RelativeHumiditySensor>();
+}
+
+AllSensorsSubHal::AllSensorsSubHal() {
+ AddSensor<AccelSensor>();
+ AddSensor<GyroSensor>();
+ AddSensor<MagnetometerSensor>();
+ AddSensor<PressureSensor>();
+ AddSensor<DeviceTempSensor>();
+ AddSensor<AmbientTempSensor>();
+ AddSensor<LightSensor>();
+ AddSensor<ProximitySensor>();
+ AddSensor<RelativeHumiditySensor>();
+}
+
+Return<Result> SetOperationModeFailingSensorsSubHal::setOperationMode(OperationMode /*mode*/) {
+ return Result::BAD_VALUE;
+}
+
+Return<void> AllSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+ std::vector<SensorInfo> 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<void> DoesNotSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+ std::vector<SensorInfo> sensors;
+ for (const auto& sensor : mSensors) {
+ SensorInfo sensorInfo = sensor.second->getSensorInfo();
+ sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
+ sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_REPORT);
+ sensors.push_back(sensorInfo);
+ }
+ _hidl_cb(sensors);
+ return Void();
+}
+
+void AddAndRemoveDynamicSensorsSubHal::addDynamicSensors(
+ const std::vector<SensorInfo>& sensorsAdded) {
+ mCallback->onDynamicSensorsConnected(sensorsAdded);
+}
+
+void AddAndRemoveDynamicSensorsSubHal::removeDynamicSensors(
+ const std::vector<int32_t>& 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 0000000..c1e3647
--- /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 <vector>
+
+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<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+
+ virtual Return<Result> setOperationMode(OperationMode mode) override;
+
+ OperationMode getOperationMode() const { return mCurrentOperationMode; }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override;
+
+ Return<Result> flush(int32_t sensorHandle) override;
+
+ Return<Result> injectSensorData(const Event& event) override;
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ registerDirectChannel_cb _hidl_cb) override;
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ configDirectReport_cb _hidl_cb) override;
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& 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<Result> initialize(const sp<IHalProxyCallback>& halProxyCallback) override;
+
+ // Method from ISensorsEventCallback.
+ void postEvents(const std::vector<Event>& events, bool wakeup) override;
+
+ protected:
+ template <class SensorType>
+ void AddSensor() {
+ std::shared_ptr<SensorType> sensor =
+ std::make_shared<SensorType>(mNextHandle++ /* sensorHandle */, this /* callback */);
+ mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
+ }
+
+ /**
+ * A map of the available sensors
+ */
+ std::map<int32_t, std::shared_ptr<Sensor>> 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<IHalProxyCallback> 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<Result> setOperationMode(OperationMode mode) override;
+};
+
+class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+ public:
+ Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+};
+
+class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+ public:
+ Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+};
+
+class AddAndRemoveDynamicSensorsSubHal : public AllSensorsSubHal {
+ public:
+ void addDynamicSensors(const std::vector<SensorInfo>& sensorsAdded);
+ void removeDynamicSensors(const std::vector<int32_t>& sensorHandlesAdded);
+};
+
+} // namespace implementation
+} // namespace subhal
+} // namespace V2_0
+} // namespace sensors
+} // namespace hardware
+} // namespace android
diff --git a/vr/1.0/vts/functional/Android.bp b/vr/1.0/vts/functional/Android.bp
index 958cce7..bd0336c 100644
--- a/vr/1.0/vts/functional/Android.bp
+++ b/vr/1.0/vts/functional/Android.bp
@@ -19,5 +19,5 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalVrV1_0TargetTest.cpp"],
static_libs: ["android.hardware.vr@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp b/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
index b165613..c08e5ca 100644
--- a/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
+++ b/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
@@ -15,11 +15,12 @@
*/
#define LOG_TAG "vr_hidl_hal_test"
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
#include <android/hardware/vr/1.0/IVr.h>
+#include <gtest/gtest.h>
#include <hardware/vr.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <log/log.h>
using ::android::hardware::vr::V1_0::IVr;
@@ -27,24 +28,11 @@
using ::android::hardware::Void;
using ::android::sp;
-// Test environment for Vr HIDL HAL.
-class VrHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static VrHidlEnvironment* Instance() {
- static VrHidlEnvironment* instance = new VrHidlEnvironment;
- return instance;
- }
-
- virtual void registerTestServices() override { registerTestService<IVr>(); }
-};
-
// The main test class for VR HIDL HAL.
-class VrHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class VrHidlTest : public ::testing::TestWithParam<std::string> {
public:
void SetUp() override {
- vr = ::testing::VtsHalHidlTargetTestBase::getService<IVr>(
- VrHidlEnvironment::Instance()->getServiceName<IVr>());
+ vr = IVr::getService(GetParam());
ASSERT_NE(vr, nullptr);
}
@@ -54,19 +42,19 @@
};
// Sanity check that Vr::init does not crash.
-TEST_F(VrHidlTest, Init) {
+TEST_P(VrHidlTest, Init) {
EXPECT_TRUE(vr->init().isOk());
}
// Sanity check Vr::setVrMode is able to enable and disable VR mode.
-TEST_F(VrHidlTest, SetVrMode) {
+TEST_P(VrHidlTest, SetVrMode) {
EXPECT_TRUE(vr->init().isOk());
EXPECT_TRUE(vr->setVrMode(true).isOk());
EXPECT_TRUE(vr->setVrMode(false).isOk());
}
// Sanity check that Vr::init and Vr::setVrMode can be used in any order.
-TEST_F(VrHidlTest, ReInit) {
+TEST_P(VrHidlTest, ReInit) {
EXPECT_TRUE(vr->init().isOk());
EXPECT_TRUE(vr->setVrMode(true).isOk());
EXPECT_TRUE(vr->init().isOk());
@@ -75,11 +63,8 @@
EXPECT_TRUE(vr->setVrMode(false).isOk());
}
-int main(int argc, char **argv) {
- ::testing::AddGlobalTestEnvironment(VrHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- VrHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, VrHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVr::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+