1 /*
2  * Copyright (C) 2020 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_audio_concurrency_test_manager.h"
18 
19 #include <pb_decode.h>
20 
21 #include "chre/util/nanoapp/log.h"
22 #include "chre/util/time.h"
23 #include "chre_audio_concurrency_test.nanopb.h"
24 #include "send_message.h"
25 
26 #define LOG_TAG "[ChreAudioConcurrencyTest]"
27 
28 namespace chre {
29 
30 using test_shared::sendEmptyMessageToHost;
31 using test_shared::sendTestResultToHost;
32 
33 namespace audio_concurrency_test {
34 
35 namespace {
36 
37 //! The message type to use with sendTestResultToHost()
38 constexpr uint32_t kTestResultMessageType =
39     chre_audio_concurrency_test_MessageType_TEST_RESULT;
40 
isTestSupported()41 bool isTestSupported() {
42   // CHRE audio was supported in CHRE v1.2
43   return chreGetVersion() >= CHRE_API_VERSION_1_2;
44 }
45 
getTestStep(const chre_audio_concurrency_test_TestCommand & command,Manager::TestStep * step)46 bool getTestStep(const chre_audio_concurrency_test_TestCommand &command,
47                  Manager::TestStep *step) {
48   bool success = true;
49   switch (command.step) {
50     case chre_audio_concurrency_test_TestCommand_Step_ENABLE_AUDIO:
51       *step = Manager::TestStep::ENABLE_AUDIO;
52       break;
53     case chre_audio_concurrency_test_TestCommand_Step_VERIFY_AUDIO_RESUME:
54       *step = Manager::TestStep::VERIFY_AUDIO_RESUME;
55       break;
56     default:
57       LOGE("Unknown test step %d", command.step);
58       success = false;
59   }
60 
61   return success;
62 }
63 
64 }  // anonymous namespace
65 
~Manager()66 Manager::~Manager() {
67   if (mAudioEnabled) {
68     chreAudioConfigureSource(kAudioHandle, false /* enable */,
69                              0 /* bufferDuration */, 0 /* deliveryInterval */);
70   }
71   cancelTimeoutTimer();
72 }
73 
handleTestCommandMessage(uint16_t hostEndpointId,TestStep step)74 bool Manager::handleTestCommandMessage(uint16_t hostEndpointId, TestStep step) {
75   bool success = true;
76 
77   // Treat as success if CHRE audio is unsupported
78   // TODO: Use all available audio sources
79   if (!isTestSupported() || !chreAudioGetSource(kAudioHandle, &mAudioSource)) {
80     sendTestResultToHost(hostEndpointId, kTestResultMessageType,
81                          true /* success */);
82   } else {
83     success = false;
84     if (step == TestStep::ENABLE_AUDIO) {
85       if (!chreAudioConfigureSource(kAudioHandle, true /* enable */,
86                                     mAudioSource.minBufferDuration,
87                                     mAudioSource.minBufferDuration)) {
88         LOGE("Failed to configure audio source");
89       } else {
90         mAudioEnabled = true;
91         // Start a timer to ensure we receive the first audio data event
92         // quickly. Since it may take some time to load the sound model, choose
93         // a reasonably long timeout.
94         success = setTimeoutTimer(20 /* durationSeconds */);
95       }
96     } else if (step == TestStep::VERIFY_AUDIO_RESUME) {
97       success = setTimeoutTimer(20 /* durationSeconds */);
98     }
99 
100     if (success) {
101       mTestSession = TestSession(hostEndpointId, step);
102       LOGI("Starting test step %" PRIu8,
103            static_cast<uint8_t>(mTestSession->step));
104     }
105   }
106 
107   return success;
108 }
109 
handleMessageFromHost(uint32_t senderInstanceId,const chreMessageFromHostData * hostData)110 void Manager::handleMessageFromHost(uint32_t senderInstanceId,
111                                     const chreMessageFromHostData *hostData) {
112   bool success = false;
113   uint32_t messageType = hostData->messageType;
114   if (senderInstanceId != CHRE_INSTANCE_ID) {
115     LOGE("Incorrect sender instance id: %" PRIu32, senderInstanceId);
116   } else if (messageType !=
117              chre_audio_concurrency_test_MessageType_TEST_COMMAND) {
118     LOGE("Invalid message type %" PRIu32, messageType);
119   } else {
120     pb_istream_t istream = pb_istream_from_buffer(
121         static_cast<const pb_byte_t *>(hostData->message),
122         hostData->messageSize);
123     chre_audio_concurrency_test_TestCommand testCommand =
124         chre_audio_concurrency_test_TestCommand_init_default;
125 
126     TestStep step;
127     if (!pb_decode(&istream, chre_audio_concurrency_test_TestCommand_fields,
128                    &testCommand)) {
129       LOGE("Failed to decode start command error %s", PB_GET_ERROR(&istream));
130     } else if (getTestStep(testCommand, &step)) {
131       success = handleTestCommandMessage(hostData->hostEndpoint, step);
132     }
133   }
134 
135   if (!success) {
136     sendTestResultToHost(hostData->hostEndpoint, kTestResultMessageType,
137                          false /* success */);
138   }
139 }
140 
handleDataFromChre(uint16_t eventType,const void * eventData)141 void Manager::handleDataFromChre(uint16_t eventType, const void *eventData) {
142   switch (eventType) {
143     case CHRE_EVENT_AUDIO_DATA:
144       handleAudioDataEvent(static_cast<const chreAudioDataEvent *>(eventData));
145       break;
146 
147     case CHRE_EVENT_TIMER:
148       handleTimer();
149       break;
150 
151     case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
152       /* ignore */
153       break;
154 
155     default:
156       LOGE("Unexpected event type %" PRIu16, eventType);
157   }
158 }
159 
handleTimer()160 void Manager::handleTimer() {
161   if (mTimerHandle != CHRE_TIMER_INVALID && mTestSession.has_value()) {
162     LOGE("Timed out during test: step %" PRIu8,
163          static_cast<uint8_t>(mTestSession->step));
164     sendTestResultToHost(mTestSession->hostEndpointId, kTestResultMessageType,
165                          false /* success */);
166     mTestSession.reset();
167   }
168 }
169 
setTimeoutTimer(uint32_t durationSeconds)170 bool Manager::setTimeoutTimer(uint32_t durationSeconds) {
171   mTimerHandle = chreTimerSet(durationSeconds * kOneSecondInNanoseconds,
172                               nullptr /* cookie */, true /* oneShot */);
173   if (mTimerHandle == CHRE_TIMER_INVALID) {
174     LOGE("Failed to set timeout timer");
175   }
176 
177   return mTimerHandle != CHRE_TIMER_INVALID;
178 }
179 
cancelTimeoutTimer()180 void Manager::cancelTimeoutTimer() {
181   if (mTimerHandle != CHRE_TIMER_INVALID) {
182     chreTimerCancel(mTimerHandle);
183     mTimerHandle = CHRE_TIMER_INVALID;
184   }
185 }
186 
validateAudioDataEvent(const chreAudioDataEvent * data)187 bool Manager::validateAudioDataEvent(const chreAudioDataEvent *data) {
188   bool ulaw8 = false;
189   if (data->format == CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW) {
190     ulaw8 = true;
191   } else if (data->format != CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM) {
192     LOGE("Invalid format %" PRIu8, data->format);
193     return false;
194   }
195 
196   // Verify that the audio data is not all zeroes
197   uint32_t numZeroes = 0;
198   for (uint32_t i = 0; i < data->sampleCount; i++) {
199     numZeroes +=
200         ulaw8 ? (data->samplesULaw8[i] == 0) : (data->samplesS16[i] == 0);
201   }
202   bool dataValid = numZeroes != data->sampleCount;
203 
204   // Verify that timestamp increases
205   static uint64_t lastTimestamp = 0;
206   bool timestampValid = data->timestamp > lastTimestamp;
207   lastTimestamp = data->timestamp;
208 
209   return dataValid && timestampValid;
210 }
211 
handleAudioDataEvent(const chreAudioDataEvent * data)212 void Manager::handleAudioDataEvent(const chreAudioDataEvent *data) {
213   if (mTestSession.has_value()) {
214     if (!validateAudioDataEvent(data)) {
215       sendTestResultToHost(mTestSession->hostEndpointId, kTestResultMessageType,
216                            false /* success */);
217       mTestSession.reset();
218     } else {
219       switch (mTestSession->step) {
220         case TestStep::ENABLE_AUDIO: {
221           cancelTimeoutTimer();
222           sendEmptyMessageToHost(
223               mTestSession->hostEndpointId,
224               chre_audio_concurrency_test_MessageType_TEST_AUDIO_ENABLED);
225 
226           // Reset the test session to avoid sending multiple TEST_AUDIO_ENABLED
227           // messages to the host, while we wait for the next step.
228           mTestSession.reset();
229           break;
230         }
231 
232         case TestStep::VERIFY_AUDIO_RESUME: {
233           cancelTimeoutTimer();
234           sendTestResultToHost(mTestSession->hostEndpointId,
235                                kTestResultMessageType, true /* success */);
236           mTestSession.reset();
237           break;
238         }
239 
240         default:
241           LOGE("Unexpected test step %" PRIu8,
242                static_cast<uint8_t>(mTestSession->step));
243           break;
244       }
245     }
246   }
247 }
248 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)249 void Manager::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
250                           const void *eventData) {
251   if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
252     handleMessageFromHost(
253         senderInstanceId,
254         static_cast<const chreMessageFromHostData *>(eventData));
255   } else if (senderInstanceId == CHRE_INSTANCE_ID) {
256     handleDataFromChre(eventType, eventData);
257   } else {
258     LOGW("Got unknown event type from senderInstanceId %" PRIu32
259          " and with eventType %" PRIu16,
260          senderInstanceId, eventType);
261   }
262 }
263 
264 }  // namespace audio_concurrency_test
265 
266 }  // namespace chre
267