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