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