Bug 925638 - Patch 1/3: hfp cdma-handling functions, r=echou
authorBen Tian <btian@mozilla.com>
Thu, 17 Oct 2013 12:40:17 +0800
changeset 165003 5df342f3d30df2cd08d263c0a4d9be2390c46969
parent 165002 4c913587603866c4af28b9da451467db5b323ce2
child 165004 6ffbf60a1fb87b9fa26028b8bc3212887677d477
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou
bugs925638
milestone27.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 925638 - Patch 1/3: hfp cdma-handling functions, r=echou
dom/bluetooth/BluetoothHfpManager.cpp
dom/bluetooth/BluetoothHfpManager.h
dom/bluetooth/BluetoothRilListener.cpp
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -137,32 +137,16 @@ static CINDItem sCINDItems[] = {
   {"call", "0,1", CallState::NO_CALL, true},
   {"callheld", "0-2", CallHeldState::NO_CALLHELD, true},
   {"callsetup", "0-3", CallSetupState::NO_CALLSETUP, true},
   {"service", "0,1", 0, true},
   {"signal", "0-5", 0, true},
   {"roam", "0,1", 0, true}
 };
 
-class mozilla::dom::bluetooth::Call {
-  public:
-    Call(uint16_t aState = nsITelephonyProvider::CALL_STATE_DISCONNECTED,
-         bool aDirection = false,
-         const nsAString& aNumber = EmptyString(),
-         int aType = TOA_UNKNOWN)
-      : mState(aState), mDirection(aDirection), mNumber(aNumber), mType(aType)
-    {
-    }
-
-    uint16_t mState;
-    bool mDirection; // true: incoming call; false: outgoing call
-    nsString mNumber;
-    int mType;
-};
-
 class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD
   Handle(const nsAString& aName, const JS::Value& aResult)
   {
@@ -308,29 +292,59 @@ IsValidDtmf(const char aChar) {
 
 static bool
 IsMandatoryIndicator(const CINDType aType) {
   return (aType == CINDType::CALL) ||
          (aType == CINDType::CALLHELD) ||
          (aType == CINDType::CALLSETUP);
 }
 
+/**
+ *  Call
+ */
+Call::Call()
+{
+  Reset();
+}
+
+void
+Call::Reset()
+{
+  mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
+  mDirection = false;
+  mNumber.Truncate();
+  mType = TOA_UNKNOWN;
+}
+
+bool
+Call::IsActive()
+{
+  return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
+}
+
+/**
+ *  BluetoothHfpManager
+ */
 BluetoothHfpManager::BluetoothHfpManager()
 {
   Reset();
 }
 
 void
 BluetoothHfpManager::ResetCallArray()
 {
   mCurrentCallArray.Clear();
   // Append a call object at the beginning of mCurrentCallArray since call
   // index from RIL starts at 1.
   Call call;
   mCurrentCallArray.AppendElement(call);
+
+  if (mPhoneType == PhoneType::CDMA) {
+    mCdmaSecondCall.Reset();
+  }
 }
 
 void
 BluetoothHfpManager::Reset()
 {
   sStopSendingRingFlag = true;
   sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
   sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
@@ -545,16 +559,20 @@ BluetoothHfpManager::HandleVoiceConnecti
   nsCOMPtr<nsIMobileConnectionProvider> connection =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
   connection->GetVoiceConnectionInfo(getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
+  nsString type;
+  voiceInfo->GetType(type);
+  mPhoneType = GetPhoneType(type);
+
   bool roaming;
   voiceInfo->GetRoaming(&roaming);
   UpdateCIND(CINDType::ROAM, roaming);
 
   bool service = false;
   nsString regState;
   voiceInfo->GetState(regState);
   if (regState.EqualsLiteral("registered")) {
@@ -642,20 +660,25 @@ BluetoothHfpManager::ReceiveSocketData(B
   nsAutoCString msg((const char*)aMessage->mData.get(), aMessage->mSize);
   msg.StripWhitespace();
 
   nsTArray<nsCString> atCommandValues;
 
   // For more information, please refer to 4.34.1 "Bluetooth Defined AT
   // Capabilities" in Bluetooth hands-free profile 1.6
   if (msg.Find("AT+BRSF=") != -1) {
-    uint32_t brsf = BRSF_BIT_THREE_WAY_CALLING |
-                    BRSF_BIT_ABILITY_TO_REJECT_CALL |
+    uint32_t brsf = BRSF_BIT_ABILITY_TO_REJECT_CALL |
                     BRSF_BIT_ENHANCED_CALL_STATUS;
 
+    // No support for three way calling in CDMA since
+    // CDMA disallows to hang existing call for CHLD=1
+    if (mPhoneType != PhoneType::CDMA) {
+      brsf |= BRSF_BIT_THREE_WAY_CALLING;
+    }
+
     if (mBSIR) {
       brsf |= BRSF_BIT_IN_BAND_RING_TONE;
     }
 
     SendCommand("+BRSF: ", brsf);
   } else if (msg.Find("AT+CIND=?") != -1) {
     // Asking for CIND range
     SendCommand("+CIND: ", 0);
@@ -1071,16 +1094,78 @@ BluetoothHfpManager::Disconnect(Bluetoot
 
   MOZ_ASSERT(!mController);
 
   mController = aController;
   mSocket->Disconnect();
   mSocket = nullptr;
 }
 
+void
+BluetoothHfpManager::SendCCWA(const nsAString& aNumber, int aType)
+{
+  if (mCCWA) {
+    nsAutoCString ccwaMsg("+CCWA: \"");
+    ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
+    ccwaMsg.AppendLiteral("\",");
+    ccwaMsg.AppendInt(aType);
+    SendLine(ccwaMsg.get());
+  }
+}
+
+bool
+BluetoothHfpManager::SendCLCC(const Call& aCall, int aIndex)
+{
+  if (aCall.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
+    return true;
+  }
+
+  nsAutoCString message("+CLCC: ");
+  message.AppendInt(aIndex);
+  message.AppendLiteral(",");
+  message.AppendInt(aCall.mDirection);
+  message.AppendLiteral(",");
+
+  int status = 0;
+  switch (aCall.mState) {
+    case nsITelephonyProvider::CALL_STATE_CONNECTED:
+      if (mPhoneType == PhoneType::CDMA && aIndex == 1) {
+        status = (mCdmaSecondCall.IsActive()) ? 1 : 0;
+      }
+      message.AppendInt(status);
+      break;
+    case nsITelephonyProvider::CALL_STATE_HELD:
+      message.AppendInt(1);
+      break;
+    case nsITelephonyProvider::CALL_STATE_DIALING:
+      message.AppendInt(2);
+      break;
+    case nsITelephonyProvider::CALL_STATE_ALERTING:
+      message.AppendInt(3);
+      break;
+    case nsITelephonyProvider::CALL_STATE_INCOMING:
+      if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
+        message.AppendInt(4);
+      } else {
+        message.AppendInt(5);
+      }
+      break;
+    default:
+      BT_WARNING("Not handling call status for CLCC");
+      break;
+  }
+
+  message.AppendLiteral(",0,0,\"");
+  message.Append(NS_ConvertUTF16toUTF8(aCall.mNumber));
+  message.AppendLiteral("\",");
+  message.AppendInt(aCall.mType);
+
+  return SendLine(message.get());
+}
+
 bool
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
   MOZ_ASSERT(mSocket);
 
   nsAutoCString msg;
 
   msg.AppendLiteral(kHfpCrlf);
@@ -1138,59 +1223,28 @@ BluetoothHfpManager::SendCommand(const c
           break;
         }
         message.AppendLiteral(",");
       }
     }
   } else if (!strcmp(aCommand, "+CLCC: ")) {
     bool rv = true;
     uint32_t callNumbers = mCurrentCallArray.Length();
-    for (uint32_t i = 1; i < callNumbers; i++) {
-      Call& call = mCurrentCallArray[i];
-      if (call.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
-        continue;
-      }
-
-      message.AssignLiteral("+CLCC: ");
-      message.AppendInt(i);
-      message.AppendLiteral(",");
-      message.AppendInt(call.mDirection);
-      message.AppendLiteral(",");
+    uint32_t i;
+    for (i = 1; i < callNumbers; i++) {
+      rv &= SendCLCC(mCurrentCallArray[i], i);
+    }
 
-      switch (call.mState) {
-        case nsITelephonyProvider::CALL_STATE_CONNECTED:
-          message.AppendInt(0);
-          break;
-        case nsITelephonyProvider::CALL_STATE_HELD:
-          message.AppendInt(1);
-          break;
-        case nsITelephonyProvider::CALL_STATE_DIALING:
-          message.AppendInt(2);
-          break;
-        case nsITelephonyProvider::CALL_STATE_ALERTING:
-          message.AppendInt(3);
-          break;
-        case nsITelephonyProvider::CALL_STATE_INCOMING:
-          if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
-            message.AppendInt(4);
-          } else {
-            message.AppendInt(5);
-          }
-          break;
-        default:
-          BT_WARNING("Not handling call status for CLCC");
-          break;
-      }
-      message.AppendLiteral(",0,0,\"");
-      message.Append(NS_ConvertUTF16toUTF8(call.mNumber));
-      message.AppendLiteral("\",");
-      message.AppendInt(call.mType);
+    if (!mCdmaSecondCall.mNumber.IsEmpty()) {
+      MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
+      MOZ_ASSERT(i == 2);
 
-      rv &= SendLine(message.get());
+      rv &= SendCLCC(mCdmaSecondCall, 2);
     }
+
     return rv;
   } else {
     message.AppendInt(aValue);
   }
 
   return SendLine(message.get());
 }
 
@@ -1271,23 +1325,17 @@ BluetoothHfpManager::HandleCallStateChan
         sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
       } else {
         sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
       }
       SendCommand("+CIEV: ", CINDType::CALLHELD);
       break;
     case nsITelephonyProvider::CALL_STATE_INCOMING:
       if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
-        if (mCCWA) {
-          nsAutoCString ccwaMsg("+CCWA: \"");
-          ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
-          ccwaMsg.AppendLiteral("\",");
-          ccwaMsg.AppendInt(mCurrentCallArray[aCallIndex].mType);
-          SendLine(ccwaMsg.get());
-        }
+        SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
       } else {
         // Start sending RING indicator to HF
         sStopSendingRingFlag = false;
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
 
         if (mBSIR) {
           // Setup audio connection for in-band ring tone
@@ -1405,16 +1453,51 @@ BluetoothHfpManager::HandleCallStateChan
       }
       break;
     default:
       BT_WARNING("Not handling state changed");
       break;
   }
 }
 
