Merge "Fix the failed case for the VtsHalWifiSupplicantV1_2Host#SupplicantStaIfaceHidlTest.RegisterCallback_1_2"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index edde1cb..690d92f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -80,3 +80,6 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/android.hardware.configstore@1.2.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk-Q/android.hardware.configstore@1.2.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/android.hardware.configstore@1.2.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@1.1*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.1*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.1*)
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/audio/common/2.0/Android.bp b/audio/common/2.0/Android.bp
index 475b309..bd3b069 100644
--- a/audio/common/2.0/Android.bp
+++ b/audio/common/2.0/Android.bp
@@ -9,6 +9,6 @@
srcs: [
"types.hal",
],
- gen_java: false,
+ gen_java: true,
gen_java_constants: true,
}
diff --git a/audio/common/4.0/Android.bp b/audio/common/4.0/Android.bp
index 83f5aad..c01c486 100644
--- a/audio/common/4.0/Android.bp
+++ b/audio/common/4.0/Android.bp
@@ -9,6 +9,6 @@
srcs: [
"types.hal",
],
- gen_java: false,
+ gen_java: true,
gen_java_constants: true,
}
diff --git a/audio/common/5.0/Android.bp b/audio/common/5.0/Android.bp
index be0f59e..761c171 100644
--- a/audio/common/5.0/Android.bp
+++ b/audio/common/5.0/Android.bp
@@ -12,6 +12,6 @@
interfaces: [
"android.hidl.safe_union@1.0",
],
- gen_java: false,
+ gen_java: true,
gen_java_constants: true,
}
diff --git a/boot/1.1/default/service.cpp b/boot/1.1/default/service.cpp
index 93eaeda..89251b5 100644
--- a/boot/1.1/default/service.cpp
+++ b/boot/1.1/default/service.cpp
@@ -15,12 +15,13 @@
*/
#define LOG_TAG "android.hardware.boot@1.1-service"
-#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <hidl/LegacySupport.h>
using android::hardware::defaultPassthroughServiceImplementation;
-using ::android::hardware::boot::V1_0::IBootControl;
+using IBootControl_V1_0 = android::hardware::boot::V1_0::IBootControl;
+using IBootControl_V1_1 = android::hardware::boot::V1_1::IBootControl;
int main(int /* argc */, char* /* argv */[]) {
- return defaultPassthroughServiceImplementation<IBootControl>();
+ return defaultPassthroughServiceImplementation<IBootControl_V1_0, IBootControl_V1_1>();
}
diff --git a/camera/device/1.0/default/CameraDevice.cpp b/camera/device/1.0/default/CameraDevice.cpp
index a03bbc8..2dd6094 100644
--- a/camera/device/1.0/default/CameraDevice.cpp
+++ b/camera/device/1.0/default/CameraDevice.cpp
@@ -397,9 +397,11 @@
CameraDevice* device = mem->handle.mDevice;
if (device == nullptr) {
ALOGE("%s: camera HAL return memory for a null device!", __FUNCTION__);
+ return;
}
if (device->mDeviceCallback == nullptr) {
ALOGE("%s: camera HAL return memory while camera is not opened!", __FUNCTION__);
+ return;
}
device->mDeviceCallback->unregisterMemory(mem->handle.mId);
{
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
index aa070d9..955b28e 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
@@ -1,6 +1,6 @@
service vendor.camera-provider-2-5 /vendor/bin/hw/android.hardware.camera.provider@2.5-service-lazy_64
interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
- interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+ interface android.hardware.camera.provider@2.4::ICameraProvider legacy/0
oneshot
disabled
class hal
diff --git a/cas/1.2/Android.bp b/cas/1.2/Android.bp
new file mode 100644
index 0000000..af98b2e
--- /dev/null
+++ b/cas/1.2/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.cas@1.2",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "ICas.hal",
+ "ICasListener.hal",
+ "IMediaCasService.hal",
+ "types.hal",
+ ],
+ interfaces: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas@1.1",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/cas/1.2/ICas.hal b/cas/1.2/ICas.hal
new file mode 100644
index 0000000..23edc50
--- /dev/null
+++ b/cas/1.2/ICas.hal
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.hardware.cas@1.2;
+
+import @1.0::HidlCasSessionId;
+import @1.1::ICas;
+import ScramblingMode;
+import SessionIntent;
+import Status;
+
+/**
+ * ICas is the API to control the cas system and is accessible from both
+ * Java and native level. It is used to manage sessions, provision/refresh
+ * the cas system, and process the EMM/ECM messages. It also allows bi-directional,
+ * scheme-specific communications between the client and the cas system.
+ */
+interface ICas extends @1.1::ICas {
+ /**
+ * Open a session to descramble one or more streams by specifying intention
+ * and scrambling mode.
+ *
+ * @param intent the intention of the session to be opened.
+ * @param mode the scrambling mode the session will use.
+ * @return status the status of the call.
+ * @return sessionId the id of the newly opened session.
+ */
+ openSession_1_2(SessionIntent intent, ScramblingMode mode)
+ generates (Status status, HidlCasSessionId sessionId);
+};
diff --git a/cas/1.2/ICasListener.hal b/cas/1.2/ICasListener.hal
new file mode 100644
index 0000000..9a8be20
--- /dev/null
+++ b/cas/1.2/ICasListener.hal
@@ -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.
+ */
+
+package android.hardware.cas@1.2;
+
+import @1.1::ICasListener;
+import StatusEvent;
+
+interface ICasListener extends @1.1::ICasListener {
+ /**
+ * Notify the listener that the status of CAS system has changed.
+ *
+ * @param event the event type of status change.
+ * @param number value for status event.
+ * For PLUGIN_PHYSICAL_MODULE_CHANGED event:
+ * the positive number presents how many plugins are inserted;
+ * the negative number presents how many plugins are removed.
+ * Client must enumerate plugins after receive the event.
+ * For PLUGIN_SESSION_NUMBER_CHANGED event:
+ * the number presents how many sessions are supported
+ * in the plugin.
+ */
+ onStatusUpdate(StatusEvent event, int32_t number);
+};
diff --git a/cas/1.2/IMediaCasService.hal b/cas/1.2/IMediaCasService.hal
new file mode 100644
index 0000000..a0bec7e
--- /dev/null
+++ b/cas/1.2/IMediaCasService.hal
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.hardware.cas@1.2;
+
+import ICas;
+import ICasListener;
+import @1.1::IMediaCasService;
+
+/**
+ * IMediaCasService is the main entry point for interacting with a vendor's
+ * cas HAL to create cas and descrambler plugin instances. A cas plugin instance
+ * opens cas sessions which are used to obtain keys for a descrambler session,
+ * which can in turn be used to descramble protected video content.
+ *
+ * The 1.2 must always create 1.2 ICas interfaces, which are
+ * returned via the 1.1 createPluginExt method.
+ *
+ * To use 1.2 features the caller must cast the returned interface to a
+ * 1.2 HAL, using V1_2::ICas::castFrom().
+ */
+interface IMediaCasService extends @1.1::IMediaCasService {};
diff --git a/cas/1.2/default/Android.bp b/cas/1.2/default/Android.bp
new file mode 100644
index 0000000..9e53148
--- /dev/null
+++ b/cas/1.2/default/Android.bp
@@ -0,0 +1,49 @@
+cc_defaults {
+ name: "cas_service_defaults@1.2",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "CasImpl.cpp",
+ "DescramblerImpl.cpp",
+ "MediaCasService.cpp",
+ "service.cpp",
+ "SharedLibrary.cpp",
+ "TypeConvert.cpp",
+ ],
+
+ compile_multilib: "32",
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas@1.1",
+ "android.hardware.cas@1.2",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.memory@1.0",
+ "libbinder",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ "media_plugin_headers",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.cas@1.2-service",
+ vintf_fragments: ["android.hardware.cas@1.2-service.xml"],
+ defaults: ["cas_service_defaults@1.2"],
+ init_rc: ["android.hardware.cas@1.2-service.rc"],
+}
+
+cc_binary {
+ name: "android.hardware.cas@1.2-service-lazy",
+ vintf_fragments: ["android.hardware.cas@1.2-service-lazy.xml"],
+ overrides: ["android.hardware.cas@1.2-service"],
+ defaults: ["cas_service_defaults@1.2"],
+ init_rc: ["android.hardware.cas@1.2-service-lazy.rc"],
+ cflags: ["-DLAZY_SERVICE"],
+}
diff --git a/cas/1.2/default/CasImpl.cpp b/cas/1.2/default/CasImpl.cpp
new file mode 100644
index 0000000..46dd251
--- /dev/null
+++ b/cas/1.2/default/CasImpl.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ icensed 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-CasImpl"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <android/hardware/cas/1.2/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+CasImpl::CasImpl(const sp<ICasListener>& listener) : mListener(listener) {
+ ALOGV("CTOR");
+}
+
+CasImpl::~CasImpl() {
+ ALOGV("DTOR");
+ release();
+}
+
+// static
+void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl* casImpl = static_cast<CasImpl*>(appData);
+ casImpl->onEvent(event, arg, data, size);
+}
+
+// static
+void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+ const CasSessionId* sessionId) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl* casImpl = static_cast<CasImpl*>(appData);
+ casImpl->onEvent(sessionId, event, arg, data, size);
+}
+
+// static
+void CasImpl::StatusUpdate(void* appData, int32_t event, int32_t arg) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl* casImpl = static_cast<CasImpl*>(appData);
+ casImpl->onStatusUpdate(event, arg);
+}
+
+void CasImpl::init(const sp<SharedLibrary>& library, CasPlugin* plugin) {
+ mLibrary = library;
+ std::shared_ptr<CasPlugin> holder(plugin);
+ std::atomic_store(&mPluginHolder, holder);
+}
+
+void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ HidlCasData eventData;
+ if (data != NULL) {
+ eventData.setToExternal(data, size);
+ }
+
+ mListener->onEvent(event, arg, eventData);
+}
+
+void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+ size_t size) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ HidlCasData eventData;
+ if (data != NULL) {
+ eventData.setToExternal(data, size);
+ }
+
+ if (sessionId != NULL) {
+ mListener->onSessionEvent(*sessionId, event, arg, eventData);
+ } else {
+ mListener->onEvent(event, arg, eventData);
+ }
+}
+
+void CasImpl::onStatusUpdate(int32_t event, int32_t arg) {
+ if (mListener == NULL) {
+ return;
+ }
+ sp<V1_2::ICasListener> listenerV1_2 = V1_2::ICasListener::castFrom(mListener);
+
+ if (listenerV1_2 != NULL) {
+ listenerV1_2->onStatusUpdate(static_cast<StatusEvent>(event), arg);
+ }
+}
+
+Return<Status> CasImpl::setPluginStatusUpdateCallback() {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->setStatusCallback(&CasImpl::StatusUpdate));
+}
+
+Return<Status> CasImpl::setPrivateData(const HidlCasData& pvtData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->setPrivateData(pvtData));
+}
+
+Return<void> CasImpl::openSession(openSession_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+ CasSessionId sessionId;
+
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ status_t err = INVALID_OPERATION;
+ if (holder.get() != nullptr) {
+ err = holder->openSession(&sessionId);
+ holder.reset();
+ }
+
+ _hidl_cb(toStatus(err), sessionId);
+
+ return Void();
+}
+
+Return<void> CasImpl::openSession_1_2(const SessionIntent intent, const ScramblingMode mode,
+ openSession_1_2_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+ CasSessionId sessionId;
+
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ status_t err = INVALID_OPERATION;
+ if (holder.get() != nullptr) {
+ err = holder->openSession(static_cast<uint32_t>(intent), static_cast<uint32_t>(mode),
+ &sessionId);
+ holder.reset();
+ }
+
+ _hidl_cb(toStatus_1_2(err), sessionId);
+
+ return Void();
+}
+
+Return<Status> CasImpl::setSessionPrivateData(const HidlCasSessionId& sessionId,
+ const HidlCasData& pvtData) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->setSessionPrivateData(sessionId, pvtData));
+}
+
+Return<Status> CasImpl::closeSession(const HidlCasSessionId& sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->closeSession(sessionId));
+}
+
+Return<Status> CasImpl::processEcm(const HidlCasSessionId& sessionId, const HidlCasData& ecm) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->processEcm(sessionId, ecm));
+}
+
+Return<Status> CasImpl::processEmm(const HidlCasData& emm) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->processEmm(emm));
+}
+
+Return<Status> CasImpl::sendEvent(int32_t event, int32_t arg, const HidlCasData& eventData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->sendEvent(event, arg, eventData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+ int32_t arg, const HidlCasData& eventData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::provision(const hidl_string& provisionString) {
+ ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->provision(String8(provisionString.c_str())));
+}
+
+Return<Status> CasImpl::refreshEntitlements(int32_t refreshType, const HidlCasData& refreshData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->refreshEntitlements(refreshType, refreshData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::release() {
+ ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+ std::shared_ptr<CasPlugin> holder(nullptr);
+ std::atomic_store(&mPluginHolder, holder);
+
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.2/default/CasImpl.h b/cas/1.2/default/CasImpl.h
new file mode 100644
index 0000000..4325c20
--- /dev/null
+++ b/cas/1.2/default/CasImpl.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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+
+#include <android/hardware/cas/1.1/ICas.h>
+#include <android/hardware/cas/1.2/ICas.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct CasPlugin;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+struct ICasListener;
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasData;
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+using ::android::hardware::cas::V1_2::ScramblingMode;
+using ::android::hardware::cas::V1_2::SessionIntent;
+using ::android::hardware::cas::V1_2::StatusEvent;
+
+class SharedLibrary;
+
+class CasImpl : public V1_2::ICas {
+ public:
+ CasImpl(const sp<ICasListener>& listener);
+ virtual ~CasImpl();
+
+ static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+ static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+ const CasSessionId* sessionId);
+
+ static void StatusUpdate(void* appData, int32_t event, int32_t arg);
+
+ void init(const sp<SharedLibrary>& library, CasPlugin* plugin);
+ void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+ void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+ size_t size);
+
+ void onStatusUpdate(int32_t event, int32_t arg);
+
+ // ICas inherits
+
+ Return<Status> setPluginStatusUpdateCallback();
+
+ virtual Return<Status> setPrivateData(const HidlCasData& pvtData) override;
+
+ virtual Return<void> openSession(openSession_cb _hidl_cb) override;
+
+ virtual Return<void> openSession_1_2(const SessionIntent intent, const ScramblingMode mode,
+ openSession_1_2_cb _hidl_cb) override;
+
+ virtual Return<Status> closeSession(const HidlCasSessionId& sessionId) override;
+
+ virtual Return<Status> setSessionPrivateData(const HidlCasSessionId& sessionId,
+ const HidlCasData& pvtData) override;
+
+ virtual Return<Status> processEcm(const HidlCasSessionId& sessionId,
+ const HidlCasData& ecm) override;
+
+ virtual Return<Status> processEmm(const HidlCasData& emm) override;
+
+ virtual Return<Status> sendEvent(int32_t event, int32_t arg,
+ const HidlCasData& eventData) override;
+
+ virtual Return<Status> sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+ int32_t arg, const HidlCasData& eventData) override;
+
+ virtual Return<Status> provision(const hidl_string& provisionString) override;
+
+ virtual Return<Status> refreshEntitlements(int32_t refreshType,
+ const HidlCasData& refreshData) override;
+
+ virtual Return<Status> release() override;
+
+ private:
+ struct PluginHolder;
+ sp<SharedLibrary> mLibrary;
+ std::shared_ptr<CasPlugin> mPluginHolder;
+ sp<ICasListener> mListener;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
diff --git a/cas/1.2/default/DescramblerImpl.cpp b/cas/1.2/default/DescramblerImpl.cpp
new file mode 100644
index 0000000..36dc1a5
--- /dev/null
+++ b/cas/1.2/default/DescramblerImpl.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
+
+#include <hidlmemory/mapping.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
+
+#include "DescramblerImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+using hidl::memory::V1_0::IMemory;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+#define CHECK_SUBSAMPLE_DEF(type) \
+ static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \
+ static_assert(offsetof(SubSample, numBytesOfClearData) == \
+ offsetof(type::SubSample, mNumBytesOfClearData), \
+ "SubSample: numBytesOfClearData offset doesn't match"); \
+ static_assert(offsetof(SubSample, numBytesOfEncryptedData) == \
+ offsetof(type::SubSample, mNumBytesOfEncryptedData), \
+ "SubSample: numBytesOfEncryptedData offset doesn't match")
+
+CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
+CHECK_SUBSAMPLE_DEF(CryptoPlugin);
+
+DescramblerImpl::DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin)
+ : mLibrary(library), mPluginHolder(plugin) {
+ ALOGV("CTOR: plugin=%p", mPluginHolder.get());
+}
+
+DescramblerImpl::~DescramblerImpl() {
+ ALOGV("DTOR: plugin=%p", mPluginHolder.get());
+ release();
+}
+
+Return<Status> DescramblerImpl::setMediaCasSession(const HidlCasSessionId& sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->setMediaCasSession(sessionId));
+}
+
+Return<bool> DescramblerImpl::requiresSecureDecoderComponent(const hidl_string& mime) {
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return false;
+ }
+
+ return holder->requiresSecureDecoderComponent(String8(mime.c_str()));
+}
+
+static inline bool validateRangeForSize(uint64_t offset, uint64_t length, uint64_t size) {
+ return isInRange<uint64_t, uint64_t>(0, size, offset, length);
+}
+
+Return<void> DescramblerImpl::descramble(ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer, uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+ descramble_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ // hidl_memory's size is stored in uint64_t, but mapMemory's mmap will map
+ // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
+ // but the mapped memory's actual size will be smaller than the reported size.
+ if (srcBuffer.heapBase.size() > SIZE_MAX) {
+ ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+ android_errorWriteLog(0x534e4554, "79376389");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ sp<IMemory> srcMem = mapMemory(srcBuffer.heapBase);
+
+ // Validate if the offset and size in the SharedBuffer is consistent with the
+ // mapped ashmem, since the offset and size is controlled by client.
+ if (srcMem == NULL) {
+ ALOGE("Failed to map src buffer.");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+ if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
+ ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
+ srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ // use 64-bit here to catch bad subsample size that might be overflowing.
+ uint64_t totalBytesInSubSamples = 0;
+ for (size_t i = 0; i < subSamples.size(); i++) {
+ totalBytesInSubSamples +=
+ (uint64_t)subSamples[i].numBytesOfClearData + subSamples[i].numBytesOfEncryptedData;
+ }
+ // Further validate if the specified srcOffset and requested total subsample size
+ // is consistent with the source shared buffer size.
+ if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
+ ALOGE("Invalid srcOffset and subsample size: "
+ "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+ srcOffset, totalBytesInSubSamples, srcBuffer.size);
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ void* srcPtr = (uint8_t*)(void*)srcMem->getPointer() + srcBuffer.offset;
+ void* dstPtr = NULL;
+ if (dstBuffer.type == BufferType::SHARED_MEMORY) {
+ // When using shared memory, src buffer is also used as dst,
+ // we don't map it again here.
+ dstPtr = srcPtr;
+
+ // In this case the dst and src would be the same buffer, need to validate
+ // dstOffset against the buffer size too.
+ if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
+ ALOGE("Invalid dstOffset and subsample size: "
+ "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+ dstOffset, totalBytesInSubSamples, srcBuffer.size);
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+ } else {
+ native_handle_t* handle =
+ const_cast<native_handle_t*>(dstBuffer.secureMemory.getNativeHandle());
+ dstPtr = static_cast<void*>(handle);
+ }
+
+ // Get a local copy of the shared_ptr for the plugin. Note that before
+ // calling the HIDL callback, this shared_ptr must be manually reset,
+ // since the client side could proceed as soon as the callback is called
+ // without waiting for this method to go out of scope.
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
+ return Void();
+ }
+
+ // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
+ // to ensure structs are actually idential
+
+ int32_t result =
+ holder->descramble(dstBuffer.type != BufferType::SHARED_MEMORY,
+ (DescramblerPlugin::ScramblingControl)scramblingControl,
+ subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(),
+ srcPtr, srcOffset, dstPtr, dstOffset, NULL);
+
+ holder.reset();
+ _hidl_cb(toStatus(result >= 0 ? OK : result), result, NULL);
+ return Void();
+}
+
+Return<Status> DescramblerImpl::release() {
+ ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+ std::shared_ptr<DescramblerPlugin> holder(nullptr);
+ std::atomic_store(&mPluginHolder, holder);
+
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.2/default/DescramblerImpl.h b/cas/1.2/default/DescramblerImpl.h
new file mode 100644
index 0000000..011eace
--- /dev/null
+++ b/cas/1.2/default/DescramblerImpl.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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct DescramblerPlugin;
+using namespace hardware::cas::native::V1_0;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+
+class SharedLibrary;
+
+class DescramblerImpl : public IDescrambler {
+ public:
+ DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin);
+ virtual ~DescramblerImpl();
+
+ virtual Return<Status> setMediaCasSession(const HidlCasSessionId& sessionId) override;
+
+ virtual Return<bool> requiresSecureDecoderComponent(const hidl_string& mime) override;
+
+ virtual Return<void> descramble(ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer, uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+ descramble_cb _hidl_cb) override;
+
+ virtual Return<Status> release() override;
+
+ private:
+ sp<SharedLibrary> mLibrary;
+ std::shared_ptr<DescramblerPlugin> mPluginHolder;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
diff --git a/cas/1.2/default/FactoryLoader.h b/cas/1.2/default/FactoryLoader.h
new file mode 100644
index 0000000..7403f86
--- /dev/null
+++ b/cas/1.2/default/FactoryLoader.h
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+#define ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <media/cas/CasAPI.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include "SharedLibrary.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+
+template <class T>
+class FactoryLoader {
+ public:
+ FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
+
+ virtual ~FactoryLoader() { closeFactory(); }
+
+ bool findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library = NULL,
+ T** factory = NULL);
+
+ bool enumeratePlugins(vector<HidlCasPluginDescriptor>* results);
+
+ private:
+ typedef T* (*CreateFactoryFunc)();
+
+ Mutex mMapLock;
+ T* mFactory;
+ const char* mCreateFactoryFuncName;
+ sp<SharedLibrary> mLibrary;
+ KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
+ KeyedVector<String8, wp<SharedLibrary>> mLibraryPathToOpenLibraryMap;
+
+ bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+ sp<SharedLibrary>* library, T** factory);
+
+ bool queryPluginsFromPath(const String8& path, vector<HidlCasPluginDescriptor>* results);
+
+ bool openFactory(const String8& path);
+ void closeFactory();
+};
+
+template <class T>
+bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library,
+ T** factory) {
+ if (library != NULL) {
+ library->clear();
+ }
+ if (factory != NULL) {
+ *factory = NULL;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ // first check cache
+ ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
+ if (index >= 0) {
+ return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
+ library, factory);
+ }
+
+ // no luck, have to search
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
+ mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
+ closedir(pDir);
+
+ return true;
+ }
+ }
+ }
+
+ closedir(pDir);
+
+ ALOGE("Failed to find plugin");
+ return false;
+}
+
+template <class T>
+bool FactoryLoader<T>::enumeratePlugins(vector<HidlCasPluginDescriptor>* results) {
+ ALOGI("enumeratePlugins");
+
+ results->clear();
+
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ queryPluginsFromPath(pluginPath, results);
+ }
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+ sp<SharedLibrary>* library, T** factory) {
+ closeFactory();
+
+ if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
+ closeFactory();
+ return false;
+ }
+
+ if (library != NULL) {
+ *library = mLibrary;
+ }
+ if (factory != NULL) {
+ *factory = mFactory;
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
+ vector<HidlCasPluginDescriptor>* results) {
+ closeFactory();
+
+ vector<CasPluginDescriptor> descriptors;
+ if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
+ closeFactory();
+ return false;
+ }
+
+ for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
+ results->push_back(
+ HidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::openFactory(const String8& path) {
+ // get strong pointer to open shared library
+ ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+ if (index >= 0) {
+ mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
+ } else {
+ index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+ }
+
+ if (!mLibrary.get()) {
+ mLibrary = new SharedLibrary(path);
+ if (!*mLibrary) {
+ return false;
+ }
+
+ mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+ }
+
+ CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
+ if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
+ return false;
+ }
+ return true;
+}
+
+template <class T>
+void FactoryLoader<T>::closeFactory() {
+ delete mFactory;
+ mFactory = NULL;
+ mLibrary.clear();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
diff --git a/cas/1.2/default/MediaCasService.cpp b/cas/1.2/default/MediaCasService.cpp
new file mode 100644
index 0000000..4ecd52b
--- /dev/null
+++ b/cas/1.2/default/MediaCasService.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-MediaCasService"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <android/hardware/cas/1.2/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "DescramblerImpl.h"
+#include "MediaCasService.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+class Wrapper : public V1_1::ICasListener {
+ public:
+ static sp<V1_1::ICasListener> wrap(sp<V1_0::ICasListener> impl) {
+ sp<V1_1::ICasListener> cast = V1_1::ICasListener::castFrom(impl);
+ if (cast == NULL) {
+ cast = new Wrapper(impl);
+ }
+ return cast;
+ }
+
+ virtual Return<void> onEvent(int32_t event, int32_t arg,
+ const hidl_vec<uint8_t>& data) override {
+ mImpl->onEvent(event, arg, data);
+ return Void();
+ }
+
+ virtual Return<void> onSessionEvent(const hidl_vec<uint8_t>& /* sessionId */,
+ int32_t /* event */, int32_t /* arg */,
+ const hidl_vec<uint8_t>& /*data*/) override {
+ ALOGV("Do nothing on Session Event for cas@1.0 client in cas@1.1");
+ return Void();
+ }
+
+ private:
+ Wrapper(sp<V1_0::ICasListener> impl) : mImpl(impl){};
+ sp<V1_0::ICasListener> mImpl;
+};
+
+MediaCasService::MediaCasService()
+ : mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {}
+
+MediaCasService::~MediaCasService() {}
+
+Return<void> MediaCasService::enumeratePlugins(enumeratePlugins_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ vector<HidlCasPluginDescriptor> results;
+ mCasLoader.enumeratePlugins(&results);
+
+ _hidl_cb(results);
+ return Void();
+}
+
+Return<bool> MediaCasService::isSystemIdSupported(int32_t CA_system_id) {
+ ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
+
+ return mCasLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<V1_0::ICas>> MediaCasService::createPlugin(int32_t CA_system_id,
+ const sp<V1_0::ICasListener>& listener) {
+ ALOGV("%s:Use createPluginExt to create plugin in cas@1.1", __FUNCTION__);
+
+ sp<ICas> result;
+
+ sp<V1_1::ICasListener> listenerV1_1 = Wrapper::wrap(listener);
+
+ result = createPluginExt(CA_system_id, listenerV1_1);
+
+ return result;
+}
+
+Return<sp<ICas>> MediaCasService::createPluginExt(int32_t CA_system_id,
+ const sp<ICasListener>& listener) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+ if (listener == NULL) ALOGV("%s: Listener is NULL", __FUNCTION__);
+
+ sp<V1_2::ICas> result;
+
+ CasFactory* factory;
+ sp<SharedLibrary> library;
+ if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ CasPlugin* plugin = NULL;
+ sp<CasImpl> casImpl = new CasImpl(listener);
+ if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) ==
+ OK &&
+ plugin != NULL) {
+ casImpl->init(library, plugin);
+ result = casImpl;
+
+ sp<V1_2::ICasListener> listenerV1_2 = V1_2::ICasListener::castFrom(listener);
+ if (listenerV1_2 != NULL) {
+ casImpl->setPluginStatusUpdateCallback();
+ }
+ }
+ }
+
+ return result;
+}
+
+Return<bool> MediaCasService::isDescramblerSupported(int32_t CA_system_id) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ return mDescramblerLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<IDescramblerBase>> MediaCasService::createDescrambler(int32_t CA_system_id) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ sp<IDescrambler> result;
+
+ DescramblerFactory* factory;
+ sp<SharedLibrary> library;
+ if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ DescramblerPlugin* plugin = NULL;
+ if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) {
+ result = new DescramblerImpl(library, plugin);
+ }
+ }
+
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.2/default/MediaCasService.h b/cas/1.2/default/MediaCasService.h
new file mode 100644
index 0000000..01e11db
--- /dev/null
+++ b/cas/1.2/default/MediaCasService.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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+#define ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+
+#include <android/hardware/cas/1.1/IMediaCasService.h>
+#include <android/hardware/cas/1.2/IMediaCasService.h>
+
+#include "FactoryLoader.h"
+
+namespace android {
+struct CasFactory;
+struct DescramblerFactory;
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using ::android::hardware::cas::V1_0::IDescramblerBase;
+
+class MediaCasService : public V1_2::IMediaCasService {
+ public:
+ MediaCasService();
+
+ virtual Return<void> enumeratePlugins(enumeratePlugins_cb _hidl_cb) override;
+
+ virtual Return<bool> isSystemIdSupported(int32_t CA_system_id) override;
+
+ virtual Return<sp<V1_0::ICas>> createPlugin(int32_t CA_system_id,
+ const sp<V1_0::ICasListener>& listener) override;
+
+ virtual Return<sp<ICas>> createPluginExt(int32_t CA_system_id,
+ const sp<ICasListener>& listener) override;
+
+ virtual Return<bool> isDescramblerSupported(int32_t CA_system_id) override;
+
+ virtual Return<sp<IDescramblerBase>> createDescrambler(int32_t CA_system_id) override;
+
+ private:
+ FactoryLoader<CasFactory> mCasLoader;
+ FactoryLoader<DescramblerFactory> mDescramblerLoader;
+
+ virtual ~MediaCasService();
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
diff --git a/cas/1.2/default/SharedLibrary.cpp b/cas/1.2/default/SharedLibrary.cpp
new file mode 100644
index 0000000..ffe4bb9
--- /dev/null
+++ b/cas/1.2/default/SharedLibrary.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-SharedLibrary"
+
+#include "SharedLibrary.h"
+#include <dlfcn.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+SharedLibrary::SharedLibrary(const String8& path) {
+ mLibHandle = dlopen(path.string(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLibHandle != NULL) {
+ dlclose(mLibHandle);
+ mLibHandle = NULL;
+ }
+}
+
+bool SharedLibrary::operator!() const {
+ return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+ if (!mLibHandle) {
+ return NULL;
+ }
+ // Clear last error before we load the symbol again,
+ // in case the caller didn't retrieve it.
+ (void)dlerror();
+ return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+ const char* error = dlerror();
+ return error ? error : "No errors or unknown error";
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.2/default/SharedLibrary.h b/cas/1.2/default/SharedLibrary.h
new file mode 100644
index 0000000..b85f557
--- /dev/null
+++ b/cas/1.2/default/SharedLibrary.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+#define ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+class SharedLibrary : public RefBase {
+ public:
+ explicit SharedLibrary(const String8& path);
+ ~SharedLibrary();
+
+ bool operator!() const;
+ void* lookup(const char* symbol) const;
+ const char* lastError() const;
+
+ private:
+ void* mLibHandle;
+ DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
diff --git a/cas/1.2/default/TypeConvert.cpp b/cas/1.2/default/TypeConvert.cpp
new file mode 100644
index 0000000..c4bd0dd
--- /dev/null
+++ b/cas/1.2/default/TypeConvert.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-TypeConvert"
+
+#include "TypeConvert.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+Status toStatus(status_t legacyStatus) {
+ Status status;
+ switch (legacyStatus) {
+ case android::OK:
+ status = Status::OK;
+ break;
+ case android::ERROR_CAS_NO_LICENSE:
+ status = Status::ERROR_CAS_NO_LICENSE;
+ break;
+ case android::ERROR_CAS_LICENSE_EXPIRED:
+ status = Status::ERROR_CAS_LICENSE_EXPIRED;
+ break;
+ case android::ERROR_CAS_SESSION_NOT_OPENED:
+ status = Status::ERROR_CAS_SESSION_NOT_OPENED;
+ break;
+ case android::ERROR_CAS_CANNOT_HANDLE:
+ status = Status::ERROR_CAS_CANNOT_HANDLE;
+ break;
+ case android::ERROR_CAS_TAMPER_DETECTED:
+ status = Status::ERROR_CAS_INVALID_STATE;
+ break;
+ case android::BAD_VALUE:
+ status = Status::BAD_VALUE;
+ break;
+ case android::ERROR_CAS_NOT_PROVISIONED:
+ status = Status::ERROR_CAS_NOT_PROVISIONED;
+ break;
+ case android::ERROR_CAS_RESOURCE_BUSY:
+ status = Status::ERROR_CAS_RESOURCE_BUSY;
+ break;
+ case android::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
+ status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
+ break;
+ case android::ERROR_CAS_DEVICE_REVOKED:
+ status = Status::ERROR_CAS_DEVICE_REVOKED;
+ break;
+ case android::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
+ status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
+ break;
+ case android::ERROR_CAS_DECRYPT:
+ status = Status::ERROR_CAS_DECRYPT;
+ break;
+ default:
+ ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus);
+ status = Status::ERROR_CAS_UNKNOWN;
+ break;
+ }
+ return status;
+}
+
+V1_2::Status toStatus_1_2(status_t legacyStatus) {
+ V1_2::Status status = static_cast<V1_2::Status>(toStatus(legacyStatus));
+ if (status == V1_2::Status::ERROR_CAS_UNKNOWN) {
+ switch (legacyStatus) {
+ case android::ERROR_CAS_NEED_ACTIVATION:
+ status = V1_2::Status::ERROR_CAS_NEED_ACTIVATION;
+ break;
+ case android::ERROR_CAS_NEED_PAIRING:
+ status = V1_2::Status::ERROR_CAS_NEED_PAIRING;
+ break;
+ case android::ERROR_CAS_NO_CARD:
+ status = V1_2::Status::ERROR_CAS_NO_CARD;
+ break;
+ case android::ERROR_CAS_CARD_MUTE:
+ status = V1_2::Status::ERROR_CAS_CARD_MUTE;
+ break;
+ case android::ERROR_CAS_CARD_INVALID:
+ status = V1_2::Status::ERROR_CAS_CARD_INVALID;
+ break;
+ case android::ERROR_CAS_BLACKOUT:
+ status = V1_2::Status::ERROR_CAS_BLACKOUT;
+ break;
+ }
+ }
+ return status;
+}
+
+String8 sessionIdToString(const CasSessionId& sessionId) {
+ String8 result;
+ for (size_t i = 0; i < sessionId.size(); i++) {
+ result.appendFormat("%02x ", sessionId[i]);
+ }
+ if (result.isEmpty()) {
+ result.append("(null)");
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.2/default/TypeConvert.h b/cas/1.2/default/TypeConvert.h
new file mode 100644
index 0000000..018f310
--- /dev/null
+++ b/cas/1.2/default/TypeConvert.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+#define ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+
+#include <android/hardware/cas/1.0/types.h>
+#include <android/hardware/cas/1.2/types.h>
+#include <media/cas/CasAPI.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::Status;
+
+Status toStatus(status_t legacyStatus);
+
+V1_2::Status toStatus_1_2(status_t legacyStatus);
+
+String8 sessionIdToString(const CasSessionId& sessionId);
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc
new file mode 100644
index 0000000..1c75100
--- /dev/null
+++ b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc
@@ -0,0 +1,11 @@
+service vendor.cas-hal-1-2 /vendor/bin/hw/android.hardware.cas@1.2-service-lazy
+ interface android.hardware.cas@1.0::IMediaCasService default
+ interface android.hardware.cas@1.1::IMediaCasService default
+ interface android.hardware.cas@1.2::IMediaCasService default
+ oneshot
+ disabled
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service-lazy.xml b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.xml
new file mode 100644
index 0000000..9b36406
--- /dev/null
+++ b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.cas</name>
+ <transport>hwbinder</transport>
+ <version>1.2</version>
+ <interface>
+ <name>IMediaCasService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service.rc b/cas/1.2/default/android.hardware.cas@1.2-service.rc
new file mode 100644
index 0000000..d1c853e
--- /dev/null
+++ b/cas/1.2/default/android.hardware.cas@1.2-service.rc
@@ -0,0 +1,6 @@
+service vendor.cas-hal-1-2 /vendor/bin/hw/android.hardware.cas@1.2-service
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service.xml b/cas/1.2/default/android.hardware.cas@1.2-service.xml
new file mode 100644
index 0000000..9b36406
--- /dev/null
+++ b/cas/1.2/default/android.hardware.cas@1.2-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.cas</name>
+ <transport>hwbinder</transport>
+ <version>1.2</version>
+ <interface>
+ <name>IMediaCasService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/cas/1.2/default/service.cpp b/cas/1.2/default/service.cpp
new file mode 100644
index 0000000..a623447
--- /dev/null
+++ b/cas/1.2/default/service.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 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_NDEBUG 0
+#ifdef LAZY_SERVICE
+#define LOG_TAG "android.hardware.cas@1.1-service-lazy"
+#else
+#define LOG_TAG "android.hardware.cas@1.1-service"
+#endif
+
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include "MediaCasService.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
+using android::hardware::cas::V1_1::implementation::MediaCasService;
+using android::hardware::cas::V1_2::IMediaCasService;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main() {
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Setup hwbinder service
+ android::sp<IMediaCasService> service = new MediaCasService();
+ android::status_t status;
+ if (kLazyService) {
+ auto serviceRegistrar = LazyServiceRegistrar::getInstance();
+ status = serviceRegistrar.registerService(service);
+ } else {
+ status = service->registerAsService();
+ }
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering cas service: %d", status);
+
+ joinRpcThreadpool();
+ return 0;
+}
diff --git a/cas/1.2/types.hal b/cas/1.2/types.hal
new file mode 100644
index 0000000..40c06cf
--- /dev/null
+++ b/cas/1.2/types.hal
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package android.hardware.cas@1.2;
+
+import android.hardware.cas@1.0;
+import android.hardware.cas@1.1;
+
+enum Status : @1.0::Status {
+ /**
+ * ERROR_CAS_NEED_ACTIVATION is used to trigger device activation process.
+ */
+ ERROR_CAS_NEED_ACTIVATION,
+ /**
+ * ERROR_CAS_NEED_PAIRING is used to trigger pairing process.
+ */
+ ERROR_CAS_NEED_PAIRING,
+ /**
+ * ERROR_CAS_NO_CARD is used to report no smart card for descrambling.
+ */
+ ERROR_CAS_NO_CARD,
+ /**
+ * ERROR_CAS_CARD_MUTE is used to report smart card is muted for
+ * descrambling.
+ */
+ ERROR_CAS_CARD_MUTE,
+ /**
+ * ERROR_CAS_CARD_INVALID is used to report smart card isn't valid.
+ */
+ ERROR_CAS_CARD_INVALID,
+ /**
+ * ERROR_CAS_BLACKOUT is used to report geographical blackout.
+ */
+ ERROR_CAS_BLACKOUT,
+};
+
+/**
+ * The intented usage for the session.
+ */
+enum SessionIntent : uint32_t {
+ /**
+ * Live Stream.
+ */
+ LIVE,
+ /**
+ * Playback Recorded Stream.
+ */
+ PLAYBACK,
+ /**
+ * Record Live Stream.
+ */
+ RECORD,
+ /**
+ * View the content with Time Shift capability
+ */
+ TIMESHIFT,
+};
+
+/**
+ * The Scrambling Mode.
+ */
+enum ScramblingMode : uint32_t {
+ RESERVED = 0,
+ /**
+ * DVB (Digital Video Broadcasting) CSA1 (Common Scrambling Algorithm 1) is
+ * the default mode and shall be used when the scrambling descriptor
+ * is not present in the program map section. DVB scrambling mode is
+ * specified in ETSI EN 300 468 specification.
+ */
+ DVB_CSA1,
+ DVB_CSA2,
+ /**
+ * DVB-CSA3 in standard mode.
+ */
+ DVB_CSA3_STANDARD,
+ /**
+ * DVB-CSA3 in minimally enhanced mode.
+ */
+ DVB_CSA3_MINIMAL,
+ /**
+ * DVB-CSA3 in fully enhanced mode.
+ */
+ DVB_CSA3_ENHANCE,
+ /**
+ * DVB-CISSA version 1.
+ */
+ DVB_CISSA_V1,
+ /**
+ * ATIS-0800006 IIF Default Scrambling Algorithm (IDSA).
+ */
+ DVB_IDSA,
+ /**
+ * a symmetric key algorithm.
+ */
+ MULTI2,
+ /**
+ * Advanced Encryption System (AES) 128-bit Encryption mode.
+ */
+ AES128,
+ /**
+ * Advanced Encryption System (AES) Electronic Code Book (ECB) mode.
+ */
+ AES_ECB,
+ /**
+ * Advanced Encryption System (AES) Society of Cable Telecommunications
+ * Engineers (SCTE) 52 mode.
+ */
+ AES_SCTE52,
+ /**
+ * Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode.
+ */
+ TDES_ECB,
+ /**
+ * Triple Data Encryption Algorithm (TDES) Society of Cable Telecommunications
+ * Engineers (SCTE) 52 mode.
+ */
+ TDES_SCTE52,
+ };
+
+/**
+ * The Event Type for status change.
+ */
+enum StatusEvent : uint8_t {
+ /**
+ * The status of CAS plugin was changed due to physical module insertion or
+ * removal. Client must call enumeratePlugins to update plugins' status.
+ */
+ PLUGIN_PHYSICAL_MODULE_CHANGED,
+ /**
+ * The status of supported session number was changed due to physical module
+ * insertion or removal. Client must update session resource according to
+ * latest StatusMessage from the StatusEvent. The plugin supports unlimited
+ * sesssion by default.
+ */
+ PLUGIN_SESSION_NUMBER_CHANGED,
+};
diff --git a/cas/1.2/vts/functional/Android.bp b/cas/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..9bc372c
--- /dev/null
+++ b/cas/1.2/vts/functional/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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_test {
+ name: "VtsHalCasV1_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalCasV1_2TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas@1.1",
+ "android.hardware.cas@1.2",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlallocatorutils",
+ "libhidlmemory",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ test_suites: ["general-tests"],
+}
+
diff --git a/cas/1.2/vts/functional/OWNERS b/cas/1.2/vts/functional/OWNERS
new file mode 100644
index 0000000..29246ed
--- /dev/null
+++ b/cas/1.2/vts/functional/OWNERS
@@ -0,0 +1,3 @@
+nchalko@google.com
+chz@google.com
+quxiangfang@google.com
diff --git a/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp b/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp
new file mode 100644
index 0000000..8439ceb
--- /dev/null
+++ b/cas/1.2/vts/functional/VtsHalCasV1_2TargetTest.cpp
@@ -0,0 +1,619 @@
+/*
+ * 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 "mediacas_hidl_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/cas/1.0/IDescramblerBase.h>
+#include <android/hardware/cas/1.0/types.h>
+#include <android/hardware/cas/1.2/ICas.h>
+#include <android/hardware/cas/1.2/ICasListener.h>
+#include <android/hardware/cas/1.2/IMediaCasService.h>
+#include <android/hardware/cas/1.2/types.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <android/hardware/cas/native/1.0/types.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define INVALID_SYSTEM_ID 0
+#define WAIT_TIMEOUT 3000000000
+
+#define PROVISION_STR \
+ "{ " \
+ " \"id\": 21140844, " \
+ " \"name\": \"Test Title\", " \
+ " \"lowercase_organization_name\": \"Android\", " \
+ " \"asset_key\": { " \
+ " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " \
+ " }, " \
+ " \"cas_type\": 1, " \
+ " \"track_types\": [ ] " \
+ "} "
+
+using android::Condition;
+using android::IMemory;
+using android::IMemoryHeap;
+using android::MemoryDealer;
+using android::Mutex;
+using android::sp;
+using android::hardware::fromHeap;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::HidlMemory;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::cas::native::V1_0::BufferType;
+using android::hardware::cas::native::V1_0::DestinationBuffer;
+using android::hardware::cas::native::V1_0::IDescrambler;
+using android::hardware::cas::native::V1_0::ScramblingControl;
+using android::hardware::cas::native::V1_0::SharedBuffer;
+using android::hardware::cas::native::V1_0::SubSample;
+using android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using android::hardware::cas::V1_0::IDescramblerBase;
+using android::hardware::cas::V1_0::Status;
+using android::hardware::cas::V1_2::ICas;
+using android::hardware::cas::V1_2::ICasListener;
+using android::hardware::cas::V1_2::IMediaCasService;
+using android::hardware::cas::V1_2::ScramblingMode;
+using android::hardware::cas::V1_2::SessionIntent;
+using android::hardware::cas::V1_2::StatusEvent;
+
+namespace {
+
+const uint8_t kEcmBinaryBuffer[] = {
+ 0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00,
+ 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60, 0x4f,
+ 0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c,
+ 0x62, 0x19, 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48,
+};
+
+const SubSample kSubSamples[] = {{162, 0}, {0, 184}, {0, 184}};
+
+const uint8_t kInBinaryBuffer[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+ 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+ 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+ 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+ 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+ 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+ 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+ 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+ 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+ 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x6e, 0x45, 0x21,
+ 0x82, 0x38, 0xf0, 0x9d, 0x7d, 0x96, 0xe6, 0x94, 0xae, 0xe2, 0x87, 0x8f, 0x04, 0x49, 0xe5,
+ 0xf6, 0x8c, 0x8b, 0x9a, 0x10, 0x18, 0xba, 0x94, 0xe9, 0x22, 0x31, 0x04, 0x7e, 0x60, 0x5b,
+ 0xc4, 0x24, 0x00, 0x90, 0x62, 0x0d, 0xdc, 0x85, 0x74, 0x75, 0x78, 0xd0, 0x14, 0x08, 0xcb,
+ 0x02, 0x1d, 0x7d, 0x9d, 0x34, 0xe8, 0x81, 0xb9, 0xf7, 0x09, 0x28, 0x79, 0x29, 0x8d, 0xe3,
+ 0x14, 0xed, 0x5f, 0xca, 0xaf, 0xf4, 0x1c, 0x49, 0x15, 0xe1, 0x80, 0x29, 0x61, 0x76, 0x80,
+ 0x43, 0xf8, 0x58, 0x53, 0x40, 0xd7, 0x31, 0x6d, 0x61, 0x81, 0x41, 0xe9, 0x77, 0x9f, 0x9c,
+ 0xe1, 0x6d, 0xf2, 0xee, 0xd9, 0xc8, 0x67, 0xd2, 0x5f, 0x48, 0x73, 0xe3, 0x5c, 0xcd, 0xa7,
+ 0x45, 0x58, 0xbb, 0xdd, 0x28, 0x1d, 0x68, 0xfc, 0xb4, 0xc6, 0xf6, 0x92, 0xf6, 0x30, 0x03,
+ 0xaa, 0xe4, 0x32, 0xf6, 0x34, 0x51, 0x4b, 0x0f, 0x8c, 0xf9, 0xac, 0x98, 0x22, 0xfb, 0x49,
+ 0xc8, 0xbf, 0xca, 0x8c, 0x80, 0x86, 0x5d, 0xd7, 0xa4, 0x52, 0xb1, 0xd9, 0xa6, 0x04, 0x4e,
+ 0xb3, 0x2d, 0x1f, 0xb8, 0x35, 0xcc, 0x45, 0x6d, 0x9c, 0x20, 0xa7, 0xa4, 0x34, 0x59, 0x72,
+ 0xe3, 0xae, 0xba, 0x49, 0xde, 0xd1, 0xaa, 0xee, 0x3d, 0x77, 0xfc, 0x5d, 0xc6, 0x1f, 0x9d,
+ 0xac, 0xc2, 0x15, 0x66, 0xb8, 0xe1, 0x54, 0x4e, 0x74, 0x93, 0xdb, 0x9a, 0x24, 0x15, 0x6e,
+ 0x20, 0xa3, 0x67, 0x3e, 0x5a, 0x24, 0x41, 0x5e, 0xb0, 0xe6, 0x35, 0x87, 0x1b, 0xc8, 0x7a,
+ 0xf9, 0x77, 0x65, 0xe0, 0x01, 0xf2, 0x4c, 0xe4, 0x2b, 0xa9, 0x64, 0x96, 0x96, 0x0b, 0x46,
+ 0xca, 0xea, 0x79, 0x0e, 0x78, 0xa3, 0x5f, 0x43, 0xfc, 0x47, 0x6a, 0x12, 0xfa, 0xc4, 0x33,
+ 0x0e, 0x88, 0x1c, 0x19, 0x3a, 0x00, 0xc3, 0x4e, 0xb5, 0xd8, 0xfa, 0x8e, 0xf1, 0xbc, 0x3d,
+ 0xb2, 0x7e, 0x50, 0x8d, 0x67, 0xc3, 0x6b, 0xed, 0xe2, 0xea, 0xa6, 0x1f, 0x25, 0x24, 0x7c,
+ 0x94, 0x74, 0x50, 0x49, 0xe3, 0xc6, 0x58, 0x2e, 0xfd, 0x28, 0xb4, 0xc6, 0x73, 0xb1, 0x53,
+ 0x74, 0x27, 0x94, 0x5c, 0xdf, 0x69, 0xb7, 0xa1, 0xd7, 0xf5, 0xd3, 0x8a, 0x2c, 0x2d, 0xb4,
+ 0x5e, 0x8a, 0x16, 0x14, 0x54, 0x64, 0x6e, 0x00, 0x6b, 0x11, 0x59, 0x8a, 0x63, 0x38, 0x80,
+ 0x76, 0xc3, 0xd5, 0x59, 0xf7, 0x3f, 0xd2, 0xfa, 0xa5, 0xca, 0x82, 0xff, 0x4a, 0x62, 0xf0,
+ 0xe3, 0x42, 0xf9, 0x3b, 0x38, 0x27, 0x8a, 0x89, 0xaa, 0x50, 0x55, 0x4b, 0x29, 0xf1, 0x46,
+ 0x7c, 0x75, 0xef, 0x65, 0xaf, 0x9b, 0x0d, 0x6d, 0xda, 0x25, 0x94, 0x14, 0xc1, 0x1b, 0xf0,
+ 0xc5, 0x4c, 0x24, 0x0e, 0x65,
+};
+
+const uint8_t kOutRefBinaryBuffer[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+ 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+ 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+ 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+ 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+ 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+ 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+ 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+ 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+ 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
+ 0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
+ 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
+ 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
+ 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
+ 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
+ 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
+ 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
+ 0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
+ 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
+ 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
+ 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
+ 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
+ 0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
+ 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
+ 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
+ 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
+ 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
+ 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+ 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
+ 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
+ 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
+ 0x73, 0x63, 0x65, 0x6e, 0x65,
+};
+
+class MediaCasListener : public ICasListener {
+ public:
+ virtual Return<void> onEvent(int32_t event, int32_t arg,
+ const hidl_vec<uint8_t>& data) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mEvent = event;
+ mEventArg = arg;
+ mEventData = data;
+
+ mEventReceived = true;
+ mMsgCondition.signal();
+ return Void();
+ }
+
+ virtual Return<void> onSessionEvent(const hidl_vec<uint8_t>& sessionId, int32_t event,
+ int32_t arg, const hidl_vec<uint8_t>& data) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mSessionId = sessionId;
+ mEvent = event;
+ mEventArg = arg;
+ mEventData = data;
+
+ mEventReceived = true;
+ mMsgCondition.signal();
+ return Void();
+ }
+
+ virtual Return<void> onStatusUpdate(StatusEvent event, int32_t arg) override {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ mStatusEvent = event;
+ mEventArg = arg;
+
+ mEventReceived = true;
+ mMsgCondition.signal();
+ return Void();
+ }
+
+ void testEventEcho(sp<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+ hidl_vec<uint8_t>& eventData);
+
+ void testSessionEventEcho(sp<ICas>& mediaCas, const hidl_vec<uint8_t>& sessionId,
+ int32_t& event, int32_t& eventArg, hidl_vec<uint8_t>& eventData);
+
+ void testStatusUpdate(sp<ICas>& mediaCas, std::vector<uint8_t>* sessionId, SessionIntent intent,
+ ScramblingMode mode);
+
+ private:
+ int32_t mEvent = -1;
+ int32_t mEventArg = -1;
+ StatusEvent mStatusEvent;
+ bool mEventReceived = false;
+ hidl_vec<uint8_t> mEventData;
+ hidl_vec<uint8_t> mSessionId;
+ android::Mutex mMsgLock;
+ android::Condition mMsgCondition;
+};
+
+void MediaCasListener::testEventEcho(sp<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+ hidl_vec<uint8_t>& eventData) {
+ mEventReceived = false;
+ auto returnStatus = mediaCas->sendEvent(event, eventArg, eventData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mEventReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "event not received within timeout";
+ return;
+ }
+ }
+
+ EXPECT_EQ(mEvent, event);
+ EXPECT_EQ(mEventArg, eventArg);
+ EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testSessionEventEcho(sp<ICas>& mediaCas, const hidl_vec<uint8_t>& sessionId,
+ int32_t& event, int32_t& eventArg,
+ hidl_vec<uint8_t>& eventData) {
+ mEventReceived = false;
+ auto returnStatus = mediaCas->sendSessionEvent(sessionId, event, eventArg, eventData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mEventReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "event not received within timeout";
+ return;
+ }
+ }
+
+ EXPECT_TRUE(mSessionId == sessionId);
+ EXPECT_EQ(mEvent, event);
+ EXPECT_EQ(mEventArg, eventArg);
+ EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testStatusUpdate(sp<ICas>& mediaCas, std::vector<uint8_t>* sessionId,
+ SessionIntent intent, ScramblingMode mode) {
+ mEventReceived = false;
+ android::hardware::cas::V1_2::Status sessionStatus;
+ auto returnVoid = mediaCas->openSession_1_2(
+ intent, mode,
+ [&](android::hardware::cas::V1_2::Status status, const hidl_vec<uint8_t>& id) {
+ sessionStatus = status;
+ *sessionId = id;
+ });
+ EXPECT_TRUE(returnVoid.isOk());
+ EXPECT_EQ(android::hardware::cas::V1_2::Status::OK, sessionStatus);
+
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (!mEventReceived) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "event not received within timeout";
+ return;
+ }
+ }
+ EXPECT_EQ(mStatusEvent, static_cast<StatusEvent>(intent));
+ EXPECT_EQ(mEventArg, static_cast<int32_t>(mode));
+}
+
+// Test environment for Cas HIDL HAL.
+class CasHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static CasHidlEnvironment* Instance() {
+ static CasHidlEnvironment* instance = new CasHidlEnvironment;
+ return instance;
+ }
+
+ virtual void registerTestServices() override { registerTestService<IMediaCasService>(); }
+};
+
+class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ mService = ::testing::VtsHalHidlTargetTestBase::getService<IMediaCasService>(
+ CasHidlEnvironment::Instance()->getServiceName<IMediaCasService>());
+ ASSERT_NE(mService, nullptr);
+ }
+
+ sp<IMediaCasService> mService;
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ sp<ICas> mMediaCas;
+ sp<IDescramblerBase> mDescramblerBase;
+ sp<MediaCasListener> mCasListener;
+ typedef struct _OobInputTestParams {
+ const SubSample* subSamples;
+ uint32_t numSubSamples;
+ size_t imemSizeActual;
+ uint64_t imemOffset;
+ uint64_t imemSize;
+ uint64_t srcOffset;
+ uint64_t dstOffset;
+ } OobInputTestParams;
+
+ ::testing::AssertionResult createCasPlugin(int32_t caSystemId);
+ ::testing::AssertionResult openCasSession(std::vector<uint8_t>* sessionId);
+ ::testing::AssertionResult openCasSession_1_2(std::vector<uint8_t>* sessionId,
+ SessionIntent intent, ScramblingMode mode);
+ ::testing::AssertionResult descrambleTestInputBuffer(const sp<IDescrambler>& descrambler,
+ Status* descrambleStatus,
+ sp<IMemory>* hidlInMemory);
+ ::testing::AssertionResult descrambleTestOobInput(const sp<IDescrambler>& descrambler,
+ Status* descrambleStatus,
+ const OobInputTestParams& params);
+};
+
+::testing::AssertionResult MediaCasHidlTest::createCasPlugin(int32_t caSystemId) {
+ auto status = mService->isSystemIdSupported(caSystemId);
+ if (!status.isOk() || !status) {
+ return ::testing::AssertionFailure();
+ }
+ status = mService->isDescramblerSupported(caSystemId);
+ if (!status.isOk() || !status) {
+ return ::testing::AssertionFailure();
+ }
+
+ mCasListener = new MediaCasListener();
+ auto pluginStatus = mService->createPluginExt(caSystemId, mCasListener);
+ if (!pluginStatus.isOk()) {
+ return ::testing::AssertionFailure();
+ }
+ mMediaCas = ICas::castFrom(pluginStatus);
+ if (mMediaCas == nullptr) {
+ return ::testing::AssertionFailure();
+ }
+
+ auto descramblerStatus = mService->createDescrambler(caSystemId);
+ if (!descramblerStatus.isOk()) {
+ return ::testing::AssertionFailure();
+ }
+ mDescramblerBase = descramblerStatus;
+ return ::testing::AssertionResult(mDescramblerBase != nullptr);
+}
+
+::testing::AssertionResult MediaCasHidlTest::openCasSession(std::vector<uint8_t>* sessionId) {
+ Status sessionStatus;
+ auto returnVoid = mMediaCas->openSession([&](Status status, const hidl_vec<uint8_t>& id) {
+ sessionStatus = status;
+ *sessionId = id;
+ });
+ return ::testing::AssertionResult(returnVoid.isOk() && (Status::OK == sessionStatus));
+}
+
+::testing::AssertionResult MediaCasHidlTest::descrambleTestInputBuffer(
+ const sp<IDescrambler>& descrambler, Status* descrambleStatus, sp<IMemory>* inMemory) {
+ hidl_vec<SubSample> hidlSubSamples;
+ hidlSubSamples.setToExternal(const_cast<SubSample*>(kSubSamples),
+ (sizeof(kSubSamples) / sizeof(SubSample)), false /*own*/);
+
+ sp<MemoryDealer> dealer = new MemoryDealer(sizeof(kInBinaryBuffer), "vts-cas");
+ if (nullptr == dealer.get()) {
+ ALOGE("couldn't get MemoryDealer!");
+ return ::testing::AssertionFailure();
+ }
+
+ sp<IMemory> mem = dealer->allocate(sizeof(kInBinaryBuffer));
+ if (nullptr == mem.get()) {
+ ALOGE("couldn't allocate IMemory!");
+ return ::testing::AssertionFailure();
+ }
+ *inMemory = mem;
+
+ // build HidlMemory from memory heap
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if (nullptr == heap.get()) {
+ ALOGE("couldn't get memory heap!");
+ return ::testing::AssertionFailure();
+ }
+
+ uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->unsecurePointer()));
+ memcpy(ipBuffer, kInBinaryBuffer, sizeof(kInBinaryBuffer));
+
+ // hidlMemory is not to be passed out of scope!
+ sp<HidlMemory> hidlMemory = fromHeap(heap);
+
+ SharedBuffer srcBuffer = {
+ .heapBase = *hidlMemory, .offset = (uint64_t)offset, .size = (uint64_t)size};
+
+ DestinationBuffer dstBuffer;
+ dstBuffer.type = BufferType::SHARED_MEMORY;
+ dstBuffer.nonsecureMemory = srcBuffer;
+
+ uint32_t outBytes;
+ hidl_string detailedError;
+ auto returnVoid = descrambler->descramble(
+ ScramblingControl::EVENKEY /*2*/, hidlSubSamples, srcBuffer, 0, dstBuffer, 0,
+ [&](Status status, uint32_t bytesWritten, const hidl_string& detailedErr) {
+ *descrambleStatus = status;
+ outBytes = bytesWritten;
+ detailedError = detailedErr;
+ });
+ if (!returnVoid.isOk() || *descrambleStatus != Status::OK) {
+ ALOGI("descramble failed, trans=%s, status=%d, outBytes=%u, error=%s",
+ returnVoid.description().c_str(), *descrambleStatus, outBytes, detailedError.c_str());
+ }
+ return ::testing::AssertionResult(returnVoid.isOk());
+}
+
+::testing::AssertionResult MediaCasHidlTest::descrambleTestOobInput(
+ const sp<IDescrambler>& descrambler, Status* descrambleStatus,
+ const OobInputTestParams& params) {
+ hidl_vec<SubSample> hidlSubSamples;
+ hidlSubSamples.setToExternal(const_cast<SubSample*>(params.subSamples), params.numSubSamples,
+ false /*own*/);
+
+ sp<MemoryDealer> dealer = new MemoryDealer(params.imemSizeActual, "vts-cas");
+ if (nullptr == dealer.get()) {
+ ALOGE("couldn't get MemoryDealer!");
+ return ::testing::AssertionFailure();
+ }
+
+ sp<IMemory> mem = dealer->allocate(params.imemSizeActual);
+ if (nullptr == mem.get()) {
+ ALOGE("couldn't allocate IMemory!");
+ return ::testing::AssertionFailure();
+ }
+
+ // build HidlMemory from memory heap
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if (nullptr == heap.get()) {
+ ALOGE("couldn't get memory heap!");
+ return ::testing::AssertionFailure();
+ }
+
+ // hidlMemory is not to be passed out of scope!
+ sp<HidlMemory> hidlMemory = fromHeap(heap);
+
+ SharedBuffer srcBuffer = {
+ .heapBase = *hidlMemory,
+ .offset = (uint64_t)offset + params.imemOffset,
+ .size = (uint64_t)params.imemSize,
+ };
+
+ DestinationBuffer dstBuffer;
+ dstBuffer.type = BufferType::SHARED_MEMORY;
+ dstBuffer.nonsecureMemory = srcBuffer;
+
+ uint32_t outBytes;
+ hidl_string detailedError;
+ auto returnVoid = descrambler->descramble(
+ ScramblingControl::EVENKEY /*2*/, hidlSubSamples, srcBuffer, params.srcOffset,
+ dstBuffer, params.dstOffset,
+ [&](Status status, uint32_t bytesWritten, const hidl_string& detailedErr) {
+ *descrambleStatus = status;
+ outBytes = bytesWritten;
+ detailedError = detailedErr;
+ });
+ if (!returnVoid.isOk() || *descrambleStatus != Status::OK) {
+ ALOGI("descramble failed, trans=%s, status=%d, outBytes=%u, error=%s",
+ returnVoid.description().c_str(), *descrambleStatus, outBytes, detailedError.c_str());
+ }
+ return ::testing::AssertionResult(returnVoid.isOk());
+}
+
+TEST_F(MediaCasHidlTest, TestClearKeyApisWithSession) {
+ description("Test that valid call sequences with SessionEvent send and receive");
+
+ ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+ auto returnStatus = mMediaCas->provision(hidl_string(PROVISION_STR));
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ hidl_vec<uint8_t> hidlPvtData;
+ hidlPvtData.resize(256);
+ returnStatus = mMediaCas->setPrivateData(hidlPvtData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ std::vector<uint8_t> sessionId;
+ ASSERT_TRUE(openCasSession(&sessionId));
+ returnStatus = mMediaCas->setSessionPrivateData(sessionId, hidlPvtData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ std::vector<uint8_t> streamSessionId;
+ ASSERT_TRUE(openCasSession(&streamSessionId));
+ returnStatus = mMediaCas->setSessionPrivateData(streamSessionId, hidlPvtData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ returnStatus = mDescramblerBase->setMediaCasSession(streamSessionId);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ hidl_vec<uint8_t> hidlNullPtr;
+ hidlNullPtr.setToExternal(static_cast<uint8_t*>(nullptr), 0);
+ returnStatus = mMediaCas->refreshEntitlements(3, hidlNullPtr);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ uint8_t refreshData[] = {0, 1, 2, 3};
+ hidl_vec<uint8_t> hidlRefreshData;
+ hidlRefreshData.setToExternal(static_cast<uint8_t*>(refreshData), sizeof(refreshData));
+ returnStatus = mMediaCas->refreshEntitlements(10, hidlRefreshData);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ int32_t eventID = 1;
+ int32_t eventArg = 2;
+ mCasListener->testEventEcho(mMediaCas, eventID, eventArg, hidlNullPtr);
+ mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, hidlNullPtr);
+
+ eventID = 3;
+ eventArg = 4;
+ uint8_t eventData[] = {'e', 'v', 'e', 'n', 't', 'd', 'a', 't', 'a'};
+ hidl_vec<uint8_t> hidlEventData;
+ hidlEventData.setToExternal(static_cast<uint8_t*>(eventData), sizeof(eventData));
+ mCasListener->testEventEcho(mMediaCas, eventID, eventArg, hidlEventData);
+ mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, hidlEventData);
+
+ SessionIntent intent = SessionIntent::LIVE;
+ ScramblingMode mode = ScramblingMode::DVB_CSA1;
+ mCasListener->testStatusUpdate(mMediaCas, &sessionId, intent, mode);
+
+ uint8_t clearKeyEmmData[] = {'c', 'l', 'e', 'a', 'r', 'k', 'e', 'y', 'e', 'm', 'm'};
+ hidl_vec<uint8_t> hidlClearKeyEmm;
+ hidlClearKeyEmm.setToExternal(static_cast<uint8_t*>(clearKeyEmmData), sizeof(clearKeyEmmData));
+ returnStatus = mMediaCas->processEmm(hidlClearKeyEmm);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ hidl_vec<uint8_t> hidlEcm;
+ hidlEcm.setToExternal(const_cast<uint8_t*>(kEcmBinaryBuffer), sizeof(kEcmBinaryBuffer));
+ returnStatus = mMediaCas->processEcm(sessionId, hidlEcm);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+ returnStatus = mMediaCas->processEcm(streamSessionId, hidlEcm);
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("video/avc"));
+
+ sp<IDescrambler> descrambler;
+ descrambler = IDescrambler::castFrom(mDescramblerBase);
+ ASSERT_NE(descrambler, nullptr);
+
+ Status descrambleStatus = Status::OK;
+ sp<IMemory> dataMemory;
+
+ ASSERT_TRUE(descrambleTestInputBuffer(descrambler, &descrambleStatus, &dataMemory));
+ EXPECT_EQ(Status::OK, descrambleStatus);
+
+ ASSERT_NE(nullptr, dataMemory.get());
+ uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->unsecurePointer()));
+
+ int compareResult =
+ memcmp(static_cast<const void*>(opBuffer),
+ static_cast<const void*>(kOutRefBinaryBuffer), sizeof(kOutRefBinaryBuffer));
+ EXPECT_EQ(0, compareResult);
+
+ returnStatus = mDescramblerBase->release();
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+
+ returnStatus = mMediaCas->release();
+ EXPECT_TRUE(returnStatus.isOk());
+ EXPECT_EQ(Status::OK, returnStatus);
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(CasHidlEnvironment::Instance());
+ ::testing::InitGoogleTest(&argc, argv);
+ CasHidlEnvironment::Instance()->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index e6b70d8..9d68264 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -465,6 +465,13 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.vibrator</name>
+ <interface>
+ <name>IVibrator</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1.0-4</version>
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/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp b/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp
index 3d138f7..5a75ae1 100644
--- a/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp
+++ b/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -1389,7 +1389,7 @@
}
static std::string approximateFloatString(float f) {
- if (static_cast<int32_t>(f) == f) {
+ if (static_cast<float>(static_cast<int32_t>(f)) == f) {
return std::to_string(static_cast<int32_t>(f));
}
int32_t truncated = static_cast<int32_t>(f * 10);
@@ -1680,10 +1680,10 @@
if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
mAttributes.at(HWC2::Attribute::DpiX) != -1) {
std::memset(buffer, 0, BUFFER_SIZE);
- writtenBytes = snprintf(buffer, BUFFER_SIZE,
- ", DPI: %.1f x %.1f",
- mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
- mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
+ writtenBytes =
+ snprintf(buffer, BUFFER_SIZE, ", DPI: %.1f x %.1f",
+ static_cast<float>(mAttributes.at(HWC2::Attribute::DpiX)) / 1000.0f,
+ static_cast<float>(mAttributes.at(HWC2::Attribute::DpiY)) / 1000.0f);
output.append(buffer, writtenBytes);
}
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/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/radio/1.5/Android.bp b/radio/1.5/Android.bp
index 06a2a6e..de9ec6e 100644
--- a/radio/1.5/Android.bp
+++ b/radio/1.5/Android.bp
@@ -19,7 +19,6 @@
"android.hardware.radio@1.3",
"android.hardware.radio@1.4",
"android.hidl.base@1.0",
- "android.hidl.safe_union@1.0",
],
gen_java: true,
}
diff --git a/radio/config/1.0/vts/functional/Android.bp b/radio/config/1.0/vts/functional/Android.bp
index 9c96030..859b24b 100644
--- a/radio/config/1.0/vts/functional/Android.bp
+++ b/radio/config/1.0/vts/functional/Android.bp
@@ -29,5 +29,5 @@
"android.hardware.radio.config@1.0",
],
header_libs: ["radio.util.header@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp b/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
index 2fc6b62..b3fae86 100644
--- a/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
+++ b/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
@@ -16,11 +16,7 @@
#include <radio_config_hidl_hal_utils.h>
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(RadioConfigHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- RadioConfigHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RadioConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp
index 6782314..4ff560f 100644
--- a/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_api.cpp
@@ -21,7 +21,7 @@
/*
* Test IRadioConfig.getSimSlotsStatus()
*/
-TEST_F(RadioConfigHidlTest, getSimSlotsStatus) {
+TEST_P(RadioConfigHidlTest, getSimSlotsStatus) {
const int serial = GetRandomSerialNumber();
Return<void> res = radioConfig->getSimSlotsStatus(serial);
ASSERT_OK(res);
@@ -38,7 +38,7 @@
/*
* Test IRadioConfig.setSimSlotsMapping()
*/
-TEST_F(RadioConfigHidlTest, setSimSlotsMapping) {
+TEST_P(RadioConfigHidlTest, setSimSlotsMapping) {
const int serial = GetRandomSerialNumber();
android::hardware::hidl_vec<uint32_t> mapping = {0};
Return<void> res = radioConfig->setSimSlotsMapping(serial, mapping);
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp
index c01dc4c..f589e2f 100644
--- a/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_test.cpp
@@ -17,14 +17,10 @@
#include <radio_config_hidl_hal_utils.h>
void RadioConfigHidlTest::SetUp() {
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
if (radioConfig == NULL) {
sleep(60);
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
}
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
index e7d697a..6bc1b65 100644
--- a/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.0/vts/functional/radio_config_hidl_hal_utils.h
@@ -16,8 +16,6 @@
#include <android-base/logging.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
@@ -26,6 +24,9 @@
#include <android/hardware/radio/config/1.0/IRadioConfigIndication.h>
#include <android/hardware/radio/config/1.0/IRadioConfigResponse.h>
#include <android/hardware/radio/config/1.0/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include "vts_test_util.h"
@@ -76,22 +77,8 @@
RadioIndicationType type, const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
};
-// Test environment for Radio HIDL HAL.
-class RadioConfigHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static RadioConfigHidlEnvironment* Instance() {
- static RadioConfigHidlEnvironment* instance = new RadioConfigHidlEnvironment;
- return instance;
- }
- virtual void registerTestServices() override { registerTestService<IRadioConfig>(); }
-
- private:
- RadioConfigHidlEnvironment() {}
-};
-
// The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
protected:
std::mutex mtx_;
std::condition_variable cv_;
diff --git a/radio/config/1.1/vts/functional/Android.bp b/radio/config/1.1/vts/functional/Android.bp
index de909a3..8cf7b62 100644
--- a/radio/config/1.1/vts/functional/Android.bp
+++ b/radio/config/1.1/vts/functional/Android.bp
@@ -29,5 +29,5 @@
"android.hardware.radio.config@1.1",
],
header_libs: ["radio.util.header@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp b/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
index 2fc6b62..b3fae86 100644
--- a/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
+++ b/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
@@ -16,11 +16,7 @@
#include <radio_config_hidl_hal_utils.h>
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(RadioConfigHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- RadioConfigHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RadioConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.1/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.1/vts/functional/radio_config_hidl_hal_api.cpp
index 5d0e867..49c7aad 100644
--- a/radio/config/1.1/vts/functional/radio_config_hidl_hal_api.cpp
+++ b/radio/config/1.1/vts/functional/radio_config_hidl_hal_api.cpp
@@ -21,7 +21,7 @@
/*
* Test IRadioConfig.getModemsConfig()
*/
-TEST_F(RadioConfigHidlTest, getModemsConfig) {
+TEST_P(RadioConfigHidlTest, getModemsConfig) {
serial = GetRandomSerialNumber();
Return<void> res = radioConfig->getModemsConfig(serial);
ASSERT_OK(res);
@@ -37,7 +37,7 @@
/*
* Test IRadioConfig.setModemsConfig()
*/
-TEST_F(RadioConfigHidlTest, setModemsConfig_invalidArgument) {
+TEST_P(RadioConfigHidlTest, setModemsConfig_invalidArgument) {
serial = GetRandomSerialNumber();
ModemsConfig* mConfig = new ModemsConfig();
Return<void> res = radioConfig->setModemsConfig(serial, *mConfig);
@@ -55,7 +55,7 @@
/*
* Test IRadioConfig.setModemsConfig()
*/
-TEST_F(RadioConfigHidlTest, setModemsConfig_goodRequest) {
+TEST_P(RadioConfigHidlTest, setModemsConfig_goodRequest) {
serial = GetRandomSerialNumber();
ModemsConfig* mConfig = new ModemsConfig();
mConfig->numOfLiveModems = 1;
@@ -73,7 +73,7 @@
/*
* Test IRadioConfig.getPhoneCapability()
*/
-TEST_F(RadioConfigHidlTest, getPhoneCapability) {
+TEST_P(RadioConfigHidlTest, getPhoneCapability) {
serial = GetRandomSerialNumber();
Return<void> res = radioConfig->getPhoneCapability(serial);
ASSERT_OK(res);
@@ -99,7 +99,7 @@
/*
* Test IRadioConfig.getPhoneCapability()
*/
-TEST_F(RadioConfigHidlTest, setPreferredDataModem) {
+TEST_P(RadioConfigHidlTest, setPreferredDataModem) {
serial = GetRandomSerialNumber();
Return<void> res = radioConfig->getPhoneCapability(serial);
ASSERT_OK(res);
@@ -141,7 +141,7 @@
/*
* Test IRadioConfig.getPhoneCapability()
*/
-TEST_F(RadioConfigHidlTest, setPreferredDataModem_invalidArgument) {
+TEST_P(RadioConfigHidlTest, setPreferredDataModem_invalidArgument) {
serial = GetRandomSerialNumber();
uint8_t modemId = -1;
Return<void> res = radioConfig->setPreferredDataModem(serial, modemId);
diff --git a/radio/config/1.1/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.1/vts/functional/radio_config_hidl_hal_test.cpp
index 39e6487..2e5e424 100644
--- a/radio/config/1.1/vts/functional/radio_config_hidl_hal_test.cpp
+++ b/radio/config/1.1/vts/functional/radio_config_hidl_hal_test.cpp
@@ -17,14 +17,10 @@
#include <radio_config_hidl_hal_utils.h>
void RadioConfigHidlTest::SetUp() {
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
if (radioConfig == NULL) {
sleep(60);
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
}
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
index c980901..e9951dc 100644
--- a/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.1/vts/functional/radio_config_hidl_hal_utils.h
@@ -16,8 +16,6 @@
#include <android-base/logging.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
@@ -25,6 +23,9 @@
#include <android/hardware/radio/config/1.1/IRadioConfig.h>
#include <android/hardware/radio/config/1.1/IRadioConfigResponse.h>
#include <android/hardware/radio/config/1.1/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include "vts_test_util.h"
@@ -73,22 +74,8 @@
Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
};
-// Test environment for Radio HIDL HAL.
-class RadioConfigHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static RadioConfigHidlEnvironment* Instance() {
- static RadioConfigHidlEnvironment* instance = new RadioConfigHidlEnvironment;
- return instance;
- }
- virtual void registerTestServices() override { registerTestService<IRadioConfig>(); }
-
- private:
- RadioConfigHidlEnvironment() {}
-};
-
// The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
protected:
std::mutex mtx_;
std::condition_variable cv_;
diff --git a/radio/config/1.2/vts/functional/Android.bp b/radio/config/1.2/vts/functional/Android.bp
index 0cafc24..2c2073a 100644
--- a/radio/config/1.2/vts/functional/Android.bp
+++ b/radio/config/1.2/vts/functional/Android.bp
@@ -31,5 +31,5 @@
"android.hardware.radio.config@1.2",
],
header_libs: ["radio.util.header@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp b/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
index ec6544e..f09ac3a 100644
--- a/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
+++ b/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
@@ -16,11 +16,7 @@
#include <radio_config_hidl_hal_utils.h>
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(RadioConfigHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- RadioConfigHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RadioConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.2/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.2/vts/functional/radio_config_hidl_hal_api.cpp
index a3729ac..2129ecd 100644
--- a/radio/config/1.2/vts/functional/radio_config_hidl_hal_api.cpp
+++ b/radio/config/1.2/vts/functional/radio_config_hidl_hal_api.cpp
@@ -21,7 +21,7 @@
/*
* Test IRadioConfig.getSimSlotsStatus()
*/
-TEST_F(RadioConfigHidlTest, getSimSlotsStatus) {
+TEST_P(RadioConfigHidlTest, getSimSlotsStatus) {
const int serial = GetRandomSerialNumber();
Return<void> res = radioConfig->getSimSlotsStatus(serial);
ASSERT_OK(res);
diff --git a/radio/config/1.2/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.2/vts/functional/radio_config_hidl_hal_test.cpp
index cd7a172..fd344b0 100644
--- a/radio/config/1.2/vts/functional/radio_config_hidl_hal_test.cpp
+++ b/radio/config/1.2/vts/functional/radio_config_hidl_hal_test.cpp
@@ -17,14 +17,10 @@
#include <radio_config_hidl_hal_utils.h>
void RadioConfigHidlTest::SetUp() {
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
if (radioConfig == NULL) {
sleep(60);
- radioConfig = ::testing::VtsHalHidlTargetTestBase::getService<IRadioConfig>(
- RadioConfigHidlEnvironment::Instance()->getServiceName<IRadioConfig>(
- hidl_string(RADIO_SERVICE_NAME)));
+ radioConfig = IRadioConfig::getService(GetParam());
}
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
index a876766..e9cbcbd 100644
--- a/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.2/vts/functional/radio_config_hidl_hal_utils.h
@@ -16,8 +16,6 @@
#include <android-base/logging.h>
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
@@ -27,6 +25,9 @@
#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
#include <android/hardware/radio/config/1.2/IRadioConfigResponse.h>
#include <android/hardware/radio/config/1.2/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include "vts_test_util.h"
@@ -112,7 +113,7 @@
};
// The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
protected:
std::mutex mtx_;
std::condition_variable cv_;
diff --git a/sensors/1.0/default/convert.cpp b/sensors/1.0/default/convert.cpp
index 52f5e4f..53ceb0d 100644
--- a/sensors/1.0/default/convert.cpp
+++ b/sensors/1.0/default/convert.cpp
@@ -68,9 +68,9 @@
typedef ::android::hardware::sensors::V1_0::MetaDataEventType MetaDataEventType;
*dst = {
- .sensorHandle = src.sensor,
- .sensorType = (SensorType)src.type,
- .timestamp = src.timestamp
+ .timestamp = src.timestamp,
+ .sensorHandle = src.sensor,
+ .sensorType = (SensorType)src.type,
};
switch (dst->sensorType) {
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..03ff605
--- /dev/null
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -0,0 +1,690 @@
+/*
+ * 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>>();
+ mSizePendingWriteEventsQueue = 0;
+
+ // 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 << " # of events on pending write writes queue: " << mSizePendingWriteEventsQueue
+ << 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);
+ mSizePendingWriteEventsQueue -= eventQueueSize;
+ } else {
+ mPendingWriteEventsQueue.pop();
+ mSizePendingWriteEventsQueue -= pendingWriteEvents.size();
+ }
+ }
+ }
+}
+
+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;
+ }
+ }
+ }
+ size_t numLeft = events.size() - numToWrite;
+ if (numToWrite < events.size() &&
+ mSizePendingWriteEventsQueue + numLeft <= kMaxSizePendingWriteEventsQueue) {
+ std::vector<Event> eventsLeft(events.begin() + numToWrite, events.end());
+ mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents});
+ mSizePendingWriteEventsQueue += numLeft;
+ 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..ce28e67
--- /dev/null
+++ b/sensors/2.0/multihal/include/HalProxy.h
@@ -0,0 +1,402 @@
+/*
+ * 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 max number of events allowed in the pending write events queue
+ static constexpr size_t kMaxSizePendingWriteEventsQueue = 100000;
+
+ //! The number of events in the pending write events queue
+ size_t mSizePendingWriteEventsQueue = 0;
+
+ //! 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/sensors/common/vts/utils/GrallocWrapper.cpp b/sensors/common/vts/utils/GrallocWrapper.cpp
index 1cad913..e63faa2 100644
--- a/sensors/common/vts/utils/GrallocWrapper.cpp
+++ b/sensors/common/vts/utils/GrallocWrapper.cpp
@@ -147,8 +147,8 @@
.width = size,
.height = 1,
.layerCount = 1,
- .usage = kBufferUsage,
.format = static_cast<decltype(descriptorInfo.format)>(PixelFormat::BLOB),
+ .usage = kBufferUsage,
};
BufferDescriptor descriptor;
diff --git a/soundtrigger/2.0/Android.bp b/soundtrigger/2.0/Android.bp
index 5613abd..07c05bc 100644
--- a/soundtrigger/2.0/Android.bp
+++ b/soundtrigger/2.0/Android.bp
@@ -15,5 +15,5 @@
"android.hardware.audio.common@2.0",
"android.hidl.base@1.0",
],
- gen_java: false,
+ gen_java: true,
}
diff --git a/tests/libhwbinder/1.0/default/Android.bp b/tests/libhwbinder/1.0/default/Android.bp
index 81022b8..3bf08ed 100644
--- a/tests/libhwbinder/1.0/default/Android.bp
+++ b/tests/libhwbinder/1.0/default/Android.bp
@@ -1,5 +1,5 @@
cc_library {
- name: "android.hardware.tests.libhwbinder@1.0-impl",
+ name: "android.hardware.tests.libhwbinder@1.0-impl.test",
defaults: ["hidl_defaults"],
relative_install_path: "hw",
srcs: [
diff --git a/tests/trie/1.0/Android.bp b/tests/trie/1.0/Android.bp
index 5a33aea..3cb67c7 100644
--- a/tests/trie/1.0/Android.bp
+++ b/tests/trie/1.0/Android.bp
@@ -10,5 +10,5 @@
interfaces: [
"android.hidl.base@1.0",
],
- gen_java: false,
+ gen_java: true,
}
diff --git a/tetheroffload/config/1.0/vts/functional/Android.bp b/tetheroffload/config/1.0/vts/functional/Android.bp
index 52b9810..7b472e3 100644
--- a/tetheroffload/config/1.0/vts/functional/Android.bp
+++ b/tetheroffload/config/1.0/vts/functional/Android.bp
@@ -17,5 +17,5 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalTetheroffloadConfigV1_0TargetTest.cpp"],
static_libs: ["android.hardware.tetheroffload.config@1.0"],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
index 34a95f2..02fe96f 100644
--- a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
+++ b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "VtsOffloadConfigV1_0TargetTest"
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
@@ -78,25 +79,10 @@
return netlinkSocket(NETLINK_NETFILTER, groups);
}
-// Test environment for OffloadConfig HIDL HAL.
-class OffloadConfigHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- public:
- // get the test environment singleton
- static OffloadConfigHidlEnvironment* Instance() {
- static OffloadConfigHidlEnvironment* instance = new OffloadConfigHidlEnvironment;
- return instance;
- }
-
- virtual void registerTestServices() override { registerTestService<IOffloadConfig>(); }
- private:
- OffloadConfigHidlEnvironment() {}
-};
-
-class OffloadConfigHidlTest : public testing::VtsHalHidlTargetTestBase {
+class OffloadConfigHidlTest : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
- config = testing::VtsHalHidlTargetTestBase::getService<IOffloadConfig>(
- OffloadConfigHidlEnvironment::Instance()->getServiceName<IOffloadConfig>());
+ config = IOffloadConfig::getService(GetParam());
ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance";
}
@@ -106,7 +92,7 @@
};
// Ensure handles can be set with correct socket options.
-TEST_F(OffloadConfigHidlTest, TestSetHandles) {
+TEST_P(OffloadConfigHidlTest, TestSetHandles) {
// Try multiple times in a row to see if it provokes file descriptor leaks.
for (int i = 0; i < 1024; i++) {
unique_fd fd1(netlinkSocket(kFd1Groups));
@@ -136,7 +122,7 @@
// Passing a handle without an associated file descriptor should return an error
// (e.g. "Failed Input Checks"). Check that this occurs when both FDs are empty.
-TEST_F(OffloadConfigHidlTest, TestSetHandleNone) {
+TEST_P(OffloadConfigHidlTest, TestSetHandleNone) {
native_handle_t* const nativeHandle1 = native_handle_create(0, 0);
hidl_handle h1;
h1.setTo(nativeHandle1, true);
@@ -150,7 +136,7 @@
// Passing a handle without an associated file descriptor should return an error
// (e.g. "Failed Input Checks"). Check that this occurs when FD2 is empty.
-TEST_F(OffloadConfigHidlTest, TestSetHandle1Only) {
+TEST_P(OffloadConfigHidlTest, TestSetHandle1Only) {
unique_fd fd1(netlinkSocket(kFd1Groups));
if (fd1.get() < 0) {
ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
@@ -171,7 +157,7 @@
// Passing a handle without an associated file descriptor should return an error
// (e.g. "Failed Input Checks"). Check that this occurs when FD1 is empty.
-TEST_F(OffloadConfigHidlTest, TestSetHandle2OnlyNotOk) {
+TEST_P(OffloadConfigHidlTest, TestSetHandle2OnlyNotOk) {
native_handle_t* const nativeHandle1 = native_handle_create(0, 0);
hidl_handle h1;
h1.setTo(nativeHandle1, true);
@@ -190,11 +176,7 @@
ASSERT_TRUE(ret.isOk());
}
-int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(OffloadConfigHidlEnvironment::Instance());
- ::testing::InitGoogleTest(&argc, argv);
- OffloadConfigHidlEnvironment::Instance()->init(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGE("Test result with status=%d", status);
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, OffloadConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/tv/input/1.0/Android.bp b/tv/input/1.0/Android.bp
index 7288558..1164430 100644
--- a/tv/input/1.0/Android.bp
+++ b/tv/input/1.0/Android.bp
@@ -15,6 +15,6 @@
"android.hardware.audio.common@2.0",
"android.hidl.base@1.0",
],
- gen_java: false,
+ gen_java: true,
gen_java_constants: true,
}
diff --git a/vibrator/aidl/Android.bp b/vibrator/aidl/Android.bp
new file mode 100644
index 0000000..18468ef
--- /dev/null
+++ b/vibrator/aidl/Android.bp
@@ -0,0 +1,14 @@
+aidl_interface {
+ name: "vintf-vibrator",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/vibrator/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+}
+
diff --git a/vibrator/aidl/android/hardware/vibrator/Effect.aidl b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
new file mode 100644
index 0000000..c60bfe9
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+@Backing(type="int")
+enum Effect {
+ /**
+ * A single click effect.
+ *
+ * This effect should produce a sharp, crisp click sensation.
+ */
+ CLICK,
+ /**
+ * A double click effect.
+ *
+ * This effect should produce two sequential sharp, crisp click sensations with a minimal
+ * amount of time between them.
+ */
+ DOUBLE_CLICK,
+ /**
+ * A tick effect.
+ *
+ * This effect should produce a soft, short sensation, like the tick of a clock.
+ */
+ TICK,
+ /**
+ * A thud effect.
+ *
+ * This effect should solid feeling bump, like the depression of a heavy mechanical button.
+ */
+ THUD,
+ /**
+ * A pop effect.
+ *
+ * A short, quick burst effect.
+ */
+ POP,
+ /**
+ * A heavy click effect.
+ *
+ * This should produce a sharp striking sensation, like a click but stronger.
+ */
+ HEAVY_CLICK,
+ /**
+ * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
+ * pattern that can be played as a ringtone with any audio, depending on the device.
+ */
+ RINGTONE_1,
+ RINGTONE_2,
+ RINGTONE_3,
+ RINGTONE_4,
+ RINGTONE_5,
+ RINGTONE_6,
+ RINGTONE_7,
+ RINGTONE_8,
+ RINGTONE_9,
+ RINGTONE_10,
+ RINGTONE_11,
+ RINGTONE_12,
+ RINGTONE_13,
+ RINGTONE_14,
+ RINGTONE_15,
+ /**
+ * A soft tick effect meant to be played as a texture.
+ *
+ * A soft, short sensation like the tick of a clock. Unlike regular effects, texture effects
+ * are expected to be played multiple times in quick succession, replicating a specific
+ * texture to the user as a form of haptic feedback.
+ */
+ TEXTURE_TICK,
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
new file mode 100644
index 0000000..66f70e5
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+@Backing(type="byte")
+enum EffectStrength {
+ LIGHT,
+ MEDIUM,
+ STRONG,
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
new file mode 100644
index 0000000..ceaa0a0
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.IVibratorCallback;
+import android.hardware.vibrator.Effect;
+import android.hardware.vibrator.EffectStrength;
+
+@VintfStability
+interface IVibrator {
+ /**
+ * Whether on w/ IVibratorCallback can be used w/ 'on' function
+ */
+ const int CAP_ON_CALLBACK = 1 << 0;
+ /**
+ * Whether on w/ IVibratorCallback can be used w/ 'perform' function
+ */
+ const int CAP_PERFORM_CALLBACK = 1 << 1;
+ /**
+ * Whether setAmplitude is supported (when external control is disabled)
+ */
+ const int CAP_AMPLITUDE_CONTROL = 1 << 2;
+ /**
+ * Whether setExternalControl is supported.
+ */
+ const int CAP_EXTERNAL_CONTROL = 1 << 3;
+ /**
+ * Whether setAmplitude is supported (when external control is enabled)
+ */
+ const int CAP_EXTERNAL_AMPLITUDE_CONTROL = 1 << 4;
+
+ /**
+ * Determine capabilities of the vibrator HAL (CAP_* mask)
+ */
+ int getCapabilities();
+
+ /**
+ * Turn off vibrator
+ *
+ * Cancel a previously-started vibration, if any.
+ */
+ void off();
+
+ /**
+ * Turn on vibrator
+ *
+ * This function must only be called after the previous timeout has expired or
+ * was canceled (through off()). A callback is only expected to be supported when
+ * getCapabilities CAP_ON_CALLBACK is specified.
+ *
+ * @param timeoutMs number of milliseconds to vibrate.
+ * @param callback A callback used to inform Frameworks of state change, if supported.
+ */
+ void on(in int timeoutMs, in IVibratorCallback callback);
+
+ /**
+ * Fire off a predefined haptic event.
+ *
+ * A callback is only expected to be supported when getCapabilities CAP_PERFORM_CALLBACK
+ * is specified.
+ *
+ * @param effect The type of haptic event to trigger.
+ * @param strength The intensity of haptic event to trigger.
+ * @param callback A callback used to inform Frameworks of state change, if supported.
+ * @return The length of time the event is expected to take in
+ * milliseconds. This doesn't need to be perfectly accurate, but should be a reasonable
+ * approximation.
+ */
+ int perform(in Effect effect, in EffectStrength strength, in IVibratorCallback callback);
+
+ /**
+ * List supported effects.
+ *
+ * Return the effects which are supported (an effect is expected to be supported at every
+ * strength level.
+ */
+ Effect[] getSupportedEffects();
+
+ /**
+ * Sets the motor's vibrational amplitude.
+ *
+ * Changes the force being produced by the underlying motor. This may not be supported and
+ * this support is reflected in getCapabilities (CAP_AMPLITUDE_CONTROL). When this device
+ * is under external control (via setExternalControl), amplitude control may not be supported
+ * even though it is supported normally. This can be checked with
+ * CAP_EXTERNAL_AMPLITUDE_CONTROL.
+ *
+ * @param amplitude The unitless force setting. Note that this number must
+ * be between 1 and 255, inclusive. If the motor does not
+ * have exactly 255 steps, it must do it's best to map it
+ * onto the number of steps it does have.
+ */
+ void setAmplitude(in int amplitude);
+
+ /**
+ * Enables/disables control override of vibrator to audio.
+ *
+ * Support is reflected in getCapabilities (CAP_EXTERNAL_CONTROL).
+ *
+ * When this API is set, the vibrator control should be ceded to audio system
+ * for haptic audio. While this is enabled, issuing of other commands to control
+ * the vibrator is unsupported and the resulting behavior is undefined. Amplitude
+ * control may or may not be supported and is reflected in the return value of
+ * getCapabilities (CAP_EXTERNAL_AMPLITUDE_CONTROL) while this is enabled. When this is
+ * disabled, the vibrator should resume to an off state.
+ *
+ * @param enabled Whether external control should be enabled or disabled.
+ */
+ void setExternalControl(in boolean enabled);
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibratorCallback.aidl b/vibrator/aidl/android/hardware/vibrator/IVibratorCallback.aidl
new file mode 100644
index 0000000..43859de
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/IVibratorCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+interface IVibratorCallback {
+ oneway void onComplete();
+}
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
new file mode 100644
index 0000000..f399887
--- /dev/null
+++ b/vibrator/aidl/default/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+ name: "android.hardware.vibrator-service.example",
+ relative_install_path: "hw",
+ init_rc: ["vibrator-default.rc"],
+ vintf_fragments: ["vibrator-default.xml"],
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "vintf-vibrator-ndk_platform",
+ ],
+ srcs: ["main.cpp", "Vibrator.cpp"],
+}
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
new file mode 100644
index 0000000..18be1a6
--- /dev/null
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 "Vibrator.h"
+
+#include <android-base/logging.h>
+#include <thread>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
+ LOG(INFO) << "Vibrator reporting capabilities";
+ *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
+ IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
+ IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::off() {
+ LOG(INFO) << "Vibrator off";
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
+ const std::shared_ptr<IVibratorCallback>& callback) {
+ LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs;
+ if (callback != nullptr) {
+ std::thread([=] {
+ LOG(INFO) << "Starting on on another thread";
+ usleep(timeoutMs * 1000);
+ LOG(INFO) << "Notifying on complete";
+ callback->onComplete();
+ }).detach();
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback>& callback,
+ int32_t* _aidl_return) {
+ LOG(INFO) << "Vibrator perform";
+
+ if (effect != Effect::CLICK && effect != Effect::TICK) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+ }
+ if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
+ strength != EffectStrength::STRONG) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+ }
+
+ constexpr size_t kEffectMillis = 100;
+
+ if (callback != nullptr) {
+ std::thread([=] {
+ LOG(INFO) << "Starting perform on another thread";
+ usleep(kEffectMillis * 1000);
+ LOG(INFO) << "Notifying perform complete";
+ callback->onComplete();
+ }).detach();
+ }
+
+ *_aidl_return = kEffectMillis;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
+ *_aidl_return = {Effect::CLICK, Effect::TICK};
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setAmplitude(int32_t amplitude) {
+ LOG(INFO) << "Vibrator set amplitude: " << amplitude;
+ if (amplitude <= 0 || amplitude > 255) {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
+ LOG(INFO) << "Vibrator set external control: " << enabled;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/aidl/default/Vibrator.h b/vibrator/aidl/default/Vibrator.h
new file mode 100644
index 0000000..14e7292
--- /dev/null
+++ b/vibrator/aidl/default/Vibrator.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <aidl/android/hardware/vibrator/BnVibrator.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class Vibrator : public BnVibrator {
+ ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
+ ndk::ScopedAStatus off() override;
+ ndk::ScopedAStatus on(int32_t timeoutMs,
+ const std::shared_ptr<IVibratorCallback>& callback) override;
+ ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback>& callback,
+ int32_t* _aidl_return) override;
+ ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override;
+ ndk::ScopedAStatus setAmplitude(int32_t amplitude) override;
+ ndk::ScopedAStatus setExternalControl(bool enabled) override;
+};
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/aidl/default/main.cpp b/vibrator/aidl/default/main.cpp
new file mode 100644
index 0000000..d1619ff
--- /dev/null
+++ b/vibrator/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "Vibrator.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::vibrator::Vibrator;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Vibrator> vib = ndk::SharedRefBase::make<Vibrator>();
+
+ const std::string instance = std::string() + Vibrator::descriptor + "/default";
+ binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/vibrator/aidl/default/vibrator-default.rc b/vibrator/aidl/default/vibrator-default.rc
new file mode 100644
index 0000000..d17f468
--- /dev/null
+++ b/vibrator/aidl/default/vibrator-default.rc
@@ -0,0 +1,4 @@
+service vendor.vibrator-default /vendor/bin/hw/android.hardware.vibrator-service.example
+ class hal
+ user system
+ group system
diff --git a/vibrator/aidl/default/vibrator-default.xml b/vibrator/aidl/default/vibrator-default.xml
new file mode 100644
index 0000000..49b11ec
--- /dev/null
+++ b/vibrator/aidl/default/vibrator-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.vibrator</name>
+ <fqname>IVibrator/default</fqname>
+ </hal>
+</manifest>
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
new file mode 100644
index 0000000..20d53c7
--- /dev/null
+++ b/vibrator/aidl/vts/Android.bp
@@ -0,0 +1,17 @@
+cc_test {
+ name: "VtsHalVibratorTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalVibratorTargetTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ ],
+ static_libs: [
+ "vintf-vibrator-cpp",
+ ],
+ test_suites: [
+ "vts-core",
+ ],
+}
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
new file mode 100644
index 0000000..aeb9b70
--- /dev/null
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <future>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::vibrator::BnVibratorCallback;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+// TODO(b/143992652): autogenerate
+const std::vector<Effect> kEffects = {
+ Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
+ Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_1, Effect::RINGTONE_2,
+ Effect::RINGTONE_3, Effect::RINGTONE_4, Effect::RINGTONE_5, Effect::RINGTONE_6,
+ Effect::RINGTONE_7, Effect::RINGTONE_8, Effect::RINGTONE_9, Effect::RINGTONE_10,
+ Effect::RINGTONE_11, Effect::RINGTONE_12, Effect::RINGTONE_13, Effect::RINGTONE_14,
+ Effect::RINGTONE_15, Effect::TEXTURE_TICK};
+
+// TODO(b/143992652): autogenerate
+const std::vector<EffectStrength> kEffectStrengths = {EffectStrength::LIGHT, EffectStrength::MEDIUM,
+ EffectStrength::STRONG};
+
+const std::vector<Effect> kInvalidEffects = {
+ static_cast<Effect>(static_cast<int32_t>(*kEffects.begin()) - 1),
+ static_cast<Effect>(static_cast<int32_t>(*kEffects.end()) + 1),
+};
+
+const std::vector<EffectStrength> kInvalidEffectStrengths = {
+ static_cast<EffectStrength>(static_cast<int8_t>(*kEffectStrengths.begin()) - 1),
+ static_cast<EffectStrength>(static_cast<int8_t>(*kEffectStrengths.end()) + 1),
+};
+
+class CompletionCallback : public BnVibratorCallback {
+ public:
+ CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
+ Status onComplete() override {
+ mCallback();
+ return Status::ok();
+ }
+
+ private:
+ std::function<void()> mCallback;
+};
+
+class VibratorAidl : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ vibrator = android::waitForDeclaredService<IVibrator>(String16(GetParam().c_str()));
+ ASSERT_NE(vibrator, nullptr);
+ ASSERT_TRUE(vibrator->getCapabilities(&capabilities).isOk());
+ }
+
+ sp<IVibrator> vibrator;
+ int32_t capabilities;
+};
+
+TEST_P(VibratorAidl, OnThenOffBeforeTimeout) {
+ EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
+ sleep(1);
+ EXPECT_TRUE(vibrator->off().isOk());
+}
+
+TEST_P(VibratorAidl, OnWithCallback) {
+ if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+
+ std::promise<void> completionPromise;
+ std::future<void> completionFuture{completionPromise.get_future()};
+ sp<CompletionCallback> callback =
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ uint32_t durationMs = 250;
+ std::chrono::milliseconds timeout{durationMs * 2};
+ EXPECT_TRUE(vibrator->on(durationMs, callback).isOk());
+ EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+ EXPECT_TRUE(vibrator->off().isOk());
+}
+
+TEST_P(VibratorAidl, OnCallbackNotSupported) {
+ if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) {
+ sp<CompletionCallback> callback = new CompletionCallback([] {});
+ EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, vibrator->on(250, callback).exceptionCode());
+ }
+}
+
+TEST_P(VibratorAidl, ValidateEffect) {
+ std::vector<Effect> supported;
+ ASSERT_TRUE(vibrator->getSupportedEffects(&supported).isOk());
+
+ for (Effect effect : kEffects) {
+ bool isEffectSupported =
+ std::find(supported.begin(), supported.end(), effect) != supported.end();
+
+ for (EffectStrength strength : kEffectStrengths) {
+ int32_t lengthMs = 0;
+ Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
+
+ if (isEffectSupported) {
+ EXPECT_TRUE(status.isOk());
+ EXPECT_GT(lengthMs, 0);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ EXPECT_EQ(lengthMs, 0);
+ }
+ }
+ }
+}
+
+TEST_P(VibratorAidl, ValidateEffectWithCallback) {
+ if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+
+ std::vector<Effect> supported;
+ ASSERT_TRUE(vibrator->getSupportedEffects(&supported).isOk());
+
+ for (Effect effect : kEffects) {
+ bool isEffectSupported =
+ std::find(supported.begin(), supported.end(), effect) != supported.end();
+
+ for (EffectStrength strength : kEffectStrengths) {
+ std::promise<void> completionPromise;
+ std::future<void> completionFuture{completionPromise.get_future()};
+ sp<CompletionCallback> callback =
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ int lengthMs = 0;
+ Status status = vibrator->perform(effect, strength, callback, &lengthMs);
+
+ if (isEffectSupported) {
+ EXPECT_TRUE(status.isOk());
+ EXPECT_GT(lengthMs, 0);
+ } else {
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ EXPECT_EQ(lengthMs, 0);
+ }
+
+ if (!status.isOk()) continue;
+
+ std::chrono::milliseconds timeout{lengthMs * 2};
+ EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+ }
+ }
+}
+
+TEST_P(VibratorAidl, ValidateEffectWithCallbackNotSupported) {
+ if (capabilities & IVibrator::CAP_PERFORM_CALLBACK) return;
+
+ for (Effect effect : kEffects) {
+ for (EffectStrength strength : kEffectStrengths) {
+ sp<CompletionCallback> callback = new CompletionCallback([] {});
+ int lengthMs;
+ Status status = vibrator->perform(effect, strength, callback, &lengthMs);
+ EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode());
+ EXPECT_EQ(lengthMs, 0);
+ }
+ }
+}
+
+TEST_P(VibratorAidl, InvalidEffectsUnsupported) {
+ for (Effect effect : kInvalidEffects) {
+ for (EffectStrength strength : kInvalidEffectStrengths) {
+ int32_t lengthMs;
+ Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
+ EXPECT_EQ(status.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+ }
+}
+
+TEST_P(VibratorAidl, ChangeVibrationAmplitude) {
+ if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
+ EXPECT_TRUE(vibrator->setAmplitude(1).isOk());
+ EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
+ EXPECT_TRUE(vibrator->setAmplitude(128).isOk());
+ sleep(1);
+ EXPECT_TRUE(vibrator->setAmplitude(255).isOk());
+ sleep(1);
+ }
+}
+
+TEST_P(VibratorAidl, AmplitudeOutsideRangeFails) {
+ if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(-1).exceptionCode());
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(0).exceptionCode());
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(256).exceptionCode());
+ }
+}
+
+TEST_P(VibratorAidl, AmplitudeReturnsUnsupportedMatchingCapabilities) {
+ if ((capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) == 0) {
+ EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, vibrator->setAmplitude(1).exceptionCode());
+ }
+}
+
+TEST_P(VibratorAidl, ChangeVibrationExternalControl) {
+ if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
+ EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
+ sleep(1);
+ EXPECT_TRUE(vibrator->setExternalControl(false).isOk());
+ sleep(1);
+ }
+}
+
+TEST_P(VibratorAidl, ExternalAmplitudeControl) {
+ const bool supportsExternalAmplitudeControl =
+ (capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0;
+
+ if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
+ EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
+
+ Status amplitudeStatus = vibrator->setAmplitude(128);
+ if (supportsExternalAmplitudeControl) {
+ EXPECT_TRUE(amplitudeStatus.isOk());
+ } else {
+ EXPECT_EQ(amplitudeStatus.exceptionCode(), Status::EX_UNSUPPORTED_OPERATION);
+ }
+ EXPECT_TRUE(vibrator->setExternalControl(false).isOk());
+ } else {
+ EXPECT_FALSE(supportsExternalAmplitudeControl);
+ }
+}
+
+TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) {
+ if ((capabilities & IVibrator::CAP_EXTERNAL_CONTROL) == 0) {
+ EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+ vibrator->setExternalControl(true).exceptionCode());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(, VibratorAidl,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
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);
+