Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73e26b66 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioSystem: refactor audio config cache and callbacks

Clean up implementation of audio configuration cache and
callback events from AudioFlinger:

- Define class AudioIoDescriptor for audio input and output
configurations outside of AudioSystem class.
- Do not use void * but an AudioIoDescriptor as argument to
audio config callbacks from AudioFlinger.
- Remove unused configuration events.
- Move AudioSystem audio input and output cache from static singletons to
members of AudioFlingerClient subclass.

Change-Id: I67c196c32c09ce2756af0755ee1fe631040c3270
parent 054d9d3d
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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_AUDIO_IO_DESCRIPTOR_H
#define ANDROID_AUDIO_IO_DESCRIPTOR_H

namespace android {

enum audio_io_config_event {
    AUDIO_OUTPUT_OPENED,
    AUDIO_OUTPUT_CLOSED,
    AUDIO_OUTPUT_CONFIG_CHANGED,
    AUDIO_INPUT_OPENED,
    AUDIO_INPUT_CLOSED,
    AUDIO_INPUT_CONFIG_CHANGED,
};

// audio input/output descriptor used to cache output configurations in client process to avoid
// frequent calls through IAudioFlinger
class AudioIoDescriptor : public RefBase {
public:
    AudioIoDescriptor() :
        mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE),
        mFrameCount(0), mLatency(0) {}

    virtual ~AudioIoDescriptor() {}

    audio_io_handle_t mIoHandle;
    uint32_t mSamplingRate;
    audio_format_t mFormat;
    audio_channel_mask_t mChannelMask;
    size_t mFrameCount;
    uint32_t mLatency;
};


};  // namespace android

#endif  /*ANDROID_AUDIO_IO_DESCRIPTOR_H*/
+20 −35
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <hardware/audio_effect.h>
#include <media/AudioPolicy.h>
#include <media/AudioIoDescriptor.h>
#include <media/IAudioFlingerClient.h>
#include <media/IAudioPolicyServiceClient.h>
#include <system/audio.h>
@@ -155,33 +156,6 @@ public:
    // or no HW sync source is used.
    static audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);

    // types of io configuration change events received with ioConfigChanged()
    enum io_config_event {
        OUTPUT_OPENED,
        OUTPUT_CLOSED,
        OUTPUT_CONFIG_CHANGED,
        INPUT_OPENED,
        INPUT_CLOSED,
        INPUT_CONFIG_CHANGED,
        STREAM_CONFIG_CHANGED,
        NUM_CONFIG_EVENTS
    };

    // audio output descriptor used to cache output configurations in client process to avoid
    // frequent calls through IAudioFlinger
    class OutputDescriptor {
    public:
        OutputDescriptor()
        : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0)
            {}

        uint32_t samplingRate;
        audio_format_t format;
        audio_channel_mask_t channelMask;
        size_t frameCount;
        uint32_t latency;
    };

    // Events used to synchronize actions between audio sessions.
    // For instance SYNC_EVENT_PRESENTATION_COMPLETE can be used to delay recording start until
    // playback is complete on another audio session.
@@ -362,9 +336,16 @@ private:
    class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
    {
    public:
        AudioFlingerClient() {
        AudioFlingerClient() :
            mInBuffSize(0), mInSamplingRate(0),
            mInFormat(AUDIO_FORMAT_DEFAULT), mInChannelMask(AUDIO_CHANNEL_NONE) {
        }

        void clearIoCache();
        status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
                                    audio_channel_mask_t channelMask, size_t* buffSize);
        sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);

        // DeathRecipient
        virtual void binderDied(const wp<IBinder>& who);

@@ -372,7 +353,17 @@ private:

        // indicate a change in the configuration of an output or input: keeps the cached
        // values for output/input parameters up-to-date in client process
        virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
        virtual void ioConfigChanged(audio_io_config_event event,
                                     const sp<AudioIoDescriptor>& ioDesc);
    private:
        Mutex                               mLock;
        DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> > mIoDescriptors;

        // cached values for recording getInputBufferSize() queries
        size_t                              mInBuffSize;    // zero indicates cache is invalid
        uint32_t                            mInSamplingRate;
        audio_format_t                      mInFormat;
        audio_channel_mask_t                mInChannelMask;
    };

    class AudioPolicyServiceClient: public IBinder::DeathRecipient,
@@ -404,8 +395,6 @@ private:
    friend class AudioPolicyServiceClient;

    static Mutex gLock;      // protects gAudioFlinger and gAudioErrorCallback,
    static Mutex gLockCache; // protects gOutputs, gPrevInSamplingRate, gPrevInFormat,
                             // gPrevInChannelMask and gInBuffSize
    static Mutex gLockAPS;   // protects gAudioPolicyService and gAudioPolicyServiceClient
    static sp<IAudioFlinger> gAudioFlinger;
    static audio_error_callback gAudioErrorCallback;
