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/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"