+PhoneType
+BluetoothHfpManager::GetPhoneType(const nsAString& aType)
+{
+  // FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
+  if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
+      aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
+      aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
+      aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
+    return PhoneType::GSM;
+  } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
+             aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
+             aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
+    return PhoneType::CDMA;
+  }
+
+  return PhoneType::NONE;
+}
+
+void
+BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
+{
+  MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
+
+  // Always regard second call as incoming call since v1.2 RIL
+  // doesn't support outgoing second call in CDMA.
+  mCdmaSecondCall.mDirection = true;
+
+  mCdmaSecondCall.mNumber = aNumber;
+  mCdmaSecondCall.mType = (aNumber[0] == '+') ? TOA_INTERNATIONAL :
+                                                TOA_UNKNOWN;
+
+  SendCCWA(aNumber, mCdmaSecondCall.mType);
+  UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
+}
+
 void
 BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
 {
   MOZ_ASSERT(aSocket);
   MOZ_ASSERT(mListener);
 
   // Success to create a SCO socket
   if (aSocket == mScoSocket) {
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -45,16 +45,34 @@ enum BluetoothCmeError {
   INVALID_CHARACTERS_IN_TEXT_STRING = 25,
   DIAL_STRING_TOO_LONG = 26,
   INVALID_CHARACTERS_IN_DIAL_STRING = 27,
   NO_NETWORK_SERVICE = 30,
   NETWORK_TIMEOUT = 31,
   NETWORK_NOT_ALLOWED = 32
 };
 
+enum PhoneType {
+  NONE, // no connection
+  GSM,
+  CDMA
+};
+
+class Call {
+public:
+  Call();
+  void Reset();
+  bool IsActive();
+
+  uint16_t mState;
+  bool mDirection; // true: incoming call; false: outgoing call
+  nsString mNumber;
+  int mType;
+};
+
 class BluetoothHfpManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
                           , public BatteryObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
@@ -98,16 +116,19 @@ public:
                               const nsAString& aError, const nsAString& aNumber,
                               const bool aIsOutgoing, bool aSend);
   void HandleIccInfoChanged();
   void HandleVoiceConnectionChanged();
 
   bool IsConnected();
   bool IsScoConnected();
 
+  // CDMA-specific functions
+  void UpdateSecondNumber(const nsAString& aNumber);
+
 private:
   class CloseScoTask;
   class GetVolumeTask;
   class RespondToBLDNTask;
   class SendRingIndicatorTask;
 
   friend class CloseScoTask;
   friend class GetVolumeTask;
@@ -120,36 +141,40 @@ private:
   void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
   void Reset();
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
+  PhoneType GetPhoneType(const nsAString& aType);
 
   void NotifyConnectionStatusChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
+  void SendCCWA(const nsAString& aNumber, int aType);
+  bool SendCLCC(const Call& aCall, int aIndex);
   bool SendCommand(const char* aCommand, uint32_t aValue = 0);
   bool SendLine(const char* aMessage);
   void UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend = true);
   void OnScoConnectSuccess();
   void OnScoConnectError();
   void OnScoDisconnect();
 
   int mCurrentVgs;
   int mCurrentVgm;
   bool mBSIR;
   bool mCCWA;
   bool mCLIP;
   bool mCMEE;
   bool mCMER;
   bool mFirstCKPD;
   int mNetworkSelectionMode;
+  PhoneType mPhoneType;
   bool mReceiveVgsFlag;
   bool mDialingRequestProcessed;
   nsString mDeviceAddress;
   nsString mMsisdn;
   nsString mOperatorName;
 
   nsTArray<Call> mCurrentCallArray;
   nsAutoPtr<BluetoothRilListener> mListener;
@@ -165,13 +190,16 @@ private:
 
   // Server sockets. Once an inbound connection is established, it will hand
   // over the ownership to mSocket, and get a new server socket while Listen()
   // is called.
   nsRefPtr<BluetoothSocket> mHandsfreeSocket;
   nsRefPtr<BluetoothSocket> mHeadsetSocket;
   nsRefPtr<BluetoothSocket> mScoSocket;
   SocketConnectionStatus mScoSocketStatus;
+
+  // CDMA-specific variable
+  Call mCdmaSecondCall;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -209,16 +209,19 @@ TelephonyListener::SupplementaryServiceN
                                                     uint16_t aNotification)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyListener::NotifyCdmaCallWaiting(const nsAString& aNumber)
 {
+  BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
+  hfp->UpdateSecondNumber(aNumber);
+
   return NS_OK;
 }
 
 } // anonymous namespace
 
 /**
  *  BluetoothRilListener
  */