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