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
37using ::android::hardware::audio::common::V2_0::AudioDevice;
38using ::android::hardware::soundtrigger::V2_0::SoundModelHandle;
39using ::android::hardware::soundtrigger::V2_0::SoundModelType;
40using ::android::hardware::soundtrigger::V2_0::RecognitionMode;
41using ::android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra;
42using ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
43using ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
44using ::android::hardware::Return;
45using ::android::hardware::Void;
46using ::android::sp;
47
48/**
49 * Test code uses this class to wait for notification from callback.
50 */
51class 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.
90class 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.
105class 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 */
170TEST_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 */
197TEST_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 */
223TEST_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 */
254TEST_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 */
274TEST_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 */
302TEST_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 */
319TEST_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
328int 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