Bug 805708 - Patch 1: Cannot answer a waiting call during a phone call when press the headset, r=qdot
authorGina Yeh <gyeh@mozilla.com>
Wed, 31 Oct 2012 11:53:36 +0800
changeset 111999 f6ced9fd4fc54fe680e3eeb37fb796bc160edb21
parent 111998 1ba88bd2f4407ccb49f73efce360714b5e36ff64
child 112000 d3f1545072f3867d58dff0130a751354c2ae2305
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersqdot
bugs805708
milestone19.0a1
Bug 805708 - Patch 1: Cannot answer a waiting call during a phone call when press the headset, r=qdot
dom/bluetooth/BluetoothHfpManager.cpp
dom/bluetooth/BluetoothHfpManager.h
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -224,22 +224,23 @@ CloseScoSocket()
     NS_WARNING("BluetoothScoManager is not available!");
     return;
   }
   sco->Disconnect();
 }
 
 BluetoothHfpManager::BluetoothHfpManager()
   : mCurrentCallIndex(0)
-  , mCurrentCallState(nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED)
   , mReceiveVgsFlag(false)
 {
   sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
   sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
   sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
+
+  mCurrentCallStateArray.AppendElement((int)nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED);
 }
 
 bool
 BluetoothHfpManager::Init()
 {
   mSocketStatus = GetConnectionStatus();
 
   sHfpObserver = new BluetoothHfpManagerObserver();
@@ -326,17 +327,16 @@ BluetoothHfpManager::NotifySettings()
   if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
     v = true;
   } else {
     v = false;
   }
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("address");
-  GetSocketAddr(mDevicePath);
   v = mDevicePath;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to dialer");
     return;
   }
 }
@@ -444,16 +444,17 @@ BluetoothHfpManager::HandleShutdown()
 
 // Virtual function of class SocketConsumer
 void
 BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   const char* msg = (const char*)aMessage->mData;
