Bug 872907 - Patch 3: Handle Sink property change, r=echou, r=mrbkap
authorGina Yeh <gyeh@mozilla.com>
Sat, 08 Jun 2013 23:25:41 +0800
changeset 134482 b659ff61bce829cfc62469f8420b3cebb1e8f05a
parent 134481 d9d5d7de3e2cf18b730c222f1d11d43f8734be90
child 134483 4df0816bb82cd52a8ab67a1863439869b27ce874
push id29232
push useremorley@mozilla.com
push dateMon, 10 Jun 2013 09:07:08 +0000
treeherdermozilla-inbound@deb589f7e2dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou, mrbkap
bugs872907
milestone24.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 872907 - Patch 3: Handle Sink property change, r=echou, r=mrbkap
dom/bluetooth/BluetoothA2dpManager.cpp
dom/bluetooth/BluetoothA2dpManager.h
dom/bluetooth/linux/BluetoothDBusService.cpp
--- a/dom/bluetooth/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/BluetoothA2dpManager.cpp
@@ -82,19 +82,19 @@ BluetoothA2dpManagerObserver::Observe(ns
 
   MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMPL_ISUPPORTS1(BluetoothA2dpManagerObserver, nsIObserver)
 
 BluetoothA2dpManager::BluetoothA2dpManager()
-  : mSinkState(SinkState::SINK_DISCONNECTED)
-  , mConnected(false)
+  : mConnected(false)
   , mPlaying(false)
+  , mSinkState(SinkState::SINK_DISCONNECTED)
 {
 }
 
 bool
 BluetoothA2dpManager::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -114,16 +114,36 @@ BluetoothA2dpManager::~BluetoothA2dpMana
 
 void
 BluetoothA2dpManager::Cleanup()
 {
   sA2dpObserver->Shutdown();
   sA2dpObserver = nullptr;
 }
 
+static SinkState
+StatusStringToSinkState(const nsAString& aStatus)
+{
+  SinkState state;
+  if (aStatus.EqualsLiteral("disconnected")) {
+    state = SinkState::SINK_DISCONNECTED;
+  } else if (aStatus.EqualsLiteral("connecting")) {
+    state = SinkState::SINK_CONNECTING;
+  } else if (aStatus.EqualsLiteral("connected")) {
+    state = SinkState::SINK_CONNECTED;
+  } else if (aStatus.EqualsLiteral("playing")) {
+    state = SinkState::SINK_PLAYING;
+  } else if (aStatus.EqualsLiteral("disconnecting")) {
+    state = SinkState::SINK_DISCONNECTING;
+  } else {
+    MOZ_ASSERT(false, "Unknown sink state");
+  }
+  return state;
+}
+
 //static
 BluetoothA2dpManager*
 BluetoothA2dpManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If we already exist, exit early
   if (gBluetoothA2dpManager) {
@@ -189,8 +209,72 @@ BluetoothA2dpManager::Disconnect()
     NS_WARNING("BluetoothA2dpManager has been disconnected");
     return;
   }
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
   bs->SendSinkMessage(mDeviceAddress, NS_LITERAL_STRING("Disconnect"));
 }
