From 0479d7c79a7fd6f112e8dc7e45c009cf6602dbaa Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 14 Jan 2016 02:46:40 -0800 Subject: audiopolicy: Add notification when default effects are updated * In M, we now have the ability to define a default set of audio effect on a per-stream basis. This allows us to get around the problem of apps not sending the control intents so we can implement smart global effects for specific media types. * We still need a session id in order to get a handle and configure them from an app like AudioFX, so we'll need to add some plumbing in order to send an event to interested applications. * This patch implements the native side of this. The Java layer will call down thru AudioSystem and register a callback which will be invoked by the audio policy when default effects are updated on a stream. This callback will receive both the stream type as well as the session id. * Attaching this listener requires that the caller hold the MODIFY_AUDIO_ROUTING permission. Change-Id: I142b15f2585ffca6a953c3e828e2a7c07b24f56c --- include/media/AudioPolicy.h | 1 + include/media/AudioSystem.h | 7 ++ include/media/IAudioPolicyService.h | 2 + include/media/IAudioPolicyServiceClient.h | 4 ++ media/libmedia/AudioSystem.cpp | 32 ++++++++- media/libmedia/IAudioPolicyService.cpp | 20 ++++++ media/libmedia/IAudioPolicyServiceClient.cpp | 22 +++++- services/audiopolicy/AudioPolicyInterface.h | 3 + .../audiopolicy/service/AudioPolicyClientImpl.cpp | 6 ++ .../audiopolicy/service/AudioPolicyEffects.cpp | 7 +- services/audiopolicy/service/AudioPolicyEffects.h | 6 +- .../audiopolicy/service/AudioPolicyService.cpp | 82 +++++++++++++++++++++- services/audiopolicy/service/AudioPolicyService.h | 27 ++++++- 13 files changed, 211 insertions(+), 8 deletions(-) diff --git a/include/media/AudioPolicy.h b/include/media/AudioPolicy.h index feed402..5a82bae 100644 --- a/include/media/AudioPolicy.h +++ b/include/media/AudioPolicy.h @@ -42,6 +42,7 @@ namespace android { // AudioSystem's implementation of the AudioPolicyClient interface // keep in sync with AudioSystem.java #define DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE 0 +#define AUDIO_OUTPUT_SESSION_EFFECTS_UPDATE 10 #define MIX_STATE_DISABLED -1 #define MIX_STATE_IDLE 0 diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 26a0bb2..35b06ba 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -31,6 +31,8 @@ namespace android { typedef void (*audio_error_callback)(status_t err); typedef void (*dynamic_policy_callback)(int event, String8 regId, int val); +typedef void (*effect_session_callback)(int event, + audio_stream_type_t stream, audio_unique_id_t sessionId, bool added); class IAudioFlinger; class IAudioPolicyService; @@ -92,6 +94,7 @@ public: static void setErrorCallback(audio_error_callback cb); static void setDynPolicyCallback(dynamic_policy_callback cb); + static status_t setEffectSessionCallback(effect_session_callback cb); // helper function to obtain AudioFlinger service handle static const sp get_audio_flinger(); @@ -419,6 +422,9 @@ private: virtual void onAudioPortListUpdate(); virtual void onAudioPatchListUpdate(); virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); + virtual void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, + bool added); private: Mutex mLock; @@ -438,6 +444,7 @@ private: static sp gAudioFlinger; static audio_error_callback gAudioErrorCallback; static dynamic_policy_callback gDynPolicyCallback; + static effect_session_callback gEffectSessionCallback; static size_t gInBuffSize; // previous parameters for recording buffer size queries diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h index 6b93f6f..3a7bd68 100644 --- a/include/media/IAudioPolicyService.h +++ b/include/media/IAudioPolicyService.h @@ -165,6 +165,8 @@ public: const audio_attributes_t *attributes, audio_io_handle_t *handle) = 0; virtual status_t stopAudioSource(audio_io_handle_t handle) = 0; + + virtual status_t setEffectSessionCallbacksEnabled(bool enabled) = 0; }; diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h index a7f2cc3..3bdeb5a 100644 --- a/include/media/IAudioPolicyServiceClient.h +++ b/include/media/IAudioPolicyServiceClient.h @@ -37,6 +37,10 @@ public: virtual void onAudioPatchListUpdate() = 0; // Notifies a change in the mixing state of a specific mix in a dynamic audio policy virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0; + // Notifies when a default effect set is attached to a session/stream + virtual void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, + bool added) = 0; }; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 9d645f0..10ec495 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -37,7 +37,7 @@ sp AudioSystem::gAudioFlinger; sp AudioSystem::gAudioFlingerClient; audio_error_callback AudioSystem::gAudioErrorCallback = NULL; dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL; - +effect_session_callback AudioSystem::gEffectSessionCallback = NULL; // establish binder interface to AudioFlinger service const sp AudioSystem::get_audio_flinger() @@ -652,6 +652,21 @@ status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( gDynPolicyCallback = cb; } +/*static*/ status_t AudioSystem::setEffectSessionCallback(effect_session_callback cb) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLock); + gEffectSessionCallback = cb; + + status_t status = aps->setEffectSessionCallbacksEnabled(cb != NULL); + if (status != OK) { + gEffectSessionCallback = NULL; + } + return status; +} + // client singleton for AudioPolicyService binder interface // protected by gLockAPS sp AudioSystem::gAudioPolicyService; @@ -1223,6 +1238,21 @@ void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate( } } +void AudioSystem::AudioPolicyServiceClient::onOutputSessionEffectsUpdate( + audio_stream_type_t stream, audio_unique_id_t sessionId, bool added) +{ + ALOGV("AudioPolicyServiceClient::onOutputSessionEffectsUpdate(%d, %d, %d)", stream, sessionId, added); + effect_session_callback cb = NULL; + { + Mutex::Autolock _l(AudioSystem::gLock); + cb = gEffectSessionCallback; + } + + if (cb != NULL) { + cb(AUDIO_OUTPUT_SESSION_EFFECTS_UPDATE, stream, sessionId, added); + } +} + void AudioSystem::AudioPolicyServiceClient::binderDied(const wp& who __unused) { { diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 76b5924..6ff8149 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -74,6 +74,7 @@ enum { START_AUDIO_SOURCE, STOP_AUDIO_SOURCE, SET_AUDIO_PORT_CALLBACK_ENABLED, + SET_EFFECT_SESSION_CALLBACK_ENABLED, }; #define MAX_ITEMS_PER_LIST 1024 @@ -655,6 +656,18 @@ public: remote()->transact(SET_AUDIO_PORT_CALLBACK_ENABLED, data, &reply); } + virtual status_t setEffectSessionCallbacksEnabled(bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(enabled ? 1 : 0); + status_t status = remote()->transact(SET_EFFECT_SESSION_CALLBACK_ENABLED, data, &reply); + if (status != NO_ERROR) { + return status; + } + return (status_t)reply.readInt32(); + } + virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device) @@ -1238,6 +1251,13 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case SET_EFFECT_SESSION_CALLBACK_ENABLED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + status_t status = setEffectSessionCallbacksEnabled(data.readInt32() == 1); + reply->writeInt32(status); + return NO_ERROR; + } break; + case ACQUIRE_SOUNDTRIGGER_SESSION: { CHECK_INTERFACE(IAudioPolicyService, data, reply); sp client = interface_cast( diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp index 65cc7d6..d6207ce 100644 --- a/media/libmedia/IAudioPolicyServiceClient.cpp +++ b/media/libmedia/IAudioPolicyServiceClient.cpp @@ -30,7 +30,8 @@ namespace android { enum { PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION, PATCH_LIST_UPDATE, - MIX_STATE_UPDATE + MIX_STATE_UPDATE, + OUTPUT_SESSION_EFFECTS_UPDATE }; class BpAudioPolicyServiceClient : public BpInterface @@ -63,6 +64,17 @@ public: data.writeInt32(state); remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); } + + void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor()); + data.writeInt32(stream); + data.writeInt32(sessionId); + data.writeInt32(added ? 1 : 0); + remote()->transact(OUTPUT_SESSION_EFFECTS_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); + } }; IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient"); @@ -90,6 +102,14 @@ status_t BnAudioPolicyServiceClient::onTransact( onDynamicPolicyMixStateUpdate(regId, state); return NO_ERROR; } + case OUTPUT_SESSION_EFFECTS_UPDATE: { + CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply); + audio_stream_type_t stream = static_cast(data.readInt32()); + audio_unique_id_t sessionId = static_cast(data.readInt32()); + bool added = data.readInt32() > 0; + onOutputSessionEffectsUpdate(stream, sessionId, added); + return NO_ERROR; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index c1e7bc0..a5edc14 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -331,6 +331,9 @@ public: virtual audio_unique_id_t newAudioUniqueId() = 0; virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0; + + virtual void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added) = 0; }; extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index 489a9be..d71daa4 100644 --- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -219,6 +219,12 @@ void AudioPolicyService::AudioPolicyClient::onDynamicPolicyMixStateUpdate( mAudioPolicyService->onDynamicPolicyMixStateUpdate(regId, state); } +void AudioPolicyService::AudioPolicyClient::onOutputSessionEffectsUpdate( + audio_stream_type_t stream, audio_unique_id_t sessionId, bool added) +{ + mAudioPolicyService->onOutputSessionEffectsUpdate(stream, sessionId, added); +} + audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId() { return AudioSystem::newAudioUniqueId(); diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index 26857b1..d72e255 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "AudioPolicyService.h" #include "AudioPolicyEffects.h" #include "ServiceUtilities.h" @@ -37,7 +38,8 @@ namespace android { // AudioPolicyEffects Implementation // ---------------------------------------------------------------------------- -AudioPolicyEffects::AudioPolicyEffects() +AudioPolicyEffects::AudioPolicyEffects(AudioPolicyService *audioPolicyService) : + mAudioPolicyService(audioPolicyService) { // load automatic audio effect modules if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE2, R_OK) == 0) { @@ -244,6 +246,8 @@ status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, if (idx < 0) { procDesc = new EffectVector(audioSession); mOutputSessions.add(audioSession, procDesc); + + mAudioPolicyService->onOutputSessionEffectsUpdate(stream, audioSession, true); } else { // EffectVector is existing and we just need to increase ref count procDesc = mOutputSessions.valueAt(idx); @@ -299,6 +303,7 @@ status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t outpu procDesc->mEffects.clear(); delete procDesc; mOutputSessions.removeItemsAt(index); + mAudioPolicyService->onOutputSessionEffectsUpdate(stream, audioSession, false); ALOGV("releaseOutputSessionEffects(): output processing released from session: %d", audioSession); } diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 3845050..2023784 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -31,6 +31,8 @@ namespace android { +class AudioPolicyService; + // ---------------------------------------------------------------------------- // AudioPolicyEffects class @@ -44,7 +46,7 @@ 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(); + AudioPolicyEffects(AudioPolicyService *audioPolicyService); virtual ~AudioPolicyEffects(); // NOTE: methods on AudioPolicyEffects should never be called with the AudioPolicyService @@ -192,6 +194,8 @@ private: KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams; // Automatic output effects are unique for audiosession ID KeyedVector< int32_t, EffectVector* > mOutputSessions; + + AudioPolicyService *mAudioPolicyService; }; }; // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 12cca65..dbeed80 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -116,7 +116,7 @@ void AudioPolicyService::onFirstRef() #endif } // load audio processing modules - spaudioPolicyEffects = new AudioPolicyEffects(); + spaudioPolicyEffects = new AudioPolicyEffects(this); { Mutex::Autolock _l(mLock); mAudioPolicyEffects = audioPolicyEffects; @@ -177,6 +177,23 @@ void AudioPolicyService::setAudioPortCallbacksEnabled(bool enabled) mNotificationClients.valueFor(uid)->setAudioPortCallbacksEnabled(enabled); } +status_t AudioPolicyService::setEffectSessionCallbacksEnabled(bool enabled) +{ + Mutex::Autolock _l(mNotificationClientsLock); + + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (mNotificationClients.indexOfKey(uid) < 0) { + return NO_INIT; + } + if (!modifyAudioRoutingAllowed()) { + ALOGE("setEffectSessionCallbacksEnabled requires MODIFY_AUDIO_ROUTING"); + return PERMISSION_DENIED; + } + mNotificationClients.valueFor(uid)->setEffectSessionCallbacksEnabled(enabled); + return OK; +} + + // removeNotificationClient() is called when the client process dies. void AudioPolicyService::removeNotificationClient(uid_t uid) { @@ -254,11 +271,31 @@ status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_co return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs); } +void AudioPolicyService::onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, + bool added) +{ + ALOGV("AudioPolicyService::onOutputSessionEffectsUpdate(%d, %d, %d)", + stream, sessionId, added); + mOutputCommandThread->effectSessionUpdateCommand(stream, sessionId, added); +} + +void AudioPolicyService::doOnOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, + bool added) +{ + Mutex::Autolock _l(mNotificationClientsLock); + for (size_t i = 0; i < mNotificationClients.size(); i++) { + mNotificationClients.valueAt(i)->onOutputSessionEffectsUpdate(stream, sessionId, added); + } +} + AudioPolicyService::NotificationClient::NotificationClient(const sp& service, const sp& client, uid_t uid) : mService(service), mUid(uid), mAudioPolicyServiceClient(client), - mAudioPortCallbacksEnabled(false) + mAudioPortCallbacksEnabled(false), + mEffectSessionCallbacksEnabled(false) { } @@ -289,6 +326,14 @@ void AudioPolicyService::NotificationClient::onAudioPatchListUpdate() } } +void AudioPolicyService::NotificationClient::onOutputSessionEffectsUpdate( + audio_stream_type_t stream, audio_unique_id_t sessionId, bool added) +{ + if (mAudioPolicyServiceClient != 0 && mEffectSessionCallbacksEnabled) { + mAudioPolicyServiceClient->onOutputSessionEffectsUpdate(stream, sessionId, added); + } +} + void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate( String8 regId, int32_t state) { @@ -302,6 +347,10 @@ void AudioPolicyService::NotificationClient::setAudioPortCallbacksEnabled(bool e mAudioPortCallbacksEnabled = enabled; } +void AudioPolicyService::NotificationClient::setEffectSessionCallbacksEnabled(bool enabled) +{ + mEffectSessionCallbacksEnabled = enabled; +} void AudioPolicyService::binderDied(const wp& who) { ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(), @@ -579,6 +628,20 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() svc->doOnDynamicPolicyMixStateUpdate(data->mRegId, data->mState); mLock.lock(); } break; + case EFFECT_SESSION_UPDATE: { + EffectSessionUpdateData *data = + (EffectSessionUpdateData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing effect session update %d %d %d", + data->mStream, data->mSessionId, data->mAdded); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doOnOutputSessionEffectsUpdate(data->mStream, data->mSessionId, data->mAdded); + mLock.lock(); + } break; + default: ALOGW("AudioCommandThread() unknown command %d", command->mCommand); } @@ -851,6 +914,21 @@ void AudioPolicyService::AudioCommandThread::dynamicPolicyMixStateUpdateCommand( sendCommand(command); } +void AudioPolicyService::AudioCommandThread::effectSessionUpdateCommand( + audio_stream_type_t stream, audio_unique_id_t sessionId, bool added) +{ + sp command = new AudioCommand(); + command->mCommand = EFFECT_SESSION_UPDATE; + EffectSessionUpdateData *data = new EffectSessionUpdateData(); + data->mStream = stream; + data->mSessionId = sessionId; + data->mAdded = added; + command->mParam = data; + ALOGV("AudioCommandThread() sending effect session update (id=%d) for stream %d (added=%d)", + stream, sessionId, added); + sendCommand(command); +} + status_t AudioPolicyService::AudioCommandThread::sendCommand(sp& command, int delayMs) { { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index d11294b..27a62f4 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -202,6 +202,8 @@ public: audio_io_handle_t *handle); virtual status_t stopAudioSource(audio_io_handle_t handle); + virtual status_t setEffectSessionCallbacksEnabled(bool enabled); + status_t doStartOutput(audio_io_handle_t output, audio_stream_type_t stream, audio_session_t session); @@ -229,6 +231,11 @@ public: void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); void doOnDynamicPolicyMixStateUpdate(String8 regId, int32_t state); + void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added); + void doOnOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added); + private: AudioPolicyService() ANDROID_API; virtual ~AudioPolicyService(); @@ -260,7 +267,8 @@ private: UPDATE_AUDIOPORT_LIST, UPDATE_AUDIOPATCH_LIST, SET_AUDIOPORT_CONFIG, - DYN_POLICY_MIX_STATE_UPDATE + DYN_POLICY_MIX_STATE_UPDATE, + EFFECT_SESSION_UPDATE }; AudioCommandThread (String8 name, const wp& service); @@ -303,6 +311,8 @@ private: int delayMs); void dynamicPolicyMixStateUpdateCommand(String8 regId, int32_t state); void insertCommand_l(AudioCommand *command, int delayMs = 0); + void effectSessionUpdateCommand(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added); private: class AudioCommandData; @@ -399,6 +409,13 @@ private: int32_t mState; }; + class EffectSessionUpdateData : public AudioCommandData { + public: + audio_stream_type_t mStream; + audio_unique_id_t mSessionId; + bool mAdded; + }; + Mutex mLock; Condition mWaitWorkCV; Vector < sp > mAudioCommands; // list of pending commands @@ -508,6 +525,9 @@ private: virtual audio_unique_id_t newAudioUniqueId(); + virtual void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added); + private: AudioPolicyService *mAudioPolicyService; }; @@ -524,7 +544,9 @@ private: void onAudioPatchListUpdate(); void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); void setAudioPortCallbacksEnabled(bool enabled); - + void setEffectSessionCallbacksEnabled(bool enabled); + void onOutputSessionEffectsUpdate(audio_stream_type_t stream, + audio_unique_id_t sessionId, bool added); // IBinder::DeathRecipient virtual void binderDied(const wp& who); @@ -536,6 +558,7 @@ private: const uid_t mUid; const sp mAudioPolicyServiceClient; bool mAudioPortCallbacksEnabled; + bool mEffectSessionCallbacksEnabled; }; // Internal dump utilities. -- cgit v1.1