Add working bluetooth support

This commit introduces basic bluetooth support.
If a headset is paired to the phone, audio will be correctly rerouted.
This is still experimental, due to the lack of testing devices.

Refs #41293
diff --git a/src/org/sflphone/utils/MediaManager.java b/src/org/sflphone/utils/MediaManager.java
index 30e2662..25ddef5 100644
--- a/src/org/sflphone/utils/MediaManager.java
+++ b/src/org/sflphone/utils/MediaManager.java
@@ -38,14 +38,17 @@
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.Handler;
 import android.util.Log;
+import org.sflphone.utils.bluetooth.BluetoothWrapper;
 
-public class MediaManager implements OnAudioFocusChangeListener {
+public class MediaManager implements OnAudioFocusChangeListener, BluetoothWrapper.BluetoothChangeListener {
 
     private static final String TAG = MediaManager.class.getSimpleName();
     private SipService mService;
     private SettingsContentObserver mSettingsContentObserver;
     AudioManager mAudioManager;
     private Ringer ringer;
+    //Bluetooth related
+    private BluetoothWrapper bluetoothWrapper;
 
     public MediaManager(SipService aService) {
         mService = aService;
@@ -56,6 +59,11 @@
     }
 
     public void startService() {
+        if(bluetoothWrapper == null) {
+            bluetoothWrapper = BluetoothWrapper.getInstance(mService);
+            bluetoothWrapper.setBluetoothChangeListener(this);
+            bluetoothWrapper.register();
+        }
         mService.getApplicationContext().getContentResolver()
                 .registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver);
     }
@@ -63,6 +71,11 @@
     public void stopService() {
         Log.i(TAG, "Remove media manager....");
         mService.getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
+        if(bluetoothWrapper != null) {
+            bluetoothWrapper.unregister();
+            bluetoothWrapper.setBluetoothChangeListener(null);
+            bluetoothWrapper = null;
+        }
     }
 
     public AudioManager getAudioManager() {
@@ -72,7 +85,10 @@
     public void obtainAudioFocus(boolean requestSpeakerOn) {
         mAudioManager.requestAudioFocus(this, Compatibility.getInCallStream(false), AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
-        if(requestSpeakerOn && !mAudioManager.isWiredHeadsetOn()){
+        if(bluetoothWrapper != null && bluetoothWrapper.canBluetooth()) {
+            Log.d(TAG, "Try to enable bluetooth");
+            bluetoothWrapper.setBluetoothOn(true);
+        } else if (requestSpeakerOn && !mAudioManager.isWiredHeadsetOn()){
             RouteToSpeaker();
         }
     }
@@ -123,4 +139,11 @@
             ringer.stopRing();
         }
     }
+
+    @Override
+    public void onBluetoothStateChanged(int status) {
+        //setSoftwareVolume();
+        //broadcastMediaChanged();
+    }
+
 }
diff --git a/src/org/sflphone/utils/bluetooth/BluetoothUtils14.java b/src/org/sflphone/utils/bluetooth/BluetoothUtils14.java
new file mode 100644
index 0000000..d7d681c
--- /dev/null
+++ b/src/org/sflphone/utils/bluetooth/BluetoothUtils14.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ * This file is part of CSipSimple.
+ *
+ *  CSipSimple is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *  If you own a pjsip commercial license you can also redistribute it
+ *  and/or modify it under the terms of the GNU Lesser General Public License
+ *  as an android library.
+ *
+ *  CSipSimple is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sflphone.utils.bluetooth;
+
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.Set;
+
+public class BluetoothUtils14 extends BluetoothWrapper {
+
+    private static String TAG = BluetoothUtils14.class.getSimpleName();
+    private AudioManager audioManager;
+    private boolean isBluetoothConnected = false;
+    
+    
+    @Override
+    public boolean isBTHeadsetConnected() {
+        if(bluetoothAdapter != null) {
+            return (bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) == BluetoothAdapter.STATE_CONNECTED);
+        }
+        return false;
+    }
+
+    
+
+    private BroadcastReceiver mediaStateReceiver = new BroadcastReceiver() {
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.d(TAG, ">>> BT SCO state changed !!! ");
+            if(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED.equals(action)) {
+                int status = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR );
+                Log.d(TAG, "BT SCO state changed : " + status + " target is " + targetBt);
+                audioManager.setBluetoothScoOn(targetBt);
+
+                if(status == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
+                    isBluetoothConnected = true;
+                }else if(status == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
+                    isBluetoothConnected = false;
+                }
+
+                if(btChangesListener != null) {
+                    btChangesListener.onBluetoothStateChanged(status);
+                }
+            }
+        }
+    };
+
+    protected BluetoothAdapter bluetoothAdapter;
+
+    @Override
+    public void setContext(Context aContext){
+        super.setContext(aContext);
+        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        if(bluetoothAdapter == null) {
+            try {
+                bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+            }catch(RuntimeException e) {
+                Log.w(TAG, "Cant get default bluetooth adapter ", e);
+            }
+        }
+    }
+
+    public boolean canBluetooth() {
+        // Detect if any bluetooth a device is available for call
+        if (bluetoothAdapter == null) {
+            // Device does not support Bluetooth
+            return false;
+        }
+        boolean hasConnectedDevice = false;
+        //If bluetooth is on
+        if(bluetoothAdapter.isEnabled()) {
+
+            //We get all bounded bluetooth devices
+            // bounded is not enough, should search for connected devices....
+            Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
+            for(BluetoothDevice device : pairedDevices) {
+                BluetoothClass bluetoothClass = device.getBluetoothClass();
+                if (bluetoothClass != null) {
+                    int deviceClass = bluetoothClass.getDeviceClass();
+                    if(bluetoothClass.hasService(BluetoothClass.Service.RENDER) ||
+                            deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET ||
+                            deviceClass == BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO ||
+                            deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ) {
+                        //And if any can be used as a audio handset
+                        hasConnectedDevice = true;
+                        break;
+                    }
+                }
+            }
+        }
+        boolean retVal = hasConnectedDevice && audioManager.isBluetoothScoAvailableOffCall();
+        Log.d(TAG, "Can I do BT ? "+retVal);
+        return retVal;
+    }
+
+    private boolean targetBt = false;
+    public void setBluetoothOn(boolean on) {
+        Log.d(TAG, "Ask for "+on+" vs "+audioManager.isBluetoothScoOn());
+        targetBt = on;
+        if(on != isBluetoothConnected) {
+            // BT SCO connection state is different from required activation
+            if(on) {
+                // First we try to connect
+                Log.d(TAG, "BT SCO on >>>");
+                audioManager.startBluetoothSco();
+            }else {
+                Log.d(TAG, "BT SCO off >>>");
+                // We stop to use BT SCO
+                audioManager.setBluetoothScoOn(false);
+                // And we stop BT SCO connection
+                audioManager.stopBluetoothSco();
+            }
+        }else if(on != audioManager.isBluetoothScoOn()) {
+            // BT SCO is already in desired connection state
+            // we only have to use it
+            audioManager.setBluetoothScoOn(on);
+        }
+    }
+
+    public boolean isBluetoothOn() {
+        return isBluetoothConnected;
+    }
+
+    @SuppressWarnings("deprecation")
+    public void register() {
+        Log.d(TAG, "Register BT media receiver");
+        context.registerReceiver(mediaStateReceiver , new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
+    }
+
+    public void unregister() {
+        try {
+            Log.d(TAG, "Unregister BT media receiver");
+            context.unregisterReceiver(mediaStateReceiver);
+        }catch(Exception e) {
+            Log.w(TAG, "Failed to unregister media state receiver",e);
+        }
+    }
+}
diff --git a/src/org/sflphone/utils/bluetooth/BluetoothWrapper.java b/src/org/sflphone/utils/bluetooth/BluetoothWrapper.java
new file mode 100644
index 0000000..90478b4
--- /dev/null
+++ b/src/org/sflphone/utils/bluetooth/BluetoothWrapper.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ * This file is part of CSipSimple.
+ *
+ *  CSipSimple is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *  If you own a pjsip commercial license you can also redistribute it
+ *  and/or modify it under the terms of the GNU Lesser General Public License
+ *  as an android library.
+ *
+ *  CSipSimple is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sflphone.utils.bluetooth;
+
+import android.content.Context;
+
+
+public abstract class BluetoothWrapper {
+	
+    public interface BluetoothChangeListener {
+        void onBluetoothStateChanged(int status);
+    }
+    
+    
+	private static BluetoothWrapper instance;
+    protected Context context;
+    
+    protected BluetoothChangeListener btChangesListener;
+	
+	public static BluetoothWrapper getInstance(Context context) {
+		if(instance == null) {
+		        instance = new BluetoothUtils14();
+		    if(instance != null) {
+		        instance.setContext(context);
+		    }
+		}
+		
+		return instance;
+	}
+	
+	protected BluetoothWrapper() {}
+
+	protected void setContext(Context ctxt) {
+	    context = ctxt;
+	}
+	
+	public void setBluetoothChangeListener(BluetoothChangeListener l) {
+	    btChangesListener = l;
+	}
+	
+	public abstract boolean canBluetooth();
+	public abstract void setBluetoothOn(boolean on);
+	public abstract boolean isBluetoothOn();
+	public abstract void register();
+	public abstract void unregister();
+	public abstract boolean isBTHeadsetConnected();
+}