xref: /aosp_15_r20/system/chre/apps/test/pts/audio_enable_disable_test/src/audio_enable_disable_test.cc (revision 84e339476a462649f82315436d70fd732297a399)
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