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 #include <cinttypes>
17 
18 #include "chre/util/nanoapp/log.h"
19 
20 #include <general_test/basic_audio_test.h>
21 
22 #include <shared/send_message.h>
23 #include <shared/time_util.h>
24 
25 #define LOG_TAG "[ChreBasicAudioTest]"
26 
27 using nanoapp_testing::kOneSecondInNanoseconds;
28 using nanoapp_testing::sendFatalFailureToHost;
29 using nanoapp_testing::sendSuccessToHost;
30 
31 namespace general_test {
32 namespace {
33 
34 //! This is a reasonably high limit on the number of audio sources that a system
35 //! would expose. Use this to verify that there are no gaps in the source
36 //! handles.
37 constexpr uint32_t kMaxAudioSources = 128;
38 
39 //! This is a reasonably high limit on the sample rate for a source that the
40 //! system would expose. Sampling rates above 96kHz are likely to be too high
41 //! for always-on low-power use-cases. Yes, this omits 192kHz, but that is
42 //! generally reserved for professional audio/recording and mixing applications.
43 //! Even 96kHz is a stretch, but capping it here allows room to grow. Expected
44 //! values are more like 16kHz.
45 constexpr uint32_t kMaxAudioSampleRate = 96000;
46 
47 //! Provide a floor for the sampling rate of an audio source that the system
48 //! would expose. Nyquist theorem dictates that the maximum frequency that can
49 //! be reproduced from given sequence of samples is equal to half that of the
50 //! sampling rate. This sets a lower bound to try to detect bugs or glitches.
51 constexpr uint32_t kMinAudioSampleRate = 4000;
52 
53 //! Provide a floor for buffer duration. This ensures that at the maximum
54 //! sample rate possible, a minimum number of samples will be delivered in
55 //! a batch.
56 constexpr uint64_t kMinBufferDuration =
57     (kOneSecondInNanoseconds / kMaxAudioSampleRate) * 10;
58 
59 //! Provide a ceiling for the maximum buffer duration. This is to catch buggy
60 //! descriptors of audio sources who expose very long buffers of data which are
61 //! not practical for always-on, low-power use-cases.
62 constexpr uint64_t kMaxBufferDuration = kOneSecondInNanoseconds * 120;
63 
64 //! While a variety of sample rates are supported, for the purposes of basic
65 //! audio data validation, we buffer about 4 seconds worth of PCM audio data
66 //! sampled at 16KHz.
67 constexpr uint32_t kRequiredSampleRate = 16000;
68 
69 /**
70  * @return true if the character is ASCII printable.
71  */
isAsciiPrintable(char c)72 bool isAsciiPrintable(char c) {
73   // A simple enough test to verify that a character is printable. These
74   // constants can be verified by reviewing an ASCII chart. All printable
75   // characters that we care about for CHRE lie between these two bounds and are
76   // contiguous.
77   return (c >= ' ' && c <= '~');
78 }
79 
80 /**
81  * @return true if the supplied string is printable, null-terminated and not
82  * longer than the supplied length (including null-terminator).
83  */
verifyStringWithLength(const char * str,size_t length)84 bool verifyStringWithLength(const char *str, size_t length) {
85   bool nullTerminatorFound = false;
86   bool isPrintable = true;
87   for (size_t i = 0; i < length; i++) {
88     if (str[i] == '\0') {
89       nullTerminatorFound = true;
90       break;
91     } else if (!isAsciiPrintable(str[i])) {
92       isPrintable = false;
93       break;
94     }
95   }
96 
97   return (isPrintable && nullTerminatorFound);
98 }
99 
100 /**
101  * Validates the fields of a chreAudioSource provided by the framework and posts
102  * a failure if the source descriptor is malformed.
103  *
104  * @return true if the source was valid.
105  */
validateAudioSource(uint32_t handle,const struct chreAudioSource & source)106 bool validateAudioSource(uint32_t handle,
107                          const struct chreAudioSource &source) {
108   bool valid = false;
109   if (!verifyStringWithLength(source.name, CHRE_AUDIO_SOURCE_NAME_MAX_SIZE)) {
110     sendFatalFailureToHost("Invalid audio source name for handle ", &handle);
111   } else if (source.sampleRate > kMaxAudioSampleRate ||
112              source.sampleRate < kMinAudioSampleRate) {
113     sendFatalFailureToHost("Invalid audio sample rate for handle ", &handle);
114   } else if (source.minBufferDuration < kMinBufferDuration ||
115              source.minBufferDuration > kMaxBufferDuration) {
116     sendFatalFailureToHost("Invalid min buffer duration for handle ", &handle);
117   } else if (source.maxBufferDuration < kMinBufferDuration ||
118              source.maxBufferDuration > kMaxBufferDuration) {
119     sendFatalFailureToHost("Invalid max buffer duration for handle ", &handle);
120   } else if (source.format != CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW &&
121              source.format != CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM) {
122     sendFatalFailureToHost("Invalid audio format for handle ", &handle);
123   } else {
124     valid = true;
125   }
126 
127   return valid;
128 }
129 
validateMinimumAudioSource(const struct chreAudioSource & source)130 bool validateMinimumAudioSource(const struct chreAudioSource &source) {
131   // CHQTS requires a 16kHz, PCM-format, 2 second buffer.
132   constexpr uint64_t kRequiredBufferDuration = 2 * kOneSecondInNanoseconds;
133 
134   // Ensure that the minimum buffer size is less than or equal to the required
135   // size.
136   return (source.sampleRate == kRequiredSampleRate &&
137           source.minBufferDuration <= kRequiredBufferDuration &&
138           source.maxBufferDuration >= kRequiredBufferDuration &&
139           source.format == CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM);
140 }
141 
142 /**
143  * Attempts to query for all audio sources up to kMaxAudioSources and posts a
144  * failure if a gap is found in the handles or the provided descriptor is
145  * invalid.
146  */
validateAudioSources()147 void validateAudioSources() {
148   uint32_t validHandleCount = 0;
149   bool previousSourceFound = true;
150   bool minimumRequirementMet = false;
151   for (uint32_t handle = 0; handle < kMaxAudioSources; handle++) {
152     struct chreAudioSource audioSource;
153     bool sourceFound = chreAudioGetSource(handle, &audioSource);
154     if (sourceFound) {
155       validHandleCount++;
156       if (!previousSourceFound) {
157         sendFatalFailureToHost("Gap detected in audio handles at ", &handle);
158       } else {
159         bool valid = validateAudioSource(handle, audioSource);
160         if (valid && !minimumRequirementMet) {
161           minimumRequirementMet = validateMinimumAudioSource(audioSource);
162         }
163       }
164     }
165 
166     previousSourceFound = sourceFound;
167   }
168 
169   if (validHandleCount > 0) {
170     if (!minimumRequirementMet) {
171       sendFatalFailureToHost(
172           "Failed to meet minimum audio source requirements");
173     }
174 
175     if (validHandleCount == kMaxAudioSources) {
176       sendFatalFailureToHost("System is reporting too many audio sources");
177     }
178   }
179 }
180 
181 /**
182  * Attempts to request audio data from the default microphone handle (0),
183  * posts a failure if the data request failed
184  */
requestAudioData()185 void requestAudioData() {
186   constexpr uint32_t kAudioHandle = 0;
187   struct chreAudioSource audioSource;
188 
189   if (!chreAudioGetSource(kAudioHandle, &audioSource)) {
190     sendFatalFailureToHost("Failed to query source for handle 0");
191   } else if (!chreAudioConfigureSource(kAudioHandle, true /* enable */,
192                                        audioSource.minBufferDuration,
193                                        audioSource.minBufferDuration)) {
194     sendFatalFailureToHost("Failed to request audio data for handle 0");
195   }
196 }
197 
198 /**
199  * Check if the audio samples are all zeros
200  *
201  * @return true on check passing
202  */
checkSamplesAllZeros(const int16_t * data,const size_t dataLen)203 bool checkSamplesAllZeros(const int16_t *data, const size_t dataLen) {
204   for (size_t i = 0; i < dataLen; ++i) {
205     if (data[i] != 0) {
206       return true;
207     }
208   }
209   return false;
210 }
211 
212 /**
213  * Check if adjacent audio samples are unique
214  *
215  * @return true on check pass
216  */
checkSamplesAllSame(const int16_t * data,const size_t dataLen)217 bool checkSamplesAllSame(const int16_t *data, const size_t dataLen) {
218   if (dataLen > 0) {
219     const int16_t controlValue = data[0];
220     for (size_t i = 1; i < dataLen; ++i) {
221       if (data[i] != controlValue) {
222         return true;
223       }
224     }
225   }
226   return false;
227 }
228 
handleAudioDataEvent(const chreAudioDataEvent * dataEvent)229 void handleAudioDataEvent(const chreAudioDataEvent *dataEvent) {
230   constexpr uint32_t kAudioHandle = 0;
231 
232   // A count of the number of data events we've received - we stop
233   // the test after receiving 2 data events.
234   static uint8_t numDataEventsSoFar = 1;
235 
236   if (dataEvent == nullptr) {
237     sendFatalFailureToHost("Null event data");
238   } else if (dataEvent->samplesS16 == nullptr) {
239     sendFatalFailureToHost("Null audio data frame");
240   } else if (dataEvent->sampleCount == 0) {
241     sendFatalFailureToHost("0 samples in audio data frame");
242   } else {
243     struct chreAudioSource audioSource;
244     if (!chreAudioGetSource(kAudioHandle, &audioSource)) {
245       sendFatalFailureToHost("Failed to get audio source for handle 0");
246     } else {
247       // Per the CHRE Audio API requirements, it is expected that we exactly
248       // the number of samples that we ask for - we verify that here.
249       const auto kNumSamplesExpected =
250           static_cast<uint32_t>(audioSource.minBufferDuration *
251                                 kRequiredSampleRate / kOneSecondInNanoseconds);
252       if (dataEvent->sampleCount != kNumSamplesExpected) {
253         LOGE("Unexpected num samples - Expected: %" PRIu32 ", Actual: %" PRIu32,
254              kNumSamplesExpected, dataEvent->sampleCount);
255         uint32_t sampleCountDifference =
256             (kNumSamplesExpected > dataEvent->sampleCount)
257                 ? (kNumSamplesExpected - dataEvent->sampleCount)
258                 : (dataEvent->sampleCount - kNumSamplesExpected);
259         sendFatalFailureToHost("Unexpected number of samples received",
260                                &sampleCountDifference);
261       }
262     }
263   }
264 
265   if (numDataEventsSoFar == 2) {
266     if (!chreAudioConfigureSource(kAudioHandle, false /* enable */,
267                                   0 /* bufferDuration */,
268                                   0 /* deliveryInterval */)) {
269       sendFatalFailureToHost("Failed to disable audio source for handle 0");
270     }
271   } else {
272     ++numDataEventsSoFar;
273   }
274 
275   if (!checkSamplesAllZeros(dataEvent->samplesS16, dataEvent->sampleCount)) {
276     sendFatalFailureToHost("All audio samples were zeros");
277   } else if (!checkSamplesAllSame(dataEvent->samplesS16,
278                                   dataEvent->sampleCount)) {
279     sendFatalFailureToHost("All audio samples were identical");
280   } else {
281     sendSuccessToHost();
282   }
283 }
284 
isAudioSupported()285 bool isAudioSupported() {
286   struct chreAudioSource source;
287   constexpr uint32_t kRequiredAudioHandle = 0;
288   // If the DUT supports CHRE audio, then audio handle 0 is required to be
289   // valid. There is the risk that the chreAudioGetSource function might
290   // legitimately fail however - we should replace this function when CHRE
291   // audio capabilities in b/185155280 are implemented.
292   // TODO (b/185155280): fix this query
293   return chreAudioGetSource(kRequiredAudioHandle, &source);
294 }
295 }  // anonymous namespace
296 
BasicAudioTest()297 BasicAudioTest::BasicAudioTest()
298     : Test(CHRE_API_VERSION_1_2), mInMethod(false), mState(State::kPreStart) {}
299 
setUp(uint32_t messageSize,const void *)300 void BasicAudioTest::setUp(uint32_t messageSize, const void * /* message */) {
301   if (messageSize != 0) {
302     sendFatalFailureToHost("Beginning message expects 0 additional bytes, got ",
303                            &messageSize);
304   }
305 
306   if (!isAudioSupported()) {
307     sendSuccessToHost();
308 
309   } else {
310     validateAudioSources();
311 
312     mState = State::kExpectingAudioData;
313 
314     requestAudioData();
315   }
316 }
317 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)318 void BasicAudioTest::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
319                                  const void *eventData) {
320   if (mInMethod) {
321     sendFatalFailureToHost("handleEvent() invoked while already in method.");
322   }
323 
324   mInMethod = true;
325 
326   if (mState == State::kPreStart) {
327     unexpectedEvent(eventType);
328   } else {
329     switch (eventType) {
330       case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
331         /* This event is received, but not relevant to this test since we're
332          * mostly interested in the audio data, so we ignore it. */
333         break;
334 
335       case CHRE_EVENT_AUDIO_DATA:
336         handleAudioDataEvent(
337             static_cast<const chreAudioDataEvent *>(eventData));
338         break;
339 
340       default:
341         unexpectedEvent(eventType);
342         break;
343     }
344   }
345 
346   mInMethod = false;
347 }
348 
349 }  // namespace general_test
350