Bug 990423 - [Bluetooth] Fix the incorrect condition for reporting call held status when call state changed. r=echou, f=btian, a=1.4+
authorJamin Liu <jaliu@mozilla.com>
Fri, 25 Apr 2014 09:59:32 +0800
changeset 193134 bb107d29aa14547372d88a677e2b2d74b9208b5f
parent 193133 202300e79538ecbb9a16d74101b7eee951671056
child 193135 a1720aa3df8d553c8f2bced5bbd8dccda040fab4
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou, 1
bugs990423
milestone30.0a2
Bug 990423 - [Bluetooth] Fix the incorrect condition for reporting call held status when call state changed. r=echou, f=btian, a=1.4+
dom/bluetooth/bluez/BluetoothHfpManager.cpp
dom/bluetooth/bluez/BluetoothHfpManager.h
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -1366,16 +1366,47 @@ BluetoothHfpManager::GetNumberOfCalls(ui
     if (mCurrentCallArray[i].mState == aState) {
       ++num;
     }
   }
 
   return num;
 }
 
+uint32_t
+BluetoothHfpManager::GetNumberOfConCalls()
+{
+  uint32_t num = 0;
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mIsConference) {
+      ++num;
+    }
+  }
+
+  return num;
+}
+
+uint32_t
+BluetoothHfpManager::GetNumberOfConCalls(uint16_t aState)
+{
+  uint32_t num = 0;
+  uint32_t callLength = mCurrentCallArray.Length();
+
+  for (uint32_t i = 1; i < callLength; ++i) {
+    if (mCurrentCallArray[i].mIsConference
+        && mCurrentCallArray[i].mState == aState) {
+      ++num;
+    }
+  }
+
+  return num;
+}
+
 void
 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
                                             uint16_t aCallState,
                                             const nsAString& aError,
                                             const nsAString& aNumber,
                                             const bool aIsOutgoing,
                                             const bool aIsConference,
                                             bool aSend)
@@ -1403,25 +1434,69 @@ BluetoothHfpManager::HandleCallStateChan
   }
   mCurrentCallArray[aCallIndex].mNumber = aNumber;
 
   nsRefPtr<nsRunnable> sendRingTask;
   nsString address;
 
   switch (aCallState) {
     case nsITelephonyProvider::CALL_STATE_HELD:
-      if (prevCallState == nsITelephonyProvider::CALL_STATE_CONNECTED) {
-        if (mCurrentCallArray.Length() == 1) {
-          // A single active call is put on hold (+CIEV, callheld=2)
-          sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
-        } else {
-          // Releases all active calls and accepts the other (+CIEV, callheld=1)
-          sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+      switch (prevCallState) {
+        case nsITelephonyProvider::CALL_STATE_CONNECTED: {
+          uint32_t numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
+          uint32_t numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
+          uint32_t numConCalls = GetNumberOfConCalls();
+
+          /**
+           * An active call becomes a held call.
+           *
+           * If this call is not a conference call,
+           * - callheld state = ONHOLD_NOACTIVE if no active call remains;
+           * - callheld state = ONHOLD_ACTIVE otherwise.
+           * If this call belongs to a conference call and all other members of
+           * the conference call have become held calls,
+           * - callheld state = ONHOLD_NOACTIVE if no active call remains;
+           * - callheld state = ONHOLD_ACTIVE otherwise.
+           *
+           * Note number of active calls may be 0 in-between state transition
+           * (c1 has become held but c2 has not become active yet), so we regard
+           * no active call remains if there is no other active/held call
+           * besides this changed call/group of conference call.
+           */
+          if (!aIsConference) {
+            if (numActive + numHeld == 1) {
+              // A single active call is put on hold.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
+            } else {
+              // An active call is placed on hold or active/held calls swapped.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            }
+            SendCommand("+CIEV: ", CINDType::CALLHELD);
+          } else if (GetNumberOfConCalls(nsITelephonyProvider::CALL_STATE_HELD)
+                     == numConCalls) {
+            if (numActive + numHeld == numConCalls) {
+              // An active conference call is put on hold.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
+            } else {
+              // Active calls are placed on hold or active/held calls swapped.
+              sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            }
+            SendCommand("+CIEV: ", CINDType::CALLHELD);
+          }
+          break;
         }
-        SendCommand("+CIEV: ", CINDType::CALLHELD);
+        case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
+          // The call state changed from DISCONNECTED to HELD. It could happen
+          // when user held a call before Bluetooth got connected.
+          if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
+            // callheld = ONHOLD_ACTIVE if an active call already exists.
+            sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
+            SendCommand("+CIEV: ", CINDType::CALLHELD);
+          }
+          break;
       }
       break;
     case nsITelephonyProvider::CALL_STATE_INCOMING:
       if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
         SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
       } else {
         // Start sending RING indicator to HF
@@ -1457,40 +1532,51 @@ BluetoothHfpManager::HandleCallStateChan
     case nsITelephonyProvider::CALL_STATE_ALERTING:
       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
 
       // If there's an ongoing call when the headset is just connected, we have
       // to open a sco socket here.
       ConnectSco();
       break;
     case nsITelephonyProvider::CALL_STATE_CONNECTED:
+      /**
+       * A call becomes active because:
+       * - user answers an incoming call,
+       * - user dials a outgoing call and it is answered, or
+       * - SLC is connected when a call is active.
+       */
       switch (prevCallState) {
         case nsITelephonyProvider::CALL_STATE_INCOMING:
         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
           // Incoming call, no break
           sStopSendingRingFlag = true;
           ConnectSco();
           // NO BREAK HERE. continue to next statement
         case nsITelephonyProvider::CALL_STATE_DIALING:
         case nsITelephonyProvider::CALL_STATE_ALERTING:
           // Outgoing call
           UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
+
+          if (FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
+            // callheld state = ONHOLD_ACTIVE if a held call already exists.
+            UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
+          }
           break;
-        // User wants to add a held call to the conversation.
-        // The original connected call become a conference call here.
         case nsITelephonyProvider::CALL_STATE_CONNECTED:
+          // User wants to add a held call to the conversation.
+          // The original connected call becomes a conference call here.
           if (aIsConference) {
             UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
           }
           break;
         case nsITelephonyProvider::CALL_STATE_HELD:
           if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
             if (aIsConference && !prevCallIsConference) {
-              // The held call was merged and become a conference call.
+              // The held call was merged and becomes a conference call.
               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
             } else if (sCINDItems[CINDType::CALLHELD].value ==
                        CallHeldState::ONHOLD_NOACTIVE) {
               // The held call(s) become connected call(s).
               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
             }
           }
           break;
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -152,16 +152,18 @@ private:
   void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 #ifdef MOZ_B2G_RIL
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
+  uint32_t GetNumberOfConCalls();
+  uint32_t GetNumberOfConCalls(uint16_t aState);
   PhoneType GetPhoneType(const nsAString& aType);
 #endif
 
   void NotifyConnectionStatusChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
 #ifdef MOZ_B2G_RIL
   void SendCCWA(const nsAString& aNumber, int aType);