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 #include <general_test/basic_audio_test.h>
18
19 #include <shared/send_message.h>
20
21 using nanoapp_testing::sendFatalFailureToHost;
22 using nanoapp_testing::sendSuccessToHost;
23
24 namespace general_test {
25 namespace {
26
27 //! Unit conversion nanoseconds per second.
28 constexpr uint64_t kNanosecondsPerSecond = 1000000000;
29
30 //! This is a reasonably high limit on the number of audio sources that a system
31 //! would expose. Use this to verify that there are no gaps in the source
32 //! handles.
33 constexpr uint32_t kMaxAudioSources = 128;
34
35 //! This is a reasonably high limit on the sample rate for a source that the
36 //! system would expose. Sampling rates above 96kHz are likely to be too high
37 //! for always-on low-power use-cases. Yes, this omits 192kHz, but that is
38 //! generally reserved for professional audio/recording and mixing applications.
39 //! Even 96kHz is a stretch, but capping it here allows room to grow. Expected
40 //! values are more like 16kHz.
41 constexpr uint32_t kMaxAudioSampleRate = 96000;
42
43 //! Provide a floor for the sampling rate of an audio source that the system
44 //! would expose. Nyquist theorem dictates that the maximum frequency that can
45 //! be reproduced from given sequence of samples is equal to half that of the
46 //! sampling rate. This sets a lower bound to try to detect bugs or glitches.
47 constexpr uint32_t kMinAudioSampleRate = 4000;
48
49 //! Provide a floor for buffer duration. This ensures that at the maximum
50 //! sample rate possible, a minimum number of samples will be delivered in
51 //! a batch.
52 constexpr uint64_t kMinBufferDuration =
53 (kNanosecondsPerSecond / kMaxAudioSampleRate) * 10;
54
55 //! Provide a ceiling for the maximum buffer duration. This is to catch buggy
56 //! descriptors of audio sources who expose very long buffers of data which are
57 //! not practical for always-on, low-power use-cases.
58 constexpr uint64_t kMaxBufferDuration = kNanosecondsPerSecond * 120;
59
60 /**
61 * @return true if the character is ASCII printable.
62 */
isAsciiPrintable(char c)63 bool isAsciiPrintable(char c) {
64 // A simple enough test to verify that a character is printable. These
65 // constants can be verified by reviewing an ASCII chart. All printable
66 // characters that we care about for CHRE lie between these two bounds and are
67 // contiguous.
68 return (c >= ' ' && c <= '~');
69 }
70
71 /**
72 * @return true if the supplied string is printable, null-terminated and not
73 * longer than the supplied length (including null-terminator).
74 */
verifyStringWithLength(const char * str,size_t length)75 bool verifyStringWithLength(const char *str, size_t length) {
76 bool nullTerminatorFound = false;
77 bool isPrintable = true;
78 for (size_t i = 0; i < length; i++) {
79 if (str[i] == '\0') {
80 nullTerminatorFound = true;
81 break;
82 } else if (!isAsciiPrintable(str[i])) {
83 isPrintable = false;
84 break;
85 }
86 }
87
88 return (isPrintable && nullTerminatorFound);
89 }
90
91 /**
92 * Validates the fields of a chreAudioSource provided by the framework and posts
93 * a failure if the source descriptor is malformed.
94 *
95 * @return true if the source was valid.
96 */
validateAudioSource(uint32_t handle,const struct chreAudioSource & source)97 bool validateAudioSource(uint32_t handle,
98 const struct chreAudioSource& source) {
99 bool valid = false;
100 if (!verifyStringWithLength(source.name, CHRE_AUDIO_SOURCE_NAME_MAX_SIZE)) {
101 sendFatalFailureToHost(
102 "Invalid audio source name for handle ", &handle);
103 } else if (source.sampleRate > kMaxAudioSampleRate
104 || source.sampleRate < kMinAudioSampleRate) {
105 sendFatalFailureToHost(
106 "Invalid audio sample rate for handle ", &handle);
107 } else if (source.minBufferDuration < kMinBufferDuration
108 || source.minBufferDuration > kMaxBufferDuration) {
109 sendFatalFailureToHost(
110 "Invalid min buffer duration for handle ", &handle);
111 } else if (source.maxBufferDuration < kMinBufferDuration
112 || source.maxBufferDuration > kMaxBufferDuration) {
113 sendFatalFailureToHost(
114 "Invalid max buffer duration for handle ", &handle);
115 } else if (source.format != CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW
116 && source.format != CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM) {
117 sendFatalFailureToHost(
118 "Invalid audio format for handle ", &handle);
119 } else {
120 valid = true;
121 }
122
123 return valid;
124 }
125
validateMinimumAudioSource(const struct chreAudioSource & source)126 bool validateMinimumAudioSource(const struct chreAudioSource& source) {
127 // CHQTS requires a 16kHz, PCM-format, 2 second buffer.
128 constexpr uint32_t kRequiredSampleRate = 16000;
129 constexpr uint64_t kRequiredBufferDuration = 2 * kNanosecondsPerSecond;
130
131 // Ensure that the minimum buffer size is less than or equal to the required
132 // size.
133 return (source.sampleRate == kRequiredSampleRate
134 && source.minBufferDuration <= kRequiredBufferDuration
135 && source.maxBufferDuration >= kRequiredBufferDuration
136 && source.format == CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM);
137 }
138
139 /**
140 * Attempts to query for all audio sources up to kMaxAudioSources and posts a
141 * failure if a gap is found in the handles or the provided descriptor is
142 * invalid.
143 */
validateAudioSources()144 void validateAudioSources() {
145 uint32_t validHandleCount = 0;
146 bool previousSourceFound = true;
147 bool minimumRequirementMet = false;
148 for (uint32_t handle = 0; handle < kMaxAudioSources; handle++) {
149 struct chreAudioSource audioSource;
150 bool sourceFound = chreAudioGetSource(handle, &audioSource);
151 if (sourceFound) {
152 validHandleCount++;
153 if (!previousSourceFound) {
154 sendFatalFailureToHost(
155 "Gap detected in audio handles at ", &handle);
156 } else {
157 bool valid = validateAudioSource(handle, audioSource);
158 if (valid && !minimumRequirementMet) {
159 minimumRequirementMet = validateMinimumAudioSource(audioSource);
160 }
161 }
162 }
163
164 previousSourceFound = sourceFound;
165 }
166
167 if (validHandleCount > 0) {
168 if (!minimumRequirementMet) {
169 sendFatalFailureToHost(
170 "Failed to meet minimum audio source requirements");
171 }
172
173 if (validHandleCount == kMaxAudioSources) {
174 sendFatalFailureToHost(
175 "System is reporting too many audio sources");
176 }
177 }
178 }
179
180 } // anonymous namespace
181
BasicAudioTest()182 BasicAudioTest::BasicAudioTest()
183 : Test(CHRE_API_VERSION_1_2),
184 mInMethod(false),
185 mState(State::kPreStart) {}
186
setUp(uint32_t messageSize,const void *)187 void BasicAudioTest::setUp(uint32_t messageSize,
188 const void * /* message */) {
189 if (messageSize != 0) {
190 sendFatalFailureToHost(
191 "Beginning message expects 0 additional bytes, got ",
192 &messageSize);
193 }
194
195 validateAudioSources();
196 sendSuccessToHost();
197 }
198
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)199 void BasicAudioTest::handleEvent(
200 uint32_t senderInstanceId, uint16_t eventType, const void* eventData) {
201 if (mInMethod) {
202 sendFatalFailureToHost("handleEvent() invoked while already in method.");
203 }
204
205 mInMethod = true;
206
207 if (mState == State::kPreStart) {
208 unexpectedEvent(eventType);
209 } else {
210 // TODO: Handle audio data from sources, perform basic sanity checks on it.
211 }
212
213 mInMethod = false;
214 }
215
216 } // namespace general_test
217