• 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 
17 #include <chre.h>
18 #include <cinttypes>
19 
20 #include "chre/util/macros.h"
21 #include "chre/util/nanoapp/audio.h"
22 #include "chre/util/nanoapp/log.h"
23 #include "chre/util/time.h"
24 
25 #define LOG_TAG "[AudioStress]"
26 
27 /**
28  * @file
29  *
30  * This nanoapp is designed to subscribe to audio for varying durations of
31  * time and verify that audio data is delivered when it is expected to be.
32  */
33 
34 using chre::Milliseconds;
35 using chre::Nanoseconds;
36 using chre::Seconds;
37 
38 namespace {
39 
40 //! The required buffer size for the stress test.
41 constexpr Nanoseconds kBufferDuration = Nanoseconds(Seconds(2));
42 
43 //! The required sample format for the stress test.
44 constexpr uint8_t kBufferFormat = CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM;
45 
46 //! The required sample rate for the stress test.
47 constexpr uint32_t kBufferSampleRate = 16000;
48 
49 //! The maximum amount of time that audio will not be delivered for.
50 constexpr Seconds kMaxAudioGap = Seconds(300);
51 
52 //! The list of durations to subscribe to audio for. Even durations are for when
53 //! audio is enabled and odd is for when audio is disabled.
54 constexpr Milliseconds kStressPlan[] = {
55     // clang-format off
56     // Enabled, Disabled
57     Milliseconds(20000), Milliseconds(20000),
58     Milliseconds(30000), Milliseconds(200),
59     Milliseconds(10000), Milliseconds(1000),
60     Milliseconds(10000), Milliseconds(1999),
61     Milliseconds(8000), Milliseconds(60000),
62     Milliseconds(1000), Milliseconds(1000),
63     Milliseconds(1000), Milliseconds(1000),
64     Milliseconds(1000), Milliseconds(1000),
65     Milliseconds(1000), Milliseconds(1000),
66     Milliseconds(1000), Milliseconds(1000),
67     Milliseconds(1000), Milliseconds(1000),
68     Milliseconds(1000), Milliseconds(1000),
69     Milliseconds(1000), Milliseconds(1000),
70     // clang-format on
71 };
72 
73 //! The discovered audio handle found at startup.
74 uint32_t gAudioHandle;
75 
76 //! The current position in the stress plan.
77 size_t gTestPosition = 0;
78 
79 //! The timer handle to advance through the stress test.
80 uint32_t gTimerHandle;
81 
82 //! Whether or not audio is currently suspended. If audio is delivered when this
83 //! is set to true, this is considered a test failure.
84 bool gAudioIsSuspended = true;
85 
86 //! The timestamp of the last audio data event.
87 Nanoseconds gLastAudioTimestamp;
88 
89 /**
90  * @return true when the current test phase is expecting audio data events to be
91  *         delivered.
92  */
audioIsExpected()93 bool audioIsExpected() {
94   // Even test intervals are expected to return audio events. The current test
95   // interval is gTestPosition - 1 so there is no need to invert the bit.
96   return (gTestPosition % 2);
97 }
98 
99 /**
100  * Discovers an audio source to use for the stress test. The gAudioHandle will
101  * be set if the audio source was found.
102  *
103  * @return true if a matching source was discovered successfully.
104  */
discoverAudioHandle()105 bool discoverAudioHandle() {
106   bool success = false;
107   struct chreAudioSource source;
108   for (uint32_t i = 0; !success && chreAudioGetSource(i, &source); i++) {
109     LOGI("Found audio source '%s' with %" PRIu32 "Hz %s data", source.name,
110          source.sampleRate, chre::getChreAudioFormatString(source.format));
111     LOGI("  buffer duration: [%" PRIu64 "ns, %" PRIu64 "ns]",
112          source.minBufferDuration, source.maxBufferDuration);
113 
114     if (source.sampleRate == kBufferSampleRate &&
115         source.minBufferDuration <= kBufferDuration.toRawNanoseconds() &&
116         source.maxBufferDuration >= kBufferDuration.toRawNanoseconds() &&
117         source.format == kBufferFormat) {
118       gAudioHandle = i;
119       success = true;
120     }
121   }
122 
123   if (!success) {
124     LOGW("Failed to find suitable audio source");
125   }
126 
127   return success;
128 }
129 
checkTestPassing()130 void checkTestPassing() {
131   auto lastAudioDuration = Nanoseconds(chreGetTime()) - gLastAudioTimestamp;
132   if (lastAudioDuration > kMaxAudioGap) {
133     LOGE("Test fail - audio not received for %" PRIu64 "ns",
134          lastAudioDuration.toRawNanoseconds());
135     chreAbort(-1);
136   }
137 }
138 
requestAudioForCurrentTestState(const Nanoseconds & testStateDuration)139 bool requestAudioForCurrentTestState(const Nanoseconds &testStateDuration) {
140   bool success = false;
141   LOGD("Test stage %zu", gTestPosition);
142   if (audioIsExpected()) {
143     if (!chreAudioConfigureSource(gAudioHandle, true,
144                                   kBufferDuration.toRawNanoseconds(),
145                                   kBufferDuration.toRawNanoseconds())) {
146       LOGE("Failed to enable audio");
147     } else {
148       LOGI("Enabled audio for %" PRIu64, testStateDuration.toRawNanoseconds());
149       success = true;
150     }
151   } else {
152     if (!chreAudioConfigureSource(0, false, 0, 0)) {
153       LOGE("Failed to disable audio");
154     } else {
155       LOGI("Disabled audio for %" PRIu64, testStateDuration.toRawNanoseconds());
156       success = true;
157     }
158   }
159 
160   return success;
161 }
162 
advanceTestPosition()163 bool advanceTestPosition() {
164   checkTestPassing();
165   gTimerHandle = chreTimerSet(kStressPlan[gTestPosition].toRawNanoseconds(),
166                               nullptr, true /* oneShot */);
167   bool success = (gTimerHandle != CHRE_TIMER_INVALID);
168   if (!success) {
169     LOGE("Failed to set timer");
170   } else {
171     // Grab the duration prior to incrementing the test position.
172     Nanoseconds timerDuration = kStressPlan[gTestPosition++];
173     if (gTestPosition >= ARRAY_SIZE(kStressPlan)) {
174       gTestPosition = 0;
175     }
176 
177     success = requestAudioForCurrentTestState(timerDuration);
178   }
179 
180   return success;
181 }
182 
handleTimerEvent()183 void handleTimerEvent() {
184   if (!advanceTestPosition()) {
185     LOGE("Test fail");
186   }
187 }
188 
handleAudioDataEvent(const chreAudioDataEvent * audioDataEvent)189 void handleAudioDataEvent(const chreAudioDataEvent *audioDataEvent) {
190   LOGI("Handling audio data event");
191   gLastAudioTimestamp = Nanoseconds(audioDataEvent->timestamp);
192 
193   if (gAudioIsSuspended) {
194     LOGE("Test fail - received audio when suspended");
195   } else if (!audioIsExpected()) {
196     LOGE("Test fail - received audio unexpectedly");
197   } else {
198     LOGI("Test passing - received audio when expected");
199   }
200 }
201 
handleAudioSamplingChangeEvent(const chreAudioSourceStatusEvent * audioSourceStatusEvent)202 void handleAudioSamplingChangeEvent(
203     const chreAudioSourceStatusEvent *audioSourceStatusEvent) {
204   LOGI("Handling audio sampling change event - suspended: %d",
205        audioSourceStatusEvent->status.suspended);
206   gAudioIsSuspended = audioSourceStatusEvent->status.suspended;
207 }
208 
209 }  // namespace
210 
nanoappStart()211 bool nanoappStart() {
212   LOGI("start");
213   gLastAudioTimestamp = Nanoseconds(chreGetTime());
214   return (discoverAudioHandle() && advanceTestPosition());
215 }
216 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)217 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
218                         const void *eventData) {
219   switch (eventType) {
220     case CHRE_EVENT_TIMER:
221       handleTimerEvent();
222       break;
223 
224     case CHRE_EVENT_AUDIO_DATA:
225       handleAudioDataEvent(static_cast<const chreAudioDataEvent *>(eventData));
226       break;
227 
228     case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
229       handleAudioSamplingChangeEvent(
230           static_cast<const chreAudioSourceStatusEvent *>(eventData));
231       break;
232 
233     default:
234       LOGW("Unexpected event %" PRIu16, eventType);
235       break;
236   }
237 }
238 
nanoappEnd()239 void nanoappEnd() {
240   LOGI("stop");
241 }
242