/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chre/core/audio_request_manager.h" #include "chre/core/event_loop_manager.h" #include "chre/platform/fatal_error.h" #include "chre/platform/system_time.h" /* * TODO(P1-62e045): Evict pending audio events from the event queue as needed. * * Under the following conditions, an audio data event may be posted to the * CHRE event queue when it should not be. * * 1. Nanoapp changes request * 2. Nanoapp removes request * * A previously scheduled audio data event may be residing in the event queue * and will be dispatched to the nanoapp after it has cancelled the request. * * The solution is to evict any audio events for a given audio handle that are * directed to a nanoapp before scheduling the next request to the platform. */ namespace chre { AudioRequestManager::AudioRequestManager() { size_t sourceCount = mPlatformAudio.getSourceCount(); if (!mAudioRequestLists.reserve(sourceCount)) { FATAL_ERROR_OOM(); } for (size_t i = 0; i < sourceCount; i++) { mAudioRequestLists.emplace_back(); } } bool AudioRequestManager::configureSource(const Nanoapp *nanoapp, uint32_t handle, bool enable, uint64_t bufferDuration, uint64_t deliveryInterval) { uint32_t numSamples; bool success = validateConfigureSourceArguments( handle, enable, bufferDuration, deliveryInterval, &numSamples); if (success) { size_t requestIndex; auto *audioRequest = findAudioRequest(handle, nanoapp->getInstanceId(), &requestIndex); Nanoseconds nextEventTimestamp = SystemTime::getMonotonicTime() + Nanoseconds(deliveryInterval); if (audioRequest == nullptr) { // The nanoapp is making a new request for audio data. if (enable) { mAudioRequestLists[handle].requests.emplace_back( nanoapp->getInstanceId(), numSamples, Nanoseconds(deliveryInterval), nextEventTimestamp); scheduleNextAudioDataEvent(handle); } else { LOGW("Nanoapp disabling nonexistent audio request"); } } else { // The nanoapp is modifying an existing request for audio. if (enable) { audioRequest->numSamples = numSamples; audioRequest->deliveryInterval = Nanoseconds(deliveryInterval); audioRequest->nextEventTimestamp = nextEventTimestamp; } else { mAudioRequestLists[handle].requests.erase(requestIndex); } // Note that if the next request did not change, this call is not strictly // necessary. The expectation is that the platform will gracefully handle // rescheduling the same request. scheduleNextAudioDataEvent(handle); } } return success; } void AudioRequestManager::handleAudioDataEvent( const struct chreAudioDataEvent *audioDataEvent) { auto callback = [](uint16_t /* eventType */, void *eventData) { auto *event = static_cast(eventData); EventLoopManagerSingleton::get()->getAudioRequestManager() .handleAudioDataEventSync(event); }; // Cast off the event const so that it can be provided to the free callback as // non-const. The event is provided to nanoapps as const and the runtime // itself will not modify this memory so this is safe. EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::AudioHandleDataEvent, const_cast(audioDataEvent), callback); } void AudioRequestManager::handleAudioAvailability(uint32_t handle, bool available) { struct CallbackState { uint32_t handle; bool available; }; auto *cbState = memoryAlloc(); if (cbState == nullptr) { LOG_OOM(); } else { cbState->handle = handle; cbState->available = available; auto callback = [](uint16_t /* eventType */, void *eventData) { auto *state = static_cast(eventData); EventLoopManagerSingleton::get()->getAudioRequestManager() .handleAudioAvailabilitySync(state->handle, state->available); memoryFree(state); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::AudioAvailabilityChange, cbState, callback); } } bool AudioRequestManager::validateConfigureSourceArguments( uint32_t handle, bool enable, uint64_t bufferDuration, uint64_t deliveryInterval, uint32_t *numSamples) { bool success = false; if (handle >= mAudioRequestLists.size()) { LOGE("Provided audio handle out of range"); } else if (enable) { chreAudioSource audioSource; if (!mPlatformAudio.getAudioSource(handle, &audioSource)) { LOGE("Failed to query for audio source"); } else if (bufferDuration > deliveryInterval) { LOGE("Buffer duration must be less than or equal to delivery interval"); } else if (bufferDuration < audioSource.minBufferDuration || bufferDuration > audioSource.maxBufferDuration) { LOGE("Invalid buffer duration %" PRIu64 " not in range [%" PRIu64 ",%" PRIu64 "]", bufferDuration, audioSource.minBufferDuration, audioSource.maxBufferDuration); } else { *numSamples = getSampleCountFromRateAndDuration( audioSource.sampleRate, Nanoseconds(bufferDuration)); success = true; } } else { // Disabling the request, no need to validate bufferDuration or // deliveryInterval. success = true; } return success; } AudioRequestManager::AudioRequest *AudioRequestManager::findAudioRequest( uint32_t handle, uint32_t instanceId, size_t *index) { AudioRequest *foundAudioRequest = nullptr; auto& requests = mAudioRequestLists[handle].requests; for (size_t i = 0; i < requests.size(); i++) { auto& audioRequest = requests[i]; if (audioRequest.instanceId == instanceId) { foundAudioRequest = &audioRequest; *index = i; break; } } return foundAudioRequest; } AudioRequestManager::AudioRequest *AudioRequestManager::findNextAudioRequest( uint32_t handle) { Nanoseconds earliestNextEventTimestamp = Nanoseconds(UINT64_MAX); AudioRequest *nextRequest = nullptr; auto& reqList = mAudioRequestLists[handle]; for (auto& req : reqList.requests) { if (req.nextEventTimestamp < earliestNextEventTimestamp) { earliestNextEventTimestamp = req.nextEventTimestamp; nextRequest = &req; } } return nextRequest; } void AudioRequestManager::handleAudioDataEventSync( struct chreAudioDataEvent *event) { uint32_t handle = event->handle; if (handle < mAudioRequestLists.size()) { auto& reqList = mAudioRequestLists[handle]; AudioRequest *nextAudioRequest = reqList.nextAudioRequest; if (reqList.nextAudioRequest != nullptr) { postAudioDataEventFatal(event, nextAudioRequest->instanceId); nextAudioRequest->nextEventTimestamp = SystemTime::getMonotonicTime() + nextAudioRequest->deliveryInterval; reqList.nextAudioRequest = nullptr; } else { LOGW("Received audio data event with no pending audio request"); } scheduleNextAudioDataEvent(handle); } else { LOGE("Audio data event handle out of range: %" PRIu32, handle); } } void AudioRequestManager::handleAudioAvailabilitySync(uint32_t handle, bool available) { if (handle < mAudioRequestLists.size()) { mAudioRequestLists[handle].available = available; if (available) { // TODO: Post an event to the nanoapps. } else { // TODO: Post an event to the nanoapps. } scheduleNextAudioDataEvent(handle); } else { LOGE("Audio availability handle out of range: %" PRIu32, handle); } } void AudioRequestManager::scheduleNextAudioDataEvent(uint32_t handle) { auto& reqList = mAudioRequestLists[handle]; AudioRequest *nextRequest = findNextAudioRequest(handle); if (reqList.available && nextRequest != nullptr) { Nanoseconds curTime = SystemTime::getMonotonicTime(); Nanoseconds eventDelay = Nanoseconds(0); if (nextRequest->nextEventTimestamp > curTime) { eventDelay = nextRequest->nextEventTimestamp - curTime; } reqList.nextAudioRequest = nextRequest; mPlatformAudio.requestAudioDataEvent( handle, nextRequest->numSamples, eventDelay); } else { mPlatformAudio.cancelAudioDataEventRequest(handle); } } void AudioRequestManager::postAudioDataEventFatal( struct chreAudioDataEvent *event, uint32_t instanceId) { // TODO: Ensure that audio data is delimited by audio change events. EventLoopManagerSingleton::get()->getEventLoop() .postEvent(CHRE_EVENT_AUDIO_DATA, event, freeAudioDataEventCallback, kSystemInstanceId, instanceId); } void AudioRequestManager::handleFreeAudioDataEvent( struct chreAudioDataEvent *audioDataEvent) { mPlatformAudio.releaseAudioDataEvent(audioDataEvent); } void AudioRequestManager::freeAudioDataEventCallback(uint16_t eventType, void *eventData) { auto *event = static_cast(eventData); EventLoopManagerSingleton::get()->getAudioRequestManager() .handleFreeAudioDataEvent(event); } } // namespace chre