From 56ec4ffcbae8aeac6c5245fc7b825d02e2e6cefd Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 23 Jan 2015 16:45:18 -0800 Subject: Refactor AudioPolicyManager AudioPolicyManager implementation is now split into the following files: files managerdefault/Gains.* class AudioGain class VolumeCurvePoint class StreamDescriptor files managerdefault/Devices.* class DeviceDescriptor class DeviceVector files managerdefault/Ports.* class AudioPort class AudioPortConfig class AudioPatch files managerdefault/IOProfile.* class IOProfile files managerdefault/HwModule.* class HwModule files managerdefault/AudioInputDescriptor.* class AudioInputDescriptor files managerdefault/AudioOutputDescriptor.* class AudioOutputDescriptor All files for libaudiopolicyservice are moved under service/ All files for libaudiopolicymanager are moved under manager/ Change-Id: I43758be1894e37d34db194b51a19ae24461e066e --- .../audiopolicy/service/AudioPolicyClientImpl.cpp | 221 ++++ .../service/AudioPolicyClientImplLegacy.cpp | 316 ++++++ .../audiopolicy/service/AudioPolicyEffects.cpp | 673 ++++++++++++ services/audiopolicy/service/AudioPolicyEffects.h | 196 ++++ .../service/AudioPolicyInterfaceImpl.cpp | 664 ++++++++++++ .../service/AudioPolicyInterfaceImplLegacy.cpp | 607 +++++++++++ .../audiopolicy/service/AudioPolicyService.cpp | 1068 ++++++++++++++++++++ services/audiopolicy/service/AudioPolicyService.h | 524 ++++++++++ 8 files changed, 4269 insertions(+) create mode 100644 services/audiopolicy/service/AudioPolicyClientImpl.cpp create mode 100644 services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp create mode 100644 services/audiopolicy/service/AudioPolicyEffects.cpp create mode 100644 services/audiopolicy/service/AudioPolicyEffects.h create mode 100644 services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp create mode 100644 services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp create mode 100644 services/audiopolicy/service/AudioPolicyService.cpp create mode 100644 services/audiopolicy/service/AudioPolicyService.h (limited to 'services/audiopolicy/service') diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp new file mode 100644 index 0000000..3e090e9 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyClientImpl" +//#define LOG_NDEBUG 0 + +#include +#include +#include "AudioPolicyService.h" + +namespace android { + +/* implementation of the client interface from the policy manager */ + +audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +status_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + return af->openOutput(module, output, config, devices, address, latencyMs, flags); +} + +audio_io_handle_t AudioPolicyService::AudioPolicyClient::openDuplicateOutput( + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +status_t AudioPolicyService::AudioPolicyClient::closeOutput(audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::suspendOutput(audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::restoreOutput(audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->openInput(module, input, config, device, address, source, flags); +} + +status_t AudioPolicyService::AudioPolicyClient::closeInput(audio_io_handle_t input) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +status_t AudioPolicyService::AudioPolicyClient::setStreamVolume(audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + return mAudioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::invalidateStream(audio_stream_type_t stream) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->invalidateStream(stream); +} + +void AudioPolicyService::AudioPolicyClient::setParameters(audio_io_handle_t io_handle, + const String8& keyValuePairs, + int delay_ms) +{ + mAudioPolicyService->setParameters(io_handle, keyValuePairs.string(), delay_ms); +} + +String8 AudioPolicyService::AudioPolicyClient::getParameters(audio_io_handle_t io_handle, + const String8& keys) +{ + String8 result = AudioSystem::getParameters(io_handle, keys); + return result; +} + +status_t AudioPolicyService::AudioPolicyClient::startTone(audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + return mAudioPolicyService->startTone(tone, stream); +} + +status_t AudioPolicyService::AudioPolicyClient::stopTone() +{ + return mAudioPolicyService->stopTone(); +} + +status_t AudioPolicyService::AudioPolicyClient::setVoiceVolume(float volume, int delay_ms) +{ + return mAudioPolicyService->setVoiceVolume(volume, delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::moveEffects(int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + +status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) +{ + return mAudioPolicyService->clientCreateAudioPatch(patch, handle, delayMs); +} + +status_t AudioPolicyService::AudioPolicyClient::releaseAudioPatch(audio_patch_handle_t handle, + int delayMs) +{ + return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs); +} + +status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig( + const struct audio_port_config *config, + int delayMs) +{ + return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs); +} + +void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate() +{ + mAudioPolicyService->onAudioPortListUpdate(); +} + +void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate() +{ + mAudioPolicyService->onAudioPatchListUpdate(); +} + +audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId() +{ + return AudioSystem::newAudioUniqueId(); +} + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp new file mode 100644 index 0000000..a79f8ae --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include + +#include +#include +#include +#include +#include +#include +#include +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include + + +namespace android { + +/* implementation of the interface to the policy manager */ +extern "C" { + +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +static audio_io_handle_t open_output(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return AUDIO_IO_HANDLE_NONE; + } + + if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || + pDevices == NULL || pLatencyMs == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = *pSamplingRate; + config.format = *pFormat; + config.channel_mask = *pChannelMask; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + status_t status = af->openOutput(module, &output, &config, pDevices, + String8(""), pLatencyMs, flags); + if (status == NO_ERROR) { + *pSamplingRate = config.sample_rate; + *pFormat = config.format; + *pChannelMask = config.channel_mask; + if (offloadInfo != NULL) { + *((audio_offload_info_t *)offloadInfo) = config.offload_info; + } + } + return output; +} + +// deprecated: replaced by aps_open_output_on_module() +audio_io_handle_t aps_open_output(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags) +{ + return open_output((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, NULL); +} + +audio_io_handle_t aps_open_output_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + return open_output(module, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, offloadInfo); +} + +audio_io_handle_t aps_open_dup_output(void *service __unused, + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +int aps_close_output(void *service __unused, audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +int aps_suspend_output(void *service __unused, audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +int aps_restore_output(void *service __unused, audio_io_handle_t output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +static audio_io_handle_t open_input(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return AUDIO_IO_HANDLE_NONE; + } + + if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || pDevices == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + + if (((*pDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) + && !captureAudioOutputAllowed()) { + ALOGE("open_input() permission denied: capture not allowed"); + return AUDIO_IO_HANDLE_NONE; + } + + audio_config_t config = AUDIO_CONFIG_INITIALIZER;; + config.sample_rate = *pSamplingRate; + config.format = *pFormat; + config.channel_mask = *pChannelMask; + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + status_t status = af->openInput(module, &input, &config, pDevices, + String8(""), AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_FAST /*FIXME*/); + if (status == NO_ERROR) { + *pSamplingRate = config.sample_rate; + *pFormat = config.format; + *pChannelMask = config.channel_mask; + } + return input; +} + + +// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored +audio_io_handle_t aps_open_input(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + audio_in_acoustics_t acoustics __unused) +{ + return open_input((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +audio_io_handle_t aps_open_input_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + return open_input(module, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +int aps_close_input(void *service __unused, audio_io_handle_t input) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->invalidateStream(stream); +} + +int aps_move_effects(void *service __unused, int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys) +{ + String8 result = AudioSystem::getParameters(io_handle, String8(keys)); + return strdup(result.string()); +} + +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms); +} + +int aps_set_stream_volume(void *service, audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->startTone(tone, stream); +} + +int aps_stop_tone(void *service) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->stopTone(); +} + +int aps_set_voice_volume(void *service, float volume, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setVoiceVolume(volume, delay_ms); +} + +}; // extern "C" + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp new file mode 100644 index 0000000..e6ace20 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -0,0 +1,673 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "AudioPolicyEffects" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "AudioPolicyEffects.h" +#include "ServiceUtilities.h" + +namespace android { + +// ---------------------------------------------------------------------------- +// AudioPolicyEffects Implementation +// ---------------------------------------------------------------------------- + +AudioPolicyEffects::AudioPolicyEffects() +{ + // load automatic audio effect modules + if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { + loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE); + } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { + loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); + } +} + + +AudioPolicyEffects::~AudioPolicyEffects() +{ + size_t i = 0; + // release audio input processing resources + for (i = 0; i < mInputSources.size(); i++) { + delete mInputSources.valueAt(i); + } + mInputSources.clear(); + + for (i = 0; i < mInputs.size(); i++) { + mInputs.valueAt(i)->mEffects.clear(); + delete mInputs.valueAt(i); + } + mInputs.clear(); + + // release audio output processing resources + for (i = 0; i < mOutputStreams.size(); i++) { + delete mOutputStreams.valueAt(i); + } + mOutputStreams.clear(); + + for (i = 0; i < mOutputSessions.size(); i++) { + mOutputSessions.valueAt(i)->mEffects.clear(); + delete mOutputSessions.valueAt(i); + } + mOutputSessions.clear(); +} + + +status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input, + audio_source_t inputSource, + int audioSession) +{ + status_t status = NO_ERROR; + + // create audio pre processors according to input source + audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; + + Mutex::Autolock _l(mLock); + ssize_t index = mInputSources.indexOfKey(aliasSource); + if (index < 0) { + ALOGV("addInputEffects(): no processing needs to be attached to this source"); + return status; + } + ssize_t idx = mInputs.indexOfKey(input); + EffectVector *inputDesc; + if (idx < 0) { + inputDesc = new EffectVector(audioSession); + mInputs.add(input, inputDesc); + } else { + // EffectVector is existing and we just need to increase ref count + inputDesc = mInputs.valueAt(idx); + } + inputDesc->mRefCount++; + + ALOGV("addInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); + if (inputDesc->mRefCount == 1) { + Vector effects = mInputSources.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, + audioSession, input); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("addInputEffects(): failed to create Fx %s on source %d", + effect->mName, (int32_t)aliasSource); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + for (size_t j = 0; j < effect->mParams.size(); j++) { + fx->setParameter(effect->mParams[j]); + } + ALOGV("addInputEffects(): added Fx %s on source: %d", + effect->mName, (int32_t)aliasSource); + inputDesc->mEffects.add(fx); + } + inputDesc->setProcessorEnabled(true); + } + return status; +} + + +status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input) +{ + status_t status = NO_ERROR; + + Mutex::Autolock _l(mLock); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + return status; + } + EffectVector *inputDesc = mInputs.valueAt(index); + inputDesc->mRefCount--; + ALOGV("releaseInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); + if (inputDesc->mRefCount == 0) { + inputDesc->setProcessorEnabled(false); + delete inputDesc; + mInputs.removeItemsAt(index); + ALOGV("releaseInputEffects(): all effects released"); + } + return status; +} + +status_t AudioPolicyEffects::queryDefaultInputEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + status_t status = NO_ERROR; + + Mutex::Autolock _l(mLock); + size_t index; + for (index = 0; index < mInputs.size(); index++) { + if (mInputs.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mInputs.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp > effects = mInputs.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + + +status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + status_t status = NO_ERROR; + + Mutex::Autolock _l(mLock); + size_t index; + for (index = 0; index < mOutputSessions.size(); index++) { + if (mOutputSessions.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mOutputSessions.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp > effects = mOutputSessions.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + + +status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession) +{ + status_t status = NO_ERROR; + + Mutex::Autolock _l(mLock); + // create audio processors according to stream + // FIXME: should we have specific post processing settings for internal streams? + // default to media for now. + if (stream >= AUDIO_STREAM_PUBLIC_CNT) { + stream = AUDIO_STREAM_MUSIC; + } + ssize_t index = mOutputStreams.indexOfKey(stream); + if (index < 0) { + ALOGV("addOutputSessionEffects(): no output processing needed for this stream"); + return NO_ERROR; + } + + ssize_t idx = mOutputSessions.indexOfKey(audioSession); + EffectVector *procDesc; + if (idx < 0) { + procDesc = new EffectVector(audioSession); + mOutputSessions.add(audioSession, procDesc); + } else { + // EffectVector is existing and we just need to increase ref count + procDesc = mOutputSessions.valueAt(idx); + } + procDesc->mRefCount++; + + ALOGV("addOutputSessionEffects(): session: %d, refCount: %d", + audioSession, procDesc->mRefCount); + if (procDesc->mRefCount == 1) { + Vector effects = mOutputStreams.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp fx = new AudioEffect(NULL, &effect->mUuid, 0, 0, 0, + audioSession, output); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d", + effect->mName, audioSession); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d", + effect->mName, audioSession, (int32_t)stream); + procDesc->mEffects.add(fx); + } + + procDesc->setProcessorEnabled(true); + } + return status; +} + +status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession) +{ + status_t status = NO_ERROR; + (void) output; // argument not used for now + (void) stream; // argument not used for now + + Mutex::Autolock _l(mLock); + ssize_t index = mOutputSessions.indexOfKey(audioSession); + if (index < 0) { + ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream"); + return NO_ERROR; + } + + EffectVector *procDesc = mOutputSessions.valueAt(index); + procDesc->mRefCount--; + ALOGV("releaseOutputSessionEffects(): session: %d, refCount: %d", + audioSession, procDesc->mRefCount); + if (procDesc->mRefCount == 0) { + procDesc->setProcessorEnabled(false); + procDesc->mEffects.clear(); + delete procDesc; + mOutputSessions.removeItemsAt(index); + ALOGV("releaseOutputSessionEffects(): output processing released from session: %d", + audioSession); + } + return status; +} + + +void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) +{ + for (size_t i = 0; i < mEffects.size(); i++) { + mEffects.itemAt(i)->setEnabled(enabled); + } +} + + +// ---------------------------------------------------------------------------- +// Audio processing configuration +// ---------------------------------------------------------------------------- + +/*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = { + MIC_SRC_TAG, + VOICE_UL_SRC_TAG, + VOICE_DL_SRC_TAG, + VOICE_CALL_SRC_TAG, + CAMCORDER_SRC_TAG, + VOICE_REC_SRC_TAG, + VOICE_COMM_SRC_TAG +}; + +// returns the audio_source_t enum corresponding to the input source name or +// AUDIO_SOURCE_CNT is no match found +/*static*/ audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name) +{ + int i; + for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { + if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { + ALOGV("inputSourceNameToEnum found source %s %d", name, i); + break; + } + } + return (audio_source_t)i; +} + +const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1] = { + AUDIO_STREAM_DEFAULT_TAG, + AUDIO_STREAM_VOICE_CALL_TAG, + AUDIO_STREAM_SYSTEM_TAG, + AUDIO_STREAM_RING_TAG, + AUDIO_STREAM_MUSIC_TAG, + AUDIO_STREAM_ALARM_TAG, + AUDIO_STREAM_NOTIFICATION_TAG, + AUDIO_STREAM_BLUETOOTH_SCO_TAG, + AUDIO_STREAM_ENFORCED_AUDIBLE_TAG, + AUDIO_STREAM_DTMF_TAG, + AUDIO_STREAM_TTS_TAG +}; + +// returns the audio_stream_t enum corresponding to the output stream name or +// AUDIO_STREAM_PUBLIC_CNT is no match found +audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) +{ + int i; + for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_PUBLIC_CNT; i++) { + if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { + ALOGV("streamNameToEnum found stream %s %d", name, i); + break; + } + } + return (audio_stream_type_t)i; +} + +// ---------------------------------------------------------------------------- +// Audio Effect Config parser +// ---------------------------------------------------------------------------- + +size_t AudioPolicyEffects::growParamSize(char *param, + size_t size, + size_t *curSize, + size_t *totSize) +{ + // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) + size_t pos = ((*curSize - 1 ) / size + 1) * size; + + if (pos + size > *totSize) { + while (pos + size > *totSize) { + *totSize += ((*totSize + 7) / 8) * 4; + } + param = (char *)realloc(param, *totSize); + } + *curSize = pos + size; + return pos; +} + +size_t AudioPolicyEffects::readParamValue(cnode *node, + char *param, + size_t *curSize, + size_t *totSize) +{ + if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(short), curSize, totSize); + *(short *)((char *)param + pos) = (short)atoi(node->value); + ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos)); + return sizeof(short); + } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(int), curSize, totSize); + *(int *)((char *)param + pos) = atoi(node->value); + ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos)); + return sizeof(int); + } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(float), curSize, totSize); + *(float *)((char *)param + pos) = (float)atof(node->value); + ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos)); + return sizeof(float); + } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(bool), curSize, totSize); + if (strncmp(node->value, "false", strlen("false") + 1) == 0) { + *(bool *)((char *)param + pos) = false; + } else { + *(bool *)((char *)param + pos) = true; + } + ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false"); + return sizeof(bool); + } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { + size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX); + if (*curSize + len + 1 > *totSize) { + *totSize = *curSize + len + 1; + param = (char *)realloc(param, *totSize); + } + strncpy(param + *curSize, node->value, len); + *curSize += len; + param[*curSize] = '\0'; + ALOGV("readParamValue() reading string %s", param + *curSize - len); + return len; + } + ALOGW("readParamValue() unknown param type %s", node->name); + return 0; +} + +effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) +{ + cnode *param; + cnode *value; + size_t curSize = sizeof(effect_param_t); + size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); + effect_param_t *fx_param = (effect_param_t *)malloc(totSize); + + param = config_find(root, PARAM_TAG); + value = config_find(root, VALUE_TAG); + if (param == NULL && value == NULL) { + // try to parse simple parameter form {int int} + param = root->first_child; + if (param != NULL) { + // Note: that a pair of random strings is read as 0 0 + int *ptr = (int *)fx_param->data; + int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); + ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); + *ptr++ = atoi(param->name); + *ptr = atoi(param->value); + fx_param->psize = sizeof(int); + fx_param->vsize = sizeof(int); + return fx_param; + } + } + if (param == NULL || value == NULL) { + ALOGW("loadEffectParameter() invalid parameter description %s", root->name); + goto error; + } + + fx_param->psize = 0; + param = param->first_child; + while (param) { + ALOGV("loadEffectParameter() reading param of type %s", param->name); + size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize); + if (size == 0) { + goto error; + } + fx_param->psize += size; + param = param->next; + } + + // align start of value field on 32 bit boundary + curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); + + fx_param->vsize = 0; + value = value->first_child; + while (value) { + ALOGV("loadEffectParameter() reading value of type %s", value->name); + size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize); + if (size == 0) { + goto error; + } + fx_param->vsize += size; + value = value->next; + } + + return fx_param; + +error: + delete fx_param; + return NULL; +} + +void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector & params) +{ + cnode *node = root->first_child; + while (node) { + ALOGV("loadEffectParameters() loading param %s", node->name); + effect_param_t *param = loadEffectParameter(node); + if (param == NULL) { + node = node->next; + continue; + } + params.add(param); + node = node->next; + } +} + + +AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig( + cnode *root, + const Vector & effects) +{ + cnode *node = root->first_child; + if (node == NULL) { + ALOGW("loadInputSource() empty element %s", root->name); + return NULL; + } + EffectDescVector *desc = new EffectDescVector(); + while (node) { + size_t i; + for (i = 0; i < effects.size(); i++) { + if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { + ALOGV("loadEffectConfig() found effect %s in list", node->name); + break; + } + } + if (i == effects.size()) { + ALOGV("loadEffectConfig() effect %s not in list", node->name); + node = node->next; + continue; + } + EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy + loadEffectParameters(node, effect->mParams); + ALOGV("loadEffectConfig() adding effect %s uuid %08x", + effect->mName, effect->mUuid.timeLow); + desc->mEffects.add(effect); + node = node->next; + } + if (desc->mEffects.size() == 0) { + ALOGW("loadEffectConfig() no valid effects found in config %s", root->name); + delete desc; + return NULL; + } + return desc; +} + +status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root, + const Vector & effects) +{ + cnode *node = config_find(root, PREPROCESSING_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + audio_source_t source = inputSourceNameToEnum(node->name); + if (source == AUDIO_SOURCE_CNT) { + ALOGW("loadInputSources() invalid input source %s", node->name); + node = node->next; + continue; + } + ALOGV("loadInputSources() loading input source %s", node->name); + EffectDescVector *desc = loadEffectConfig(node, effects); + if (desc == NULL) { + node = node->next; + continue; + } + mInputSources.add(source, desc); + node = node->next; + } + return NO_ERROR; +} + +status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, + const Vector & effects) +{ + cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + audio_stream_type_t stream = streamNameToEnum(node->name); + if (stream == AUDIO_STREAM_PUBLIC_CNT) { + ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); + node = node->next; + continue; + } + ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name); + EffectDescVector *desc = loadEffectConfig(node, effects); + if (desc == NULL) { + node = node->next; + continue; + } + mOutputStreams.add(stream, desc); + node = node->next; + } + return NO_ERROR; +} + +AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root) +{ + cnode *node = config_find(root, UUID_TAG); + if (node == NULL) { + return NULL; + } + effect_uuid_t uuid; + if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { + ALOGW("loadEffect() invalid uuid %s", node->value); + return NULL; + } + return new EffectDesc(root->name, uuid); +} + +status_t AudioPolicyEffects::loadEffects(cnode *root, Vector & effects) +{ + cnode *node = config_find(root, EFFECTS_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + ALOGV("loadEffects() loading effect %s", node->name); + EffectDesc *effect = loadEffect(node); + if (effect == NULL) { + node = node->next; + continue; + } + effects.add(effect); + node = node->next; + } + return NO_ERROR; +} + +status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + Vector effects; + loadEffects(root, effects); + loadInputEffectConfigurations(root, effects); + loadStreamEffectConfigurations(root, effects); + + for (size_t i = 0; i < effects.size(); i++) { + delete effects[i]; + } + + config_free(root); + free(root); + free(data); + + return NO_ERROR; +} + + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h new file mode 100644 index 0000000..3dec437 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_AUDIOPOLICYEFFECTS_H +#define ANDROID_AUDIOPOLICYEFFECTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +// AudioPolicyEffects class +// This class will manage all effects attached to input and output streams in +// AudioPolicyService as configured in audio_effects.conf. +class AudioPolicyEffects : public RefBase +{ + +public: + + // The constructor will parse audio_effects.conf + // First it will look whether vendor specific file exists, + // otherwise it will parse the system default file. + AudioPolicyEffects(); + virtual ~AudioPolicyEffects(); + + // NOTE: methods on AudioPolicyEffects should never be called with the AudioPolicyService + // main mutex (mLock) held as they will indirectly call back into AudioPolicyService when + // managing audio effects. + + // Return a list of effect descriptors for default input effects + // associated with audioSession + status_t queryDefaultInputEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + + // Add all input effects associated with this input + // Effects are attached depending on the audio_source_t + status_t addInputEffects(audio_io_handle_t input, + audio_source_t inputSource, + int audioSession); + + // Add all input effects associated to this input + status_t releaseInputEffects(audio_io_handle_t input); + + + // Return a list of effect descriptors for default output effects + // associated with audioSession + status_t queryDefaultOutputSessionEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + + // Add all output effects associated to this output + // Effects are attached depending on the audio_stream_type_t + status_t addOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession); + + // release all output effects associated with this output stream and audiosession + status_t releaseOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession); + +private: + + // class to store the description of an effects and its parameters + // as defined in audio_effects.conf + class EffectDesc { + public: + EffectDesc(const char *name, const effect_uuid_t& uuid) : + mName(strdup(name)), + mUuid(uuid) { } + EffectDesc(const EffectDesc& orig) : + mName(strdup(orig.mName)), + mUuid(orig.mUuid) { + // deep copy mParams + for (size_t k = 0; k < orig.mParams.size(); k++) { + effect_param_t *origParam = orig.mParams[k]; + // psize and vsize are rounded up to an int boundary for allocation + size_t origSize = sizeof(effect_param_t) + + ((origParam->psize + 3) & ~3) + + ((origParam->vsize + 3) & ~3); + effect_param_t *dupParam = (effect_param_t *) malloc(origSize); + memcpy(dupParam, origParam, origSize); + // This works because the param buffer allocation is also done by + // multiples of 4 bytes originally. In theory we should memcpy only + // the actual param size, that is without rounding vsize. + mParams.add(dupParam); + } + } + /*virtual*/ ~EffectDesc() { + free(mName); + for (size_t k = 0; k < mParams.size(); k++) { + free(mParams[k]); + } + } + char *mName; + effect_uuid_t mUuid; + Vector mParams; + }; + + // class to store voctor of EffectDesc + class EffectDescVector { + public: + EffectDescVector() {} + /*virtual*/ ~EffectDescVector() { + for (size_t j = 0; j < mEffects.size(); j++) { + delete mEffects[j]; + } + } + Vector mEffects; + }; + + // class to store voctor of AudioEffects + class EffectVector { + public: + EffectVector(int session) : mSessionId(session), mRefCount(0) {} + /*virtual*/ ~EffectVector() {} + + // Enable or disable all effects in effect vector + void setProcessorEnabled(bool enabled); + + const int mSessionId; + // AudioPolicyManager keeps mLock, no need for lock on reference count here + int mRefCount; + Vector< sp >mEffects; + }; + + + static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1]; + static audio_source_t inputSourceNameToEnum(const char *name); + + static const char *kStreamNames[AUDIO_STREAM_PUBLIC_CNT+1]; //+1 required as streams start from -1 + audio_stream_type_t streamNameToEnum(const char *name); + + // Parse audio_effects.conf + status_t loadAudioEffectConfig(const char *path); + + // Load all effects descriptors in configuration file + status_t loadEffects(cnode *root, Vector & effects); + EffectDesc *loadEffect(cnode *root); + + // Load all automatic effect configurations + status_t loadInputEffectConfigurations(cnode *root, const Vector & effects); + status_t loadStreamEffectConfigurations(cnode *root, const Vector & effects); + EffectDescVector *loadEffectConfig(cnode *root, const Vector & effects); + + // Load all automatic effect parameters + void loadEffectParameters(cnode *root, Vector & params); + effect_param_t *loadEffectParameter(cnode *root); + size_t readParamValue(cnode *node, + char *param, + size_t *curSize, + size_t *totSize); + size_t growParamSize(char *param, + size_t size, + size_t *curSize, + size_t *totSize); + + // protects access to mInputSources, mInputs, mOutputStreams, mOutputSessions + Mutex mLock; + // Automatic input effects are configured per audio_source_t + KeyedVector< audio_source_t, EffectDescVector* > mInputSources; + // Automatic input effects are unique for audio_io_handle_t + KeyedVector< audio_io_handle_t, EffectVector* > mInputs; + + // Automatic output effects are organized per audio_stream_type_t + KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams; + // Automatic output effects are unique for audiosession ID + KeyedVector< int32_t, EffectVector* > mOutputSessions; +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYEFFECTS_H diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp new file mode 100644 index 0000000..e9ff838 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyIntefaceImpl" +//#define LOG_NDEBUG 0 + +#include +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setDeviceConnectionState(device, state, + device_address, device_name); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mAudioPolicyManager->getDeviceConnectionState(device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setPhoneState(state); + mPhoneState = state; + return NO_ERROR; +} + +audio_mode_t AudioPolicyService::getPhoneState() +{ + Mutex::Autolock _l(mLock); + return mPhoneState; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setForceUse(usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mAudioPolicyManager->getForceUse(usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return AUDIO_IO_HANDLE_NONE; + } + if (mAudioPolicyManager == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutput(stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + // create audio processors according to stream + status_t status = audioPolicyEffects->addOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on session %d", session); + } + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->startOutput(output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + // release audio processors from the stream + status_t status = audioPolicyEffects->releaseOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to release effects on session %d", session); + } + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->stopOutput(output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output, stream, session); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseOutput(output, stream, session); +} + +status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + // already checked by client, but double-check in case the client wrapper is bypassed + if (attr->source >= AUDIO_SOURCE_CNT && attr->source != AUDIO_SOURCE_HOTWORD && + attr->source != AUDIO_SOURCE_FM_TUNER) { + return BAD_VALUE; + } + + if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || + ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { + return BAD_VALUE; + } + spaudioPolicyEffects; + status_t status; + AudioPolicyInterface::input_type_t inputType; + { + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + status = mAudioPolicyManager->getInputForAttr(attr, input, session, + samplingRate, format, channelMask, + flags, &inputType); + audioPolicyEffects = mAudioPolicyEffects; + + if (status == NO_ERROR) { + // enforce permission (if any) required for each type of input + switch (inputType) { + case AudioPolicyInterface::API_INPUT_LEGACY: + break; + case AudioPolicyInterface::API_INPUT_MIX_CAPTURE: + if (!captureAudioOutputAllowed()) { + ALOGE("getInputForAttr() permission denied: capture not allowed"); + status = PERMISSION_DENIED; + } + break; + case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE: + if (!modifyAudioRoutingAllowed()) { + ALOGE("getInputForAttr() permission denied: modify audio routing not allowed"); + status = PERMISSION_DENIED; + } + break; + case AudioPolicyInterface::API_INPUT_INVALID: + default: + LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d", + (int)inputType); + } + } + + if (status != NO_ERROR) { + if (status == PERMISSION_DENIED) { + mAudioPolicyManager->releaseInput(*input, session); + } + return status; + } + } + + if (audioPolicyEffects != 0) { + // create audio pre processors according to input source + status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on input %d", *input); + } + } + return NO_ERROR; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->startInput(input, session); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->stopInput(input, session); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return; + } + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseInput(input, session); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + // release audio processors from the input + status_t status = audioPolicyEffects->releaseInputEffects(input); + if(status != NO_ERROR) { + ALOGW("Failed to release effects on input %d", input); + } + } +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mAudioPolicyManager->initStreamVolume(stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setStreamVolumeIndex(stream, + index, + device); +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getStreamVolumeIndex(stream, + index, + device); +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return 0; + } + if (mAudioPolicyManager == NULL) { + return 0; + } + return mAudioPolicyManager->getStrategyForStream(stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return AUDIO_DEVICE_NONE; + } + if (mAudioPolicyManager == NULL) { + return AUDIO_DEVICE_NONE; + } + return mAudioPolicyManager->getDevicesForStream(stream); +} + +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) +{ + // FIXME change return type to status_t, and return NO_INIT here + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutputForEffect(desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->registerEffect(desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->unregisterEffect(id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->setEffectEnabled(id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return false; + } + if (mAudioPolicyManager == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActive(stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return false; + } + if (mAudioPolicyManager == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActiveRemotely(stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mAudioPolicyManager == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isSourceActive(source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + if (mAudioPolicyManager == NULL) { + *count = 0; + return NO_INIT; + } + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects == 0) { + *count = 0; + return NO_INIT; + } + return audioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count); +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mAudioPolicyManager == NULL) { + ALOGV("mAudioPolicyManager == NULL"); + return false; + } + + return mAudioPolicyManager->isOffloadSupported(info); +} + +status_t AudioPolicyService::listAudioPorts(audio_port_role_t role, + audio_port_type_t type, + unsigned int *num_ports, + struct audio_port *ports, + unsigned int *generation) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation); +} + +status_t AudioPolicyService::getAudioPort(struct audio_port *port) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->getAudioPort(port); +} + +status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->createAudioPatch(patch, handle, + IPCThreadState::self()->getCallingUid()); +} + +status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->releaseAudioPatch(handle, + IPCThreadState::self()->getCallingUid()); +} + +status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches, + unsigned int *generation) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation); +} + +status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->setAudioPortConfig(config); +} + +status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->acquireSoundTriggerSession(session, ioHandle, device); +} + +status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->releaseSoundTriggerSession(session); +} + +status_t AudioPolicyService::registerPolicyMixes(Vector mixes, bool registration) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (registration) { + return mAudioPolicyManager->registerPolicyMixes(mixes); + } else { + return mAudioPolicyManager->unregisterPolicyMixes(mixes); + } +} + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp new file mode 100644 index 0000000..5a91192 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +#include +#include +#include +#include + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device, + state, device_address); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_phone_state(mpAudioPolicy, state); + mPhoneState = state; + return NO_ERROR; +} + +audio_mode_t AudioPolicyService::getPhoneState() +{ + Mutex::Autolock _l(mLock); + return mPhoneState; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mpAudioPolicy->get_force_use(mpAudioPolicy, usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return AUDIO_IO_HANDLE_NONE; + } + if (mpAudioPolicy == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + // create audio processors according to stream + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + status_t status = audioPolicyEffects->addOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on session %d", session); + } + } + + Mutex::Autolock _l(mLock); + return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + // release audio processors from the stream + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + status_t status = audioPolicyEffects->releaseOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to release effects on session %d", session); + } + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + if (mpAudioPolicy == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output, stream, session); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream __unused, + audio_session_t session __unused) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_output(mpAudioPolicy, output); +} + +status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + + audio_source_t inputSource = attr->source; + + // already checked by client, but double-check in case the client wrapper is bypassed + if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD && + inputSource != AUDIO_SOURCE_FM_TUNER) { + return BAD_VALUE; + } + + if (inputSource == AUDIO_SOURCE_DEFAULT) { + inputSource = AUDIO_SOURCE_MIC; + } + + if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) || + ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { + return BAD_VALUE; + } + + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + *input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, + format, channelMask, (audio_in_acoustics_t) 0); + audioPolicyEffects = mAudioPolicyEffects; + } + if (*input == AUDIO_IO_HANDLE_NONE) { + return INVALID_OPERATION; + } + + if (audioPolicyEffects != 0) { + // create audio pre processors according to input source + status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on input %d", input); + } + } + return NO_ERROR; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->start_input(mpAudioPolicy, input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->stop_input(mpAudioPolicy, input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return; + } + + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_input(mpAudioPolicy, input); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects != 0) { + // release audio processors from the input + status_t status = audioPolicyEffects->releaseInputEffects(input); + if(status != NO_ERROR) { + ALOGW("Failed to release effects on input %d", input); + } + } +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->set_stream_volume_index_for_device) { + return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->get_stream_volume_index_for_device) { + return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return 0; + } + if (mpAudioPolicy == NULL) { + return 0; + } + return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return AUDIO_DEVICE_NONE; + } + if (mpAudioPolicy == NULL) { + return AUDIO_DEVICE_NONE; + } + return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); +} + +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) +{ + // FIXME change return type to status_t, and return NO_INIT here + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->unregister_effect(mpAudioPolicy, id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return false; + } + if (mpAudioPolicy == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { + return false; + } + if (mpAudioPolicy == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mpAudioPolicy == NULL) { + return false; + } + if (mpAudioPolicy->is_source_active == 0) { + return false; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_source_active(mpAudioPolicy, source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + if (mpAudioPolicy == NULL) { + *count = 0; + return NO_INIT; + } + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects == 0) { + *count = 0; + return NO_INIT; + } + return audioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count); +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mpAudioPolicy == NULL) { + ALOGV("mpAudioPolicy == NULL"); + return false; + } + + if (mpAudioPolicy->is_offload_supported == NULL) { + ALOGV("HAL does not implement is_offload_supported"); + return false; + } + + return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info); +} + +status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused, + audio_port_type_t type __unused, + unsigned int *num_ports, + struct audio_port *ports __unused, + unsigned int *generation __unused) +{ + *num_ports = 0; + return INVALID_OPERATION; +} + +status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused, + audio_patch_handle_t *handle __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches __unused, + unsigned int *generation __unused) +{ + *num_patches = 0; + return INVALID_OPERATION; +} + +status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session __unused, + audio_stream_type_t *stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (attr != NULL) { + *stream = audio_attributes_to_stream_type(attr); + } else { + if (*stream == AUDIO_STREAM_DEFAULT) { + return BAD_VALUE; + } + } + *output = getOutput(*stream, samplingRate, format, channelMask, + flags, offloadInfo); + if (*output == AUDIO_IO_HANDLE_NONE) { + return INVALID_OPERATION; + } + return NO_ERROR; +} + +status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session __unused, + audio_io_handle_t *ioHandle __unused, + audio_devices_t *device __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::registerPolicyMixes(Vector mixes __unused, + bool registration __unused) +{ + return INVALID_OPERATION; +} + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp new file mode 100644 index 0000000..eb9116d --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -0,0 +1,1068 @@ +/* + * Copyright (C) 2009 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. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include + +#include +#include +#include +#include +#include +#include +#include +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; +static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n"; + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleepUs = 20000; + +static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds + +namespace { + extern struct audio_policy_service_ops aps_ops; +}; + +// ---------------------------------------------------------------------------- + +AudioPolicyService::AudioPolicyService() + : BnAudioPolicyService(), mpAudioPolicyDev(NULL), mpAudioPolicy(NULL), + mAudioPolicyManager(NULL), mAudioPolicyClient(NULL), mPhoneState(AUDIO_MODE_INVALID) +{ +} + +void AudioPolicyService::onFirstRef() +{ + char value[PROPERTY_VALUE_MAX]; + const struct hw_module_t *module; + int forced_val; + int rc; + + { + Mutex::Autolock _l(mLock); + + // start tone playback thread + mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this); + // start audio commands thread + mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); + // start output activity command thread + mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this); + +#ifdef USE_LEGACY_AUDIO_POLICY + ALOGI("AudioPolicyService CSTOR in legacy mode"); + + /* instantiate the audio policy manager */ + rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module); + if (rc) { + return; + } + rc = audio_policy_dev_open(module, &mpAudioPolicyDev); + ALOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc)); + if (rc) { + return; + } + + rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this, + &mpAudioPolicy); + ALOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc)); + if (rc) { + return; + } + + rc = mpAudioPolicy->init_check(mpAudioPolicy); + ALOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc)); + if (rc) { + return; + } + ALOGI("Loaded audio policy from %s (%s)", module->name, module->id); +#else + ALOGI("AudioPolicyService CSTOR in new mode"); + + mAudioPolicyClient = new AudioPolicyClient(this); + mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); +#endif + } + // load audio processing modules + spaudioPolicyEffects = new AudioPolicyEffects(); + { + Mutex::Autolock _l(mLock); + mAudioPolicyEffects = audioPolicyEffects; + } +} + +AudioPolicyService::~AudioPolicyService() +{ + mTonePlaybackThread->exit(); + mAudioCommandThread->exit(); + mOutputCommandThread->exit(); + +#ifdef USE_LEGACY_AUDIO_POLICY + if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) { + mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy); + } + if (mpAudioPolicyDev != NULL) { + audio_policy_dev_close(mpAudioPolicyDev); + } +#else + destroyAudioPolicyManager(mAudioPolicyManager); + delete mAudioPolicyClient; +#endif + + mNotificationClients.clear(); + mAudioPolicyEffects.clear(); +} + +// A notification client is always registered by AudioSystem when the client process +// connects to AudioPolicyService. +void AudioPolicyService::registerClient(const sp& client) +{ + + Mutex::Autolock _l(mNotificationClientsLock); + + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (mNotificationClients.indexOfKey(uid) < 0) { + sp notificationClient = new NotificationClient(this, + client, + uid); + ALOGV("registerClient() client %p, uid %d", client.get(), uid); + + mNotificationClients.add(uid, notificationClient); + + sp binder = IInterface::asBinder(client); + binder->linkToDeath(notificationClient); + } +} + +// removeNotificationClient() is called when the client process dies. +void AudioPolicyService::removeNotificationClient(uid_t uid) +{ + { + Mutex::Autolock _l(mNotificationClientsLock); + mNotificationClients.removeItem(uid); + } +#ifndef USE_LEGACY_AUDIO_POLICY + { + Mutex::Autolock _l(mLock); + if (mAudioPolicyManager) { + mAudioPolicyManager->clearAudioPatches(uid); + } + } +#endif +} + +void AudioPolicyService::onAudioPortListUpdate() +{ + mOutputCommandThread->updateAudioPortListCommand(); +} + +void AudioPolicyService::doOnAudioPortListUpdate() +{ + Mutex::Autolock _l(mNotificationClientsLock); + for (size_t i = 0; i < mNotificationClients.size(); i++) { + mNotificationClients.valueAt(i)->onAudioPortListUpdate(); + } +} + +void AudioPolicyService::onAudioPatchListUpdate() +{ + mOutputCommandThread->updateAudioPatchListCommand(); +} + +status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) +{ + return mAudioCommandThread->createAudioPatchCommand(patch, handle, delayMs); +} + +status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle, + int delayMs) +{ + return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs); +} + +void AudioPolicyService::doOnAudioPatchListUpdate() +{ + Mutex::Autolock _l(mNotificationClientsLock); + for (size_t i = 0; i < mNotificationClients.size(); i++) { + mNotificationClients.valueAt(i)->onAudioPatchListUpdate(); + } +} + +status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config, + int delayMs) +{ + return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs); +} + +AudioPolicyService::NotificationClient::NotificationClient(const sp& service, + const sp& client, + uid_t uid) + : mService(service), mUid(uid), mAudioPolicyServiceClient(client) +{ +} + +AudioPolicyService::NotificationClient::~NotificationClient() +{ +} + +void AudioPolicyService::NotificationClient::binderDied(const wp& who __unused) +{ + sp keep(this); + sp service = mService.promote(); + if (service != 0) { + service->removeNotificationClient(mUid); + } +} + +void AudioPolicyService::NotificationClient::onAudioPortListUpdate() +{ + if (mAudioPolicyServiceClient != 0) { + mAudioPolicyServiceClient->onAudioPortListUpdate(); + } +} + +void AudioPolicyService::NotificationClient::onAudioPatchListUpdate() +{ + if (mAudioPolicyServiceClient != 0) { + mAudioPolicyServiceClient->onAudioPatchListUpdate(); + } +} + +void AudioPolicyService::binderDied(const wp& who) { + ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(), + IPCThreadState::self()->getCallingPid()); +} + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleepUs); + } + return locked; +} + +status_t AudioPolicyService::dumpInternals(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + +#ifdef USE_LEGACY_AUDIO_POLICY + snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy); +#else + snprintf(buffer, SIZE, "AudioPolicyManager: %p\n", mAudioPolicyManager); +#endif + result.append(buffer); + snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); + result.append(buffer); + snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get()); + result.append(buffer); + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::dump(int fd, const Vector& args __unused) +{ + if (!dumpAllowed()) { + dumpPermissionDenial(fd); + } else { + bool locked = tryLock(mLock); + if (!locked) { + String8 result(kDeadlockedString); + write(fd, result.string(), result.size()); + } + + dumpInternals(fd); + if (mAudioCommandThread != 0) { + mAudioCommandThread->dump(fd); + } + if (mTonePlaybackThread != 0) { + mTonePlaybackThread->dump(fd); + } + +#ifdef USE_LEGACY_AUDIO_POLICY + if (mpAudioPolicy) { + mpAudioPolicy->dump(mpAudioPolicy, fd); + } +#else + if (mAudioPolicyManager) { + mAudioPolicyManager->dump(fd); + } +#endif + + if (locked) mLock.unlock(); + } + return NO_ERROR; +} + +status_t AudioPolicyService::dumpPermissionDenial(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioPolicyService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioPolicyService::onTransact(code, data, reply, flags); +} + + +// ----------- AudioPolicyService::AudioCommandThread implementation ---------- + +AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, + const wp& service) + : Thread(false), mName(name), mService(service) +{ + mpToneGenerator = NULL; +} + + +AudioPolicyService::AudioCommandThread::~AudioCommandThread() +{ + if (!mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + mAudioCommands.clear(); + delete mpToneGenerator; +} + +void AudioPolicyService::AudioCommandThread::onFirstRef() +{ + run(mName.string(), ANDROID_PRIORITY_AUDIO); +} + +bool AudioPolicyService::AudioCommandThread::threadLoop() +{ + nsecs_t waitTime = INT64_MAX; + + mLock.lock(); + while (!exitPending()) + { + sp svc; + while (!mAudioCommands.isEmpty() && !exitPending()) { + nsecs_t curTime = systemTime(); + // commands are sorted by increasing time stamp: execute them from index 0 and up + if (mAudioCommands[0]->mTime <= curTime) { + sp command = mAudioCommands[0]; + mAudioCommands.removeAt(0); + mLastCommand = command; + + switch (command->mCommand) { + case START_TONE: { + mLock.unlock(); + ToneData *data = (ToneData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing start tone %d on stream %d", + data->mType, data->mStream); + delete mpToneGenerator; + mpToneGenerator = new ToneGenerator(data->mStream, 1.0); + mpToneGenerator->startTone(data->mType); + mLock.lock(); + }break; + case STOP_TONE: { + mLock.unlock(); + ALOGV("AudioCommandThread() processing stop tone"); + if (mpToneGenerator != NULL) { + mpToneGenerator->stopTone(); + delete mpToneGenerator; + mpToneGenerator = NULL; + } + mLock.lock(); + }break; + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set volume stream %d, \ + volume %f, output %d", data->mStream, data->mVolume, data->mIO); + command->mStatus = AudioSystem::setStreamVolume(data->mStream, + data->mVolume, + data->mIO); + }break; + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set parameters string %s, io %d", + data->mKeyValuePairs.string(), data->mIO); + command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); + }break; + case SET_VOICE_VOLUME: { + VoiceVolumeData *data = (VoiceVolumeData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set voice volume volume %f", + data->mVolume); + command->mStatus = AudioSystem::setVoiceVolume(data->mVolume); + }break; + case STOP_OUTPUT: { + StopOutputData *data = (StopOutputData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing stop output %d", + data->mIO); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doStopOutput(data->mIO, data->mStream, data->mSession); + mLock.lock(); + }break; + case RELEASE_OUTPUT: { + ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing release output %d", + data->mIO); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doReleaseOutput(data->mIO, data->mStream, data->mSession); + mLock.lock(); + }break; + case CREATE_AUDIO_PATCH: { + CreateAudioPatchData *data = (CreateAudioPatchData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing create audio patch"); + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + command->mStatus = PERMISSION_DENIED; + } else { + command->mStatus = af->createAudioPatch(&data->mPatch, &data->mHandle); + } + } break; + case RELEASE_AUDIO_PATCH: { + ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing release audio patch"); + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + command->mStatus = PERMISSION_DENIED; + } else { + command->mStatus = af->releaseAudioPatch(data->mHandle); + } + } break; + case UPDATE_AUDIOPORT_LIST: { + ALOGV("AudioCommandThread() processing update audio port list"); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doOnAudioPortListUpdate(); + mLock.lock(); + }break; + case UPDATE_AUDIOPATCH_LIST: { + ALOGV("AudioCommandThread() processing update audio patch list"); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doOnAudioPatchListUpdate(); + mLock.lock(); + }break; + case SET_AUDIOPORT_CONFIG: { + SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set port config"); + sp af = AudioSystem::get_audio_flinger(); + if (af == 0) { + command->mStatus = PERMISSION_DENIED; + } else { + command->mStatus = af->setAudioPortConfig(&data->mConfig); + } + } break; + default: + ALOGW("AudioCommandThread() unknown command %d", command->mCommand); + } + { + Mutex::Autolock _l(command->mLock); + if (command->mWaitStatus) { + command->mWaitStatus = false; + command->mCond.signal(); + } + } + waitTime = INT64_MAX; + } else { + waitTime = mAudioCommands[0]->mTime - curTime; + break; + } + } + // release mLock before releasing strong reference on the service as + // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock. + mLock.unlock(); + svc.clear(); + mLock.lock(); + if (!exitPending() && mAudioCommands.isEmpty()) { + // release delayed commands wake lock + release_wake_lock(mName.string()); + ALOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.waitRelative(mLock, waitTime); + ALOGV("AudioCommandThread() waking up"); + } + } + // release delayed commands wake lock before quitting + if (!mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + mLock.unlock(); + return false; +} + +status_t AudioPolicyService::AudioCommandThread::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "AudioCommandThread %p Dump\n", this); + result.append(buffer); + write(fd, result.string(), result.size()); + + bool locked = tryLock(mLock); + if (!locked) { + String8 result2(kCmdDeadlockedString); + write(fd, result2.string(), result2.size()); + } + + snprintf(buffer, SIZE, "- Commands:\n"); + result = String8(buffer); + result.append(" Command Time Wait pParam\n"); + for (size_t i = 0; i < mAudioCommands.size(); i++) { + mAudioCommands[i]->dump(buffer, SIZE); + result.append(buffer); + } + result.append(" Last Command\n"); + if (mLastCommand != 0) { + mLastCommand->dump(buffer, SIZE); + result.append(buffer); + } else { + result.append(" none\n"); + } + + write(fd, result.string(), result.size()); + + if (locked) mLock.unlock(); + + return NO_ERROR; +} + +void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type, + audio_stream_type_t stream) +{ + sp command = new AudioCommand(); + command->mCommand = START_TONE; + sp data = new ToneData(); + data->mType = type; + data->mStream = stream; + command->mParam = data; + ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); + sendCommand(command); +} + +void AudioPolicyService::AudioCommandThread::stopToneCommand() +{ + sp command = new AudioCommand(); + command->mCommand = STOP_TONE; + ALOGV("AudioCommandThread() adding tone stop"); + sendCommand(command); +} + +status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs) +{ + sp command = new AudioCommand(); + command->mCommand = SET_VOLUME; + sp data = new VolumeData(); + data->mStream = stream; + data->mVolume = volume; + data->mIO = output; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", + stream, volume, output); + return sendCommand(command, delayMs); +} + +status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs) +{ + sp command = new AudioCommand(); + command->mCommand = SET_PARAMETERS; + sp data = new ParametersData(); + data->mIO = ioHandle; + data->mKeyValuePairs = String8(keyValuePairs); + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", + keyValuePairs, ioHandle, delayMs); + return sendCommand(command, delayMs); +} + +status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs) +{ + sp command = new AudioCommand(); + command->mCommand = SET_VOICE_VOLUME; + sp data = new VoiceVolumeData(); + data->mVolume = volume; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set voice volume volume %f", volume); + return sendCommand(command, delayMs); +} + +void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + sp command = new AudioCommand(); + command->mCommand = STOP_OUTPUT; + sp data = new StopOutputData(); + data->mIO = output; + data->mStream = stream; + data->mSession = session; + command->mParam = data; + ALOGV("AudioCommandThread() adding stop output %d", output); + sendCommand(command); +} + +void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session) +{ + sp command = new AudioCommand(); + command->mCommand = RELEASE_OUTPUT; + sp data = new ReleaseOutputData(); + data->mIO = output; + data->mStream = stream; + data->mSession = session; + command->mParam = data; + ALOGV("AudioCommandThread() adding release output %d", output); + sendCommand(command); +} + +status_t AudioPolicyService::AudioCommandThread::createAudioPatchCommand( + const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) +{ + status_t status = NO_ERROR; + + sp command = new AudioCommand(); + command->mCommand = CREATE_AUDIO_PATCH; + CreateAudioPatchData *data = new CreateAudioPatchData(); + data->mPatch = *patch; + data->mHandle = *handle; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding create patch delay %d", delayMs); + status = sendCommand(command, delayMs); + if (status == NO_ERROR) { + *handle = data->mHandle; + } + return status; +} + +status_t AudioPolicyService::AudioCommandThread::releaseAudioPatchCommand(audio_patch_handle_t handle, + int delayMs) +{ + sp command = new AudioCommand(); + command->mCommand = RELEASE_AUDIO_PATCH; + ReleaseAudioPatchData *data = new ReleaseAudioPatchData(); + data->mHandle = handle; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding release patch delay %d", delayMs); + return sendCommand(command, delayMs); +} + +void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand() +{ + sp command = new AudioCommand(); + command->mCommand = UPDATE_AUDIOPORT_LIST; + ALOGV("AudioCommandThread() adding update audio port list"); + sendCommand(command); +} + +void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand() +{ + spcommand = new AudioCommand(); + command->mCommand = UPDATE_AUDIOPATCH_LIST; + ALOGV("AudioCommandThread() adding update audio patch list"); + sendCommand(command); +} + +status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand( + const struct audio_port_config *config, int delayMs) +{ + sp command = new AudioCommand(); + command->mCommand = SET_AUDIOPORT_CONFIG; + SetAudioPortConfigData *data = new SetAudioPortConfigData(); + data->mConfig = *config; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set port config delay %d", delayMs); + return sendCommand(command, delayMs); +} + +status_t AudioPolicyService::AudioCommandThread::sendCommand(sp& command, int delayMs) +{ + { + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + mWaitWorkCV.signal(); + } + Mutex::Autolock _l(command->mLock); + while (command->mWaitStatus) { + nsecs_t timeOutNs = kAudioCommandTimeoutNs + milliseconds(delayMs); + if (command->mCond.waitRelative(command->mLock, timeOutNs) != NO_ERROR) { + command->mStatus = TIMED_OUT; + command->mWaitStatus = false; + } + } + return command->mStatus; +} + +// insertCommand_l() must be called with mLock held +void AudioPolicyService::AudioCommandThread::insertCommand_l(sp& command, int delayMs) +{ + ssize_t i; // not size_t because i will count down to -1 + Vector < sp > removedCommands; + command->mTime = systemTime() + milliseconds(delayMs); + + // acquire wake lock to make sure delayed commands are processed + if (mAudioCommands.isEmpty()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string()); + } + + // check same pending commands with later time stamps and eliminate them + for (i = mAudioCommands.size()-1; i >= 0; i--) { + sp command2 = mAudioCommands[i]; + // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands + if (command2->mTime <= command->mTime) break; + + // create audio patch or release audio patch commands are equivalent + // with regard to filtering + if ((command->mCommand == CREATE_AUDIO_PATCH) || + (command->mCommand == RELEASE_AUDIO_PATCH)) { + if ((command2->mCommand != CREATE_AUDIO_PATCH) && + (command2->mCommand != RELEASE_AUDIO_PATCH)) { + continue; + } + } else if (command2->mCommand != command->mCommand) continue; + + switch (command->mCommand) { + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam.get(); + ParametersData *data2 = (ParametersData *)command2->mParam.get(); + if (data->mIO != data2->mIO) break; + ALOGV("Comparing parameter command %s to new command %s", + data2->mKeyValuePairs.string(), data->mKeyValuePairs.string()); + AudioParameter param = AudioParameter(data->mKeyValuePairs); + AudioParameter param2 = AudioParameter(data2->mKeyValuePairs); + for (size_t j = 0; j < param.size(); j++) { + String8 key; + String8 value; + param.getAt(j, key, value); + for (size_t k = 0; k < param2.size(); k++) { + String8 key2; + String8 value2; + param2.getAt(k, key2, value2); + if (key2 == key) { + param2.remove(key2); + ALOGV("Filtering out parameter %s", key2.string()); + break; + } + } + } + // if all keys have been filtered out, remove the command. + // otherwise, update the key value pairs + if (param2.size() == 0) { + removedCommands.add(command2); + } else { + data2->mKeyValuePairs = param2.toString(); + } + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam.get(); + VolumeData *data2 = (VolumeData *)command2->mParam.get(); + if (data->mIO != data2->mIO) break; + if (data->mStream != data2->mStream) break; + ALOGV("Filtering out volume command on output %d for stream %d", + data->mIO, data->mStream); + removedCommands.add(command2); + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case CREATE_AUDIO_PATCH: + case RELEASE_AUDIO_PATCH: { + audio_patch_handle_t handle; + struct audio_patch patch; + if (command->mCommand == CREATE_AUDIO_PATCH) { + handle = ((CreateAudioPatchData *)command->mParam.get())->mHandle; + patch = ((CreateAudioPatchData *)command->mParam.get())->mPatch; + } else { + handle = ((ReleaseAudioPatchData *)command->mParam.get())->mHandle; + } + audio_patch_handle_t handle2; + struct audio_patch patch2; + if (command2->mCommand == CREATE_AUDIO_PATCH) { + handle2 = ((CreateAudioPatchData *)command2->mParam.get())->mHandle; + patch2 = ((CreateAudioPatchData *)command2->mParam.get())->mPatch; + } else { + handle2 = ((ReleaseAudioPatchData *)command2->mParam.get())->mHandle; + } + if (handle != handle2) break; + /* Filter CREATE_AUDIO_PATCH commands only when they are issued for + same output. */ + if( (command->mCommand == CREATE_AUDIO_PATCH) && + (command2->mCommand == CREATE_AUDIO_PATCH) ) { + bool isOutputDiff = false; + if (patch.num_sources == patch2.num_sources) { + for (unsigned count = 0; count < patch.num_sources; count++) { + if (patch.sources[count].id != patch2.sources[count].id) { + isOutputDiff = true; + break; + } + } + if (isOutputDiff) + break; + } + } + ALOGV("Filtering out %s audio patch command for handle %d", + (command->mCommand == CREATE_AUDIO_PATCH) ? "create" : "release", handle); + removedCommands.add(command2); + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case START_TONE: + case STOP_TONE: + default: + break; + } + } + + // remove filtered commands + for (size_t j = 0; j < removedCommands.size(); j++) { + // removed commands always have time stamps greater than current command + for (size_t k = i + 1; k < mAudioCommands.size(); k++) { + if (mAudioCommands[k].get() == removedCommands[j].get()) { + ALOGV("suppressing command: %d", mAudioCommands[k]->mCommand); + mAudioCommands.removeAt(k); + break; + } + } + } + removedCommands.clear(); + + // Disable wait for status if delay is not 0. + // Except for create audio patch command because the returned patch handle + // is needed by audio policy manager + if (delayMs != 0 && command->mCommand != CREATE_AUDIO_PATCH) { + command->mWaitStatus = false; + } + + // insert command at the right place according to its time stamp + ALOGV("inserting command: %d at index %zd, num commands %zu", + command->mCommand, i+1, mAudioCommands.size()); + mAudioCommands.insertAt(command, i + 1); +} + +void AudioPolicyService::AudioCommandThread::exit() +{ + ALOGV("AudioCommandThread::exit"); + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %02d %06d.%03d %01u %p\n", + mCommand, + (int)ns2s(mTime), + (int)ns2ms(mTime)%1000, + mWaitStatus, + mParam.get()); +} + +/******* helpers for the service_ops callbacks defined below *********/ +void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs) +{ + mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs, + delayMs); +} + +int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs) +{ + return (int)mAudioCommandThread->volumeCommand(stream, volume, + output, delayMs); +} + +int AudioPolicyService::startTone(audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) { + ALOGE("startTone: illegal tone requested (%d)", tone); + } + if (stream != AUDIO_STREAM_VOICE_CALL) { + ALOGE("startTone: illegal stream (%d) requested for tone %d", stream, + tone); + } + mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING, + AUDIO_STREAM_VOICE_CALL); + return 0; +} + +int AudioPolicyService::stopTone() +{ + mTonePlaybackThread->stopToneCommand(); + return 0; +} + +int AudioPolicyService::setVoiceVolume(float volume, int delayMs) +{ + return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); +} + +extern "C" { +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name); +audio_io_handle_t aps_open_output(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags); + +audio_io_handle_t aps_open_output_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); +audio_io_handle_t aps_open_dup_output(void *service __unused, + audio_io_handle_t output1, + audio_io_handle_t output2); +int aps_close_output(void *service __unused, audio_io_handle_t output); +int aps_suspend_output(void *service __unused, audio_io_handle_t output); +int aps_restore_output(void *service __unused, audio_io_handle_t output); +audio_io_handle_t aps_open_input(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + audio_in_acoustics_t acoustics __unused); +audio_io_handle_t aps_open_input_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask); +int aps_close_input(void *service __unused, audio_io_handle_t input); +int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream); +int aps_move_effects(void *service __unused, int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output); +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys); +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms); +int aps_set_stream_volume(void *service, audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms); +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream); +int aps_stop_tone(void *service); +int aps_set_voice_volume(void *service, float volume, int delay_ms); +}; + +namespace { + struct audio_policy_service_ops aps_ops = { + .open_output = aps_open_output, + .open_duplicate_output = aps_open_dup_output, + .close_output = aps_close_output, + .suspend_output = aps_suspend_output, + .restore_output = aps_restore_output, + .open_input = aps_open_input, + .close_input = aps_close_input, + .set_stream_volume = aps_set_stream_volume, + .invalidate_stream = aps_invalidate_stream, + .set_parameters = aps_set_parameters, + .get_parameters = aps_get_parameters, + .start_tone = aps_start_tone, + .stop_tone = aps_stop_tone, + .set_voice_volume = aps_set_voice_volume, + .move_effects = aps_move_effects, + .load_hw_module = aps_load_hw_module, + .open_output_on_module = aps_open_output_on_module, + .open_input_on_module = aps_open_input_on_module, + }; +}; // namespace + +}; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h new file mode 100644 index 0000000..0378384 --- /dev/null +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2009 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. + */ + +#ifndef ANDROID_AUDIOPOLICYSERVICE_H +#define ANDROID_AUDIOPOLICYSERVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_LEGACY_AUDIO_POLICY +#include +#endif +#include "AudioPolicyEffects.h" +#include "managerdefault/AudioPolicyManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioPolicyService : + public BinderService, + public BnAudioPolicyService, + public IBinder::DeathRecipient +{ + friend class BinderService; + +public: + // for BinderService + static const char *getServiceName() ANDROID_API { return "media.audio_policy"; } + + virtual status_t dump(int fd, const Vector& args); + + // + // BnAudioPolicyService (see AudioPolicyInterface for method descriptions) + // + + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name); + virtual audio_policy_dev_state_t getDeviceConnectionState( + audio_devices_t device, + const char *device_address); + virtual status_t setPhoneState(audio_mode_t state); + virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = + AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); + virtual status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + virtual void releaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + virtual status_t getInputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *input, + audio_session_t session, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags); + virtual status_t startInput(audio_io_handle_t input, + audio_session_t session); + virtual status_t stopInput(audio_io_handle_t input, + audio_session_t session); + virtual void releaseInput(audio_io_handle_t input, + audio_session_t session); + virtual status_t initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device); + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device); + + virtual uint32_t getStrategyForStream(audio_stream_type_t stream); + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); + + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc); + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id); + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isSourceActive(audio_source_t source) const; + + virtual status_t queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + + // IBinder::DeathRecipient + virtual void binderDied(const wp& who); + + // RefBase + virtual void onFirstRef(); + + // + // Helpers for the struct audio_policy_service_ops implementation. + // This is used by the audio policy manager for certain operations that + // are implemented by the policy service. + // + virtual void setParameters(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs); + + virtual status_t setStreamVolume(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs = 0); + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); + virtual status_t stopTone(); + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + virtual bool isOffloadSupported(const audio_offload_info_t &config); + + virtual status_t listAudioPorts(audio_port_role_t role, + audio_port_type_t type, + unsigned int *num_ports, + struct audio_port *ports, + unsigned int *generation); + virtual status_t getAudioPort(struct audio_port *port); + virtual status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle); + virtual status_t releaseAudioPatch(audio_patch_handle_t handle); + virtual status_t listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches, + unsigned int *generation); + virtual status_t setAudioPortConfig(const struct audio_port_config *config); + + virtual void registerClient(const sp& client); + + virtual status_t acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device); + + virtual status_t releaseSoundTriggerSession(audio_session_t session); + + virtual audio_mode_t getPhoneState(); + + virtual status_t registerPolicyMixes(Vector mixes, bool registration); + + status_t doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + void doReleaseOutput(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + + status_t clientCreateAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs); + status_t clientReleaseAudioPatch(audio_patch_handle_t handle, + int delayMs); + virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config, + int delayMs); + + void removeNotificationClient(uid_t uid); + void onAudioPortListUpdate(); + void doOnAudioPortListUpdate(); + void onAudioPatchListUpdate(); + void doOnAudioPatchListUpdate(); + +private: + AudioPolicyService() ANDROID_API; + virtual ~AudioPolicyService(); + + status_t dumpInternals(int fd); + + // Thread used for tone playback and to send audio config commands to audio flinger + // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because + // startTone() and stopTone() are normally called with mLock locked and requesting a tone start + // or stop will cause calls to AudioPolicyService and an attempt to lock mLock. + // For audio config commands, it is necessary because audio flinger requires that the calling + // process (user) has permission to modify audio settings. + class AudioCommandThread : public Thread { + class AudioCommand; + public: + + // commands for tone AudioCommand + enum { + START_TONE, + STOP_TONE, + SET_VOLUME, + SET_PARAMETERS, + SET_VOICE_VOLUME, + STOP_OUTPUT, + RELEASE_OUTPUT, + CREATE_AUDIO_PATCH, + RELEASE_AUDIO_PATCH, + UPDATE_AUDIOPORT_LIST, + UPDATE_AUDIOPATCH_LIST, + SET_AUDIOPORT_CONFIG, + }; + + AudioCommandThread (String8 name, const wp& service); + virtual ~AudioCommandThread(); + + status_t dump(int fd); + + // Thread virtuals + virtual void onFirstRef(); + virtual bool threadLoop(); + + void exit(); + void startToneCommand(ToneGenerator::tone_type type, + audio_stream_type_t stream); + void stopToneCommand(); + status_t volumeCommand(audio_stream_type_t stream, float volume, + audio_io_handle_t output, int delayMs = 0); + status_t parametersCommand(audio_io_handle_t ioHandle, + const char *keyValuePairs, int delayMs = 0); + status_t voiceVolumeCommand(float volume, int delayMs = 0); + void stopOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + void releaseOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + audio_session_t session); + status_t sendCommand(sp& command, int delayMs = 0); + void insertCommand_l(sp& command, int delayMs = 0); + status_t createAudioPatchCommand(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs); + status_t releaseAudioPatchCommand(audio_patch_handle_t handle, + int delayMs); + void updateAudioPortListCommand(); + void updateAudioPatchListCommand(); + status_t setAudioPortConfigCommand(const struct audio_port_config *config, + int delayMs); + void insertCommand_l(AudioCommand *command, int delayMs = 0); + + private: + class AudioCommandData; + + // descriptor for requested tone playback event + class AudioCommand: public RefBase { + + public: + AudioCommand() + : mCommand(-1), mStatus(NO_ERROR), mWaitStatus(false) {} + + void dump(char* buffer, size_t size); + + int mCommand; // START_TONE, STOP_TONE ... + nsecs_t mTime; // time stamp + Mutex mLock; // mutex associated to mCond + Condition mCond; // condition for status return + status_t mStatus; // command status + bool mWaitStatus; // true if caller is waiting for status + sp mParam; // command specific parameter data + }; + + class AudioCommandData: public RefBase { + public: + virtual ~AudioCommandData() {} + protected: + AudioCommandData() {} + }; + + class ToneData : public AudioCommandData { + public: + ToneGenerator::tone_type mType; // tone type (START_TONE only) + audio_stream_type_t mStream; // stream type (START_TONE only) + }; + + class VolumeData : public AudioCommandData { + public: + audio_stream_type_t mStream; + float mVolume; + audio_io_handle_t mIO; + }; + + class ParametersData : public AudioCommandData { + public: + audio_io_handle_t mIO; + String8 mKeyValuePairs; + }; + + class VoiceVolumeData : public AudioCommandData { + public: + float mVolume; + }; + + class StopOutputData : public AudioCommandData { + public: + audio_io_handle_t mIO; + audio_stream_type_t mStream; + audio_session_t mSession; + }; + + class ReleaseOutputData : public AudioCommandData { + public: + audio_io_handle_t mIO; + audio_stream_type_t mStream; + audio_session_t mSession; + }; + + class CreateAudioPatchData : public AudioCommandData { + public: + struct audio_patch mPatch; + audio_patch_handle_t mHandle; + }; + + class ReleaseAudioPatchData : public AudioCommandData { + public: + audio_patch_handle_t mHandle; + }; + + class SetAudioPortConfigData : public AudioCommandData { + public: + struct audio_port_config mConfig; + }; + + Mutex mLock; + Condition mWaitWorkCV; + Vector < sp > mAudioCommands; // list of pending commands + ToneGenerator *mpToneGenerator; // the tone generator + sp mLastCommand; // last processed command (used by dump) + String8 mName; // string used by wake lock fo delayed commands + wp mService; + }; + + class AudioPolicyClient : public AudioPolicyClientInterface + { + public: + AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {} + virtual ~AudioPolicyClient() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name); + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual status_t openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags); + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output); + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output); + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output); + + // + // Audio input Control functions + // + + // opens an audio input + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + audio_source_t source, + audio_input_flags_t flags); + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input); + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0); + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(audio_stream_type_t stream); + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0); + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); + virtual status_t stopTone(); + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput); + + /* Create a patch between several source and sink ports */ + virtual status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs); + + /* Release a patch */ + virtual status_t releaseAudioPatch(audio_patch_handle_t handle, + int delayMs); + + /* Set audio port configuration */ + virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs); + + virtual void onAudioPortListUpdate(); + virtual void onAudioPatchListUpdate(); + + virtual audio_unique_id_t newAudioUniqueId(); + + private: + AudioPolicyService *mAudioPolicyService; + }; + + // --- Notification Client --- + class NotificationClient : public IBinder::DeathRecipient { + public: + NotificationClient(const sp& service, + const sp& client, + uid_t uid); + virtual ~NotificationClient(); + + void onAudioPortListUpdate(); + void onAudioPatchListUpdate(); + + // IBinder::DeathRecipient + virtual void binderDied(const wp& who); + + private: + NotificationClient(const NotificationClient&); + NotificationClient& operator = (const NotificationClient&); + + const wp mService; + const uid_t mUid; + const sp mAudioPolicyServiceClient; + }; + + // Internal dump utilities. + status_t dumpPermissionDenial(int fd); + + + mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing + // device connection state or routing + sp mAudioCommandThread; // audio commands thread + sp mTonePlaybackThread; // tone playback thread + sp mOutputCommandThread; // process stop and release output + struct audio_policy_device *mpAudioPolicyDev; + struct audio_policy *mpAudioPolicy; + AudioPolicyInterface *mAudioPolicyManager; + AudioPolicyClient *mAudioPolicyClient; + + DefaultKeyedVector< uid_t, sp > mNotificationClients; + Mutex mNotificationClientsLock; // protects mNotificationClients + // Manage all effects configured in audio_effects.conf + sp mAudioPolicyEffects; + audio_mode_t mPhoneState; +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYSERVICE_H -- cgit v1.1