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
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
index 5e89b22..9ee5666 100644
--- a/services/radio/Android.mk
+++ b/services/radio/Android.mk
@@ -26,6 +26,7 @@
     libutils \
     libbinder \
     libcutils \
+    libmedia \
     libhardware \
     libradio \
     libradio_metadata
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index 152619b..a6c2bdf 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -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 @@
     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 @@
 #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 @@
     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 @@
     }
 
     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 @@
         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 @@
     }
 
     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 @@
                                 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 @@
     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"
 
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
index 9ede020..49feda6 100644
--- a/services/radio/RadioService.h
+++ b/services/radio/RadioService.h
@@ -66,8 +66,7 @@
     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 @@
        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 @@
                 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 @@
 
     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 @@
 
     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;
 };