BT: Serialize SHO

This change serialize multiple SHO request
triggerred from multiple devices.

CRs-Fixed: 2400666

Change-Id: I3cb10c1b65f3463e744e03b5b95ff9d492f0602a
diff --git a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
index 43aaf77..413ccdb 100644
--- a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
+++ b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
@@ -171,6 +171,7 @@
     private byte changePathDirection;
     HashMap<BluetoothDevice, Integer> mVolumeMap = new HashMap();
     public static final String VOLUME_MAP = "bluetooth_volume_map";
+    private boolean isShoActive = false;
 
     private boolean twsShoEnabled = false;
     private static final String playerStateUpdateBlackListedAddr[] = {
@@ -234,6 +235,7 @@
     private final static int MESSAGE_UPDATE_ABS_VOLUME_STATUS = 31;
     private final static int MESSAGE_UPDATE_ABSOLUTE_VOLUME = 32;
     private static final int MSG_PLAY_STATUS_CMD_TIMEOUT = 33;
+    private final static int MESSAGE_START_SHO = 34;
 
     private static final int STACK_CLEANUP = 0;
     private static final int APP_CLEANUP = 1;
@@ -406,6 +408,11 @@
     };
     DeviceDependentFeature[] deviceFeatures;
 
+    private static class SHOQueue {
+        static BluetoothDevice device;
+        static boolean PlayReq;
+    }
+
     static {
         classInitNative();
     }
@@ -1353,9 +1360,28 @@
                 // This is for some remote devices, which send PLAY/PAUSE based on AVRCP State.
                 BATService mBatService = BATService.getBATService();
                 if ((mBatService == null) || !mBatService.isA2dpSuspendFromBA()) {
-                  // if this suspend was triggered by BA, then don't update AVRCP state
+                  // if this suspend was triggered by BA, then don't update AVRCP states
                   updateCurrentMediaState((BluetoothDevice)msg.obj);
                 }
+
+                if (mA2dpState == BluetoothA2dp.STATE_PLAYING) {
+                    boolean shoComplete = false;
+                    synchronized(Avrcp_ext.this) {
+                        if(isShoActive) {
+                            isShoActive = false;
+                            shoComplete = true;
+                            Log.e(TAG, "1: SHO complete");
+                        }
+
+                        if(mHandler.hasMessages(MESSAGE_START_SHO)) {
+                            mHandler.removeMessages(MESSAGE_START_SHO);
+                            triggerSHO(SHOQueue.device, SHOQueue.PlayReq);
+                        }
+                    }
+                    if(shoComplete == true) {
+                        CompleteSHO();
+                    }
+                }
               }
               break;
 
@@ -1520,6 +1546,36 @@
                 deviceFeatures[deviceIndex].isPlayStatusTimeOut = true;
                 break;
 
+            case MESSAGE_START_SHO:
+                if (mA2dpService != null)
+                    break;
+
+                synchronized (Avrcp_ext.this) {
+                    if(mHandler.hasMessages(MESSAGE_START_SHO)) {
+                        Log.e(TAG, "Queue already has another SHO pending");
+                        break;
+                    }
+                    isShoActive = true;
+                    Log.d(TAG, "2: SHO started. PlayReq = " + msg.arg1);
+                }
+
+                BluetoothDevice dev = (BluetoothDevice)msg.obj;
+                boolean PlayReq = (msg.arg1 == 1);
+                mA2dpService.startSHO(dev);
+
+                if(!PlayReq) {
+                    synchronized (Avrcp_ext.this) {
+                        isShoActive = false;
+                        Log.d(TAG, "3: SHO complete");
+                        if (mHandler.hasMessages(MESSAGE_START_SHO)) {
+                            mHandler.removeMessages(MESSAGE_START_SHO);
+                            triggerSHO(SHOQueue.device, SHOQueue.PlayReq);
+                        }
+                    }
+                    CompleteSHO();
+                }
+                break;
+
             default:
                 Log.e(TAG, "unknown message! msg.what=" + msg.what);
                 break;
@@ -4645,7 +4701,7 @@
             if((rspStatus == AvrcpConstants.RSP_NO_ERROR) && ((mA2dpService != null) &&
                     !Objects.equals(mA2dpService.getActiveDevice(), device))) {
                 Log.d(TAG, "Trigger Handoff by playItem");
-                mA2dpService.setActiveDevice(device);
+                startSHO(device, true);
             }
             if (!playItemRspNative(address, rspStatus)) {
                 Log.e(TAG, "playItemRspNative failed!");
@@ -4817,7 +4873,43 @@
     }
 
     public boolean startSHO(BluetoothDevice device, boolean PlayReq) {
-        return false;
+        synchronized (Avrcp_ext.this) {
+            if(isShoActive) {
+                mHandler.removeMessages (MESSAGE_START_SHO);
+                Message msg = mHandler.obtainMessage(MESSAGE_START_SHO, PlayReq?1:0, 0, device);
+                SHOQueue.device = device;
+                SHOQueue.PlayReq = PlayReq;
+                mHandler.sendMessageDelayed(msg, 3000);
+                Log.d(TAG, "4: SHO Queued");
+                return true;
+            } else {
+                isShoActive = true;
+                Log.d(TAG, "5: SHO started: PlayReq = " + PlayReq);
+            }
+        }
+        boolean ret = mA2dpService.startSHO(device);
+        synchronized (Avrcp_ext.this) {
+            if (!PlayReq) {
+                isShoActive = false;
+                Log.d(TAG, "6: SHO complete");
+
+                if (mHandler.hasMessages(MESSAGE_START_SHO)) {
+                    mHandler.removeMessages(MESSAGE_START_SHO);
+                    triggerSHO(SHOQueue.device, SHOQueue.PlayReq);
+                }
+            }
+        }
+        CompleteSHO();
+        return ret;
+    }
+
+    private void triggerSHO(BluetoothDevice device, boolean PlayReq) {
+        Message msg = mHandler.obtainMessage(MESSAGE_START_SHO, PlayReq?1:0, 0, device);
+        mHandler.sendMessage(msg);
+    }
+
+    public void CompleteSHO() {
+        /*For device setup after SHO*/
     }
 
     public void setActiveDevice(BluetoothDevice device) {
@@ -5018,7 +5110,7 @@
                 }
                 if (action == KeyEvent.ACTION_DOWN) {
                     Log.d(TAG, "AVRCP Trigger Handoff");
-                    mA2dpService.setActiveDevice(device);
+                    startSHO(device, true);
                 } else {
                     Log.d(TAG, "release for play PT from inactive device");
                 }