1 /*
2  * Copyright (C) 2018 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 <gtest/gtest.h>
27 #include <hidl/GtestPrinter.h>
28 #include <hidl/ServiceManagement.h>
29 #include <log/log.h>
30 
31 #include <android/hardware/audio/common/2.0/types.h>
32 #include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
33 #include <android/hardware/soundtrigger/2.0/types.h>
34 #include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
35 #include <android/hidl/allocator/1.0/IAllocator.h>
36 #include <hidlmemory/mapping.h>
37 
38 #define SHORT_TIMEOUT_PERIOD (1)
39 
40 using ::android::sp;
41 using ::android::hardware::hidl_memory;
42 using ::android::hardware::hidl_string;
43 using ::android::hardware::hidl_vec;
44 using ::android::hardware::Return;
45 using ::android::hardware::Void;
46 using ::android::hardware::audio::common::V2_0::AudioDevice;
47 using ::android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra;
48 using ::android::hardware::soundtrigger::V2_0::RecognitionMode;
49 using ::android::hardware::soundtrigger::V2_0::SoundModelHandle;
50 using ::android::hardware::soundtrigger::V2_0::SoundModelType;
51 using V2_0_ISoundTriggerHw = ::android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
52 using V2_0_ISoundTriggerHwCallback =
53     ::android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
54 using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHw;
55 using ::android::hardware::soundtrigger::V2_1::ISoundTriggerHwCallback;
56 using ::android::hidl::allocator::V1_0::IAllocator;
57 using ::android::hidl::memory::V1_0::IMemory;
58 
59 /**
60  * Test code uses this class to wait for notification from callback.
61  */
62 class Monitor {
63    public:
Monitor()64     Monitor() : mCount(0) {}
65 
66     /**
67      * Adds 1 to the internal counter and unblocks one of the waiting threads.
68      */
notify()69     void notify() {
70         std::unique_lock<std::mutex> lock(mMtx);
71         mCount++;
72         mCv.notify_one();
73     }
74 
75     /**
76      * Blocks until the internal counter becomes greater than 0.
77      *
78      * If notified, this method decreases the counter by 1 and returns true.
79      * If timeout, returns false.
80      */
wait(int timeoutSeconds)81     bool wait(int timeoutSeconds) {
82         auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(timeoutSeconds);
83         std::unique_lock<std::mutex> lock(mMtx);
84         if (!mCv.wait_until(lock, deadline, [& count = mCount] { return count > 0; })) {
85             return false;
86         }
87         mCount--;
88         return true;
89     }
90 
91    private:
92     std::mutex mMtx;
93     std::condition_variable mCv;
94     int mCount;
95 };
96 
97 // The main test class for Sound Trigger HIDL HAL.
98 class SoundTriggerHidlTest : public ::testing::TestWithParam<std::string> {
99    public:
SetUp()100     virtual void SetUp() override {
101         mSoundTriggerHal = ISoundTriggerHw::getService(GetParam());
102         ASSERT_NE(nullptr, mSoundTriggerHal.get());
103         mCallback = new SoundTriggerHwCallback(*this);
104         ASSERT_NE(nullptr, mCallback.get());
105     }
106 
SetUpTestCase()107     static void SetUpTestCase() { srand(1234); }
108 
109     class SoundTriggerHwCallback : public ISoundTriggerHwCallback {
110        private:
111         SoundTriggerHidlTest& mParent;
112 
113        public:
SoundTriggerHwCallback(SoundTriggerHidlTest & parent)114         SoundTriggerHwCallback(SoundTriggerHidlTest& parent) : mParent(parent) {}
115 
recognitionCallback(const V2_0_ISoundTriggerHwCallback::RecognitionEvent & event __unused,int32_t cookie __unused)116         Return<void> recognitionCallback(const V2_0_ISoundTriggerHwCallback::RecognitionEvent& event
117                                              __unused,
118                                          int32_t cookie __unused) override {
119             ALOGI("%s", __FUNCTION__);
120             return Void();
121         };
122 
phraseRecognitionCallback(const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent & event __unused,int32_t cookie __unused)123         Return<void> phraseRecognitionCallback(
124             const V2_0_ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused,
125             int32_t cookie __unused) override {
126             ALOGI("%s", __FUNCTION__);
127             return Void();
128         };
129 
soundModelCallback(const V2_0_ISoundTriggerHwCallback::ModelEvent & event,int32_t cookie __unused)130         Return<void> soundModelCallback(const V2_0_ISoundTriggerHwCallback::ModelEvent& event,
131                                         int32_t cookie __unused) override {
132             ALOGI("%s", __FUNCTION__);
133             mParent.lastModelEvent_2_0 = event;
134             mParent.monitor.notify();
135             return Void();
136         }
137 
recognitionCallback_2_1(const ISoundTriggerHwCallback::RecognitionEvent & event __unused,int32_t cookie __unused)138         Return<void> recognitionCallback_2_1(const ISoundTriggerHwCallback::RecognitionEvent& event
139                                                  __unused,
140                                              int32_t cookie __unused) override {
141             ALOGI("%s", __FUNCTION__);
142             return Void();
143         }
144 
phraseRecognitionCallback_2_1(const ISoundTriggerHwCallback::PhraseRecognitionEvent & event __unused,int32_t cookie __unused)145         Return<void> phraseRecognitionCallback_2_1(
146             const ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused,
147             int32_t cookie __unused) override {
148             ALOGI("%s", __FUNCTION__);
149             return Void();
150         }
151 
soundModelCallback_2_1(const ISoundTriggerHwCallback::ModelEvent & event,int32_t cookie __unused)152         Return<void> soundModelCallback_2_1(const ISoundTriggerHwCallback::ModelEvent& event,
153                                             int32_t cookie __unused) {
154             ALOGI("%s", __FUNCTION__);
155             mParent.lastModelEvent = event;
156             mParent.monitor.notify();
157             return Void();
158         }
159     };
160 
TearDown()161     virtual void TearDown() override {}
162 
163     Monitor monitor;
164     // updated by soundModelCallback()
165     V2_0_ISoundTriggerHwCallback::ModelEvent lastModelEvent_2_0;
166     // updated by soundModelCallback_2_1()
167     ISoundTriggerHwCallback::ModelEvent lastModelEvent;
168 
169    protected:
170     sp<ISoundTriggerHw> mSoundTriggerHal;
171     sp<SoundTriggerHwCallback> mCallback;
172 };
173 
174 /**
175  * Test ISoundTriggerHw::loadPhraseSoundModel_2_1() method
176  *
177  * Verifies that:
178  *  - the implementation implements the method
179  *  - the implementation returns an error when passed a malformed sound model
180  *
181  * There is no way to verify that implementation actually can load a sound model because each
182  * sound model is vendor specific.
183  */
TEST_P(SoundTriggerHidlTest,LoadInvalidModelFail_2_1)184 TEST_P(SoundTriggerHidlTest, LoadInvalidModelFail_2_1) {
185     Return<void> hidlReturn;
186     int ret = -ENODEV;
187     ISoundTriggerHw::PhraseSoundModel model;
188     SoundModelHandle handle;
189 
190     model.common.header.type = SoundModelType::UNKNOWN;
191 
192     hidlReturn = mSoundTriggerHal->loadPhraseSoundModel_2_1(model, mCallback, 0,
193                                                             [&](int32_t retval, auto res) {
194                                                                 ret = retval;
195                                                                 handle = res;
196                                                             });
197 
198     EXPECT_TRUE(hidlReturn.isOk());
199     EXPECT_NE(0, ret);
200     EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
201 }
202 
203 /**
204  * Test ISoundTriggerHw::loadSoundModel() method
205  *
206  * Verifies that:
207  *  - the implementation returns an error when passed an empty sound model
208  */
TEST_P(SoundTriggerHidlTest,LoadEmptyGenericSoundModelFail)209 TEST_P(SoundTriggerHidlTest, LoadEmptyGenericSoundModelFail) {
210     int ret = -ENODEV;
211     V2_0_ISoundTriggerHw::SoundModel model;
212     SoundModelHandle handle = 0;
213 
214     model.type = SoundModelType::GENERIC;
215 
216     Return<void> loadReturn =
217         mSoundTriggerHal->loadSoundModel(model, mCallback, 0, [&](int32_t retval, auto res) {
218             ret = retval;
219             handle = res;
220         });
221 
222     EXPECT_TRUE(loadReturn.isOk());
223     EXPECT_NE(0, ret);
224     EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
225 }
226 
227 /**
228  * Test ISoundTriggerHw::loadSoundModel_2_1() method
229  *
230  * Verifies that:
231  *  - the implementation returns error when passed a sound model with random data.
232  */
TEST_P(SoundTriggerHidlTest,LoadEmptyGenericSoundModelFail_2_1)233 TEST_P(SoundTriggerHidlTest, LoadEmptyGenericSoundModelFail_2_1) {
234     int ret = -ENODEV;
235     ISoundTriggerHw::SoundModel model;
236     SoundModelHandle handle = 0;
237 
238     model.header.type = SoundModelType::GENERIC;
239 
240     Return<void> loadReturn =
241         mSoundTriggerHal->loadSoundModel_2_1(model, mCallback, 0, [&](int32_t retval, auto res) {
242             ret = retval;
243             handle = res;
244         });
245 
246     EXPECT_TRUE(loadReturn.isOk());
247     EXPECT_NE(0, ret);
248     EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
249 }
250 
251 /**
252  * Test ISoundTriggerHw::loadSoundModel_2_1() method
253  *
254  * Verifies that:
255  *  - the implementation returns error when passed a sound model with random data.
256  */
TEST_P(SoundTriggerHidlTest,LoadGenericSoundModelFail_2_1)257 TEST_P(SoundTriggerHidlTest, LoadGenericSoundModelFail_2_1) {
258     int ret = -ENODEV;
259     ISoundTriggerHw::SoundModel model;
260     SoundModelHandle handle = 0;
261 
262     model.header.type = SoundModelType::GENERIC;
263     sp<IAllocator> ashmem = IAllocator::getService("ashmem");
264     ASSERT_NE(nullptr, ashmem.get());
265     hidl_memory hmemory;
266     int size = 100;
267     Return<void> allocReturn = ashmem->allocate(size, [&](bool success, const hidl_memory& m) {
268         ASSERT_TRUE(success);
269         hmemory = m;
270     });
271     sp<IMemory> memory = ::android::hardware::mapMemory(hmemory);
272     ASSERT_NE(nullptr, memory.get());
273     memory->update();
274     for (uint8_t *p = static_cast<uint8_t*>(static_cast<void*>(memory->getPointer())); size >= 0;
275          p++, size--) {
276         *p = rand();
277     }
278 
279     Return<void> loadReturn =
280         mSoundTriggerHal->loadSoundModel_2_1(model, mCallback, 0, [&](int32_t retval, auto res) {
281             ret = retval;
282             handle = res;
283         });
284 
285     EXPECT_TRUE(loadReturn.isOk());
286     EXPECT_NE(0, ret);
287     EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
288 }
289 
290 /**
291  * Test ISoundTriggerHw::startRecognition_2_1() method
292  *
293  * Verifies that:
294  *  - the implementation implements the method
295  *  - the implementation returns an error when called without a valid loaded sound model
296  *
297  * There is no way to verify that implementation actually starts recognition because no model can
298  * be loaded.
299  */
TEST_P(SoundTriggerHidlTest,StartRecognitionNoModelFail_2_1)300 TEST_P(SoundTriggerHidlTest, StartRecognitionNoModelFail_2_1) {
301     Return<int32_t> hidlReturn(0);
302     SoundModelHandle handle = 0;
303     PhraseRecognitionExtra phrase;
304     ISoundTriggerHw::RecognitionConfig config;
305 
306     config.header.captureHandle = 0;
307     config.header.captureDevice = AudioDevice::IN_BUILTIN_MIC;
308     phrase.id = 0;
309     phrase.recognitionModes = (uint32_t)RecognitionMode::VOICE_TRIGGER;
310     phrase.confidenceLevel = 0;
311 
312     config.header.phrases.setToExternal(&phrase, 1);
313 
314     hidlReturn = mSoundTriggerHal->startRecognition_2_1(handle, config, mCallback, 0);
315 
316     EXPECT_TRUE(hidlReturn.isOk());
317     EXPECT_NE(0, hidlReturn);
318 }
319 
320 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoundTriggerHidlTest);
321 INSTANTIATE_TEST_SUITE_P(
322         PerInstance, SoundTriggerHidlTest,
323         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISoundTriggerHw::descriptor)),
324         android::hardware::PrintInstanceNameToString);
325