@@ -417,10 +406,6 @@ private:
    static audio_channel_mask_t gPrevInChannelMask;

    static sp<IAudioPolicyService> gAudioPolicyService;

    // list of output descriptors containing cached parameters
    // (sampling rate, framecount, channel count...)
    static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
};

};  // namespace android
+3 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <binder/IInterface.h>
#include <utils/KeyedVector.h>
#include <system/audio.h>
#include <media/AudioIoDescriptor.h>

namespace android {

@@ -33,7 +34,8 @@ public:
    DECLARE_META_INTERFACE(AudioFlingerClient);

    // Notifies a change of audio input/output configuration.
    virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) = 0;
    virtual void ioConfigChanged(audio_io_config_event event,
                                 const sp<AudioIoDescriptor>& ioDesc) = 0;

};

+102 −108
Original line number Diff line number Diff line
@@ -32,20 +32,11 @@ namespace android {

// client singleton for AudioFlinger binder interface
Mutex AudioSystem::gLock;
Mutex AudioSystem::gLockCache;
Mutex AudioSystem::gLockAPS;
sp<IAudioFlinger> AudioSystem::gAudioFlinger;
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;

// Cached values for output handles
DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(NULL);

// Cached values for recording queries, all protected by gLock
uint32_t AudioSystem::gPrevInSamplingRate;
audio_format_t AudioSystem::gPrevInFormat;
audio_channel_mask_t AudioSystem::gPrevInChannelMask;
size_t AudioSystem::gInBuffSize = 0;    // zero indicates cache is invalid

// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
@@ -258,17 +249,14 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t output,
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;

    Mutex::Autolock _l(gLockCache);

    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
    if (outputDesc == NULL) {
    LOG_ALWAYS_FATAL_IF(gAudioFlingerClient == 0);
    sp<AudioIoDescriptor> outputDesc = gAudioFlingerClient->getIoDescriptor(output);
    if (outputDesc == 0) {
        ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
        gLockCache.unlock();
        *samplingRate = af->sampleRate(output);
        gLockCache.lock();
    } else {
        ALOGV("getOutputSamplingRate() reading from output desc");
        *samplingRate = outputDesc->samplingRate;
        *samplingRate = outputDesc->mSamplingRate;
    }
    if (*samplingRate == 0) {
        ALOGE("AudioSystem::getSamplingRate failed for output %d", output);
@@ -302,15 +290,12 @@ status_t AudioSystem::getFrameCount(audio_io_handle_t output,
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;

    Mutex::Autolock _l(gLockCache);

    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
    if (outputDesc == NULL) {
        gLockCache.unlock();
    LOG_ALWAYS_FATAL_IF(gAudioFlingerClient == 0);
    sp<AudioIoDescriptor> outputDesc = gAudioFlingerClient->getIoDescriptor(output);
    if (outputDesc == 0) {
        *frameCount = af->frameCount(output);
        gLockCache.lock();
    } else {
        *frameCount = outputDesc->frameCount;
        *frameCount = outputDesc->mFrameCount;
    }
    if (*frameCount == 0) {
        ALOGE("AudioSystem::getFrameCount failed for output %d", output);
@@ -344,15 +329,12 @@ status_t AudioSystem::getLatency(audio_io_handle_t output,
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;

    Mutex::Autolock _l(gLockCache);

    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
    if (outputDesc == NULL) {
        gLockCache.unlock();
    LOG_ALWAYS_FATAL_IF(gAudioFlingerClient == 0);
    sp<AudioIoDescriptor> outputDesc = gAudioFlingerClient->getIoDescriptor(output);
    if (outputDesc == 0) {
        *latency = af->latency(output);
        gLockCache.lock();
    } else {
        *latency = outputDesc->latency;
        *latency = outputDesc->mLatency;
    }

    ALOGV("getLatency() output %d, latency %d", output, *latency);
@@ -364,33 +346,9 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t for
        audio_channel_mask_t channelMask, size_t* buffSize)
{
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) {
        return PERMISSION_DENIED;
    }
    Mutex::Autolock _l(gLockCache);
    // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
    size_t inBuffSize = gInBuffSize;
    if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
        || (channelMask != gPrevInChannelMask)) {
        gLockCache.unlock();
        inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
        gLockCache.lock();
        if (inBuffSize == 0) {
            ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
                    sampleRate, format, channelMask);
            return BAD_VALUE;
        }
        // A benign race is possible here: we could overwrite a fresher cache entry
        // save the request params
        gPrevInSamplingRate = sampleRate;
        gPrevInFormat = format;
        gPrevInChannelMask = channelMask;

        gInBuffSize = inBuffSize;
    }
    *buffSize = inBuffSize;

    return NO_ERROR;
    if (af == 0) return PERMISSION_DENIED;
    LOG_ALWAYS_FATAL_IF(gAudioFlingerClient == 0);
    return gAudioFlingerClient->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}

status_t AudioSystem::setVoiceVolume(float value)
@@ -452,6 +410,17 @@ audio_hw_sync_t AudioSystem::getAudioHwSyncForSession(audio_session_t sessionId)

// ---------------------------------------------------------------------------


void AudioSystem::AudioFlingerClient::clearIoCache()
{
    Mutex::Autolock _l(mLock);
    mIoDescriptors.clear();
    mInBuffSize = 0;
    mInSamplingRate = 0;
    mInFormat = AUDIO_FORMAT_DEFAULT;
    mInChannelMask = AUDIO_CHANNEL_NONE;
}

void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
{
    audio_error_callback cb = NULL;
@@ -461,11 +430,8 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused
        cb = gAudioErrorCallback;
    }

    {
    // clear output handles and stream to output map caches
        Mutex::Autolock _l(gLockCache);
        AudioSystem::gOutputs.clear();
    }
    clearIoCache();

    if (cb) {
        cb(DEAD_OBJECT);
@@ -473,67 +439,96 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused
    ALOGW("AudioFlinger server died!");
}

void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle,
        const void *param2) {
void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event,
                                                      const sp<AudioIoDescriptor>& ioDesc) {
    ALOGV("ioConfigChanged() event %d", event);
    const OutputDescriptor *desc;

    if (ioHandle == AUDIO_IO_HANDLE_NONE) return;
    if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;

    Mutex::Autolock _l(AudioSystem::gLockCache);
    Mutex::Autolock _l(mLock);

    switch (event) {
    case STREAM_CONFIG_CHANGED:
        break;
    case OUTPUT_OPENED: {
        if (gOutputs.indexOfKey(ioHandle) >= 0) {
            ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
    case AUDIO_OUTPUT_OPENED:
    case AUDIO_INPUT_OPENED: {
        if (getIoDescriptor(ioDesc->mIoHandle) != 0) {
            ALOGV("ioConfigChanged() opening already existing output! %d", ioDesc->mIoHandle);
            break;
        }
        if (param2 == NULL) break;
        desc = (const OutputDescriptor *)param2;

        OutputDescriptor *outputDesc =  new OutputDescriptor(*desc);
        gOutputs.add(ioHandle, outputDesc);
        ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x "
                "frameCount %zu latency %d",
                outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask,
                outputDesc->frameCount, outputDesc->latency);
        mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
        ALOGV("ioConfigChanged() new %s opened %d samplingRate %u, format %#x channel mask %#x "
                "frameCount %zu", event == AUDIO_OUTPUT_OPENED ? "output" : "input",
                ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask,
                ioDesc->mFrameCount);
        } break;
    case OUTPUT_CLOSED: {
        if (gOutputs.indexOfKey(ioHandle) < 0) {
            ALOGW("ioConfigChanged() closing unknown output! %d", ioHandle);
    case AUDIO_OUTPUT_CLOSED:
    case AUDIO_INPUT_CLOSED: {
        if (getIoDescriptor(ioDesc->mIoHandle) == 0) {
            ALOGW("ioConfigChanged() closing unknown %s %d",
                  event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
            break;
        }
        ALOGV("ioConfigChanged() output %d closed", ioHandle);
        ALOGV("ioConfigChanged() %s %d closed", event == AUDIO_OUTPUT_CLOSED ? "output" : "input",
                ioDesc->mIoHandle);

        gOutputs.removeItem(ioHandle);
        mIoDescriptors.removeItem(ioDesc->mIoHandle);
        } break;

    case OUTPUT_CONFIG_CHANGED: {
        int index = gOutputs.indexOfKey(ioHandle);
        if (index < 0) {
            ALOGW("ioConfigChanged() modifying unknown output! %d", ioHandle);
    case AUDIO_OUTPUT_CONFIG_CHANGED:
    case AUDIO_INPUT_CONFIG_CHANGED: {
        if (getIoDescriptor(ioDesc->mIoHandle) == 0) {
            ALOGW("ioConfigChanged() modifying unknown output! %d", ioDesc->mIoHandle);
            break;
        }
        if (param2 == NULL) break;
        desc = (const OutputDescriptor *)param2;

        ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x "
                "channel mask %#x frameCount %zu latency %d",
                ioHandle, desc->samplingRate, desc->format,
                desc->channelMask, desc->frameCount, desc->latency);
        OutputDescriptor *outputDesc = gOutputs.valueAt(index);
        delete outputDesc;
        outputDesc =  new OutputDescriptor(*desc);
        gOutputs.replaceValueFor(ioHandle, outputDesc);
        mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
        ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
                "channel mask %#x frameCount %zu",
                event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
                ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
                ioDesc->mChannelMask, ioDesc->mFrameCount);
    } break;
    case INPUT_OPENED:
    case INPUT_CLOSED:
    case INPUT_CONFIG_CHANGED:
        break;
    }
}

status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
                                                uint32_t sampleRate, audio_format_t format,
                                                audio_channel_mask_t channelMask, size_t* buffSize)
{
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) {
        return PERMISSION_DENIED;
    }
    Mutex::Autolock _l(mLock);
    // Do we have a stale mInBuffSize or are we requesting the input buffer size for new values
    if ((mInBuffSize == 0) || (sampleRate != mInSamplingRate) || (format != mInFormat)
        || (channelMask != mInChannelMask)) {
        size_t inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
        if (inBuffSize == 0) {
            ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
                    sampleRate, format, channelMask);
            return BAD_VALUE;
        }
        // A benign race is possible here: we could overwrite a fresher cache entry
        // save the request params
        mInSamplingRate = sampleRate;
        mInFormat = format;
        mInChannelMask = channelMask;

        mInBuffSize = inBuffSize;
    }

    *buffSize = mInBuffSize;

    return NO_ERROR;
}

sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle)
{
    sp<AudioIoDescriptor> desc;
    ssize_t index = mIoDescriptors.indexOfKey(ioHandle);
    if (index >= 0) {
        desc = mIoDescriptors.valueAt(index);
    }
    return desc;
}

void AudioSystem::setErrorCallback(audio_error_callback cb)
@@ -860,9 +855,8 @@ void AudioSystem::clearAudioConfigCache()
{
    // called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances
    ALOGV("clearAudioConfigCache()");
    {
        Mutex::Autolock _l(gLockCache);
        gOutputs.clear();
    if (gAudioFlingerClient != 0) {
        gAudioFlingerClient->clearIoCache();
    }
    {
        Mutex::Autolock _l(gLock);
+16 −33
Original line number Diff line number Diff line
@@ -39,25 +39,17 @@ public:
    {
    }

    void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
    void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
        data.writeInt32(event);
        data.writeInt32((int32_t) ioHandle);
        if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
            uint32_t stream = *(const uint32_t *)param2;
            ALOGV("ioConfigChanged stream %d", stream);
            data.writeInt32(stream);
        } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
            const AudioSystem::OutputDescriptor *desc =
                    (const AudioSystem::OutputDescriptor *)param2;
            data.writeInt32(desc->samplingRate);
            data.writeInt32(desc->format);
            data.writeInt32(desc->channelMask);
            data.writeInt64(desc->frameCount);
            data.writeInt32(desc->latency);
        }
        data.writeInt32((int32_t)ioDesc->mIoHandle);
        data.writeInt32(ioDesc->mSamplingRate);
        data.writeInt32(ioDesc->mFormat);
        data.writeInt32(ioDesc->mChannelMask);
        data.writeInt64(ioDesc->mFrameCount);
        data.writeInt32(ioDesc->mLatency);
        remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
    }
};
@@ -72,24 +64,15 @@ status_t BnAudioFlingerClient::onTransact(
    switch (code) {
    case IO_CONFIG_CHANGED: {
            CHECK_INTERFACE(IAudioFlingerClient, data, reply);
            int event = data.readInt32();
            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
            const void *param2 = NULL;
            AudioSystem::OutputDescriptor desc;
            uint32_t stream;
            if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
                stream = data.readInt32();
                param2 = &stream;
                ALOGV("STREAM_CONFIG_CHANGED stream %d", stream);
            } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
                desc.samplingRate = data.readInt32();
                desc.format = (audio_format_t) data.readInt32();
                desc.channelMask = (audio_channel_mask_t) data.readInt32();
                desc.frameCount = data.readInt64();
                desc.latency = data.readInt32();
                param2 = &desc;
            }
            ioConfigChanged(event, ioHandle, param2);
            audio_io_config_event event = (audio_io_config_event)data.readInt32();
            sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
            ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32();
            ioDesc->mSamplingRate = data.readInt32();
            ioDesc->mFormat = (audio_format_t) data.readInt32();
            ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32();
            ioDesc->mFrameCount = data.readInt64();
            ioDesc->mLatency = data.readInt32();
            ioConfigChanged(event, ioDesc);
            return NO_ERROR;
        } break;
        default:
Loading