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

Commit 53810823 authored by Eric Laurent's avatar Eric Laurent
Browse files

audio routing management for radio

Added radio tuner device connection/disconnection
indication to audio policy manager.

Added documentation and removed unused Module
class member.

Change-Id: I92438c1ff212c4d76f008149554fa89e367fee42
parent b89b86d4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ LOCAL_SHARED_LIBRARIES:= \
    libutils \
    libbinder \
    libcutils \
    libmedia \
    libhardware \
    libradio \
    libradio_metadata
+75 −19
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <sys/types.h>
#include <pthread.h>

#include <system/audio.h>
#include <system/audio_policy.h>
#include <system/radio.h>
#include <system/radio_metadata.h>
#include <cutils/atomic.h>
@@ -33,11 +35,13 @@
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <hardware/radio.h>
#include <media/AudioSystem.h>
#include "RadioService.h"
#include "RadioRegions.h"

namespace android {

static const char kRadioTunerAudioDeviceName[] = "Radio tuner source";

RadioService::RadioService()
    : BnRadioService(), mNextUniqueId(1)
@@ -84,7 +88,7 @@ void RadioService::onFirstRef()
    ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);

    convertProperties(&properties, &halProperties);
    sp<Module> module = new Module(this, dev, properties);
    sp<Module> module = new Module(dev, properties);
    mModules.add(properties.handle, module);
}

@@ -380,10 +384,8 @@ void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event)
#undef LOG_TAG
#define LOG_TAG "RadioService::Module"

RadioService::Module::Module(const sp<RadioService>& service,
                                      radio_hw_device* hwDevice,
                                      radio_properties properties)
 : mService(service), mHwDevice(hwDevice), mProperties(properties), mMute(true)
RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties)
 : mHwDevice(hwDevice), mProperties(properties), mMute(true)
{
}

@@ -416,6 +418,31 @@ sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioCl
    struct radio_hal_band_config halConfig;
    halConfig = config->band;

    // Tuner preemption logic:
    // There is a limited amount of tuners and a limited amount of radio audio sources per module.
    // The minimum is one tuner and one audio source.
    // The numbers of tuners and sources are indicated in the module properties.
    // NOTE: current framework implementation only supports one radio audio source.
    // It is possible to open more than one tuner at a time but only one tuner can be connected
    // to the radio audio source (AUDIO_DEVICE_IN_FM_TUNER).
    // The base rule is that a newly connected tuner always wins, i.e. always gets a tuner
    // and can use the audio source if requested.
    // If another client is preempted, it is notified by a callback with RADIO_EVENT_CONTROL
    // indicating loss of control.
    // - If the newly connected client requests the audio source (audio == true):
    //    - if an audio source is available
    //          no problem
    //    - if not:
    //          the oldest client in the list using audio is preempted.
    // - If the newly connected client does not request the audio source (audio == false):
    //    - if a tuner is available
    //          no problem
    //    - if not:
    //          The oldest client not using audio is preempted first and if none is found the
    //          the oldest client using audio is preempted.
    // Each time a tuner using the audio source is opened or closed, the audio policy manager is
    // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.

    sp<ModuleClient> oldestTuner;
    sp<ModuleClient> oldestAudio;
    size_t allocatedTuners = 0;
@@ -437,28 +464,31 @@ sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioCl
    }

    const struct radio_tuner *halTuner;
    sp<ModuleClient> preemtedClient;
    if (audio) {
        if (allocatedAudio >= mProperties.num_audio_sources) {
            ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
            halTuner = oldestAudio->getTuner();
            oldestAudio->setTuner(NULL);
            mHwDevice->close_tuner(mHwDevice, halTuner);
            preemtedClient = oldestAudio;
        }
    } else {
        if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) {
            if (allocatedTuners != 0) {
                ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch");
                halTuner = oldestTuner->getTuner();
                oldestTuner->setTuner(NULL);
                mHwDevice->close_tuner(mHwDevice, halTuner);
                preemtedClient = oldestTuner;
            } else {
                ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
                halTuner = oldestAudio->getTuner();
                oldestAudio->setTuner(NULL);
                mHwDevice->close_tuner(mHwDevice, halTuner);
                preemtedClient = oldestAudio;
            }
        }
    }
    if (preemtedClient != 0) {
        halTuner = preemtedClient->getTuner();
        preemtedClient->setTuner(NULL);
        mHwDevice->close_tuner(mHwDevice, halTuner);
        if (preemtedClient->audio()) {
            notifyDeviceConnection(false, "");
        }
    }

    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
                                RadioService::callback, moduleClient->callbackThread().get(),
@@ -467,11 +497,13 @@ sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioCl
        ALOGV("addClient() setTuner %p", halTuner);
        moduleClient->setTuner(halTuner);
        mModuleClients.add(moduleClient);
        if (audio) {
            notifyDeviceConnection(true, "");
        }
    } else {
        moduleClient.clear();
    }

    //TODO notify audio device connection to audio policy manager if audio is on

    ALOGV("addClient() DONE moduleClient %p", moduleClient.get());

