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