Merge "Adding v2 of audiocontrol hal"
diff --git a/biometrics/face/1.1/Android.bp b/biometrics/face/1.1/Android.bp
new file mode 100644
index 0000000..2206597
--- /dev/null
+++ b/biometrics/face/1.1/Android.bp
@@ -0,0 +1,17 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.biometrics.face@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "IBiometricsFace.hal",
+ ],
+ interfaces: [
+ "android.hardware.biometrics.face@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal
new file mode 100644
index 0000000..975001f
--- /dev/null
+++ b/biometrics/face/1.1/IBiometricsFace.hal
@@ -0,0 +1,82 @@
+/*
+ * 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.biometrics.face@1.1;
+import @1.0::IBiometricsFace;
+import @1.0::Status;
+import @1.0::Feature;
+
+/**
+ * The HAL interface for biometric face authentication.
+ */
+interface IBiometricsFace extends @1.0::IBiometricsFace {
+ /**
+ * Enrolls a user's face for a remote client, for example Android Auto.
+ *
+ * The HAL implementation is responsible for creating a secure communication
+ * channel and receiving the enrollment images from a mobile device with
+ * face authentication hardware.
+ *
+ * Note that the Hardware Authentication Token must be valid for the
+ * duration of enrollment and thus should be explicitly invalidated by a
+ * call to revokeChallenge() when enrollment is complete, to reduce the
+ * window of opportunity to re-use the challenge and HAT. For example,
+ * Settings calls generateChallenge() once to allow the user to enroll one
+ * or more faces or toggle secure settings without having to re-enter the
+ * PIN/pattern/password. Once the user completes the operation, Settings
+ * invokes revokeChallenge() to close the transaction. If the HAT is expired,
+ * the implementation must invoke onError with UNABLE_TO_PROCESS.
+ *
+ * Requirements for using this API:
+ * - Mobile devices MUST NOT delegate enrollment to another device by calling
+ * this API. This feature is intended only to allow enrollment on devices
+ * where it is impossible to enroll locally on the device.
+ * - The path MUST be protected by a secret key with rollback protection.
+ * - Synchronizing between devices MUST be accomplished by having both
+ * devices agree on a secret PIN entered by the user (similar to BT
+ * pairing procedure) and use a salted version of that PIN plus other secret
+ * to encrypt traffic.
+ * - All communication to/from the remote device MUST be encrypted and signed
+ * to prevent image injection and other man-in-the-middle type attacks.
+ * - generateChallenge() and revokeChallenge() MUST be implemented on both
+ * remote and local host (e.g. hash the result of the remote host with a
+ * local secret before responding to the API call) and any transmission of
+ * the challenge between hosts MUST be signed to prevent man-in-the-middle
+ * attacks.
+ * - In the event of a lost connection, the result of the last
+ * generateChallenge() MUST be invalidated and the process started over.
+ * - Both the remote and local host MUST honor the timeout and invalidate the
+ * challenge.
+ *
+ * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
+ * method.
+ *
+ * @param hat A valid Hardware Authentication Token, generated as a result
+ * of a generateChallenge() challenge being wrapped by the gatekeeper
+ * after a successful strong authentication request.
+ * @param timeoutSec A timeout in seconds, after which this enroll
+ * attempt is cancelled. Note that the framework can continue
+ * enrollment by calling this again with a valid HAT. This timeout is
+ * expected to be used to limit power usage if the device becomes idle
+ * during enrollment. The implementation is expected to send
+ * ERROR_TIMEOUT if this happens.
+ * @param disabledFeatures A list of features to be disabled during
+ * enrollment. Note that all features are enabled by default.
+ * @return status The status of this method call.
+ */
+ enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec,
+ vec<Feature> disabledFeatures) generates (Status status);
+};
diff --git a/biometrics/face/1.1/vts/functional/Android.bp b/biometrics/face/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..ccbb399
--- /dev/null
+++ b/biometrics/face/1.1/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "VtsHalBiometricsFaceV1_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.biometrics.face@1.0",
+ "android.hardware.biometrics.face@1.1",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
new file mode 100644
index 0000000..c2431c6
--- /dev/null
+++ b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "biometrics_face_hidl_hal_test"
+
+#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
+#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <chrono>
+#include <cstdint>
+#include <random>
+
+using android::sp;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo;
+using android::hardware::biometrics::face::V1_0::FaceError;
+using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
+using android::hardware::biometrics::face::V1_0::OptionalUint64;
+using android::hardware::biometrics::face::V1_0::Status;
+using android::hardware::biometrics::face::V1_1::IBiometricsFace;
+
+namespace {
+
+// Arbitrary, nonexistent userId
+constexpr uint32_t kUserId = 9;
+constexpr uint32_t kTimeoutSec = 3;
+constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
+constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata";
+constexpr char kCallbackNameOnError[] = "onError";
+
+// Callback arguments that need to be captured for the tests.
+struct FaceCallbackArgs {
+ // The error passed to the last onError() callback.
+ FaceError error;
+
+ // The userId passed to the last callback.
+ int32_t userId;
+};
+
+// Test callback class for the BiometricsFace HAL.
+// The HAL will call these callback methods to notify about completed operations
+// or encountered errors.
+class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>,
+ public IBiometricsFaceClientCallback {
+ public:
+ Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); }
+
+ Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override {
+ return Void();
+ }
+
+ Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override {
+ return Void();
+ }
+
+ Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override {
+ FaceCallbackArgs args = {};
+ args.error = error;
+ args.userId = userId;
+ NotifyFromCallback(kCallbackNameOnError, args);
+ return Void();
+ }
+
+ Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); }
+
+ Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override {
+ return Void();
+ }
+
+ Return<void> onLockoutChanged(uint64_t) override { return Void(); }
+};
+
+// Test class for the BiometricsFace HAL.
+class FaceHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ mService = IBiometricsFace::getService(GetParam());
+ ASSERT_NE(mService, nullptr);
+ mCallback = new FaceCallback();
+ mCallback->SetWaitTimeoutDefault(kTimeout);
+ Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) {
+ ASSERT_EQ(Status::OK, res.status);
+ // Makes sure the "deviceId" represented by "res.value" is not 0.
+ // 0 would mean the HIDL is not available.
+ ASSERT_NE(0UL, res.value);
+ });
+ ASSERT_TRUE(ret1.isOk());
+ Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir);
+ ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
+ }
+
+ void TearDown() override {}
+
+ sp<IBiometricsFace> mService;
+ sp<FaceCallback> mCallback;
+};
+
+// enroll with an invalid (all zeroes) HAT should fail.
+TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) {
+ // Filling HAT with zeros
+ hidl_vec<uint8_t> token(69);
+ for (size_t i = 0; i < 69; i++) {
+ token[i] = 0;
+ }
+
+ Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
+ ASSERT_EQ(Status::OK, static_cast<Status>(ret));
+
+ // onError should be called with a meaningful (nonzero) error.
+ auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+ EXPECT_TRUE(res.no_timeout);
+ EXPECT_EQ(kUserId, res.args->userId);
+ EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
+}
+
+// enroll with an invalid HAT should fail.
+TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) {
+ // Filling HAT with pseudorandom invalid data.
+ // Using default seed to make the test reproducible.
+ std::mt19937 gen(std::mt19937::default_seed);
+ std::uniform_int_distribution<uint8_t> dist;
+ hidl_vec<uint8_t> token(69);
+ for (size_t i = 0; i < 69; ++i) {
+ token[i] = dist(gen);
+ }
+
+ Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
+ ASSERT_EQ(Status::OK, static_cast<Status>(ret));
+
+ // onError should be called with a meaningful (nonzero) error.
+ auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+ EXPECT_TRUE(res.no_timeout);
+ EXPECT_EQ(kUserId, res.args->userId);
+ EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
+}
+
+} // anonymous namespace
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, FaceHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/biometrics/fingerprint/2.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp
new file mode 100644
index 0000000..6c769ac
--- /dev/null
+++ b/biometrics/fingerprint/2.2/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.biometrics.fingerprint@2.2",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IBiometricsFingerprint.hal",
+ "IBiometricsFingerprintClientCallback.hal",
+ ],
+ interfaces: [
+ "android.hardware.biometrics.fingerprint@2.1",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal
new file mode 100644
index 0000000..0651034
--- /dev/null
+++ b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::IBiometricsFingerprint;
+import @2.1::RequestStatus;
+
+interface IBiometricsFingerprint extends @2.1::IBiometricsFingerprint {
+ /**
+ * Fingerprint enroll request:
+ * Switches the HAL state machine to collect and store a new fingerprint
+ * template. Switches back as soon as enroll is complete, signalled by
+ * (fingerprintMsg.type == FINGERPRINT_TEMPLATE_ENROLLING &&
+ * fingerprintMsg.data.enroll.samplesRemaining == 0)
+ * or after timeoutSec seconds.
+ * The fingerprint template must be assigned to the group gid.
+ *
+ * @param hat a valid Hardware Authentication Token (HAT), generated
+ * as a result of a preEnroll() call.
+ * @param gid a framework defined fingerprint set (group) id.
+ * @param timeoutSec a timeout in seconds.
+ * @param windowId optional ID of an illumination window for optical under
+ * display fingerprint sensors. Must contain a null pointer if not used.
+ *
+ * @return debugErrno is a value the framework logs in case it is not 0.
+ *
+ * A notify() function may be called with a more detailed error structure.
+ */
+ enroll_2_2(vec<uint8_t> hat, uint32_t gid, uint32_t timeoutSec, handle windowId)
+ generates (RequestStatus debugErrno);
+
+ /**
+ * Authenticates an operation identified by operationId
+ *
+ * @param operationId operation id.
+ * @param gid fingerprint group id.
+ * @param windowId optional ID of an illumination window for optical under
+ * display fingerprint sensors. Must contain a null pointer if not used.
+ *
+ * @return debugErrno is a value the framework logs in case it is not 0.
+ */
+ authenticate_2_2(uint64_t operationId, uint32_t gid, handle windowId)
+ generates (RequestStatus debugErrno);
+};
diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal
new file mode 100644
index 0000000..14c2b12
--- /dev/null
+++ b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::IBiometricsFingerprintClientCallback;
+
+/*
+ * This HAL interface communicates asynchronous results from the
+ * fingerprint driver in response to user actions on the fingerprint sensor
+ */
+interface IBiometricsFingerprintClientCallback extends @2.1::IBiometricsFingerprintClientCallback {
+ /**
+ * Sent when a fingerprint image is acquired by the sensor
+ * @param deviceId the instance of this fingerprint device
+ * @param acquiredInfo a message about the quality of the acquired image
+ * @param vendorCode a vendor-specific message about the quality of the image. Only
+ * valid when acquiredInfo == ACQUIRED_VENDOR
+ */
+ oneway onAcquired_2_2(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo,
+ int32_t vendorCode);
+};
diff --git a/biometrics/fingerprint/2.2/types.hal b/biometrics/fingerprint/2.2/types.hal
new file mode 100644
index 0000000..2c1d3f3
--- /dev/null
+++ b/biometrics/fingerprint/2.2/types.hal
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.fingerprint@2.2;
+
+import @2.1::FingerprintAcquiredInfo;
+
+/**
+ * Fingerprint acquisition info is meant as feedback for the current operation.
+ * Anything but START and ACQUIRED_GOOD must be shown to the user as feedback on
+ * how to take action on the current operation. For example,
+ * ACQUIRED_IMAGER_DIRTY may be used to tell the user to clean the sensor if it
+ * is detected to be dirty.
+ * If this causes the current operation to fail, an additional ERROR_CANCELED
+ * must be sent to stop the operation in progress (e.g. enrollment).
+ * In general, these messages will result in a "Try again" message.
+ */
+enum FingerprintAcquiredInfo : @2.1::FingerprintAcquiredInfo {
+ /**
+ * This message represents the earliest message sent at the beginning of the
+ * authentication pipeline. It is expected to be used to measure latency. For
+ * example, in a camera-based authentication system it's expected to be sent
+ * prior to camera initialization. Note this should be sent whenever
+ * authentication is restarted (see IBiometricsFace#userActivity).
+ * The framework will measure latency based on the time between the last START
+ * message and the onAuthenticated callback.
+ */
+ START = 7,
+};
diff --git a/biometrics/fingerprint/2.2/vts/functional/Android.bp b/biometrics/fingerprint/2.2/vts/functional/Android.bp
new file mode 100644
index 0000000..496570c
--- /dev/null
+++ b/biometrics/fingerprint/2.2/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "VtsHalBiometricsFingerprintV2_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalBiometricsFingerprintV2_2TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.biometrics.fingerprint@2.1",
+ "android.hardware.biometrics.fingerprint@2.2",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp
new file mode 100644
index 0000000..50bd4ab
--- /dev/null
+++ b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "fingerprint_hidl_hal_test"
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/properties.h>
+#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprintClientCallback.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+
+#include <cinttypes>
+#include <random>
+
+using android::sp;
+using android::base::GetUintProperty;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::biometrics::fingerprint::V2_1::FingerprintAcquiredInfo;
+using android::hardware::biometrics::fingerprint::V2_1::FingerprintError;
+using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
+using android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+
+namespace {
+
+constexpr uint32_t kTimeoutSec = 3;
+constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
+constexpr uint32_t kGroupId = 99;
+constexpr char kCallbackNameOnError[] = "onError";
+
+// Callback arguments that need to be captured for the tests.
+struct FingerprintCallbackArgs {
+ // The error passed to the last onError() callback.
+ FingerprintError error;
+
+ // The deviceId passed to the last callback.
+ uint64_t deviceId;
+};
+
+// Test callback class for the BiometricsFingerprint HAL.
+// The HAL will call these callback methods to notify about completed operations
+// or encountered errors.
+class FingerprintCallback : public ::testing::VtsHalHidlTargetCallbackBase<FingerprintCallbackArgs>,
+ public IBiometricsFingerprintClientCallback {
+ public:
+ Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+
+ Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo, int32_t) override { return Void(); }
+
+ Return<void> onAuthenticated(uint64_t, uint32_t, uint32_t, const hidl_vec<uint8_t>&) override {
+ return Void();
+ }
+
+ Return<void> onError(uint64_t deviceId, FingerprintError error, int32_t) override {
+ FingerprintCallbackArgs args = {};
+ args.error = error;
+ args.deviceId = deviceId;
+ NotifyFromCallback(kCallbackNameOnError, args);
+ return Void();
+ }
+
+ Return<void> onRemoved(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+
+ Return<void> onEnumerate(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); }
+};
+
+class FingerprintHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ mService = IBiometricsFingerprint::getService(GetParam());
+ ASSERT_NE(mService, nullptr);
+ mCallback = new FingerprintCallback();
+ mCallback->SetWaitTimeoutDefault(kTimeout);
+ Return<uint64_t> ret1 = mService->setNotify(mCallback);
+ ASSERT_NE(0UL, static_cast<uint64_t>(ret1));
+
+ /*
+ * Devices shipped from now on will instead store
+ * fingerprint data under /data/vendor_de/<user-id>/fpdata.
+ * Support for /data/vendor_de and /data/vendor_ce has been added to vold.
+ */
+
+ auto api_level = GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
+ if (api_level == 0) {
+ api_level = GetUintProperty<uint64_t>("ro.build.version.sdk", 0);
+ }
+ ASSERT_NE(api_level, 0);
+
+ // 27 is the API number for O-MR1
+ string tmpDir;
+ if (api_level <= 27) {
+ tmpDir = "/data/system/users/0/fpdata/";
+ } else {
+ tmpDir = "/data/vendor_de/0/fpdata/";
+ }
+
+ Return<RequestStatus> res = mService->setActiveGroup(kGroupId, tmpDir);
+ ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res));
+ }
+
+ sp<IBiometricsFingerprint> mService;
+ sp<FingerprintCallback> mCallback;
+};
+
+// Enroll with an invalid (all zeroes) HAT should fail.
+TEST_P(FingerprintHidlTest, EnrollZeroHatTest) {
+ // Filling HAT with zeros
+ hidl_vec<uint8_t> token(69);
+ for (size_t i = 0; i < 69; i++) {
+ token[i] = 0;
+ }
+
+ hidl_handle windowId = nullptr;
+ Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId);
+ ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret));
+
+ // At least one call to onError should occur
+ auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+ ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error);
+}
+
+// Enroll with an invalid (null) HAT should fail.
+TEST_P(FingerprintHidlTest, EnrollGarbageHatTest) {
+ // Filling HAT with pseudorandom invalid data.
+ // Using default seed to make the test reproducible.
+ std::mt19937 gen(std::mt19937::default_seed);
+ std::uniform_int_distribution<uint8_t> dist;
+ hidl_vec<uint8_t> token(69);
+ for (size_t i = 0; i < 69; ++i) {
+ token[i] = dist(gen);
+ }
+
+ hidl_handle windowId = nullptr;
+ Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId);
+ ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret));
+
+ // At least one call to onError should occur
+ auto res = mCallback->WaitForCallback(kCallbackNameOnError);
+ ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error);
+}
+
+} // anonymous namespace
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, FingerprintHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ IBiometricsFingerprint::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 24f114b..14d9866 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -174,7 +174,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.dumpstate</name>
- <version>1.0</version>
+ <version>1.1</version>
<interface>
<name>IDumpstateDevice</name>
<instance>default</instance>
diff --git a/current.txt b/current.txt
index 3af2d95..89881b0 100644
--- a/current.txt
+++ b/current.txt
@@ -633,6 +633,8 @@
4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types
66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory
994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory
+881aa8720fb1d69aa9843bfab69d810ab7654a61d2f5ab5e2626cbf240f24eaf android.hardware.dumpstate@1.1::types
+13b33f623521ded51a6c0f7ea5b77e97066d0aa1e38a83c2873f08ad67294f89 android.hardware.dumpstate@1.1::IDumpstateDevice
3dacec7801968e1e4479724dc0180442d9e915466bff051f80996266b1a51c2c android.hardware.gnss@2.1::IGnss
ba62e1e8993bfb9f27fa04816fa0f2241ae2d01edfa3d0c04182e2e5de80045c android.hardware.gnss@2.1::IGnssCallback
ccdf3c0fb2c02a6d4dc57afb276c3497ae8172b80b00ebc0bf8a0238dd38b01d android.hardware.gnss@2.1::IGnssConfiguration
@@ -667,7 +669,7 @@
##
# BEGIN Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present
##
-616456d7ce4435d88995f9fe0025a76bca14bd70799e4ca3ff4bae74d54d1166 android.hardware.radio@1.5::types
+430f8449ddb24c02284da561bfd24bb5a2a226d9ed2aec38e876e323e2b7eeee android.hardware.radio@1.5::types
c68f5bd87f747f8e7968ff66ecc548b2d26f8e186b7bb805c11d6c883a838fc6 android.hardware.radio@1.5::IRadio
e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
9e962eff568dc8c712d83846f8c27460de5005ed9b836d3e08390e8aa56b5a46 android.hardware.radio@1.5::IRadioResponse
diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp
index 02aa3a9..b169268 100644
--- a/drm/1.2/vts/functional/drm_hal_common.cpp
+++ b/drm/1.2/vts/functional/drm_hal_common.cpp
@@ -87,7 +87,7 @@
return new DrmHalVTSClearkeyModule();
}
- return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModule(instance));
+ return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModuleByName(instance));
}
/**
@@ -120,21 +120,12 @@
GTEST_SKIP() << "No vendor module installed";
}
- if (instance == "clearkey") {
- // TODO(b/147449315)
- // Only the clearkey plugged into the "default" instance supports
- // this test. Currently the "clearkey" instance fails some tests
- // here.
- GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet.";
- }
-
ASSERT_EQ(instance, vendorModule->getServiceName());
contentConfigurations = vendorModule->getContentConfigurations();
// If drm scheme not installed skip subsequent tests
- if (drmFactory.get() == nullptr || !drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
- vendorModule->setInstalled(false);
- return;
+ if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+ GTEST_SKIP() << "vendor module drm scheme not supported";
}
ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " drm@1.2 plugin";
diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/drm_hal_common.h
index b2d654c..03b1b87 100644
--- a/drm/1.2/vts/functional/drm_hal_common.h
+++ b/drm/1.2/vts/functional/drm_hal_common.h
@@ -62,14 +62,6 @@
#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
-#define RETURN_IF_SKIPPED \
- if (!vendorModule->isInstalled()) { \
- GTEST_SKIP() << "This drm scheme not supported." \
- << " library:" << GetParam() \
- << " service-name:" << vendorModule->getServiceName() << std::endl; \
- return; \
- }
-
namespace android {
namespace hardware {
namespace drm {
@@ -131,9 +123,11 @@
public:
virtual void SetUp() override {
DrmHalTest::SetUp();
-
- if (vendorModule == nullptr) {
- GTEST_SKIP() << "Instance not supported";
+ const uint8_t kClearKeyUUID[16] = {
+ 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+ 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
+ if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) {
+ GTEST_SKIP() << "ClearKey not supported by " << GetParam();
}
}
virtual void TearDown() override {}
diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp
index 54c5751..48becc1 100644
--- a/drm/1.2/vts/functional/drm_hal_test.cpp
+++ b/drm/1.2/vts/functional/drm_hal_test.cpp
@@ -53,7 +53,6 @@
* Ensure drm factory supports module UUID Scheme
*/
TEST_P(DrmHalTest, VendorUuidSupported) {
- RETURN_IF_SKIPPED;
auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto);
ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res);
EXPECT_TRUE(res);
@@ -63,7 +62,6 @@
* Ensure drm factory doesn't support an invalid scheme UUID
*/
TEST_P(DrmHalTest, InvalidPluginNotSupported) {
- RETURN_IF_SKIPPED;
const uint8_t kInvalidUUID[16] = {
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
@@ -74,7 +72,6 @@
* Ensure drm factory doesn't support an empty UUID
*/
TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
- RETURN_IF_SKIPPED;
hidl_array<uint8_t, 16> emptyUUID;
memset(emptyUUID.data(), 0, 16);
EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(emptyUUID, kVideoMp4, kSwSecureCrypto));
@@ -84,7 +81,6 @@
* Ensure drm factory doesn't support an invalid mime type
*/
TEST_P(DrmHalTest, BadMimeNotSupported) {
- RETURN_IF_SKIPPED;
EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
}
@@ -101,7 +97,6 @@
* that is delivered back to the HAL.
*/
TEST_P(DrmHalTest, DoProvisioning) {
- RETURN_IF_SKIPPED;
hidl_string certificateType;
hidl_string certificateAuthority;
hidl_vec<uint8_t> provisionRequest;
@@ -138,7 +133,6 @@
* A get key request should fail if no sessionId is provided
*/
TEST_P(DrmHalTest, GetKeyRequestNoSession) {
- RETURN_IF_SKIPPED;
SessionId invalidSessionId;
hidl_vec<uint8_t> initData;
KeyedVector optionalParameters;
@@ -156,7 +150,6 @@
* invalid mime type
*/
TEST_P(DrmHalTest, GetKeyRequestBadMime) {
- RETURN_IF_SKIPPED;
auto sessionId = openSession();
hidl_vec<uint8_t> initData;
KeyedVector optionalParameters;
@@ -193,7 +186,6 @@
* Test drm plugin offline key support
*/
TEST_P(DrmHalTest, OfflineLicenseTest) {
- RETURN_IF_SKIPPED;
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
@@ -233,7 +225,6 @@
* Test drm plugin offline key state
*/
TEST_P(DrmHalTest, OfflineLicenseStateTest) {
- RETURN_IF_SKIPPED;
auto sessionId = openSession();
DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
hidl_vec<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
@@ -258,7 +249,6 @@
* Negative offline license test. Remove empty keySetId
*/
TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
- RETURN_IF_SKIPPED;
KeySetId emptyKeySetId;
Status err = drmPlugin->removeOfflineLicense(emptyKeySetId);
EXPECT_EQ(Status::BAD_VALUE, err);
@@ -268,7 +258,6 @@
* Negative offline license test. Get empty keySetId state
*/
TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
- RETURN_IF_SKIPPED;
KeySetId emptyKeySetId;
auto res = drmPlugin->getOfflineLicenseState(emptyKeySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
EXPECT_OK(res);
@@ -278,7 +267,6 @@
* Test that the plugin returns valid connected and max HDCP levels
*/
TEST_P(DrmHalTest, GetHdcpLevels) {
- RETURN_IF_SKIPPED;
auto res = drmPlugin->getHdcpLevels_1_2(
[&](StatusV1_2 status, const HdcpLevel &connectedLevel,
const HdcpLevel &maxLevel) {
@@ -294,7 +282,6 @@
* the listener gets them.
*/
TEST_P(DrmHalTest, ListenerKeysChange) {
- RETURN_IF_SKIPPED;
sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
auto res = drmPlugin->setListener(listener);
EXPECT_OK(res);
@@ -326,7 +313,6 @@
* Positive decrypt test. "Decrypt" a single clear segment
*/
TEST_P(DrmHalTest, ClearSegmentTest) {
- RETURN_IF_SKIPPED;
for (const auto& config : contentConfigurations) {
for (const auto& key : config.keys) {
const size_t kSegmentSize = 1024;
@@ -354,7 +340,6 @@
* Verify data matches.
*/
TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
- RETURN_IF_SKIPPED;
for (const auto& config : contentConfigurations) {
for (const auto& key : config.keys) {
const size_t kSegmentSize = 1024;
@@ -381,7 +366,6 @@
* Negative decrypt test. Decrypted frame too large to fit in output buffer
*/
TEST_P(DrmHalTest, ErrorFrameTooLarge) {
- RETURN_IF_SKIPPED;
for (const auto& config : contentConfigurations) {
for (const auto& key : config.keys) {
const size_t kSegmentSize = 1024;
@@ -407,7 +391,6 @@
* Negative decrypt test. Decrypt without loading keys.
*/
TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
- RETURN_IF_SKIPPED;
for (const auto& config : contentConfigurations) {
for (const auto& key : config.keys) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
@@ -433,7 +416,6 @@
* Ensure clearkey drm factory doesn't support security level higher than supported
*/
TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
- RETURN_IF_SKIPPED;
const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll));
}
@@ -442,7 +424,6 @@
* Test resource contention during attempt to generate key request
*/
TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
- RETURN_IF_SKIPPED;
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
EXPECT_EQ(Status::OK, status);
auto sessionId = openSession();
@@ -464,7 +445,6 @@
* Test clearkey plugin offline key with mock error
*/
TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
- RETURN_IF_SKIPPED;
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
@@ -486,7 +466,6 @@
* Test SessionLostState is triggered on error
*/
TEST_P(DrmHalClearkeyTest, SessionLostState) {
- RETURN_IF_SKIPPED;
sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
auto res = drmPlugin->setListener(listener);
EXPECT_OK(res);
@@ -507,7 +486,6 @@
* Negative decrypt test. Decrypt with invalid key.
*/
TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
- RETURN_IF_SKIPPED;
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
@@ -545,7 +523,6 @@
* Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
*/
TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
- RETURN_IF_SKIPPED;
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp
index efcb90a..08131cf 100644
--- a/drm/1.2/vts/functional/vendor_modules.cpp
+++ b/drm/1.2/vts/functional/vendor_modules.cpp
@@ -23,6 +23,7 @@
#include <utils/String8.h>
#include <SharedLibrary.h>
+#include "drm_hal_vendor_module_api.h"
#include "vendor_modules.h"
using std::string;
@@ -69,4 +70,14 @@
ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
return (*moduleFactory)();
}
+
+DrmHalVTSVendorModule* VendorModules::getModuleByName(const string& name) {
+ for (const auto &path : mPathList) {
+ auto module = getModule(path);
+ if (module->getServiceName() == name) {
+ return module;
+ }
+ }
+ return NULL;
+}
};
diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h
index 9b730ad..eacd2d1 100644
--- a/drm/1.2/vts/functional/vendor_modules.h
+++ b/drm/1.2/vts/functional/vendor_modules.h
@@ -47,6 +47,11 @@
DrmHalVTSVendorModule* getModule(const std::string& path);
/**
+ * Retrieve a DrmHalVTSVendorModule given a service name.
+ */
+ DrmHalVTSVendorModule* getModuleByName(const std::string& name);
+
+ /**
* Return the list of paths to available vendor modules.
*/
std::vector<std::string> getPathList() const {return mPathList;}
diff --git a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
index 96b13c5..343d4c9 100644
--- a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
+++ b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
@@ -78,6 +78,7 @@
ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing";
native_handle_close(handle);
+ native_handle_delete(handle);
}
// Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
@@ -96,6 +97,7 @@
ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
native_handle_close(handle);
+ native_handle_delete(handle);
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/dumpstate/1.1/Android.bp b/dumpstate/1.1/Android.bp
new file mode 100644
index 0000000..2aa8c82
--- /dev/null
+++ b/dumpstate/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.dumpstate@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IDumpstateDevice.hal",
+ ],
+ interfaces: [
+ "android.hardware.dumpstate@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/dumpstate/1.1/IDumpstateDevice.hal b/dumpstate/1.1/IDumpstateDevice.hal
new file mode 100644
index 0000000..24831b3
--- /dev/null
+++ b/dumpstate/1.1/IDumpstateDevice.hal
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.dumpstate@1.1;
+
+import @1.0::IDumpstateDevice;
+
+interface IDumpstateDevice extends @1.0::IDumpstateDevice {
+ /**
+ * Extension of dumpstateBoard which also accepts a mode parameter to limit dumped data.
+ *
+ * For an example of when this is relevant, consider a bug report being generated with
+ * DumpstateMode::CONNECTIVITY - there is no reason to include camera or USB logs in this type
+ * of report.
+ *
+ * The 1.0 version of #dumpstateBoard(handle) should just delegate to this new method and pass
+ * DumpstateMode::DEFAULT and a timeout of 30,000ms (30 seconds).
+ *
+ * @param h A native handle with one or two valid file descriptors. The first FD is for text
+ * output, the second (if present) is for binary output.
+ * @param mode A mode value to restrict dumped content.
+ * @param timeoutMillis An approximate "budget" for how much time this call has been allotted.
+ * If execution runs longer than this, the IDumpstateDevice service may be killed and only
+ * partial information will be included in the report.
+ */
+ dumpstateBoard_1_1(handle h, DumpstateMode mode, uint64_t timeoutMillis);
+
+ /**
+ * Turns device vendor logging on or off.
+ *
+ * The setting should be persistent across reboots. Underlying implementations may need to start
+ * vendor logging daemons, set system properties, or change logging masks, for example. Given
+ * that many vendor logs contain significant amounts of private information and may come with
+ * memory/storage/battery impacts, calling this method on a user build should only be done after
+ * user consent has been obtained, e.g. from a toggle in developer settings.
+ *
+ * @param enable Whether to enable or disable device vendor logging.
+ * @return success Whether or not the change took effect.
+ */
+ setDeviceLoggingEnabled(bool enable) generates (bool success);
+};
diff --git a/dumpstate/1.1/types.hal b/dumpstate/1.1/types.hal
new file mode 100644
index 0000000..a6f391a
--- /dev/null
+++ b/dumpstate/1.1/types.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.dumpstate@1.1;
+
+/**
+ * Constants that define the type of bug report being taken to restrict content appropriately.
+ */
+enum DumpstateMode : uint32_t {
+ /**
+ * Takes a bug report without user interference.
+ */
+ FULL = 0,
+
+ /**
+ * Interactive bug report, i.e. triggered by the user.
+ */
+ INTERACTIVE = 1,
+
+ /**
+ * Remote bug report triggered by DevicePolicyManager, for example.
+ */
+ REMOTE = 2,
+
+ /**
+ * Bug report triggered on a wear device.
+ */
+ WEAR = 3,
+
+ /**
+ * Bug report limited to only connectivity info (cellular, wifi, and networking). Sometimes
+ * called "telephony" in legacy contexts.
+ *
+ * All reported information MUST directly relate to connectivity debugging or customer support
+ * and MUST NOT contain unrelated private information. This information MUST NOT identify
+ * user-installed packages (UIDs are OK, package names are not), and MUST NOT contain logs of
+ * user application traffic.
+ */
+ CONNECTIVITY = 4,
+
+ /**
+ * Bug report limited to only wifi info.
+ */
+ WIFI = 5,
+
+ /**
+ * Default mode.
+ */
+ DEFAULT = 6
+};
diff --git a/dumpstate/1.1/vts/functional/Android.bp b/dumpstate/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..5267706
--- /dev/null
+++ b/dumpstate/1.1/vts/functional/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalDumpstateV1_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalDumpstateV1_1TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.dumpstate@1.0",
+ "android.hardware.dumpstate@1.1",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
new file mode 100644
index 0000000..3b6051c
--- /dev/null
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate_1_1_hidl_hal_test"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <vector>
+
+#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/types.h>
+#include <cutils/native_handle.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
+using ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
+
+class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ dumpstate = IDumpstateDevice::getService(GetParam());
+ ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance";
+ }
+
+ sp<IDumpstateDevice> dumpstate;
+};
+
+#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \
+ TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); }
+
+#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body) \
+ TEST_FOR_DUMPSTATE_MODE(name, body, FULL); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, REMOTE); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, WEAR); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, WIFI); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);
+
+const uint64_t kDefaultTimeoutMillis = 30 * 1000; // 30 seconds
+
+// Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) {
+ Return<void> status = dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+});
+
+// Negative test: make sure dumpstateBoard() ignores a handle with no FD.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) {
+ native_handle_t* handle = native_handle_create(0, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+
+ Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+});
+
+// Positive test: make sure dumpstateBoard() writes something to the FD.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) {
+ // Index 0 corresponds to the read end of the pipe; 1 to the write end.
+ int fds[2];
+ ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds[1];
+
+ Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+ // Check that at least one byte was written
+ char buff;
+ ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing";
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+});
+
+// Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) {
+ int fds1[2];
+ int fds2[2];
+ ASSERT_EQ(0, pipe2(fds1, O_NONBLOCK)) << errno;
+ ASSERT_EQ(0, pipe2(fds2, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(2, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds1[1];
+ handle->data[1] = fds2[1];
+
+ Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+});
+
+// Make sure dumpstateBoard_1_1 actually validates its arguments.
+TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) {
+ int fds[2];
+ ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds[1];
+
+ Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(-100),
+ kDefaultTimeoutMillis);
+ ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
+ << status.description();
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+}
+
+TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) {
+ int fds[2];
+ ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds[1];
+
+ Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(9001),
+ kDefaultTimeoutMillis);
+ ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
+ << status.description();
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+}
+
+// Make sure toggling device logging doesn't crash.
+TEST_P(DumpstateHidl1_1Test, TestEnableDeviceLogging) {
+ Return<bool> status = dumpstate->setDeviceLoggingEnabled(true);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+}
+
+TEST_P(DumpstateHidl1_1Test, TestDisableDeviceLogging) {
+ Return<bool> status = dumpstate->setDeviceLoggingEnabled(false);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, DumpstateHidl1_1Test,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
index b0d153f..ebfd8d8 100644
--- a/health/utils/libhealthloop/utils.cpp
+++ b/health/utils/libhealthloop/utils.cpp
@@ -40,6 +40,8 @@
.batteryChargeCounterPath = String8(String8::kEmptyString),
.batteryFullChargePath = String8(String8::kEmptyString),
.batteryCycleCountPath = String8(String8::kEmptyString),
+ .batteryCapacityLevelPath = String8(String8::kEmptyString),
+ .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString),
.energyCounter = NULL,
.boot_min_cap = 0,
.screen_on = NULL,
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index 5482aca..c0fa8af 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -600,6 +600,9 @@
/** Information about any closed subscriber group ID for this cell */
OptionalCsgInfo optionalCsgInfo;
+
+ /** Bands used by the cell. */
+ vec<EutranBands> bands;
};
/**
@@ -615,8 +618,8 @@
/** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */
vec<string> additionalPlmns;
- /** Band used by the cell */
- NgranBands band;
+ /** Bands used by the cell. */
+ vec<NgranBands> bands;
};
struct CellInfoGsm {
diff --git a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
index f69cf87..cd8cc3e 100644
--- a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
+++ b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp
@@ -26,6 +26,9 @@
using android::String16;
using android::hardware::rebootescrow::IRebootEscrow;
+#define SKIP_UNSUPPORTED \
+ if (rebootescrow == nullptr) GTEST_SKIP() << "Not supported on this device"
+
/**
* This tests that the key can be written, read, and removed. It does not test
* that the key survives a reboot. That needs a host-based test.
@@ -36,7 +39,6 @@
public:
virtual void SetUp() override {
rebootescrow = android::waitForDeclaredService<IRebootEscrow>(String16(GetParam().c_str()));
- ASSERT_NE(rebootescrow, nullptr);
}
sp<IRebootEscrow> rebootescrow;
@@ -59,6 +61,8 @@
};
TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) {
+ SKIP_UNSUPPORTED;
+
ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
std::vector<uint8_t> actualKey;
@@ -67,6 +71,8 @@
}
TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) {
+ SKIP_UNSUPPORTED;
+
ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
std::vector<uint8_t> actualKey;
@@ -78,6 +84,8 @@
}
TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) {
+ SKIP_UNSUPPORTED;
+
ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk());
ASSERT_TRUE(rebootescrow->storeKey(KEY_2).isOk());
@@ -87,6 +95,8 @@
}
TEST_P(RebootEscrowAidlTest, StoreEmpty_AfterGetEmptyKey_Success) {
+ SKIP_UNSUPPORTED;
+
rebootescrow->storeKey(KEY_1);
rebootescrow->storeKey(EMPTY_KEY);
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
index b9a80ec..0fdfa5d 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
@@ -49,4 +49,9 @@
* A haptic effect that simulates quick downwards movement with gravity.
*/
QUICK_FALL,
+ /**
+ * This very short effect should produce a light crisp sensation intended
+ * to be used repetitively for dynamic feedback.
+ */
+ LIGHT_TICK,
}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index f553664..06a8bf5 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -158,12 +158,31 @@
int getCompositionSizeMax();
/**
+ * List of supported effect primitive.
+ *
+ * Return the effect primitives which are supported by the compose API.
+ * Implementations are expected to support all primitives of the interface
+ * version that they implement.
+ */
+ CompositePrimitive[] getSupportedPrimitives();
+
+ /**
+ * Retrieve effect primitive's duration in milliseconds.
+ *
+ * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
+ *
+ * @return Best effort estimation of effect primitive's duration.
+ * @param primitive Effect primitive being queried.
+ */
+ int getPrimitiveDuration(CompositePrimitive primitive);
+
+ /**
* Fire off a string of effect primitives, combined to perform richer effects.
*
* Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS).
*
* Doing this operation while the vibrator is already on is undefined behavior. Clients should
- * explicitly call off.
+ * explicitly call off. IVibratorCallback.onComplete() support is required for this API.
*
* @param composite Array of composition parameters.
*/
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index cedd9cb..0d7131a 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -113,6 +113,26 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
+ *supported = {
+ CompositePrimitive::NOOP, CompositePrimitive::CLICK,
+ CompositePrimitive::THUD, CompositePrimitive::SPIN,
+ CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
+ CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
+ };
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
+ int32_t* durationMs) {
+ if (primitive != CompositePrimitive::NOOP) {
+ *durationMs = 100;
+ } else {
+ *durationMs = 0;
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
const std::shared_ptr<IVibratorCallback>& callback) {
if (composite.size() > kComposeSizeMax) {
@@ -127,7 +147,7 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (e.primitive < CompositePrimitive::NOOP ||
- e.primitive > CompositePrimitive::QUICK_FALL) {
+ e.primitive > CompositePrimitive::LIGHT_TICK) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
}
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index 0eb957d..c3f3616 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -36,6 +36,9 @@
ndk::ScopedAStatus setExternalControl(bool enabled) override;
ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs);
ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize);
+ ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) override;
+ ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
+ int32_t* durationMs) override;
ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
const std::shared_ptr<IVibratorCallback>& callback) override;
ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index f197763..411fe7a 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -261,6 +261,29 @@
}
}
+TEST_P(VibratorAidl, GetSupportedPrimitives) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ std::vector<CompositePrimitive> supported;
+
+ EXPECT_EQ(Status::EX_NONE, vibrator->getSupportedPrimitives(&supported).exceptionCode());
+
+ std::sort(supported.begin(), supported.end());
+
+ EXPECT_EQ(kCompositePrimitives, supported);
+ }
+}
+
+TEST_P(VibratorAidl, GetPrimitiveDuration) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ int32_t duration;
+
+ for (auto primitive : kCompositePrimitives) {
+ EXPECT_EQ(Status::EX_NONE,
+ vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode());
+ }
+ }
+}
+
TEST_P(VibratorAidl, ComposeValidPrimitives) {
if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
int32_t maxDelay, maxSize;
@@ -357,6 +380,30 @@
}
}
+TEST_P(VibratorAidl, ComposeCallback) {
+ if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
+ std::promise<void> completionPromise;
+ std::future<void> completionFuture{completionPromise.get_future()};
+ sp<CompletionCallback> callback =
+ new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+ CompositePrimitive primitive = CompositePrimitive::CLICK;
+ CompositeEffect effect;
+ std::vector<CompositeEffect> composite;
+ int32_t duration;
+
+ effect.delayMs = 0;
+ effect.primitive = primitive;
+ effect.scale = 1.0f;
+ composite.emplace_back(effect);
+
+ EXPECT_EQ(Status::EX_NONE,
+ vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode());
+ EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode());
+ EXPECT_EQ(completionFuture.wait_for(std::chrono::milliseconds(duration * 2)),
+ std::future_status::ready);
+ }
+}
+
TEST_P(VibratorAidl, AlwaysOn) {
if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) {
std::vector<Effect> supported;