Bug 906305 - Patch 6: Fix BluetoothA2dpManager, r=echou
authorGina Yeh <gyeh@mozilla.com>
Fri, 06 Sep 2013 19:21:31 +0800
changeset 158789 141a21a230d2bdcb2e53ce4246e99ce882e152b8
parent 158788 066ddde89edf0e5bb16108bae1cefe2904c9a3bc
child 158790 abaf0fa5fd0de6a556a2a62f10e56ca96954796e
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou
bugs906305
milestone26.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 906305 - Patch 6: Fix BluetoothA2dpManager, r=echou
dom/bluetooth/BluetoothA2dpManager.cpp
dom/bluetooth/BluetoothA2dpManager.h
--- a/dom/bluetooth/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/BluetoothA2dpManager.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "BluetoothA2dpManager.h"
 
 #include "BluetoothCommon.h"
+#include "BluetoothProfileController.h"
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIAudioManager.h"
@@ -73,18 +74,18 @@ BluetoothA2dpManager::~BluetoothA2dpMana
     BT_WARNING("Failed to remove shutdown observer!");
   }
 }
 
 void
 BluetoothA2dpManager::ResetA2dp()
 {
   mA2dpConnected = false;
-  mPlaying = false;
   mSinkState = SinkState::SINK_DISCONNECTED;
+  mController = nullptr;
 }
 
 void
 BluetoothA2dpManager::ResetAvrcp()
 {
   mAvrcpConnected = false;
   mDuration = 0;
   mMediaNumber = 0;
@@ -135,121 +136,203 @@ BluetoothA2dpManager::Get()
   return sBluetoothA2dpManager;
 }
 
 void
 BluetoothA2dpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sInShutdown = true;
-  Disconnect();
+  Disconnect(nullptr);
   sBluetoothA2dpManager = nullptr;
 }
 
-bool
-BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress)
+void
+BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress,
+                              BluetoothProfileController* aController)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
+  MOZ_ASSERT(aController && !mController);
 
-  NS_ENSURE_FALSE(sInShutdown, false);
-  NS_ENSURE_FALSE(mA2dpConnected, false);
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs || sInShutdown) {
+    aController->OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
+    return;
+  }
+
+  if (mA2dpConnected) {
+    aController->OnConnect(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
+    return;
+  }
 
   mDeviceAddress = aDeviceAddress;
+  mController = aController;
 
+  bs->SendSinkMessage(aDeviceAddress, NS_LITERAL_STRING("Connect"));
+}
+
+void
+BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController)
+{
   BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE(bs, false);
-  nsresult rv = bs->SendSinkMessage(aDeviceAddress,
-                                    NS_LITERAL_STRING("Connect"));
+  if (!bs) {
+    if (aController) {
+      aController->OnDisconnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
+    }
+    return;
+  }
 
-  return NS_SUCCEEDED(rv);
+  if (!mA2dpConnected) {
+    if (aController) {
+      aController->OnDisconnect(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
+    }
+    return;
+  }
+
+  MOZ_ASSERT(!mDeviceAddress.IsEmpty());
+  MOZ_ASSERT(!mController);
+
+  mController = aController;
+
+  bs->SendSinkMessage(mDeviceAddress, NS_LITERAL_STRING("Disconnect"));
 }
 
 void
-BluetoothA2dpManager::Disconnect()
+BluetoothA2dpManager::OnConnect(const nsAString& aErrorStr)
 {
-  NS_ENSURE_TRUE_VOID(mA2dpConnected);
+  MOZ_ASSERT(NS_IsMainThread());
 
-  MOZ_ASSERT(!mDeviceAddress.IsEmpty());
+  /**
+   * On the one hand, notify the controller that we've done for outbound
+   * connections. On the other hand, we do nothing for inbound connections.
+   */
+  NS_ENSURE_TRUE_VOID(mController);
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->SendSinkMessage(mDeviceAddress, NS_LITERAL_STRING("Disconnect"));
+  mController->OnConnect(aErrorStr);
+  mController = nullptr;
 }
 
 void
+BluetoothA2dpManager::OnDisconnect(const nsAString& aErrorStr)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  /**
+   * On the one hand, notify the controller that we've done for outbound
+   * connections. On the other hand, we do nothing for inbound connections.
+   */
+  NS_ENSURE_TRUE_VOID(mController);
+
+  mController->OnDisconnect(aErrorStr);
+  mController = nullptr;
+}
+
+/* HandleSinkPropertyChanged update sink state in A2dp
+ *
+ * Possible values: "disconnected", "disconnecting",
+ *                  "connecting", "connected",
+ *                  "playing"
+ *
+ * 1. "disconnected" -> "connecting"
+ *    Either an incoming or outgoing connection attempt ongoing
+ * 2. "connecting" -> "disconnected"
+ *    Connection attempt failed
+ * 3. "connecting" -> "connected"
+ *    Successfully connected
+ * 4. "connected" -> "playing"
+ *    Audio stream active
+ * 5. "playing" -> "connected"
+ *    Audio stream suspended
+ * 6. "connected" -> "disconnected"
+ *    "playing" -> "disconnected"
+ *    Disconnected from the remote device
+ * 7. "disconnecting" -> "disconnected"
+ *    Disconnected from local
+ */
+void
 BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
 
+  const nsString& address = aSignal.path();
   const InfallibleTArray<BluetoothNamedValue>& arr =
     aSignal.value().get_ArrayOfBluetoothNamedValue();
   MOZ_ASSERT(arr.Length() == 1);
 
-  const nsString& name = arr[0].name();
-  const BluetoothValue& value = arr[0].value();
-  if (name.EqualsLiteral("Connected")) {
-    // Indicates if a stream is setup to a A2DP sink on the remote device.
-    MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
-    MOZ_ASSERT(mA2dpConnected != value.get_bool());
+  /**
+   * There are three properties:
+   * - "State": a string
+   * - "Connected": a boolean value
+   * - "Playing": a boolean value
+   *
+   * Note that only "State" is handled in this function.
+   */
 
-    mA2dpConnected = value.get_bool();
-    NotifyConnectionStatusChanged();
-    DispatchConnectionStatusChanged();
-  } else if (name.EqualsLiteral("Playing")) {
-    // Indicates if a stream is active to a A2DP sink on the remote device.
-    MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
-    MOZ_ASSERT(mPlaying != value.get_bool());
+  const nsString& name = arr[0].name();
+  NS_ENSURE_TRUE_VOID(name.EqualsLiteral("State"));
 
-    mPlaying = value.get_bool();
-  } else if (name.EqualsLiteral("State")) {
-    MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
-    MOZ_ASSERT(mSinkState != StatusStringToSinkState(value.get_nsString()));
+  const BluetoothValue& value = arr[0].value();
+  MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
+  SinkState prevState = mSinkState;
+  mSinkState = StatusStringToSinkState(value.get_nsString());
 
-    HandleSinkStateChanged(StatusStringToSinkState(value.get_nsString()));
-  } else {
-    NS_WARNING("Unknown sink property");
-  }
-}
+  NS_ENSURE_TRUE_VOID(mSinkState != prevState);
 
-/* HandleSinkStateChanged updates sink state in A2dp
- *
- * Possible values: "disconnected", "connecting", "connected", "playing"
- *
- * 1. "disconnected" -> "connecting"
- * Either an incoming or outgoing connection attempt ongoing
- * 2. "connecting" -> "disconnected"
- * Connection attempt failed
- * 3. "connecting" -> "connected"
- * Successfully connected
- * 4. "connected" -> "playing"
- * Audio stream active
- * 5. "playing" -> "connected"
- * Audio stream suspended
- * 6. "connected" -> "disconnected"
- *    "playing" -> "disconnected"
- * Disconnected from the remote device
- * 7. "disconnecting" -> "disconnected"
- * Disconnected from local
- */
-void
-BluetoothA2dpManager::HandleSinkStateChanged(SinkState aState)
-{
-  MOZ_ASSERT_IF(aState == SinkState::SINK_CONNECTED,
-                mSinkState == SinkState::SINK_CONNECTING ||
-                mSinkState == SinkState::SINK_PLAYING);
-  MOZ_ASSERT_IF(aState == SinkState::SINK_PLAYING,
-                mSinkState == SinkState::SINK_CONNECTED);
+  switch(mSinkState) {
+    case SinkState::SINK_CONNECTING:
+      // case 1: Either an incoming or outgoing connection attempt ongoing
+      MOZ_ASSERT(prevState == SinkState::SINK_DISCONNECTED);
+      break;
+    case SinkState::SINK_PLAYING:
+      // case 4: Audio stream active
+      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED);
+      break;
+    case SinkState::SINK_CONNECTED:
+      // case 5: Audio stream suspended
+      if (prevState == SinkState::SINK_PLAYING) {
+        break;
+      }
+      
+      // case 3: Successfully connected
+      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTING);
+
+      mA2dpConnected = true;
+      mDeviceAddress = address;
+      NotifyConnectionStatusChanged();
+      DispatchConnectionStatusChanged();
 
-  if (aState == SinkState::SINK_DISCONNECTED) {
-    mDeviceAddress.Truncate();
+      OnConnect(EmptyString());
+      break;
+    case SinkState::SINK_DISCONNECTED:
+      // XXX
+      // case 2: Connection attempt failed
+      if (prevState == SinkState::SINK_CONNECTING) {  
+        OnConnect(NS_LITERAL_STRING("A2dpConnectionError"));
+        break;
+      }
+      
+      // case 6: Disconnected from the remote device
+      // case 7: Disconnected from local
+      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED ||
+                 prevState == SinkState::SINK_PLAYING ||
+                 prevState == SinkState::SINK_DISCONNECTING);
+  
+      mA2dpConnected = false;
+      NotifyConnectionStatusChanged();
+      DispatchConnectionStatusChanged();
+      mDeviceAddress.Truncate();
+
+      // case 7 only
+      if (prevState == SinkState::SINK_DISCONNECTING) {
+        OnDisconnect(EmptyString());
+      }
+      break;
   }
-
-  mSinkState = aState;
 }
 
 void
 BluetoothA2dpManager::DispatchConnectionStatusChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   DispatchStatusChangedEvent(
--- a/dom/bluetooth/BluetoothA2dpManager.h
+++ b/dom/bluetooth/BluetoothA2dpManager.h
@@ -26,27 +26,30 @@ public:
     SINK_DISCONNECTING
   };
 
   static BluetoothA2dpManager* Get();
   ~BluetoothA2dpManager();
   void ResetA2dp();
   void ResetAvrcp();
 
-  // Member functions inherited from parent class BluetoothProfileManagerBase
+  // The following functions are inherited from BluetoothProfileManagerBase
   virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
                                    const nsAString& aServiceUuid,
                                    int aChannel) MOZ_OVERRIDE;
   virtual void OnUpdateSdpRecords(const nsAString& aDeviceAddress) MOZ_OVERRIDE;
   virtual void GetAddress(nsAString& aDeviceAddress) MOZ_OVERRIDE;
   virtual bool IsConnected() MOZ_OVERRIDE;
+  virtual void Connect(const nsAString& aDeviceAddress,
+                       BluetoothProfileController* aController) MOZ_OVERRIDE;
+  virtual void Disconnect(BluetoothProfileController* aController) MOZ_OVERRIDE;
+  virtual void OnConnect(const nsAString& aErrorStr) MOZ_OVERRIDE;
+  virtual void OnDisconnect(const nsAString& aErrorStr) MOZ_OVERRIDE;
 
   // A2DP member functions
-  bool Connect(const nsAString& aDeviceAddress);
-  void Disconnect();
   void HandleSinkPropertyChanged(const BluetoothSignal& aSignal);
 
   // AVRCP member functions
   void SetAvrcpConnected(bool aConnected);
   bool IsAvrcpConnected();
   void UpdateMetaData(const nsAString& aTitle,
                       const nsAString& aArtist,
                       const nsAString& aAlbum,
@@ -62,27 +65,26 @@ public:
   uint32_t GetPosition();
   uint32_t GetMediaNumber();
   void GetTitle(nsAString& aTitle);
 
 private:
   BluetoothA2dpManager();
   bool Init();
 
-  void HandleSinkStateChanged(SinkState aState);
   void HandleShutdown();
 
   void DispatchConnectionStatusChanged();
   void NotifyConnectionStatusChanged();
 
   nsString mDeviceAddress;
+  BluetoothProfileController* mController;
 
   // A2DP data member
   bool mA2dpConnected;
-  bool mPlaying;
   SinkState mSinkState;
 
   // AVRCP data member
   bool mAvrcpConnected;
   nsString mAlbum;
   nsString mArtist;
   nsString mTitle;
   uint32_t mDuration;