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 111862 f6ced9fd4fc54fe680e3eeb37fb796bc160edb21
parent 111861 1ba88bd2f4407ccb49f73efce360714b5e36ff64
child 111863 d3f1545072f3867d58dff0130a751354c2ae2305
push id17296
push userechou@mozilla.com
push dateWed, 31 Oct 2012 03:54:03 +0000
treeherdermozilla-inbound@f6ced9fd4fc5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs805708
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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