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