Merge "A2dp: Support A2dp Source and A2dpSink profiles concurrency." into q-keystone-qcom-dev
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
old mode 100644
new mode 100755
index 6b1678e..521b00c
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -23,6 +23,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothA2dp;
+import com.android.bluetooth.a2dpsink.A2dpSinkService;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -65,8 +66,12 @@
 public class A2dpService extends ProfileService {
     private static final boolean DBG = true;
     private static final String TAG = "A2dpService";
+    private static final String A2DP_CONCURRENCY_SUPPORTED_PROPERTY =
+            "persist.vendor.service.bt.a2dp_concurrency";
 
     private static A2dpService sA2dpService;
+    private static A2dpSinkService sA2dpSinkService;
+    private static boolean mA2dpSrcSnkConcurrency;
 
     private AdapterService mAdapterService;
     private HandlerThread mStateMachinesThread;
@@ -275,6 +280,11 @@
         // Step 10: Clear active device
         setActiveDevice(null);
 
+        // Step 11: Check if A2DP is in concurrency mode
+        mA2dpSrcSnkConcurrency = SystemProperties.getBoolean(A2DP_CONCURRENCY_SUPPORTED_PROPERTY, false);
+        if (DBG) {
+            Log.d(TAG, "A2DP concurrency mode set to " + mA2dpSrcSnkConcurrency);
+        }
         return true;
     }
 
@@ -440,12 +450,20 @@
                 Log.e(TAG, "Cannot connect to " + device + " : no state machine");
                 return false;
             }
+            if (mA2dpSrcSnkConcurrency) {
+                sA2dpSinkService = A2dpSinkService.getA2dpSinkService();
+                List<BluetoothDevice> srcDevs = sA2dpSinkService.getConnectedDevices();
+                for ( BluetoothDevice src : srcDevs ) {
+                    Log.d(TAG, "calling sink disconnect to " + src);
+                    sA2dpSinkService.disconnect(src);
+                }
+            }
             smConnect.sendMessage(A2dpStateMachine.CONNECT);
             return true;
         }
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
         if (DBG) {
             Log.d(TAG, "disconnect(): " + device);
@@ -1406,6 +1424,16 @@
                 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
                 return;
             }
+            if (mA2dpSrcSnkConcurrency &&
+                    (A2dpStackEvent.CONNECTION_STATE_CONNECTING == stackEvent.valueInt ||
+                     A2dpStackEvent.CONNECTION_STATE_CONNECTED == stackEvent.valueInt )) {
+                sA2dpSinkService = A2dpSinkService.getA2dpSinkService();
+                List<BluetoothDevice> srcDevs = sA2dpSinkService.getConnectedDevices();
+                for ( BluetoothDevice src : srcDevs ) {
+                    Log.d(TAG, "calling sink disconnect to " + src);
+                    sA2dpSinkService.disconnect(src);
+                }
+            }
             sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent);
         }
     }
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
old mode 100644
new mode 100755
index b51ffea..3bac062
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -26,6 +26,8 @@
 
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.hfp.HeadsetService;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
@@ -54,6 +56,9 @@
     private  static final Object mBtA2dpLock = new Object();
     private static A2dpSinkStreamHandler mA2dpSinkStreamHandler;
     private static A2dpSinkService sService;
+    private static A2dpService sA2dpService;
+    private static HeadsetService sHeadsetService;
+    private static boolean mA2dpSrcSnkConcurrency;
     protected static BluetoothDevice mStreamingDevice;
 
     private static int mMaxA2dpSinkConnections = 1;
@@ -70,6 +75,11 @@
         mMaxA2dpSinkConnections = Math.min(
                 SystemProperties.getInt("persist.vendor.bt.a2dp.sink_conn", 1),
                 MAX_ALLOWED_SINK_CONNECTIONS);
+        mA2dpSrcSnkConcurrency= SystemProperties.getBoolean(
+                                "persist.vendor.service.bt.a2dp_concurrency", false);
+        if (DBG) {
+            Log.d(TAG, "A2DP concurrency set to " + mA2dpSrcSnkConcurrency);
+        }
         return true;
     }
 
