1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "SoundTriggerHidlHalTest" 18#include <stdlib.h> 19#include <time.h> 20 21#include <condition_variable> 22#include <mutex> 23 24#include <android/log.h> 25#include <cutils/native_handle.h> 26#include <log/log.h> 27 28#include <android/hardware/audio/common/2.0/types.h> 29#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h> 30#include <android/hardware/soundtrigger/2.0/types.h> 31 32#include <VtsHalHidlTargetTestBase.h> 33 34#define SHORT_TIMEOUT_PERIOD (1) 35 36using ::android::hardware::audio::common::V2_0::AudioDevice; 37using ::android::hardware::soundtrigger::V2_0::SoundModelHandle; 38using ::android::hardware::soundtrigger::V2_0::SoundModelType; 39using ::android::hardware::soundtrigger::V2_0::RecognitionMode; 40using ::android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra; 41using ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw; 42using ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback; 43using ::android::hardware::Return; 44using ::android::hardware::Void; 45using ::android::sp; 46 47/** 48 * Test code uses this class to wait for notification from callback. 49 */ 50class Monitor { 51 public: 52 Monitor() : mCount(0) {} 53 54 /** 55 * Adds 1 to the internal counter and unblocks one of the waiting threads. 56 */ 57 void notify() { 58 std::unique_lock<std::mutex> lock(mMtx); 59 mCount++; 60 mCv.notify_one(); 61 } 62 63 /** 64 * Blocks until the internal counter becomes greater than 0. 65 * 66 * If notified, this method decreases the counter by 1 and returns true. 67 * If timeout, returns false. 68 */ 69 bool wait(int timeoutSeconds) { 70 std::unique_lock<std::mutex> lock(mMtx); 71 auto deadline = std::chrono::system_clock::now() + 72 std::chrono::seconds(timeoutSeconds); 73 while (mCount == 0) { 74 if (mCv.wait_until(lock, deadline) == std::cv_status::timeout) { 75 return false; 76 } 77 } 78 mCount--; 79 return true; 80 } 81 82 private: 83 std::mutex mMtx; 84 std::condition_variable mCv; 85 int mCount; 86}; 87 88// The main test class for Sound Trigger HIDL HAL. 89class SoundTriggerHidlTest : public ::testing::VtsHalHidlTargetTestBase { 90 public: 91 virtual void SetUp() override { 92 mSoundTriggerHal = 93 ::testing::VtsHalHidlTargetTestBase::getService<ISoundTriggerHw>(); 94 ASSERT_NE(nullptr, mSoundTriggerHal.get()); 95 mCallback = new SoundTriggerHwCallback(*this); 96 ASSERT_NE(nullptr, mCallback.get()); 97 } 98 99 static void SetUpTestCase() { 100 srand(time(nullptr)); 101 } 102 103 class SoundTriggerHwCallback : public ISoundTriggerHwCallback { 104 private: 105 SoundTriggerHidlTest& mParent; 106 107 public: 108 SoundTriggerHwCallback(SoundTriggerHidlTest& parent) : mParent(parent) {} 109 110 virtual Return<void> recognitionCallback( 111 const ISoundTriggerHwCallback::RecognitionEvent& event __unused, 112 int32_t cookie __unused) { 113 ALOGI("%s", __FUNCTION__); 114 return Void(); 115 } 116 117 virtual Return<void> phraseRecognitionCallback( 118 const ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused, 119 int32_t cookie __unused) { 120 ALOGI("%s", __FUNCTION__); 121 return Void(); 122 } 123 124 virtual Return<void> soundModelCallback( 125 const ISoundTriggerHwCallback::ModelEvent& event, 126 int32_t cookie __unused) { 127 ALOGI("%s", __FUNCTION__); 128 mParent.lastModelEvent = event; 129 mParent.monitor.notify(); 130 return Void(); 131 } 132 }; 133 134 virtual void TearDown() override {} 135 136 Monitor monitor; 137 // updated by soundModelCallback() 138 ISoundTriggerHwCallback::ModelEvent lastModelEvent; 139 140 protected: 141 sp<ISoundTriggerHw> mSoundTriggerHal; 142 sp<SoundTriggerHwCallback> mCallback; 143}; 144 145// A class for test environment setup (kept since this file is a template). 146class SoundTriggerHidlEnvironment : public ::testing::Environment { 147 public: 148 virtual void SetUp() {} 149 virtual void TearDown() {} 150 151 private: 152}; 153 154/** 155 * Test ISoundTriggerHw::getProperties() method 156 * 157 * Verifies that: 158 * - the implementation implements the method 159 * - the method returns 0 (no error) 160 * - the implementation supports at least one sound model and one key phrase 161 * - the implementation supports at least VOICE_TRIGGER recognition mode 162 */ 163TEST_F(SoundTriggerHidlTest, GetProperties) { 164 ISoundTriggerHw::Properties halProperties; 165 Return<void> hidlReturn; 166 int ret = -ENODEV; 167 168 hidlReturn = mSoundTriggerHal->getProperties([&](int rc, auto res) { 169 ret = rc; 170 halProperties = res; 171 }); 172 173 EXPECT_TRUE(hidlReturn.isOk()); 174 EXPECT_EQ(0, ret); 175 EXPECT_GT(halProperties.maxSoundModels, 0u); 176 EXPECT_GT(halProperties.maxKeyPhrases, 0u); 177 EXPECT_NE(0u, (halProperties.recognitionModes & (uint32_t)RecognitionMode::VOICE_TRIGGER)); 178} 179 180/** 181 * Test ISoundTriggerHw::loadPhraseSoundModel() method 182 * 183 * Verifies that: 184 * - the implementation implements the method 185 * - the implementation returns an error when passed a malformed sound model 186 * 187 * There is no way to verify that implementation actually can load a sound model because each 188 * sound model is vendor specific. 189 */ 190TEST_F(SoundTriggerHidlTest, LoadInvalidModelFail) { 191 Return<void> hidlReturn; 192 int ret = -ENODEV; 193 ISoundTriggerHw::PhraseSoundModel model; 194 SoundModelHandle handle; 195 196 model.common.type = SoundModelType::UNKNOWN; 197 198 hidlReturn = mSoundTriggerHal->loadPhraseSoundModel( 199 model, 200 mCallback, 0, [&](int32_t retval, auto res) { 201 ret = retval; 202 handle = res; 203 }); 204 205 EXPECT_TRUE(hidlReturn.isOk()); 206 EXPECT_NE(0, ret); 207 EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); 208} 209 210/** 211 * Test ISoundTriggerHw::loadSoundModel() method 212 * 213 * Verifies that: 214 * - the implementation returns error when passed a sound model with random data. 215 */ 216TEST_F(SoundTriggerHidlTest, LoadGenericSoundModelFail) { 217 int ret = -ENODEV; 218 ISoundTriggerHw::SoundModel model; 219 SoundModelHandle handle = 0; 220 221 model.type = SoundModelType::GENERIC; 222 model.data.resize(100); 223 for (auto& d : model.data) { 224 d = rand(); 225 } 226 227 Return<void> loadReturn = mSoundTriggerHal->loadSoundModel( 228 model, 229 mCallback, 0, [&](int32_t retval, auto res) { 230 ret = retval; 231 handle = res; 232 }); 233 234 EXPECT_TRUE(loadReturn.isOk()); 235 EXPECT_NE(0, ret); 236 EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD)); 237} 238 239/** 240 * Test ISoundTriggerHw::unloadSoundModel() method 241 * 242 * Verifies that: 243 * - the implementation implements the method 244 * - the implementation returns an error when called without a valid loaded sound model 245 * 246 */ 247TEST_F(SoundTriggerHidlTest, UnloadModelNoModelFail) { 248 Return<int32_t> hidlReturn(0); 249 SoundModelHandle halHandle = 0; 250 251 hidlReturn = mSoundTriggerHal->unloadSoundModel(halHandle); 252 253 EXPECT_TRUE(hidlReturn.isOk()); 254 EXPECT_NE(0, hidlReturn); 255} 256 257/** 258 * Test ISoundTriggerHw::startRecognition() method 259 * 260 * Verifies that: 261 * - the implementation implements the method 262 * - the implementation returns an error when called without a valid loaded sound model 263 * 264 * There is no way to verify that implementation actually starts recognition because no model can 265 * be loaded. 266 */ 267TEST_F(SoundTriggerHidlTest, StartRecognitionNoModelFail) { 268 Return<int32_t> hidlReturn(0); 269 SoundModelHandle handle = 0; 270 PhraseRecognitionExtra phrase; 271 ISoundTriggerHw::RecognitionConfig config; 272 273 config.captureHandle = 0; 274 config.captureDevice = AudioDevice::IN_BUILTIN_MIC; 275 phrase.id = 0; 276 phrase.recognitionModes = (uint32_t)RecognitionMode::VOICE_TRIGGER; 277 phrase.confidenceLevel = 0; 278 279 config.phrases.setToExternal(&phrase, 1); 280 281 hidlReturn = mSoundTriggerHal->startRecognition(handle, config, mCallback, 0); 282 283 EXPECT_TRUE(hidlReturn.isOk()); 284 EXPECT_NE(0, hidlReturn); 285} 286 287/** 288 * Test ISoundTriggerHw::stopRecognition() method 289 * 290 * Verifies that: 291 * - the implementation implements the method 292 * - the implementation returns an error when called without an active recognition running 293 * 294 */ 295TEST_F(SoundTriggerHidlTest, StopRecognitionNoAStartFail) { 296 Return<int32_t> hidlReturn(0); 297 SoundModelHandle handle = 0; 298 299 hidlReturn = mSoundTriggerHal->stopRecognition(handle); 300 301 EXPECT_TRUE(hidlReturn.isOk()); 302 EXPECT_NE(0, hidlReturn); 303} 304 305/** 306 * Test ISoundTriggerHw::stopAllRecognitions() method 307 * 308 * Verifies that: 309 * - the implementation implements this optional method or indicates it is not support by 310 * returning -ENOSYS 311 */ 312TEST_F(SoundTriggerHidlTest, stopAllRecognitions) { 313 Return<int32_t> hidlReturn(0); 314 315 hidlReturn = mSoundTriggerHal->stopAllRecognitions(); 316 317 EXPECT_TRUE(hidlReturn.isOk()); 318 EXPECT_TRUE(hidlReturn == 0 || hidlReturn == -ENOSYS); 319} 320 321 322int main(int argc, char** argv) { 323 ::testing::AddGlobalTestEnvironment(new SoundTriggerHidlEnvironment); 324 ::testing::InitGoogleTest(&argc, argv); 325 int status = RUN_ALL_TESTS(); 326 ALOGI("Test result = %d", status); 327 return status; 328} 329