FM: update fm app to use device loopback

Update fm app to use device loopback for
playback on headset, headphone or speaker.

Change-Id: I44642858cc9a42fad0fad2ad3f8487aaa624d35e
diff --git a/fmapp2/src/com/caf/fmradio/FMRadioService.java b/fmapp2/src/com/caf/fmradio/FMRadioService.java
index 35e7e24..56d9aa6 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadioService.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadioService.java
@@ -207,6 +207,7 @@
    private boolean mIsRecordSink = false;
    private static final int AUDIO_FRAMES_COUNT_TO_IGNORE = 3;
    private Object mRecordSinkLock = new Object();
+   private boolean mIsFMDeviceLoopbackActive = false;
 
    public FMRadioService() {
    }
@@ -368,7 +369,7 @@
 
    private synchronized void stopRecordSink() {
         Log.d(LOGTAG, "stopRecordSink");
-        mRecordSinkLock = false;
+        mIsRecordSink = false;
         synchronized (mRecordSinkLock) {
             mRecordSinkLock.notify();
         }
@@ -464,6 +465,57 @@
         }
     }
 
+    private boolean configureFMDeviceLoopback(boolean enable) {
+        boolean success = true;
+        int status = AudioSystem.SUCCESS;
+
+        Log.d(LOGTAG, "configureFMDeviceLoopback enable:" + enable +
+              " DeviceLoopbackActive:" + mIsFMDeviceLoopbackActive);
+        if (enable && mIsFMDeviceLoopbackActive == false) {
+            status = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
+                                          AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
+            if (status != AudioSystem.SUCCESS) {
+                success = false;
+                Log.e(LOGTAG, "configureFMDeviceLoopback failed! status:" + status);
+                AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
+                                     AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
+            } else {
+                mIsFMDeviceLoopbackActive = true;
+            }
+        } else if (!enable && mIsFMDeviceLoopbackActive == true) {
+            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
+                                 AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
+            mIsFMDeviceLoopbackActive = false;
+        }
+
+        return success;
+    }
+
+    private synchronized void configureAudioDataPath(boolean enable) {
+        Log.d(LOGTAG, "configureAudioDataPath:" + enable +
+                      " mA2dpConnected:" + mA2dpConnected +
+                      " isRecordSinking" + isRecordSinking() +
+                      " mIsFMDeviceLoopbackActive:" + mIsFMDeviceLoopbackActive);
+        if (enable) {
+            // stop existing playback path before starting new one
+            if (mA2dpConnected && mIsFMDeviceLoopbackActive) {
+                // on BT but earlier device loopback is active
+                configureFMDeviceLoopback(false);
+            } else if (!mA2dpConnected && !mIsFMDeviceLoopbackActive) {
+                // not on BT and device loop is also not active
+                exitRecordSinkThread();
+                configureFMDeviceLoopback(true);
+            }
+
+            // start app thread if none of the path started yet
+            if (!mIsFMDeviceLoopbackActive && !isRecordSinking())
+                startRecordSink();
+        } else {
+            configureFMDeviceLoopback(false);
+            exitRecordSinkThread();
+        }
+    }
+
     /**
       * Registers an intent to listen for ACTION_MEDIA_UNMOUNTED notifications.
       * The intent will call closeExternalStorageFiles() if the external media
@@ -570,6 +622,7 @@
                             mA2dpDisconnected = false;
                             mA2dpConnected = true;
                         }
+                        configureAudioDataPath(true);
                     } else if (action.equals("HDMI_CONNECTED")) {
                         //FM should be off when HDMI is connected.
                         fmOff();
@@ -592,6 +645,9 @@
 
                 }
             };
+            AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+            mA2dpConnected = am.isBluetoothA2dpOn();
+            mA2dpDisconnected = !mA2dpConnected;
             IntentFilter iFilter = new IntentFilter();
             iFilter.addAction(Intent.ACTION_HEADSET_PLUG);
             iFilter.addAction(mA2dpDeviceState.getActionSinkStateChangedString());
@@ -1058,14 +1114,13 @@
                AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
            }
        }
-       startRecordSink();
        mPlaybackInProgress = true;
+       configureAudioDataPath(true);
    }
 
    private void stopFM(){
        Log.d(LOGTAG, "In stopFM");
-       stopRecordSink();
-       exitRecordSinkThread();
+       configureAudioDataPath(false);
        mPlaybackInProgress = false;
    }