Merge "Vibrator: Stable AIDL interface." am: ad51fe22b7 am: dfbaa68529

am: aaa72ec044

Change-Id: I23c481d5174c780f1de1a41f73fac89c22d215ac
This commit is contained in:
Steven Moreland
2019-11-01 14:54:12 -07:00
committed by android-build-merger
14 changed files with 686 additions and 0 deletions

View File

@@ -467,6 +467,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>

View File

@@ -0,0 +1,14 @@
aidl_interface {
name: "vintf-vibrator",
vendor_available: true,
srcs: [
"android/hardware/vibrator/*.aidl",
],
stability: "vintf",
backend: {
java: {
enabled: false,
},
},
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -0,0 +1,101 @@
/*
* 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.
*/
const int CAP_AMPLITUDE_CONTROL = 1 << 2;
/**
* Whether setExternalControl is supported.
*/
const int CAP_EXTERNAL_CONTROL = 1 << 3;
/**
* Determine capabilities of the vibrator HAL (CAP_* values)
*/
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()).
* @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.
*
* @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);
/**
* Sets the motor's vibrational amplitude.
*
* Changes the force being produced by the underlying motor.
*
* @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.
*
* 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
* supportsAmplitudeControl() 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);
}

View File

@@ -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();
}

View File

@@ -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"],
}

View File

@@ -0,0 +1,97 @@
/*
* 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;
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::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

View File

@@ -0,0 +1,41 @@
/*
* 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 setAmplitude(int32_t amplitude) override;
ndk::ScopedAStatus setExternalControl(bool enabled) override;
};
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -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
}

View File

@@ -0,0 +1,4 @@
service vendor.vibrator-default /vendor/bin/hw/android.hardware.vibrator-service.example
class hal
user system
group system

View File

@@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.vibrator</name>
<fqname>IVibrator/default</fqname>
</hal>
</manifest>

View File

@@ -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",
],
}

View File

@@ -0,0 +1,218 @@
/*
* 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;
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};
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) {
for (Effect effect : kEffects) {
for (EffectStrength strength : kEffectStrengths) {
int32_t lengthMs = 0;
Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
EXPECT_TRUE(status.isOk() ||
status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
if (status.isOk()) {
EXPECT_GT(lengthMs, 0);
} else {
EXPECT_EQ(lengthMs, 0);
}
}
}
}
TEST_P(VibratorAidl, ValidateEffectWithCallback) {
if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
for (Effect effect : kEffects) {
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;
Status status = vibrator->perform(effect, strength, callback, &lengthMs);
EXPECT_TRUE(status.isOk() ||
status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
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, 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();
}