@@ -501,19 +533,32 @@ void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) {
    }

    mHwDevice->close_tuner(mHwDevice, halTuner);
    if (moduleClient->audio()) {
        notifyDeviceConnection(false, "");
    }

    //TODO notify audio device disconnection to audio policy manager if audio was on
    mMute = true;

    if (mModuleClients.isEmpty()) {
        return;
    }

    // Tuner reallocation logic:
    // When a client is removed and was controlling a tuner, this tuner will be allocated to a
    // previously preempted client. This client will be notified by a callback with
    // RADIO_EVENT_CONTROL indicating gain of control.
    // - If a preempted client is waiting for an audio source and one becomes available:
    //    Allocate the tuner to the most recently added client waiting for an audio source
    // - If not:
    //    Allocate the tuner to the most recently added client.
    // Each time a tuner using the audio source is opened or closed, the audio policy manager is
    // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.

    sp<ModuleClient> youngestClient;
    sp<ModuleClient> youngestClientAudio;
    size_t allocatedTuners = 0;
    size_t allocatedAudio = 0;
    for (ssize_t i = mModuleClients.size(); i >= 0; i--) {
    for (ssize_t i = mModuleClients.size() - 1; i >= 0; i--) {
        if (mModuleClients[i]->getTuner() == NULL) {
            if (mModuleClients[i]->audio()) {
                if (youngestClientAudio == 0) {
@@ -550,10 +595,11 @@ void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) {
                                RadioService::callback, moduleClient->callbackThread().get(),
                                &halTuner);

    //TODO notify audio device connection to audio policy manager if audio is on

    if (ret == 0) {
        youngestClient->setTuner(halTuner);
        if (youngestClient->audio()) {
            notifyDeviceConnection(true, "");
        }
    }
}

@@ -583,6 +629,16 @@ const struct radio_band_config *RadioService::Module::getDefaultConfig() const
    return &mProperties.bands[0];
}

void RadioService::Module::notifyDeviceConnection(bool connected,
                                                  const char *address) {
    int64_t token = IPCThreadState::self()->clearCallingIdentity();
    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER,
                                          connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
                                                  AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
                                          address, kRadioTunerAudioDeviceName);
    IPCThreadState::self()->restoreCallingIdentity(token);
}

#undef LOG_TAG
#define LOG_TAG "RadioService::ModuleClient"

+25 −23
Original line number Diff line number Diff line
@@ -66,8 +66,7 @@ public:
    class Module : public virtual RefBase {
    public:

       Module(const sp<RadioService>& service,
              radio_hw_device* hwDevice,
       Module(radio_hw_device* hwDevice,
              struct radio_properties properties);

       virtual ~Module();
@@ -88,16 +87,17 @@ public:
       const struct radio_properties properties() const { return mProperties; }
       const struct radio_band_config *getDefaultConfig() const ;

       wp<RadioService> service() const { return mService; }

    private:

        Mutex                         mLock;
        wp<RadioService>              mService;
        const struct radio_hw_device        *mHwDevice;
        const struct radio_properties       mProperties;
        Vector< sp<ModuleClient> >    mModuleClients;
        bool                          mMute;
       void notifyDeviceConnection(bool connected, const char *address);

        Mutex                         mLock;          // protects  mModuleClients
        const struct radio_hw_device  *mHwDevice;     // HAL hardware device
        const struct radio_properties mProperties;    // cached hardware module properties
        Vector< sp<ModuleClient> >    mModuleClients; // list of attached clients
        bool                          mMute;          // radio audio source state
                                                      // when unmuted, audio is routed to the
                                                      // output device selected for media use case.
    }; // class Module

    class CallbackThread : public Thread {
@@ -120,11 +120,11 @@ public:
                sp<IMemory> prepareEvent(radio_hal_event_t *halEvent);

    private:
        wp<ModuleClient>      mModuleClient;
        Condition             mCallbackCond;
        Mutex                 mCallbackLock;
        Vector< sp<IMemory> > mEventQueue;
        sp<MemoryDealer>      mMemoryDealer;
        wp<ModuleClient>      mModuleClient;    // client module the thread belongs to
        Condition             mCallbackCond;    // condition signaled when a new event is posted
        Mutex                 mCallbackLock;    // protects mEventQueue
        Vector< sp<IMemory> > mEventQueue;      // pending callback events
        sp<MemoryDealer>      mMemoryDealer;    // shared memory for callback event
    }; // class CallbackThread

    class ModuleClient : public BnRadio,
@@ -181,13 +181,15 @@ public:

    private:

        mutable Mutex               mLock;
        wp<Module>                  mModule;
        sp<IRadioClient>            mClient;
        radio_band_config_t         mConfig;
        sp<CallbackThread>          mCallbackThread;
        mutable Mutex               mLock;           // protects mClient, mConfig and mTuner
        wp<Module>                  mModule;         // The module this client is attached to
        sp<IRadioClient>            mClient;         // event callback binder interface
        radio_band_config_t         mConfig;         // current band configuration
        sp<CallbackThread>          mCallbackThread; // event callback thread
        const bool                  mAudio;
        const struct radio_tuner    *mTuner;
        const struct radio_tuner    *mTuner;        // HAL tuner interface. NULL indicates that
                                                    // this client does not have control on any
                                                    // tuner
    }; // class ModuleClient


@@ -199,8 +201,8 @@ private:

    static void convertProperties(radio_properties_t *properties,
                                  const radio_hal_properties_t *halProperties);
    Mutex               mServiceLock;
    volatile int32_t    mNextUniqueId;
    Mutex               mServiceLock;   // protects mModules
    volatile int32_t    mNextUniqueId;  // for module ID allocation
    DefaultKeyedVector< radio_handle_t, sp<Module> > mModules;
};