+  int currentCallState = mCurrentCallStateArray[mCurrentCallIndex];
 
   // For more information, please refer to 4.34.1 "Bluetooth Defined AT
   // Capabilities" in Bluetooth hands-free profile 1.6
   if (!strncmp(msg, "AT+BRSF=", 8)) {
     SendCommand("+BRSF: ", 23);
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CIND=?", 9)) {
     // Asking for CIND range
@@ -462,19 +463,43 @@ BluetoothHfpManager::ReceiveSocketData(U
   } else if (!strncmp(msg, "AT+CIND?", 8)) {
     // Asking for CIND value
     SendCommand("+CIND: ", 1);
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CMER=", 8)) {
     // SLC establishment
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHLD=?", 9)) {
-    SendLine("+CHLD: (0,1,2,3)");
+    SendLine("+CHLD: (1,2)");
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHLD=", 8)) {
+    int length = strlen(msg) - 9;
+    nsAutoCString chldString(nsDependentCSubstring(msg+8, length));
+
+    nsresult rv;
+    int chld = chldString.ToInteger(&rv);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to extract volume value from bluetooth headset!");
+    }
+
+    switch(chld) {
+      case 1:
+        // Releases active calls and accepts the other (held or waiting) call
+        NotifyDialer(NS_LITERAL_STRING("CHUP+ATA"));
+        break;
+      case 2:
+        // Places active calls on hold and accepts the other (held or waiting) call
+        NotifyDialer(NS_LITERAL_STRING("CHLD+ATA"));
+        break;
+      default:
+#ifdef DEBUG
+        NS_WARNING("Not handling chld value");
+#endif
+        break;
+    }
     SendLine("OK");
   } else if (!strncmp(msg, "AT+VGS=", 7)) {
     mReceiveVgsFlag = true;
 
     int length = strlen(msg) - 8;
     nsAutoCString vgsString(nsDependentCSubstring(msg+7, length));
 
     nsresult rv;
@@ -508,17 +533,17 @@ BluetoothHfpManager::ReceiveSocketData(U
   } else if (!strncmp(msg, "ATA", 3)) {
     NotifyDialer(NS_LITERAL_STRING("ATA"));
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHUP", 7)) {
     NotifyDialer(NS_LITERAL_STRING("CHUP"));
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CKPD", 7)) {
     // For Headset
-    switch (mCurrentCallState) {
+    switch (currentCallState) {
       case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
         NotifyDialer(NS_LITERAL_STRING("ATA"));
         break;
       case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
       case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
       case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
         NotifyDialer(NS_LITERAL_STRING("CHUP"));
         break;
@@ -570,23 +595,16 @@ BluetoothHfpManager::Connect(const nsASt
 
   nsString serviceUuidStr;
   if (aIsHandsfree) {
     serviceUuidStr = NS_ConvertUTF8toUTF16(HANDSFREE_UUID);
   } else {
     serviceUuidStr = NS_ConvertUTF8toUTF16(HEADSET_UUID);
   }
 
-  nsCOMPtr<nsIRILContentHelper> ril =
-    do_GetService("@mozilla.org/ril/content-helper;1");
-  if (!ril) {
-    MOZ_ASSERT("Failed to get RIL Content Helper");
-  }
-  ril->EnumerateCalls(mListener->GetCallback());
-
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
   nsresult rv = bs->GetSocketViaService(aDevicePath,
                                         serviceUuidStr,
                                         BluetoothSocketType::RFCOMM,
                                         true,
                                         false,
                                         this,
@@ -706,27 +724,35 @@ BluetoothHfpManager::SendCommand(const c
 }
 
 void
 BluetoothHfpManager::SetupCIND(int aCallIndex, int aCallState, bool aInitial)
 {
   nsRefPtr<nsRunnable> sendRingTask;
   nsString address;
 
+  while (aCallIndex >= mCurrentCallStateArray.Length()) {
+    mCurrentCallStateArray.AppendElement((int)nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED);
+  }
+
+  int currentCallState = mCurrentCallStateArray[aCallIndex];
+
   switch (aCallState) {
     case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
       sCINDItems[CINDType::CALLSETUP].value = CallSetupState::INCOMING;
       if (!aInitial) {
         SendCommand("+CIEV: ", CINDType::CALLSETUP);
       }
 
-      // Start sending RING indicator to HF
-      sStopSendingRingFlag = false;
-      MessageLoop::current()->PostTask(FROM_HERE,
-                                       new SendRingIndicatorTask());
+      if (!mCurrentCallIndex) {
+        // Start sending RING indicator to HF
+        sStopSendingRingFlag = false;
+        MessageLoop::current()->PostTask(FROM_HERE,
+                                         new SendRingIndicatorTask());
+      }
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
       sCINDItems[CINDType::CALLSETUP].value = CallSetupState::OUTGOING;
       if (!aInitial) {
         SendCommand("+CIEV: ", CINDType::CALLSETUP);
 
         GetSocketAddr(address);
         OpenScoSocket(address);
@@ -734,27 +760,29 @@ BluetoothHfpManager::SetupCIND(int aCall
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
       sCINDItems[CINDType::CALLSETUP].value = CallSetupState::OUTGOING_ALERTING;
       if (!aInitial) {
         SendCommand("+CIEV: ", CINDType::CALLSETUP);
       }
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
+      mCurrentCallIndex = aCallIndex;
       if (aInitial) {
         sCINDItems[CINDType::CALL].value = CallState::IN_PROGRESS;
         sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
       } else {
-        switch (mCurrentCallState) {
+        switch (currentCallState) {
           case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
             // Incoming call, no break
             sStopSendingRingFlag = true;
 
             GetSocketAddr(address);
             OpenScoSocket(address);
+          case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED:
           case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
             // Outgoing call
             sCINDItems[CINDType::CALL].value = CallState::IN_PROGRESS;
             SendCommand("+CIEV: ", CINDType::CALL);
             sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
             SendCommand("+CIEV: ", CINDType::CALLSETUP);
             break;
           default:
@@ -762,69 +790,102 @@ BluetoothHfpManager::SetupCIND(int aCall
             NS_WARNING("Not handling state changed");
 #endif
             break;
         }
       }
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED:
       if (!aInitial) {
-        switch (mCurrentCallState) {
+        switch (currentCallState) {
           case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
             // Incoming call, no break
             sStopSendingRingFlag = true;
           case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
           case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
             // Outgoing call
             sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
             SendCommand("+CIEV: ", CINDType::CALLSETUP);
             break;
           case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
             sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
             SendCommand("+CIEV: ", CINDType::CALL);
             break;
+          case nsIRadioInterfaceLayer::CALL_STATE_HELD:
+            sCINDItems[CINDType::CALLHELD].value = NO_CALLHELD;
+            SendCommand("+CIEV: ", CINDType::CALLHELD);
           default:
 #ifdef DEBUG
             NS_WARNING("Not handling state changed");
 #endif
             break;
         }
-        CloseScoSocket();
+
+        if (aCallIndex == mCurrentCallIndex) {
+#ifdef DEBUG
+          NS_ASSERTION(mCurrentCallStateArray.Length() > aCallIndex,
+            "Call index out of bounds!");
+#endif
+          mCurrentCallStateArray[aCallIndex] = aCallState;
+
+          // Find the first non-disconnected call (like connected, held),
+          // and update mCurrentCallIndex
+          int c;
+          for (c = 1; c < mCurrentCallStateArray.Length(); c++) {
+            if (mCurrentCallStateArray[c] != nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED) {
+              mCurrentCallIndex = c;
+              break;
+            }
+          }
+
+          // There is no call 
+          if (c == mCurrentCallStateArray.Length()) {
+            mCurrentCallIndex = 0;
+            CloseScoSocket();
+          } 
+        }
       }
       break;
+    case nsIRadioInterfaceLayer::CALL_STATE_HELD:
+      sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+
+      if (!aInitial) {
+        SendCommand("+CIEV: ", CINDType::CALLHELD);
+      }
+      
+      break;
     default:
 #ifdef DEBUG
       NS_WARNING("Not handling state changed");
       sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
       sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
+      sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
 #endif
       break;
   }
 
-  mCurrentCallIndex = aCallIndex;
-  mCurrentCallState = aCallState;
+  mCurrentCallStateArray[aCallIndex] = aCallState;
 }
 
 /*
  * EnumerateCallState will be called for each call
  */
 void
 BluetoothHfpManager::EnumerateCallState(int aCallIndex, int aCallState,
                                         const char* aNumber, bool aIsActive)
 {
-  if (aIsActive &&
-      (sCINDItems[CINDType::CALLHELD].value == CallHeldState::ONHOLD_NOACTIVE)) {
-    sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
-  }
+  SetupCIND(aCallIndex, aCallState, true);
 
-  if (sCINDItems[CINDType::CALL].value || sCINDItems[CINDType::CALLSETUP].value) {
-    return;
+  if (sCINDItems[CINDType::CALL].value == CallState::IN_PROGRESS ||
+      sCINDItems[CINDType::CALLSETUP].value == CallSetupState::OUTGOING ||
+      sCINDItems[CINDType::CALLSETUP].value == CallSetupState::OUTGOING_ALERTING) {
+    nsString address;
+    GetSocketAddr(address);
+    OpenScoSocket(address);
   }
-
-  SetupCIND(aCallIndex, aCallState, true);
 }
 
 /*
  * CallStateChanged will be called whenever call status is changed, and it
  * also means we need to notify HS about the change. For more information, 
  * please refer to 4.13 ~ 4.15 in Bluetooth hands-free profile 1.6.
  */
 void
@@ -839,24 +900,24 @@ BluetoothHfpManager::CallStateChanged(in
 }
 
 void
 BluetoothHfpManager::OnConnectSuccess()
 {
   // Cache device path for NotifySettings() since we can't get socket address
   // when a headset disconnect with us
   GetSocketAddr(mDevicePath);
+  mSocketStatus = GetConnectionStatus();
 
-  if (mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED ||
-      mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_DIALING ||
-      mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_ALERTING) {
-    OpenScoSocket(mDevicePath);
+  nsCOMPtr<nsIRILContentHelper> ril =
+    do_GetService("@mozilla.org/ril/content-helper;1");
+  if (!ril) {
+    MOZ_ASSERT("Failed to get RIL Content Helper");
   }
-
-  mSocketStatus = GetConnectionStatus();
+  ril->EnumerateCalls(mListener->GetCallback());
 
   NotifySettings();
 }
 
 void
 BluetoothHfpManager::OnConnectError()
 {
   CloseSocket();
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -47,18 +47,18 @@ private:
   void NotifyDialer(const nsAString& aCommand);
   void NotifySettings();
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
 
   int mCurrentVgs;
   int mCurrentCallIndex;
-  int mCurrentCallState;
   bool mReceiveVgsFlag;
   nsString mDevicePath;
   enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
+  nsTArray<int> mCurrentCallStateArray;
   nsAutoPtr<BluetoothRilListener> mListener;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif