Bug 860166 - Use two server sockets to listen to both HFP and HSP services. r=gyeh, r=mrbkap, a=tef+
authorEric Chou <echou@mozilla.com>
Fri, 19 Apr 2013 08:25:34 -0400
changeset 119138 c3761490ffac6df2c2e2b3a341842f7b89d1bfca
parent 119137 9ef08dac0122c08cb3e8cf62965cb279d6382242
child 119139 19b5590e161c98c650061285fb5534313d567f2e
push id693
push userryanvm@gmail.com
push dateFri, 19 Apr 2013 12:25:43 +0000
reviewersgyeh, mrbkap, tef
bugs860166
milestone18.0
Bug 860166 - Use two server sockets to listen to both HFP and HSP services. r=gyeh, r=mrbkap, a=tef+
dom/bluetooth/BluetoothHfpManager.cpp
dom/bluetooth/BluetoothHfpManager.h
dom/bluetooth/BluetoothScoManager.cpp
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -386,22 +386,16 @@ BluetoothHfpManager::Reset()
   ResetCallArray();
 }
 
 bool
 BluetoothHfpManager::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mSocket = new BluetoothSocket(this,
-                                BluetoothSocketType::RFCOMM,
-                                true,
-                                true);
-  mPrevSocketStatus = mSocket->GetConnectionStatus();
-
   sHfpObserver = new BluetoothHfpManagerObserver();
   if (!sHfpObserver->Init()) {
     NS_WARNING("Cannot set up Hfp Observers!");
   }
 
   mListener = new BluetoothRilListener();
   if (!mListener->StartListening()) {
     NS_WARNING("Failed to start listening RIL");
@@ -415,16 +409,18 @@ BluetoothHfpManager::Init()
   nsCOMPtr<nsISettingsServiceLock> settingsLock;
   nsresult rv = settings->CreateLock(getter_AddRefs(settingsLock));
   NS_ENSURE_SUCCESS(rv, false);
 
   nsRefPtr<GetVolumeTask> callback = new GetVolumeTask();
   rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO, callback);
   NS_ENSURE_SUCCESS(rv, false);
 
+  Listen();
+
   return true;
 }
 
 BluetoothHfpManager::~BluetoothHfpManager()
 {
   Cleanup();
 }
 
@@ -469,18 +465,17 @@ void
 BluetoothHfpManager::NotifySettings()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-hfp-status-changed");
 
   name.AssignLiteral("connected");
-  v = (mSocket->GetConnectionStatus() ==
-       SocketConnectionStatus::SOCKET_CONNECTED);
+  v = IsConnected();
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("address");
   v = mDevicePath;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to settings");
@@ -566,17 +561,17 @@ BluetoothHfpManager::HandleVolumeChanged
 
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return NS_OK;
   }
 
   // Only send volume back when there's a connected headset
-  if (mSocket->GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
+  if (IsConnected()) {
     SendCommand("+VGS: ", mCurrentVgs);
   }
 
   return NS_OK;
 }
 
 nsresult
 BluetoothHfpManager::HandleVoiceConnectionChanged()
@@ -939,37 +934,45 @@ BluetoothHfpManager::Connect(const nsASt
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (gInShutdown) {
     NS_WARNING("Connect called while in shutdown!");
     return false;
   }
 
-  SocketConnectionStatus s = mSocket->GetConnectionStatus();
-
-  if (s == SocketConnectionStatus::SOCKET_CONNECTED ||
-      s == SocketConnectionStatus::SOCKET_CONNECTING) {
+  if (mSocket) {
     NS_WARNING("BluetoothHfpManager has been already connected");
     return false;
   }
 
-  mSocket->Disconnect();
+  // Stop listening because currently we only support one connection at a time.
+  if (mHandsfreeSocket) {
+    mHandsfreeSocket->Disconnect();
+    mHandsfreeSocket = nullptr;
+  }
+
+  if (mHeadsetSocket) {
+    mHeadsetSocket->Disconnect();
+    mHeadsetSocket = nullptr;
+  }
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE(bs, false);
 
   nsString uuid;
   if (aIsHandsfree) {
     BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid);
   } else {
     BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, uuid);
   }
 
   mRunnable = aRunnable;
+  mSocket =
+    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
 
   nsresult rv = bs->GetSocketViaService(aDevicePath,
                                         uuid,
                                         BluetoothSocketType::RFCOMM,
                                         true,
                                         true,
                                         mSocket,
                                         mRunnable);
@@ -982,55 +985,78 @@ BluetoothHfpManager::Listen()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (gInShutdown) {
     MOZ_ASSERT(false, "Listen called while in shutdown!");
     return false;
   }
 
-  if (mSocket->GetConnectionStatus() ==
-      SocketConnectionStatus::SOCKET_LISTENING) {
-    NS_WARNING("BluetoothHfpManager has been already listening");
-    return true;
-  }
-
-  mSocket->Disconnect();
-
-  if (!mSocket->Listen(BluetoothReservedChannels::CHANNEL_HANDSFREE_AG)) {
-    NS_WARNING("[HFP] Can't listen on socket!");
+  if (mSocket) {
+    NS_WARNING("mSocket exists. Failed to listen.");
     return false;
   }
 
-  mPrevSocketStatus = mSocket->GetConnectionStatus();
+  if (!mHandsfreeSocket) {
+    mHandsfreeSocket =
+      new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+
+    if (!mHandsfreeSocket->Listen(
+          BluetoothReservedChannels::CHANNEL_HANDSFREE_AG)) {
+      NS_WARNING("[HFP] Can't listen on RFCOMM socket!");
+      mHandsfreeSocket = nullptr;
+      return false;
+    }
+  }
+
+  if (!mHeadsetSocket) {
+    mHeadsetSocket =
+      new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+
+    if (!mHeadsetSocket->Listen(
+          BluetoothReservedChannels::CHANNEL_HEADSET_AG)) {
+      NS_WARNING("[HSP] Can't listen on RFCOMM socket!");
+      mHandsfreeSocket->Disconnect();
+      mHandsfreeSocket = nullptr;
+      mHeadsetSocket = nullptr;
+      return false;
+    }
+  }
+
   return true;
 }
 
 void
 BluetoothHfpManager::Disconnect()
 {
-  mSocket->Disconnect();
+  if (mSocket) {
+    mSocket->Disconnect();
+    mSocket = nullptr;
+  }
 }
 
 bool
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
+  MOZ_ASSERT(mSocket);
+
   nsAutoCString msg;
 
   msg.AppendLiteral(kHfpCrlf);
   msg.Append(aMessage);
   msg.AppendLiteral(kHfpCrlf);
 
   return mSocket->SendSocketData(msg);
 }
 
 bool
 BluetoothHfpManager::SendCommand(const char* aCommand, uint8_t aValue)
 {
-  if (mPrevSocketStatus != SocketConnectionStatus::SOCKET_CONNECTED) {
+  if (!IsConnected()) {
+    NS_WARNING("Trying to SendCommand() without a SLC");
     return false;
   }
 
   nsAutoCString message;
   int value = aValue;
   message += aCommand;
 
   if (!strcmp(aCommand, "+CIEV: ")) {
@@ -1135,18 +1161,18 @@ BluetoothHfpManager::UpdateCIND(uint8_t 
 }
 
 void
 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
                                             uint16_t aCallState,
                                             const nsAString& aNumber,
                                             bool aSend)
 {
-  if (mSocket->GetConnectionStatus() !=
-      SocketConnectionStatus::SOCKET_CONNECTED) {
+  if (!IsConnected()) {
+    // Normal case. No need to print out warnings.
     return;
   }
 
   while (aCallIndex >= mCurrentCallArray.Length()) {
     Call call;
     mCurrentCallArray.AppendElement(call);
   }
 
@@ -1186,18 +1212,20 @@ BluetoothHfpManager::HandleCallStateChan
         sStopSendingRingFlag = false;
         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
 
         nsAutoString number(aNumber);
         if (!mCLIP) {
           number.AssignLiteral("");
         }
 
-        MessageLoop::current()->PostDelayedTask(FROM_HERE,
-          new SendRingIndicatorTask(number, mCurrentCallArray[aCallIndex].mType),
+        MessageLoop::current()->PostDelayedTask(
+          FROM_HERE,
+          new SendRingIndicatorTask(number,
+                                    mCurrentCallArray[aCallIndex].mType),
           sRingInterval);
       }
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
       mCurrentCallArray[aCallIndex].mDirection = false;
       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING, aSend);
 
       mSocket->GetAddress(address);
@@ -1260,17 +1288,18 @@ BluetoothHfpManager::HandleCallStateChan
           sStopSendingRingFlag = true;
         case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
         case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
           // Outgoing call
           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
           break;
         case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
           // No call is ongoing
-          if (sCINDItems[CINDType::CALLHELD].value == CallHeldState::NO_CALLHELD) {
+          if (sCINDItems[CINDType::CALLHELD].value ==
+              CallHeldState::NO_CALLHELD) {
             UpdateCIND(CINDType::CALL, CallState::NO_CALL, aSend);
           }
           break;
         case nsIRadioInterfaceLayer::CALL_STATE_HELD:
           UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
           break;
         default:
           NS_WARNING("Not handling state changed");
@@ -1301,17 +1330,38 @@ BluetoothHfpManager::HandleCallStateChan
       sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
       sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
   }
 }
 
 void
 BluetoothHfpManager::OnConnectSuccess(BluetoothSocket* aSocket)
 {
-  MOZ_ASSERT(aSocket == mSocket);
+  MOZ_ASSERT(aSocket);
+
+  /**
+   * If the created connection is an inbound connection, close another server
+   * socket because currently only one SLC is allowed. After that, we need to
+   * make sure that both server socket would be nulled out. As for outbound
+   * connections, we do nothing since sockets have been already handled in
+   * function Connect().
+   */
+  if (aSocket == mHandsfreeSocket) {
+    MOZ_ASSERT(!mSocket);
+    mHandsfreeSocket.swap(mSocket);
+
+    mHeadsetSocket->Disconnect();
+    mHeadsetSocket = nullptr;
+  } else if (aSocket == mHeadsetSocket) {
+    MOZ_ASSERT(!mSocket);
+    mHeadsetSocket.swap(mSocket);
+
+    mHandsfreeSocket->Disconnect();
+    mHandsfreeSocket = nullptr;
+  }
 
   nsCOMPtr<nsIRILContentHelper> ril =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(ril);
   ril->EnumerateCalls(mListener->GetCallback());
 
   // For active connection request, we need to reply the DOMRequest
   if (mRunnable) {
@@ -1320,56 +1370,61 @@ BluetoothHfpManager::OnConnectSuccess(Bl
     DispatchBluetoothReply(mRunnable, v, errorStr);
 
     mRunnable.forget();
   }
 
   // Cache device path for NotifySettings() since we can't get socket address
   // when a headset disconnect with us
   mSocket->GetAddress(mDevicePath);
-  mPrevSocketStatus = mSocket->GetConnectionStatus();
 
   NotifySettings();
 }
 
 void
 BluetoothHfpManager::OnConnectError(BluetoothSocket* aSocket)
 {
-  MOZ_ASSERT(aSocket == mSocket);
-
   // For active connection request, we need to reply the DOMRequest
   if (mRunnable) {
     BluetoothValue v;
     nsString errorStr;
     errorStr.AssignLiteral("Failed to connect with a bluetooth headset!");
     DispatchBluetoothReply(mRunnable, v, errorStr);
 
     mRunnable.forget();
   }
 
+  mSocket = nullptr;
+  mHandsfreeSocket = nullptr;
+  mHeadsetSocket = nullptr;
+
   // If connecting for some reason didn't work, restart listening
   Listen();
 }
 
 void
 BluetoothHfpManager::OnDisconnect(BluetoothSocket* aSocket)
 {
-  MOZ_ASSERT(aSocket == mSocket);
+  MOZ_ASSERT(aSocket);
 
-  // When we close a connected socket, then restart listening again and
-  // notify Settings app.
-  if (mPrevSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
-    Listen();
-    NotifySettings();
-  } else if (mPrevSocketStatus == SocketConnectionStatus::SOCKET_CONNECTING) {
-    NS_WARNING("BluetoothHfpManager got unexpected socket status!");
+  if (aSocket != mSocket) {
+    // Do nothing when a listening server socket is closed.
+    return;
   }
 
+  mSocket = nullptr;
   CloseScoSocket();
+
+  Listen();
+  NotifySettings();
   Reset();
 }
 
 bool
 BluetoothHfpManager::IsConnected()
 {
-  return mSocket->GetConnectionStatus() ==
-         SocketConnectionStatus::SOCKET_CONNECTED;
+  if (mSocket) {
+    return mSocket->GetConnectionStatus() ==
+           SocketConnectionStatus::SOCKET_CONNECTED;
+  }
+
+  return false;
 }
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -109,19 +109,29 @@ private:
   bool mCLIP;
   bool mCMEE;
   bool mCMER;
   int mNetworkSelectionMode;
   bool mReceiveVgsFlag;
   nsString mDevicePath;
   nsString mMsisdn;
   nsString mOperatorName;
-  SocketConnectionStatus mPrevSocketStatus;
 
   nsTArray<Call> mCurrentCallArray;
   nsAutoPtr<BluetoothRilListener> mListener;
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
+
+  // If a connection has been established, mSocket will be the socket
+  // communicating with the remote socket. We maintain the invariant that if
+  // mSocket is non-null, mHandsfreeSocket and mHeadsetSocket must be null (and
+  // vice versa).
   nsRefPtr<BluetoothSocket> mSocket;
+
+  // 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;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothScoManager.cpp
+++ b/dom/bluetooth/BluetoothScoManager.cpp
@@ -100,17 +100,17 @@ bool gInShutdown = false;
 } // anonymous namespace
 
 NS_IMETHODIMP
 BluetoothScoManagerObserver::Observe(nsISupports* aSubject,
                                      const char* aTopic,
                                      const PRUnichar* aData)
 {
   MOZ_ASSERT(gBluetoothScoManager);
-  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {    
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return gBluetoothScoManager->HandleShutdown();
   }
 
   MOZ_ASSERT(false, "BluetoothScoManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 BluetoothScoManager::BluetoothScoManager()