1 /*
2 * Copyright (C) 2017 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/core/audio_request_manager.h"
18
19 #include "chre/core/audio_util.h"
20 #include "chre/core/event_loop_manager.h"
21 #include "chre/platform/fatal_error.h"
22 #include "chre/platform/system_time.h"
23 #include "chre/util/nested_data_ptr.h"
24 #include "chre/util/system/debug_dump.h"
25
26 /*
27 * TODO(P1-62e045): Evict pending audio events from the event queue as needed.
28 *
29 * Under the following conditions, an audio data event may be posted to the
30 * CHRE event queue when it should not be.
31 *
32 * 1. Nanoapp changes request
33 * 2. Nanoapp removes request
34 *
35 * A previously scheduled audio data event may be residing in the event queue
36 * and will be dispatched to the nanoapp after it has cancelled the request.
37 *
38 * The solution is to evict any audio events for a given audio handle that are
39 * directed to a nanoapp before scheduling the next request to the platform.
40 */
41
42 namespace chre {
43
init()44 void AudioRequestManager::init() {
45 mPlatformAudio.init();
46
47 size_t sourceCount = mPlatformAudio.getSourceCount();
48 if (!mAudioRequestLists.reserve(sourceCount)) {
49 FATAL_ERROR_OOM();
50 }
51
52 for (size_t i = 0; i < sourceCount; i++) {
53 mAudioRequestLists.emplace_back();
54 }
55 }
56
configureSource(const Nanoapp * nanoapp,uint32_t handle,bool enable,uint64_t bufferDuration,uint64_t deliveryInterval)57 bool AudioRequestManager::configureSource(const Nanoapp *nanoapp,
58 uint32_t handle, bool enable,
59 uint64_t bufferDuration,
60 uint64_t deliveryInterval) {
61 uint32_t numSamples;
62 return validateConfigureSourceArguments(handle, enable, bufferDuration,
63 deliveryInterval, &numSamples) &&
64 doConfigureSource(nanoapp->getInstanceId(), handle, enable, numSamples,
65 Nanoseconds(deliveryInterval));
66 }
67
handleAudioDataEvent(const struct chreAudioDataEvent * audioDataEvent)68 void AudioRequestManager::handleAudioDataEvent(
69 const struct chreAudioDataEvent *audioDataEvent) {
70 uint32_t handle = audioDataEvent->handle;
71 if (handle >= mAudioRequestLists.size()) {
72 LOGE("Received audio event for unknown handle %" PRIu32, handle);
73 } else {
74 mAudioRequestLists[handle].lastEventTimestamp =
75 SystemTime::getMonotonicTime();
76 }
77
78 auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) {
79 auto *event = static_cast<struct chreAudioDataEvent *>(data);
80 EventLoopManagerSingleton::get()
81 ->getAudioRequestManager()
82 .handleAudioDataEventSync(event);
83 };
84
85 // Cast off the event const so that it can be provided to the callback as
86 // non-const. The event is provided to nanoapps as const and the runtime
87 // itself will not modify this memory so this is safe.
88 EventLoopManagerSingleton::get()->deferCallback(
89 SystemCallbackType::AudioHandleDataEvent,
90 const_cast<struct chreAudioDataEvent *>(audioDataEvent), callback);
91 }
92
handleAudioAvailability(uint32_t handle,bool available)93 void AudioRequestManager::handleAudioAvailability(uint32_t handle,
94 bool available) {
95 auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
96 uint32_t cbHandle = NestedDataPtr<uint32_t>(data);
97 bool cbAvailable = NestedDataPtr<bool>(extraData);
98 EventLoopManagerSingleton::get()
99 ->getAudioRequestManager()
100 .handleAudioAvailabilitySync(cbHandle, cbAvailable);
101 };
102
103 EventLoopManagerSingleton::get()->deferCallback(
104 SystemCallbackType::AudioAvailabilityChange,
105 NestedDataPtr<uint32_t>(handle), callback,
106 NestedDataPtr<bool>(available));
107 }
108
logStateToBuffer(DebugDumpWrapper & debugDump) const109 void AudioRequestManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
110 debugDump.print("\nAudio:\n");
111 for (size_t i = 0; i < mAudioRequestLists.size(); i++) {
112 uint32_t handle = static_cast<uint32_t>(i);
113 struct chreAudioSource source;
114 mPlatformAudio.getAudioSource(handle, &source);
115
116 Nanoseconds timeSinceLastAudioEvent =
117 SystemTime::getMonotonicTime() -
118 mAudioRequestLists[i].lastEventTimestamp;
119 debugDump.print(
120 " handle=%" PRIu32 ", name=\"%s\", available=%d, sampleRate=%" PRIu32
121 ", buffer(ms)=[%" PRIu64 ",%" PRIu64 "], format=%" PRIu8
122 ", timeSinceLastAudioEvent(ms)=%" PRIu64 "\n",
123 handle, source.name, mAudioRequestLists[i].available, source.sampleRate,
124 Milliseconds(Nanoseconds(source.minBufferDuration)).getMilliseconds(),
125 Milliseconds(Nanoseconds(source.maxBufferDuration)).getMilliseconds(),
126 source.format, Milliseconds(timeSinceLastAudioEvent).getMilliseconds());
127
128 for (const auto &request : mAudioRequestLists[i].requests) {
129 for (const auto &instanceId : request.instanceIds) {
130 debugDump.print(" nanoappId=%" PRIu32 ", numSamples=%" PRIu32
131 ", interval(ms)=%" PRIu64 "\n",
132 instanceId, request.numSamples,
133 Milliseconds(Nanoseconds(request.deliveryInterval))
134 .getMilliseconds());
135 }
136 }
137 }
138 }
139
validateConfigureSourceArguments(uint32_t handle,bool enable,uint64_t bufferDuration,uint64_t deliveryInterval,uint32_t * numSamples)140 bool AudioRequestManager::validateConfigureSourceArguments(
141 uint32_t handle, bool enable, uint64_t bufferDuration,
142 uint64_t deliveryInterval, uint32_t *numSamples) {
143 bool success = false;
144 if (handle >= mAudioRequestLists.size()) {
145 LOGE("Provided audio handle out of range");
146 } else if (enable) {
147 chreAudioSource audioSource;
148 if (!mPlatformAudio.getAudioSource(handle, &audioSource)) {
149 LOGE("Failed to query for audio source");
150 } else if (bufferDuration > deliveryInterval) {
151 LOGE("Buffer duration must be less than or equal to delivery interval");
152 } else if (bufferDuration < audioSource.minBufferDuration ||
153 bufferDuration > audioSource.maxBufferDuration) {
154 LOGE("Invalid buffer duration %" PRIu64 " not in range [%" PRIu64
155 ",%" PRIu64 "]",
156 bufferDuration, audioSource.minBufferDuration,
157 audioSource.maxBufferDuration);
158 } else {
159 *numSamples = AudioUtil::getSampleCountFromRateAndDuration(
160 audioSource.sampleRate, Nanoseconds(bufferDuration));
161 success = true;
162 }
163 } else {
164 // Disabling the request, no need to validate bufferDuration or
165 // deliveryInterval.
166 success = true;
167 }
168 return success;
169 }
170
doConfigureSource(uint32_t instanceId,uint32_t handle,bool enable,uint32_t numSamples,Nanoseconds deliveryInterval)171 bool AudioRequestManager::doConfigureSource(uint32_t instanceId,
172 uint32_t handle, bool enable,
173 uint32_t numSamples,
174 Nanoseconds deliveryInterval) {
175 size_t requestIndex;
176 size_t requestInstanceIdIndex;
177 auto *audioRequest = findAudioRequestByInstanceId(
178 handle, instanceId, &requestIndex, &requestInstanceIdIndex);
179
180 AudioRequestList &requestList = mAudioRequestLists[handle];
181 size_t lastNumRequests = requestList.requests.size();
182
183 bool success = false;
184 if (audioRequest == nullptr) {
185 if (enable) {
186 success =
187 createAudioRequest(handle, instanceId, numSamples, deliveryInterval);
188 } else {
189 LOGW("Nanoapp disabling nonexistent audio request");
190 }
191 } else {
192 if (audioRequest->instanceIds.size() > 1) {
193 // If there are other clients listening in this configuration, remove
194 // just the instance ID.
195 audioRequest->instanceIds.erase(requestInstanceIdIndex);
196 } else {
197 // If this is the last client listening in this configuration, remove
198 // the entire request.
199 requestList.requests.erase(requestIndex);
200 }
201
202 // If the client is disabling, there is nothing to do, otherwise a request
203 // must be created successfully.
204 success = (!enable || createAudioRequest(handle, instanceId, numSamples,
205 deliveryInterval));
206 }
207
208 if (success &&
209 (getSettingState(Setting::MICROPHONE) != SettingState::DISABLED)) {
210 scheduleNextAudioDataEvent(handle);
211 updatePlatformHandleEnabled(handle, lastNumRequests);
212 }
213
214 return success;
215 }
216
updatePlatformHandleEnabled(uint32_t handle,size_t lastNumRequests)217 void AudioRequestManager::updatePlatformHandleEnabled(uint32_t handle,
218 size_t lastNumRequests) {
219 size_t numRequests = mAudioRequestLists[handle].requests.size();
220 if (lastNumRequests == 0 && numRequests > 0) {
221 mPlatformAudio.setHandleEnabled(handle, true /* enabled */);
222 } else if (lastNumRequests > 0 && numRequests == 0) {
223 mPlatformAudio.setHandleEnabled(handle, false /* enabled */);
224 }
225 }
226
createAudioRequest(uint32_t handle,uint32_t instanceId,uint32_t numSamples,Nanoseconds deliveryInterval)227 bool AudioRequestManager::createAudioRequest(uint32_t handle,
228 uint32_t instanceId,
229 uint32_t numSamples,
230 Nanoseconds deliveryInterval) {
231 AudioRequestList &requestList = mAudioRequestLists[handle];
232
233 size_t matchingRequestIndex;
234 auto *matchingAudioRequest = findAudioRequestByConfiguration(
235 handle, numSamples, deliveryInterval, &matchingRequestIndex);
236
237 bool success = false;
238 if (matchingAudioRequest != nullptr) {
239 if (!matchingAudioRequest->instanceIds.push_back(instanceId)) {
240 LOG_OOM();
241 } else {
242 success = true;
243 }
244 } else {
245 Nanoseconds timeNow = SystemTime::getMonotonicTime();
246 Nanoseconds nextEventTimestamp = timeNow + deliveryInterval;
247 if (!requestList.requests.emplace_back(numSamples, deliveryInterval,
248 nextEventTimestamp)) {
249 LOG_OOM();
250 } else if (!requestList.requests.back().instanceIds.push_back(instanceId)) {
251 requestList.requests.pop_back();
252 LOG_OOM();
253 } else {
254 success = true;
255 }
256 }
257
258 if (success) {
259 bool suspended =
260 (getSettingState(Setting::MICROPHONE) == SettingState::DISABLED);
261 postAudioSamplingChangeEvent(instanceId, handle, requestList.available,
262 suspended);
263 }
264
265 return success;
266 }
267
268 AudioRequestManager::AudioRequest *
findAudioRequestByInstanceId(uint32_t handle,uint32_t instanceId,size_t * index,size_t * instanceIdIndex)269 AudioRequestManager::findAudioRequestByInstanceId(uint32_t handle,
270 uint32_t instanceId,
271 size_t *index,
272 size_t *instanceIdIndex) {
273 AudioRequest *foundAudioRequest = nullptr;
274 auto &requests = mAudioRequestLists[handle].requests;
275 for (size_t i = 0; i < requests.size(); i++) {
276 auto &audioRequest = requests[i];
277 size_t foundInstanceIdIndex = audioRequest.instanceIds.find(instanceId);
278 if (foundInstanceIdIndex != audioRequest.instanceIds.size()) {
279 foundAudioRequest = &audioRequest;
280 *index = i;
281 *instanceIdIndex = foundInstanceIdIndex;
282 break;
283 }
284 }
285
286 return foundAudioRequest;
287 }
288
289 AudioRequestManager::AudioRequest *
findAudioRequestByConfiguration(uint32_t handle,uint32_t numSamples,Nanoseconds deliveryInterval,size_t * index)290 AudioRequestManager::findAudioRequestByConfiguration(
291 uint32_t handle, uint32_t numSamples, Nanoseconds deliveryInterval,
292 size_t *index) {
293 AudioRequest *foundAudioRequest = nullptr;
294 auto &requests = mAudioRequestLists[handle].requests;
295 for (size_t i = 0; i < requests.size(); i++) {
296 auto &audioRequest = requests[i];
297 if (audioRequest.numSamples == numSamples &&
298 audioRequest.deliveryInterval == deliveryInterval) {
299 foundAudioRequest = &audioRequest;
300 *index = i;
301 break;
302 }
303 }
304
305 return foundAudioRequest;
306 }
307
findNextAudioRequest(uint32_t handle)308 AudioRequestManager::AudioRequest *AudioRequestManager::findNextAudioRequest(
309 uint32_t handle) {
310 Nanoseconds earliestNextEventTimestamp = Nanoseconds(UINT64_MAX);
311 AudioRequest *nextRequest = nullptr;
312
313 auto &reqList = mAudioRequestLists[handle];
314 for (auto &req : reqList.requests) {
315 if (req.nextEventTimestamp < earliestNextEventTimestamp) {
316 earliestNextEventTimestamp = req.nextEventTimestamp;
317 nextRequest = &req;
318 }
319 }
320
321 return nextRequest;
322 }
323
handleAudioDataEventSync(struct chreAudioDataEvent * event)324 void AudioRequestManager::handleAudioDataEventSync(
325 struct chreAudioDataEvent *event) {
326 uint32_t handle = event->handle;
327 if (handle < mAudioRequestLists.size()) {
328 auto &reqList = mAudioRequestLists[handle];
329 AudioRequest *nextAudioRequest = reqList.nextAudioRequest;
330 if (nextAudioRequest != nullptr) {
331 postAudioDataEventFatal(event, nextAudioRequest->instanceIds);
332 nextAudioRequest->nextEventTimestamp =
333 SystemTime::getMonotonicTime() + nextAudioRequest->deliveryInterval;
334 } else {
335 LOGW("Received audio data event with no pending audio request");
336 mPlatformAudio.releaseAudioDataEvent(event);
337 }
338
339 scheduleNextAudioDataEvent(handle);
340 } else {
341 LOGE("Audio data event handle out of range: %" PRIu32, handle);
342 }
343 }
344
handleAudioAvailabilitySync(uint32_t handle,bool available)345 void AudioRequestManager::handleAudioAvailabilitySync(uint32_t handle,
346 bool available) {
347 if (handle < mAudioRequestLists.size()) {
348 if (mAudioRequestLists[handle].available != available) {
349 bool suspended =
350 (getSettingState(Setting::MICROPHONE) == SettingState::DISABLED);
351 mAudioRequestLists[handle].available = available;
352 postAudioSamplingChangeEvents(handle, suspended);
353 }
354
355 scheduleNextAudioDataEvent(handle);
356 } else {
357 LOGE("Audio availability handle out of range: %" PRIu32, handle);
358 }
359 }
360
scheduleNextAudioDataEvent(uint32_t handle)361 void AudioRequestManager::scheduleNextAudioDataEvent(uint32_t handle) {
362 if (getSettingState(Setting::MICROPHONE) == SettingState::DISABLED) {
363 LOGD("Mic access disabled, doing nothing");
364 return;
365 }
366
367 auto &reqList = mAudioRequestLists[handle];
368 AudioRequest *nextRequest = findNextAudioRequest(handle);
369
370 // Clear the next request and it will be reset below if needed.
371 reqList.nextAudioRequest = nullptr;
372 if (reqList.available && (nextRequest != nullptr)) {
373 Nanoseconds curTime = SystemTime::getMonotonicTime();
374 Nanoseconds eventDelay = Nanoseconds(0);
375 if (nextRequest->nextEventTimestamp > curTime) {
376 eventDelay = nextRequest->nextEventTimestamp - curTime;
377 }
378 reqList.nextAudioRequest = nextRequest;
379 mPlatformAudio.requestAudioDataEvent(handle, nextRequest->numSamples,
380 eventDelay);
381 } else {
382 mPlatformAudio.cancelAudioDataEventRequest(handle);
383 }
384 }
385
postAudioSamplingChangeEvents(uint32_t handle,bool suspended)386 void AudioRequestManager::postAudioSamplingChangeEvents(uint32_t handle,
387 bool suspended) {
388 const auto &requestList = mAudioRequestLists[handle];
389 for (const auto &request : requestList.requests) {
390 for (const auto &instanceId : request.instanceIds) {
391 postAudioSamplingChangeEvent(instanceId, handle, requestList.available,
392 suspended);
393 }
394 }
395 }
396
postAudioSamplingChangeEvent(uint32_t instanceId,uint32_t handle,bool available,bool suspended)397 void AudioRequestManager::postAudioSamplingChangeEvent(uint32_t instanceId,
398 uint32_t handle,
399 bool available,
400 bool suspended) {
401 auto *event = memoryAlloc<struct chreAudioSourceStatusEvent>();
402 event->handle = handle;
403 event->status.enabled = true;
404 event->status.suspended = !available || suspended;
405
406 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
407 CHRE_EVENT_AUDIO_SAMPLING_CHANGE, event, freeEventDataCallback,
408 instanceId);
409 }
410
postAudioDataEventFatal(struct chreAudioDataEvent * event,const DynamicVector<uint32_t> & instanceIds)411 void AudioRequestManager::postAudioDataEventFatal(
412 struct chreAudioDataEvent *event,
413 const DynamicVector<uint32_t> &instanceIds) {
414 if (instanceIds.empty()) {
415 LOGW("Received audio data event for no clients");
416 mPlatformAudio.releaseAudioDataEvent(event);
417 } else {
418 for (const auto &instanceId : instanceIds) {
419 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
420 CHRE_EVENT_AUDIO_DATA, event, freeAudioDataEventCallback, instanceId);
421 }
422
423 mAudioDataEventRefCounts.emplace_back(
424 event, static_cast<uint32_t>(instanceIds.size()));
425 }
426 }
427
handleFreeAudioDataEvent(struct chreAudioDataEvent * audioDataEvent)428 void AudioRequestManager::handleFreeAudioDataEvent(
429 struct chreAudioDataEvent *audioDataEvent) {
430 size_t audioDataEventRefCountIndex =
431 mAudioDataEventRefCounts.find(AudioDataEventRefCount(audioDataEvent));
432 if (audioDataEventRefCountIndex == mAudioDataEventRefCounts.size()) {
433 LOGE("Freeing invalid audio data event");
434 } else {
435 auto &audioDataEventRefCount =
436 mAudioDataEventRefCounts[audioDataEventRefCountIndex];
437 if (audioDataEventRefCount.refCount == 0) {
438 LOGE("Attempting to free an event with zero published events");
439 } else {
440 audioDataEventRefCount.refCount--;
441 if (audioDataEventRefCount.refCount == 0) {
442 mAudioDataEventRefCounts.erase(audioDataEventRefCountIndex);
443 mPlatformAudio.releaseAudioDataEvent(audioDataEvent);
444 }
445 }
446 }
447 }
448
freeAudioDataEventCallback(uint16_t eventType,void * eventData)449 void AudioRequestManager::freeAudioDataEventCallback(uint16_t eventType,
450 void *eventData) {
451 auto *event = static_cast<struct chreAudioDataEvent *>(eventData);
452 EventLoopManagerSingleton::get()
453 ->getAudioRequestManager()
454 .handleFreeAudioDataEvent(event);
455 }
456
onSettingChanged(Setting setting,SettingState state)457 void AudioRequestManager::onSettingChanged(Setting setting,
458 SettingState state) {
459 if (setting == Setting::MICROPHONE) {
460 for (size_t i = 0; i < mAudioRequestLists.size(); ++i) {
461 uint32_t handle = static_cast<uint32_t>(i);
462 if (mAudioRequestLists[i].available) {
463 if (state == SettingState::DISABLED) {
464 LOGD("Canceling data event request for handle %" PRIu32, handle);
465 postAudioSamplingChangeEvents(handle, true /* suspended */);
466 mPlatformAudio.cancelAudioDataEventRequest(handle);
467 } else {
468 LOGD("Scheduling data event for handle %" PRIu32, handle);
469 postAudioSamplingChangeEvents(handle, false /* suspended */);
470 scheduleNextAudioDataEvent(handle);
471 }
472 }
473 }
474 }
475 }
476
477 } // namespace chre
478