diff --git a/soundtrigger/2.1/vts/functional/Android.bp b/soundtrigger/2.1/vts/functional/Android.bp new file mode 100644 index 0000000000..925a17ce11 --- /dev/null +++ b/soundtrigger/2.1/vts/functional/Android.bp @@ -0,0 +1,28 @@ +// +// Copyright (C) 2018 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: "VtsHalSoundtriggerV2_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalSoundtriggerV2_1TargetTest.cpp"], + static_libs: [ + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "android.hardware.soundtrigger@2.0", + "android.hardware.soundtrigger@2.1", + "libhidlmemory" + ], +} diff --git a/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp new file mode 100644 index 0000000000..9876cdd911 --- /dev/null +++ b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2018 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 "SoundTriggerHidlHalTest" +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define SHORT_TIMEOUT_PERIOD (1) + +using ::android::sp; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::audio::common::V2_0::AudioDevice; +using ::android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra; +using ::android::hardware::soundtrigger::V2_0::RecognitionMode; +using ::android::hardware::soundtrigger::V2_0::SoundModelHandle; +using ::android::hardware::soundtrigger::V2_0::SoundModelType; +using V2_0_ISoundTriggerHw = ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw; +using V2_0_ISoundTriggerHwCallback = + ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback; +using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw; +using ParameterValue = ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw::ParameterValue; +using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHwCallback; +using ::android::hidl::allocator::V1_0::IAllocator; +using ::android::hidl::memory::V1_0::IMemory; + +/** + * Test code uses this class to wait for notification from callback. + */ +class Monitor { + public: + Monitor() : mCount(0) {} + + /** + * Adds 1 to the internal counter and unblocks one of the waiting threads. + */ + void notify() { + std::unique_lock lock(mMtx); + mCount++; + mCv.notify_one(); + } + + /** + * Blocks until the internal counter becomes greater than 0. + * + * If notified, this method decreases the counter by 1 and returns true. + * If timeout, returns false. + */ + bool wait(int timeoutSeconds) { + auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(timeoutSeconds); + std::unique_lock lock(mMtx); + if (!mCv.wait_until(lock, deadline, [& count = mCount] { return count > 0; })) { + return false; + } + mCount--; + return true; + } + + private: + std::mutex mMtx; + std::condition_variable mCv; + int mCount; +}; + +// The main test class for Sound Trigger HIDL HAL. +class SoundTriggerHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + mSoundTriggerHal = ::testing::VtsHalHidlTargetTestBase::getService(); + ASSERT_NE(nullptr, mSoundTriggerHal.get()); + mCallback = new SoundTriggerHwCallback(*this); + ASSERT_NE(nullptr, mCallback.get()); + } + + static void SetUpTestCase() { srand(1234); } + + class SoundTriggerHwCallback : public ISoundTriggerHwCallback { + private: + SoundTriggerHidlTest& mParent; + + public: + SoundTriggerHwCallback(SoundTriggerHidlTest& parent) : mParent(parent) {} + + Return recognitionCallback(const V2_0_ISoundTriggerHwCallback::RecognitionEvent& event + __unused, + int32_t cookie __unused) override { + ALOGI("%s", __FUNCTION__); + return Void(); + }; + + Return phraseRecognitionCallback( + const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused, + int32_t cookie __unused) override { + ALOGI("%s", __FUNCTION__); + return Void(); + }; + + Return soundModelCallback(const V2_0_ISoundTriggerHwCallback::ModelEvent& event, + int32_t cookie __unused) override { + ALOGI("%s", __FUNCTION__); + mParent.lastModelEvent_2_0 = event; + mParent.monitor.notify(); + return Void(); + } + + Return recognitionCallback_2_1(const ISoundTriggerHwCallback::RecognitionEvent& event + __unused, + int32_t cookie __unused) override { + ALOGI("%s", __FUNCTION__); + return Void(); + } + + Return phraseRecognitionCallback_2_1( + const ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused, + int32_t cookie __unused) override { + ALOGI("%s", __FUNCTION__); + return Void(); + } + + Return soundModelCallback_2_1(const ISoundTriggerHwCallback::ModelEvent& event, + int32_t cookie __unused) { + ALOGI("%s", __FUNCTION__); + mParent.lastModelEvent = event; + mParent.monitor.notify(); + return Void(); + } + }; + + virtual void TearDown() override {} + + Monitor monitor; + // updated by soundModelCallback() + V2_0_ISoundTriggerHwCallback::ModelEvent lastModelEvent_2_0; + // updated by soundModelCallback_2_1() + ISoundTriggerHwCallback::ModelEvent lastModelEvent; + + protected: + sp mSoundTriggerHal; + sp mCallback; +}; + +/** + * Test ISoundTriggerHw::getProperties() method + * + * Verifies that: + * - the implementation implements the method + * - the method returns 0 (no error) + * - the implementation supports at least one sound model and one key phrase + * - the implementation supports at least VOICE_TRIGGER recognition mode + */ +TEST_F(SoundTriggerHidlTest, GetProperties) { + ISoundTriggerHw::Properties halProperties; + Return hidlReturn; + int ret = -ENODEV; + + hidlReturn = mSoundTriggerHal->getProperties([&](int rc, auto res) { + ret = rc; + halProperties = res; + }); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_EQ(0, ret); + EXPECT_GT(halProperties.maxSoundModels, 0u); + EXPECT_GT(halProperties.maxKeyPhrases, 0u); + EXPECT_NE(0u, (halProperties.recognitionModes & (uint32_t)RecognitionMode::VOICE_TRIGGER)); +} + +/** + * Test ISoundTriggerHw::loadPhraseSoundModel() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when passed a malformed sound model + * + * There is no way to verify that implementation actually can load a sound model because each + * sound model is vendor specific. + */ +TEST_F(SoundTriggerHidlTest, LoadInvalidModelFail) { + Return hidlReturn; + int ret = -ENODEV; + V2_0_ISoundTriggerHw::PhraseSoundModel model; + SoundModelHandle handle; + + model.common.type = SoundModelType::UNKNOWN; + + hidlReturn = + mSoundTriggerHal->loadPhraseSoundModel(model, mCallback, 0, [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::loadPhraseSoundModel_2_1() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when passed a malformed sound model + * + * There is no way to verify that implementation actually can load a sound model because each + * sound model is vendor specific. + */ +TEST_F(SoundTriggerHidlTest, LoadInvalidModelFail_2_1) { + Return hidlReturn; + int ret = -ENODEV; + ISoundTriggerHw::PhraseSoundModel model; + SoundModelHandle handle; + + model.common.header.type = SoundModelType::UNKNOWN; + + hidlReturn = mSoundTriggerHal->loadPhraseSoundModel_2_1(model, mCallback, 0, + [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::loadSoundModel() method + * + * Verifies that: + * - the implementation returns an error when passed an empty sound model + */ +TEST_F(SoundTriggerHidlTest, LoadEmptyGenericSoundModelFail) { + int ret = -ENODEV; + V2_0_ISoundTriggerHw::SoundModel model; + SoundModelHandle handle = 0; + + model.type = SoundModelType::GENERIC; + + Return loadReturn = + mSoundTriggerHal->loadSoundModel(model, mCallback, 0, [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(loadReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::loadSoundModel() method + * + * Verifies that: + * - the implementation returns error when passed a sound model with random data. + */ +TEST_F(SoundTriggerHidlTest, LoadGenericSoundModelFail) { + int ret = -ENODEV; + V2_0_ISoundTriggerHw::SoundModel model; + SoundModelHandle handle = 0; + + model.type = SoundModelType::GENERIC; + model.data.resize(100); + for (auto& d : model.data) { + d = rand(); + } + + Return loadReturn = + mSoundTriggerHal->loadSoundModel(model, mCallback, 0, [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(loadReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::loadSoundModel_2_1() method + * + * Verifies that: + * - the implementation returns error when passed a sound model with random data. + */ +TEST_F(SoundTriggerHidlTest, LoadEmptyGenericSoundModelFail_2_1) { + int ret = -ENODEV; + ISoundTriggerHw::SoundModel model; + SoundModelHandle handle = 0; + + model.header.type = SoundModelType::GENERIC; + + Return loadReturn = + mSoundTriggerHal->loadSoundModel_2_1(model, mCallback, 0, [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(loadReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::loadSoundModel_2_1() method + * + * Verifies that: + * - the implementation returns error when passed a sound model with random data. + */ +TEST_F(SoundTriggerHidlTest, LoadGenericSoundModelFail_2_1) { + int ret = -ENODEV; + ISoundTriggerHw::SoundModel model; + SoundModelHandle handle = 0; + + model.header.type = SoundModelType::GENERIC; + sp ashmem = IAllocator::getService("ashmem"); + ASSERT_NE(nullptr, ashmem.get()); + hidl_memory hmemory; + int size = 100; + Return allocReturn = ashmem->allocate(size, [&](bool success, const hidl_memory& m) { + ASSERT_TRUE(success); + hmemory = m; + }); + sp memory = ::android::hardware::mapMemory(hmemory); + ASSERT_NE(nullptr, memory.get()); + memory->update(); + for (uint8_t *p = static_cast(static_cast(memory->getPointer())); size >= 0; + p++, size--) { + *p = rand(); + } + + Return loadReturn = + mSoundTriggerHal->loadSoundModel_2_1(model, mCallback, 0, [&](int32_t retval, auto res) { + ret = retval; + handle = res; + }); + + EXPECT_TRUE(loadReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); +} + +/** + * Test ISoundTriggerHw::unloadSoundModel() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when called without a valid loaded sound model + * + */ +TEST_F(SoundTriggerHidlTest, UnloadModelNoModelFail) { + Return hidlReturn(0); + SoundModelHandle halHandle = 0; + + hidlReturn = mSoundTriggerHal->unloadSoundModel(halHandle); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, hidlReturn); +} + +/** + * Test ISoundTriggerHw::startRecognition() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when called without a valid loaded sound model + * + * There is no way to verify that implementation actually starts recognition because no model can + * be loaded. + */ +TEST_F(SoundTriggerHidlTest, StartRecognitionNoModelFail) { + Return hidlReturn(0); + SoundModelHandle handle = 0; + PhraseRecognitionExtra phrase; + V2_0_ISoundTriggerHw::RecognitionConfig config; + + config.captureHandle = 0; + config.captureDevice = AudioDevice::IN_BUILTIN_MIC; + phrase.id = 0; + phrase.recognitionModes = (uint32_t)RecognitionMode::VOICE_TRIGGER; + phrase.confidenceLevel = 0; + + config.phrases.setToExternal(&phrase, 1); + + hidlReturn = mSoundTriggerHal->startRecognition(handle, config, mCallback, 0); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, hidlReturn); +} + +/** + * Test ISoundTriggerHw::startRecognition_2_1() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when called without a valid loaded sound model + * + * There is no way to verify that implementation actually starts recognition because no model can + * be loaded. + */ +TEST_F(SoundTriggerHidlTest, StartRecognitionNoModelFail_2_1) { + Return hidlReturn(0); + SoundModelHandle handle = 0; + PhraseRecognitionExtra phrase; + ISoundTriggerHw::RecognitionConfig config; + + config.header.captureHandle = 0; + config.header.captureDevice = AudioDevice::IN_BUILTIN_MIC; + phrase.id = 0; + phrase.recognitionModes = (uint32_t)RecognitionMode::VOICE_TRIGGER; + phrase.confidenceLevel = 0; + + config.header.phrases.setToExternal(&phrase, 1); + + hidlReturn = mSoundTriggerHal->startRecognition_2_1(handle, config, mCallback, 0); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, hidlReturn); +} + +/** + * Test ISoundTriggerHw::stopRecognition() method + * + * Verifies that: + * - the implementation implements the method + * - the implementation returns an error when called without an active recognition running + * + */ +TEST_F(SoundTriggerHidlTest, StopRecognitionNoAStartFail) { + Return hidlReturn(0); + SoundModelHandle handle = 0; + + hidlReturn = mSoundTriggerHal->stopRecognition(handle); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, hidlReturn); +} + +/** + * Test ISoundTriggerHw::stopAllRecognitions() method + * + * Verifies that: + * - the implementation implements this optional method or indicates it is not supported by + * returning -ENOSYS + */ +TEST_F(SoundTriggerHidlTest, stopAllRecognitions) { + Return hidlReturn(0); + + hidlReturn = mSoundTriggerHal->stopAllRecognitions(); + + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_TRUE(hidlReturn == 0 || hidlReturn == -ENOSYS); +} + +/** + * Test ISoundTriggerHw::getParameters() and setParameters() methods + * + * Verifies that: + * - the implementation implements these optional methods or indicates it is not supported by + * returning -ENOSYS + */ +TEST_F(SoundTriggerHidlTest, getAndSetParameters) { + hidl_vec keys; + hidl_vec values; + + int32_t ret = -ENODEV; + Return hidlReturn = + mSoundTriggerHal->getParameters(keys, [&](int32_t retval, auto params) { + ret = retval; + values = params; + }); + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_TRUE(ret == 0 || ret == -ENOSYS); + if (ret == 0) { + Return hidlReturn = mSoundTriggerHal->setParameters(values); + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_EQ(0, hidlReturn); + } +} + +/** + * Test ISoundTriggerHw::setParameters() method + * + * Verifies that: + * - the implementation accepts empty parameters to be set or indicates it is not supported by + * returning -ENOSYS + */ +TEST_F(SoundTriggerHidlTest, setParameters) { + hidl_vec values; + Return hidlReturn = mSoundTriggerHal->setParameters(values); + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_TRUE(hidlReturn == 0 || hidlReturn == -ENOSYS); +} + +/** + * Test ISoundTriggerHw::getSoundModelParameters() and setSoundModelParameters() methods + * + * Verifies that: + * - the implementation implements these optional methods or indicates it is not supported by + * returning -ENOSYS; + * - if the methods are supported, the implementation returns an error when called without + * an active recognition running. + * + */ +TEST_F(SoundTriggerHidlTest, getAndSetSoundModelParameters) { + SoundModelHandle handle = 0; + hidl_vec keys; + hidl_vec values; + + { + int32_t ret = 0; + Return hidlReturn = mSoundTriggerHal->getSoundModelParameters( + handle, keys, [&](int32_t retval, auto params) { + ret = retval; + values = params; + }); + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, ret); + EXPECT_EQ(0u, values.size()); + } + + values.resize(0); + { + Return hidlReturn = mSoundTriggerHal->setSoundModelParameters(handle, values); + EXPECT_TRUE(hidlReturn.isOk()); + EXPECT_NE(0, hidlReturn); + } +}