+
+void
+BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+
+  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);
+    mConnected = value.get_bool();
+  } 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);
+    mPlaying = value.get_bool();
+  } else if (name.EqualsLiteral("State")) {
+    MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
+    HandleSinkStateChanged(StatusStringToSinkState(value.get_nsString()));
+  } else {
+    NS_WARNING("Unknown sink property");
+  }
+}
+
+/* HandleSinkPropertyChanged update 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);
+
+  if (aState == SinkState::SINK_DISCONNECTED) {
+    mDeviceAddress.Truncate();
+  }
+
+  mSinkState = aState;
+}
--- a/dom/bluetooth/BluetoothA2dpManager.h
+++ b/dom/bluetooth/BluetoothA2dpManager.h
@@ -34,16 +34,18 @@ public:
   void HandleSinkPropertyChanged(const BluetoothSignal& aSignal);
   nsresult HandleShutdown();
 
 private:
   BluetoothA2dpManager();
   bool Init();
   void Cleanup();
 
+  void HandleSinkStateChanged(SinkState aState);
+
   bool mConnected;
   bool mPlaying;
   nsString mDeviceAddress;
   SinkState mSinkState;
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -13,16 +13,17 @@
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #include "base/basictypes.h"
 #include "BluetoothDBusService.h"
+#include "BluetoothA2dpManager.h"
 #include "BluetoothHfpManager.h"
 #include "BluetoothOppManager.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUnixSocketConnector.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 
 #include <cstdio>
@@ -111,16 +112,22 @@ static Properties sAdapterProperties[] =
   {"UUIDs", DBUS_TYPE_ARRAY},
   {"Type", DBUS_TYPE_STRING}
 };
 
 static Properties sManagerProperties[] = {
   {"Adapters", DBUS_TYPE_ARRAY},
 };
 
+static Properties sSinkProperties[] = {
+  {"State", DBUS_TYPE_STRING},
+  {"Connected", DBUS_TYPE_BOOLEAN},
+  {"Playing", DBUS_TYPE_BOOLEAN}
+};
+
 static const char* sBluetoothDBusIfaces[] =
 {
   DBUS_MANAGER_IFACE,
   DBUS_ADAPTER_IFACE,
   DBUS_DEVICE_IFACE
 };
 
 static const char* sBluetoothDBusSignals[] =
@@ -344,16 +351,45 @@ public:
     }
     return NS_OK;
   }
 
 private:
   BluetoothSignal mSignal;
 };
 
+class SinkPropertyChangedHandler : public nsRunnable
+{
+public:
+  SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
+    : mSignal(aSignal)
+  {
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged"));
+    MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+
+    // Replace object path with device address
+    nsString address = GetAddressFromObjectPath(mSignal.path());
+    mSignal.path() = address;
+
+    BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
+    NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
+    a2dp->HandleSinkPropertyChanged(mSignal);
+    return NS_OK;
+  }
+
+private:
+  BluetoothSignal mSignal;
+};
+
 static bool
 IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
 {
   if (aErr && dbus_error_is_set(aErr)) {
     aErrorStr = NS_ConvertUTF8toUTF16(aErr->message);
     LOG_AND_FREE_DBUS_ERROR(aErr);
     return true;
   }
@@ -1526,33 +1562,45 @@ EventFilter(DBusConnection* aConn, DBusM
     }
   } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE,
                                     "PropertyChanged")) {
     ParsePropertyChange(aMsg,
                         v,
                         errorStr,
                         sManagerProperties,
                         ArrayLength(sManagerProperties));
+  } else if (dbus_message_is_signal(aMsg, DBUS_SINK_IFACE,
+                                    "PropertyChanged")) {
+    ParsePropertyChange(aMsg,
+                        v,
+                        errorStr,
+                        sSinkProperties,
+                        ArrayLength(sSinkProperties));
   } else {
     nsAutoCString signalStr;
     signalStr += dbus_message_get_member(aMsg);
     signalStr += " Signal not handled!";
     NS_WARNING(signalStr.get());
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   }
 
   if (!errorStr.IsEmpty()) {
     NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   }
 
   BluetoothSignal signal(signalName, signalPath, v);
-  nsRefPtr<DistributeBluetoothSignalTask>
-    t = new DistributeBluetoothSignalTask(signal);
-  if (NS_FAILED(NS_DispatchToMainThread(t))) {
+  nsRefPtr<nsRunnable> task;
+  if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) {
+    task = new SinkPropertyChangedHandler(signal);
+  } else {
+    task = new DistributeBluetoothSignalTask(signal);
+  }
+
+  if (NS_FAILED(NS_DispatchToMainThread(task))) {
     NS_WARNING("Failed to dispatch to main thread!");
   }
 
   return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 nsresult
 GetDefaultAdapterPath(BluetoothValue& aValue, nsString& aError)