@@ -250,6 +260,22 @@
         }
         A2dpSinkStateMachine stateMachine = getOrCreateStateMachine(device);
         if (stateMachine != null) {
+            if (mA2dpSrcSnkConcurrency) {
+                //in A2dp Concurrency mode, it need to disconnect all sink devices and headset clients
+                //before connecting to A2dp Source device.
+                sA2dpService = A2dpService.getA2dpService();
+                List<BluetoothDevice> snkDevs = sA2dpService.getConnectedDevices();
+                for( BluetoothDevice snk : snkDevs ) {
+                    Log.d(TAG, "calling src disconnect to " + snk);
+                    sA2dpService.disconnect(snk);
+                }
+                sHeadsetService = HeadsetService.getHeadsetService();
+                List<BluetoothDevice> hsDevs = sHeadsetService.getConnectedDevices();
+                for ( BluetoothDevice hs : hsDevs ) {
+                    Log.d(TAG, "calling headset disconnect to " + hs);
+                    sHeadsetService.disconnect(hs);
+                }
+            }
             stateMachine.connect();
             return true;
         } else {
@@ -532,14 +558,20 @@
             mStreamingDevice = null;
         }
 
-        // Intiate Handoff operations when state has been connectiond
-        if (state == BluetoothProfile.STATE_CONNECTED) {
-            if (mStreamingDevice != null && !mStreamingDevice.equals(device)) {
-                Log.d(TAG, "current connected device: " + device + "is different from previous device");
-                initiateHandoffOperations(device);
-                mStreamingDevice = device;
-            } else if (device != null) {
-                mStreamingDevice = device;
+        if (mA2dpSrcSnkConcurrency &&
+            (state == BluetoothProfile.STATE_CONNECTING ||
+             state == BluetoothProfile.STATE_CONNECTED)) {
+            sA2dpService = A2dpService.getA2dpService();
+            List<BluetoothDevice> snkDevs = sA2dpService.getConnectedDevices();
+            for ( BluetoothDevice snk : snkDevs ) {
+                Log.d(TAG, "calling src disconnect to " + snk);
+                sA2dpService.disconnect(snk);
+            }
+            sHeadsetService = HeadsetService.getHeadsetService();
+            List<BluetoothDevice> hsDevs = sHeadsetService.getConnectedDevices();
+            for ( BluetoothDevice hs : hsDevs ) {
+                Log.d(TAG, "calling headset disconnect to " + hs);
+                sHeadsetService.disconnect(hs);
             }
         }
         stateMachine.sendMessage(A2dpSinkStateMachine.STACK_EVENT, event);
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
old mode 100644
new mode 100755
index 0e170b1..c906317
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -171,14 +171,24 @@
 
     private static synchronized boolean addAudioProfiles(String serviceName) {
         Log.d(TAG," addAudioProfiles profile" + serviceName);
-        boolean isA2dpSink = SystemProperties.getBoolean(
-                "persist.vendor.service.bt.a2dp.sink", false);
-        Log.i(TAG, "addAudioProfiles isA2dpSink :" + isA2dpSink);
-        /* If property not enabled and request is for A2DPSinkService, don't add */
-        if ((serviceName.equals("A2dpSinkService")) && (!isA2dpSink))
-            return false;
-        if ((serviceName.equals("A2dpService")) && (isA2dpSink))
-            return false;
+        boolean isA2dpConcurrency= SystemProperties.getBoolean(
+                "persist.vendor.service.bt.a2dp_concurrency", false);
+        Log.i(TAG, "addAudioProfiles isA2dpConcurrency:" + isA2dpConcurrency);
+
+        if(isA2dpConcurrency) {
+            if ((serviceName.equals("A2dpSinkService")) || (serviceName.equals("A2dpService"))) {
+                return true;
+            }
+        } else {
+                boolean isA2dpSink = SystemProperties.getBoolean(
+                        "persist.vendor.service.bt.a2dp.sink", false);
+                Log.i(TAG, "addAudioProfiles isA2dpSink :" + isA2dpSink);
+                /* If property not enabled and request is for A2DPSinkService, don't add */
+                if ((serviceName.equals("A2dpSinkService")) && (!isA2dpSink))
+                    return false;
+                if ((serviceName.equals("A2dpService")) && (isA2dpSink))
+                    return false;
+        }
 
         boolean isBAEnabled = SystemProperties.getBoolean("persist.vendor.service.bt.bca", false);
 
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
old mode 100644
new mode 100755
index d9cadd9..8fefdba
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -1049,7 +1049,7 @@
         return true;
     }
 
-    boolean disconnect(BluetoothDevice device) {
+    public boolean disconnect(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
         Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString());
         synchronized (mStateMachines) {