1 /*
2 * Copyright (C) 2019 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 /**
18 * A simple nanoapp to test the CHRE audio feature.
19 *
20 * Test flow:
21 * 1) Nanoapp waits for a TEST_START message from the host.
22 * 2) Nanoapp finds a suitable audio source and requests for data.
23 * 3) Upon receiving an audio data event, cancel the audio request.
24 * 4) Verify that we do not receive any audio data events for a few seconds,
25 * and report test success.
26 */
27
28 #include <cinttypes>
29
30 #include <pb_encode.h>
31
32 #include "chre/util/macros.h"
33 #include "chre/util/nanoapp/callbacks.h"
34 #include "chre/util/nanoapp/log.h"
35 #include "chre/util/time.h"
36 #include "chre_api/chre.h"
37 #include "pts_chre.nanopb.h"
38
39 #define LOG_TAG "[PtsAudioEnableDisable]"
40
41 namespace chre {
42
43 namespace {
44
45 //! The audio handle to use, currently assuming only one source and we use the
46 //! first one available.
47 constexpr uint32_t kAudioHandle = 0;
48
49 //! The audio source to use during the test.
50 struct chreAudioSource gAudioSource;
51
52 //! The endpoint ID of the test app host.
53 uint16_t gHostEndpointId;
54
55 //! True if the nanoapp has enabled audio, expecting an audio data event.
56 //! TODO: Use TestSuccessMarker instead
57 bool gAudioEnabled = false;
58
59 //! True if the test is currently running.
60 bool gTestRunning = false;
61
62 //! Timer for validating no audio data when disabled.
63 uint32_t gTimerHandle = CHRE_TIMER_INVALID;
64
65 // TODO: Refactor this in a shared helper function.
sendTestResult(uint16_t hostEndpointId,bool success)66 void sendTestResult(uint16_t hostEndpointId, bool success) {
67 pts_chre_TestResult result = pts_chre_TestResult_init_default;
68 result.has_code = true;
69 result.code = success ? pts_chre_TestResult_Code_TEST_PASSED
70 : pts_chre_TestResult_Code_TEST_FAILED;
71 size_t size;
72 if (!pb_get_encoded_size(&size, pts_chre_TestResult_fields, &result)) {
73 LOGE("Failed to get message size");
74 } else {
75 pb_byte_t *bytes = static_cast<pb_byte_t *>(chreHeapAlloc(size));
76 if (bytes == nullptr) {
77 LOGE("Could not allocate message size %zu", size);
78 } else {
79 pb_ostream_t stream = pb_ostream_from_buffer(bytes, size);
80 if (!pb_encode(&stream, pts_chre_TestResult_fields, &result)) {
81 LOGE("Failed to encode protobuf error %s", PB_GET_ERROR(&stream));
82 chreHeapFree(bytes);
83 } else {
84 chreSendMessageToHostEndpoint(bytes, size,
85 pts_chre_MessageType_PTS_TEST_RESULT,
86 hostEndpointId, heapFreeMessageCallback);
87 gTestRunning = false;
88 }
89 }
90 }
91 }
92
discoverAudioSource()93 bool discoverAudioSource() {
94 return chreAudioGetSource(kAudioHandle, &gAudioSource);
95 }
96
handleMessageFromHost(const chreMessageFromHostData * message)97 void handleMessageFromHost(const chreMessageFromHostData *message) {
98 if (message->messageType != pts_chre_MessageType_PTS_TEST_START) {
99 LOGE("Unexpected message from host: type %" PRIu32, message->messageType);
100 } else {
101 gTestRunning = true;
102 gHostEndpointId = message->hostEndpoint;
103
104 bool success = false;
105 if (!discoverAudioSource()) {
106 LOGE("Failed to find audio source");
107 } else if (!chreAudioConfigureSource(kAudioHandle, true /* enable */,
108 gAudioSource.minBufferDuration,
109 gAudioSource.minBufferDuration)) {
110 LOGE("Failed to enable audio source");
111 } else {
112 gAudioEnabled = true;
113 // Start a timer to ensure we receive the first audio data event quickly.
114 // Since it may take some time to load the sound model, choose a
115 // reasonably long timeout.
116 gTimerHandle = chreTimerSet(20 * kOneSecondInNanoseconds,
117 nullptr /* cookie */, true /* oneShot */);
118 if (gTimerHandle == CHRE_TIMER_INVALID) {
119 LOGE("Failed to set audio enabled timer");
120 } else {
121 success = true;
122 }
123 }
124
125 if (!success) {
126 sendTestResult(gHostEndpointId, success);
127 }
128 }
129 }
130
handleAudioDataEvent(const chreAudioDataEvent *)131 void handleAudioDataEvent(const chreAudioDataEvent * /* data */) {
132 bool success = false;
133 if (!gAudioEnabled) {
134 LOGE("Received unexpected audio data");
135 } else if (!chreTimerCancel(gTimerHandle)) {
136 LOGE("Failed to cancel audio enable timer");
137 } else if (!chreAudioConfigureSource(kAudioHandle, false /* enable */,
138 0 /* bufferDuration */,
139 0 /* deliveryInterval */)) {
140 LOGE("Failed to stop audio source");
141 } else {
142 gAudioEnabled = false;
143 gTimerHandle = chreTimerSet(5 * kOneSecondInNanoseconds,
144 nullptr /* cookie */, true /* oneShot */);
145 if (gTimerHandle == CHRE_TIMER_INVALID) {
146 LOGE("Failed to set audio disabled timer");
147 } else {
148 success = true;
149 }
150 }
151
152 if (!success) {
153 sendTestResult(gHostEndpointId, success);
154 }
155 }
156
handleTimer()157 void handleTimer() {
158 bool success = false;
159 if (gTimerHandle == CHRE_TIMER_INVALID) {
160 LOGE("Unexpected timer event");
161 } else if (gAudioEnabled) {
162 LOGE("Did not receive audio data in time");
163 } else {
164 success = true;
165 }
166
167 sendTestResult(gHostEndpointId, success);
168 }
169
170 } // anonymous namespace
171
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)172 extern "C" void nanoappHandleEvent(uint32_t senderInstanceId,
173 uint16_t eventType, const void *eventData) {
174 UNUSED_VAR(senderInstanceId);
175
176 if (!gTestRunning && eventType != CHRE_EVENT_MESSAGE_FROM_HOST) {
177 return;
178 }
179
180 switch (eventType) {
181 case CHRE_EVENT_MESSAGE_FROM_HOST:
182 handleMessageFromHost(
183 static_cast<const chreMessageFromHostData *>(eventData));
184 break;
185
186 case CHRE_EVENT_AUDIO_DATA:
187 handleAudioDataEvent(static_cast<const chreAudioDataEvent *>(eventData));
188 break;
189
190 case CHRE_EVENT_TIMER:
191 handleTimer();
192 break;
193
194 case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
195 /* ignore */
196 break;
197
198 default:
199 LOGE("Unexpected event type %" PRIu16, eventType);
200 }
201 }
202
nanoappStart(void)203 extern "C" bool nanoappStart(void) {
204 return true;
205 }
206
nanoappEnd(void)207 extern "C" void nanoappEnd(void) {
208 if (gAudioEnabled) {
209 chreAudioConfigureSource(kAudioHandle, false /* enable */,
210 0 /* bufferDuration */, 0 /* deliveryInterval */);
211 }
212 }
213
214 } // namespace chre
215