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