Bug 794903 - Final version: BluetoothScoManager implementation, r=qdot
authorGina Yeh <gyeh@mozilla.com>
Sat, 29 Sep 2012 17:39:05 +0800
changeset 108612 097994d8c0bc02d3c4de3485d9bade6e082ac074
parent 108611 f1c3d164cffce9508c1e9e0db955cd162299d28c
child 108613 1ccdb1019296fabace060cab79e05db47ca66852
push id15600
push userechou@mozilla.com
push dateSat, 29 Sep 2012 09:39:20 +0000
treeherdermozilla-inbound@097994d8c0bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs794903
milestone18.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 794903 - Final version: BluetoothScoManager implementation, r=qdot
dom/bluetooth/BluetoothHfpManager.cpp
dom/bluetooth/BluetoothHfpManager.h
dom/bluetooth/BluetoothScoManager.cpp
dom/bluetooth/BluetoothScoManager.h
dom/bluetooth/BluetoothService.h
dom/bluetooth/Makefile.in
dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
dom/bluetooth/ipc/BluetoothServiceChildProcess.h
dom/bluetooth/linux/BluetoothDBusService.cpp
dom/bluetooth/linux/BluetoothDBusService.h
dom/system/gonk/AudioManager.cpp
dom/system/gonk/AudioManager.h
hal/HalTypes.h
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -1,18 +1,20 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "base/basictypes.h" 
+
 #include "BluetoothHfpManager.h"
 
 #include "BluetoothReplyRunnable.h"
+#include "BluetoothScoManager.h"
 #include "BluetoothService.h"
 #include "BluetoothServiceUuid.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsIRadioInterfaceLayer.h"
@@ -52,16 +54,47 @@ public:
 
       usleep(kRingInterval);
     }
 
     return NS_OK;
   }
 };
 
+void
+OpenScoSocket(const nsAString& aDevicePath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothScoManager* sco = BluetoothScoManager::Get();
+  if (!sco) {
+    NS_WARNING("BluetoothScoManager is not available!");
+    return;
+  }
+
+  if (!sco->Connect(aDevicePath)) {
+    NS_WARNING("Failed to create a sco socket!");
+  }
+}
+
+void
+CloseScoSocket()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothScoManager* sco = BluetoothScoManager::Get();
+  if (!sco) {
+    NS_WARNING("BluetoothScoManager is not available!");
+    return;
+  }
+
+  if (sco->GetConnected())
+    sco->Disconnect();
+}
+
 BluetoothHfpManager::BluetoothHfpManager()
   : mCurrentVgs(-1)
   , mCurrentCallIndex(0)
   , mCurrentCallState(nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED)
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 
   if (obs && NS_FAILED(obs->AddObserver(sInstance, MOZSETTINGS_CHANGED_ID, false))) {
@@ -350,16 +383,17 @@ BluetoothHfpManager::Connect(const nsASt
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     NS_WARNING("BluetoothService not available!");
     return false;
   }
+  mDevicePath = aDeviceObjectPath;
 
   nsString serviceUuidStr =
     NS_ConvertUTF8toUTF16(mozilla::dom::bluetooth::BluetoothServiceUuidStr::Handsfree);
 
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
   nsresult rv = bs->GetSocketViaService(aDeviceObjectPath,
                                         serviceUuidStr,
@@ -442,17 +476,17 @@ BluetoothHfpManager::CallStateChanged(in
           SendLine("+CIEV: 5,0");
           break;
         default:
 #ifdef DEBUG
           NS_WARNING("Not handling state changed");
 #endif
           break;
       }
-
+      OpenScoSocket(mDevicePath);
       break;
     case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED:
       switch (mCurrentCallState) {
         case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
           sStopSendingRingFlag = true;
           // Continue executing, no break
         case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
         case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
@@ -464,16 +498,17 @@ BluetoothHfpManager::CallStateChanged(in
           SendLine("+CIEV: 4,0");
           break;
         default:
 #ifdef DEBUG
           NS_WARNING("Not handling state changed");
 #endif
           break;
       }
+      CloseScoSocket();
       break;
 
     default:
 #ifdef DEBUG
       NS_WARNING("Not handling state changed");
 #endif
       break;
   }
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -41,13 +41,14 @@ private:
   bool BroadcastSystemMessage(const char* aCommand,
                               const int aCommandLength);
   nsresult HandleVolumeChanged(const nsAString& aData);
 
   int mCurrentVgs;
   int mCurrentCallIndex;
   int mCurrentCallState;
   nsAutoPtr<BluetoothRilListener> mListener;
+  nsString mDevicePath;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothScoManager.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "BluetoothScoManager.h"
+
+#include "BluetoothReplyRunnable.h"
+#include "BluetoothService.h"
+#include "BluetoothServiceUuid.h"
+
+#include "mozilla/Services.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "nsContentUtils.h"
+#include "nsIDOMDOMRequest.h"
+#include "nsIObserverService.h"
+#include "nsISystemMessagesInternal.h"
+#include "nsVariant.h"
+
+USING_BLUETOOTH_NAMESPACE
+using namespace mozilla::ipc;
+
+static nsRefPtr<BluetoothScoManager> sInstance;
+static nsCOMPtr<nsIThread> sScoCommandThread;
+
+BluetoothScoManager::BluetoothScoManager()
+{
+  if (!sScoCommandThread) {
+    if (NS_FAILED(NS_NewThread(getter_AddRefs(sScoCommandThread)))) {
+      NS_ERROR("Failed to new thread for sScoCommandThread");
+    }
+  }
+  mConnected = false;
+}
+
+BluetoothScoManager::~BluetoothScoManager()
+{
+  // Shut down the command thread if it still exists.
+  if (sScoCommandThread) {
+    nsCOMPtr<nsIThread> thread;
+    sScoCommandThread.swap(thread);
+    if (NS_FAILED(thread->Shutdown())) {
+      NS_WARNING("Failed to shut down the bluetooth hfpmanager command thread!");
+    }
+  }
+}
+
+//static
+BluetoothScoManager*
+BluetoothScoManager::Get()
+{
+  if (sInstance == nullptr) {
+    sInstance = new BluetoothScoManager();
+  }
+
+  // TODO: destroy pointer sInstance on shutdown
+  return sInstance;
+}
+
+// Virtual function of class SocketConsumer
+void
+BluetoothScoManager::ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage)
+{
+  // SCO socket do nothing here
+  MOZ_NOT_REACHED("This should never be called!");
+}
+
+bool
+BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mConnected) {
+    NS_WARNING("Sco socket has been ready");
+    return true;
+  }
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return false;
+  }
+
+  nsresult rv = bs->GetScoSocket(aDeviceObjectPath,
+                                 true,
+                                 false,
+                                 this);
+
+  return NS_FAILED(rv) ? false : true;
+}
+
+void
+BluetoothScoManager::Disconnect()
+{
+  CloseSocket();
+  mConnected = false;
+}
+
+// FIXME: detect connection in UnixSocketConsumer
+bool
+BluetoothScoManager::GetConnected()
+{
+  return mConnected;
+}
+
+void
+BluetoothScoManager::SetConnected(bool aConnected)
+{
+  mConnected = aConnected;
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothScoManager.h
@@ -0,0 +1,38 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothscomanager_h__
+#define mozilla_dom_bluetooth_bluetoothscomanager_h__
+
+#include "BluetoothCommon.h"
+#include "mozilla/ipc/UnixSocket.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothReplyRunnable;
+
+class BluetoothScoManager : public mozilla::ipc::UnixSocketConsumer
+{
+public:
+  ~BluetoothScoManager();
+
+  static BluetoothScoManager* Get();
+  void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage);
+
+  bool Connect(const nsAString& aDeviceObjectPath);
+  void Disconnect();
+  void SetConnected(bool aConnected);
+  bool GetConnected();
+
+private:
+  BluetoothScoManager();
+  void CreateScoSocket(const nsAString& aDevicePath);
+  bool mConnected;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -225,16 +225,22 @@ public:
                              BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
                        const nsAString& aObjectPath,
                        BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual nsresult
+  GetScoSocket(const nsAString& aObjectPath,
+               bool aAuth,
+               bool aEncrypt,
+               mozilla::ipc::UnixSocketConsumer* aConsumer) = 0;
+
+  virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
                       BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
                       mozilla::ipc::UnixSocketConsumer* aSocketConsumer,
                       BluetoothReplyRunnable* aRunnable) = 0;
 
--- a/dom/bluetooth/Makefile.in
+++ b/dom/bluetooth/Makefile.in
@@ -50,16 +50,17 @@ CPPSRCS += \
   BluetoothUtils.cpp \
   BluetoothChild.cpp \
   BluetoothParent.cpp \
   BluetoothServiceChildProcess.cpp \
   BluetoothUnixSocketConnector.cpp \
   BluetoothHfpManager.cpp \
   BluetoothOppManager.cpp \
   ObexBase.cpp \
+  BluetoothScoManager.cpp \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += BluetoothRilListener.cpp
 endif
 
 XPIDLSRCS = \
   nsIDOMNavigatorBluetooth.idl \
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -197,16 +197,27 @@ BluetoothServiceChildProcess::RemoveDevi
                                               BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
               UnpairRequest(nsString(aAdapterPath), nsString(aObjectPath)));
   return NS_OK;
 }
 
 nsresult
+BluetoothServiceChildProcess::GetScoSocket(
+                                    const nsAString& aObjectPath,
+                                    bool aAuth,
+                                    bool aEncrypt,
+                                    mozilla::ipc::UnixSocketConsumer* aConsumer)
+{
+  MOZ_NOT_REACHED("This should never be called!");
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
 BluetoothServiceChildProcess::GetSocketViaService(
                                        const nsAString& aObjectPath,
                                        const nsAString& aService,
                                        BluetoothSocketType aType,
                                        bool aAuth,
                                        bool aEncrypt,
                                        mozilla::ipc::UnixSocketConsumer* aConsumer,
                                        BluetoothReplyRunnable* aRunnable)
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -82,16 +82,22 @@ public:
                              BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
                        const nsAString& aObjectPath,
                        BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
+  GetScoSocket(const nsAString& aObjectPath,
+               bool aAuth,
+               bool aEncrypt,
+               mozilla::ipc::UnixSocketConsumer* aConsumer) MOZ_OVERRIDE;
+
+  virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
                       BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
                       mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -13,28 +13,33 @@
 ** 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 "BluetoothHfpManager.h"
+#include "BluetoothScoManager.h"
 #include "BluetoothServiceUuid.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUnixSocketConnector.h"
 
 #include <cstdio>
 #include <dbus/dbus.h>
 
 #include "nsIDOMDOMRequest.h"
+#include "nsIObserverService.h"
+#include "AudioManager.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsDebug.h"
 #include "nsDataHashtable.h"
+#include "mozilla/Hal.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/ipc/DBusThread.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 /**
@@ -63,16 +68,17 @@ USING_BLUETOOTH_NAMESPACE
 #define B2G_AGENT_CAPABILITIES "DisplayYesNo"
 #define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager"
 #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
 #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
 #define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
 #define BLUEZ_DBUS_BASE_PATH      "/org/bluez"
 #define BLUEZ_DBUS_BASE_IFC       "org.bluez"
 #define BLUEZ_ERROR_IFC           "org.bluez.Error"
+#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
 
 typedef struct {
   const char* name;
   int type;
 } Properties;
 
 static Properties sDeviceProperties[] = {
   {"Address", DBUS_TYPE_STRING},
@@ -195,16 +201,44 @@ public:
       NS_WARNING("BluetoothService not available!");
       return NS_ERROR_FAILURE;
     }
     bs->DistributeSignal(mSignal);
     return NS_OK;
   }
 };
 
+class NotifyAudioManagerTask : public nsRunnable {
+public:
+  NotifyAudioManagerTask(nsString aObjectPath) : 
+    mObjectPath(aObjectPath)
+  {
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIAudioManager> am = do_GetService("@mozilla.org/telephony/audiomanager;1");
+    am->SetForceForUse(am->USE_COMMUNICATION, am->FORCE_BT_SCO);
+
+    nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
+    if (obs) {
+      if (NS_FAILED(obs->NotifyObservers(nullptr, BLUETOOTH_SCO_STATUS_CHANGED, mObjectPath.get()))) {
+        NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
+        return NS_ERROR_FAILURE;
+      }
+    }
+    return NS_OK;
+  }
+private:
+  nsString mObjectPath;
+};
+
 class PrepareAdapterTask : public nsRunnable {
 public:
   PrepareAdapterTask(const nsAString& aPath) :
     mPath(aPath)
   {
   }
 
   NS_IMETHOD
@@ -2159,16 +2193,62 @@ BluetoothDBusService::PrepareAdapterInte
   if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Cannot dispatch task!");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+class CreateBluetoothScoSocket : public nsRunnable
+{
+public: 
+  CreateBluetoothScoSocket(UnixSocketConsumer* aConsumer,
+                           const nsAString& aObjectPath,
+                           bool aAuth,
+                           bool aEncrypt)
+    : mConsumer(aConsumer),
+      mObjectPath(aObjectPath),
+      mAuth(aAuth),
+      mEncrypt(aEncrypt)
+  {
+  }
+
+  nsresult
+  Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    nsString address = GetAddressFromObjectPath(mObjectPath);
+    nsString replyError;
+    BluetoothUnixSocketConnector c(BluetoothSocketType::SCO, -1, mAuth, mEncrypt);
+
+    BluetoothScoManager* sco = BluetoothScoManager::Get();
+    if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
+      replyError.AssignLiteral("SocketConnectionError");
+      sco->SetConnected(false); 
+      return NS_ERROR_FAILURE;
+    }
+    sco->SetConnected(true);
+
+    nsRefPtr<NotifyAudioManagerTask> task = new NotifyAudioManagerTask(address);
+    if (NS_FAILED(NS_DispatchToMainThread(task))) {
+      NS_WARNING("Failed to dispatch to main thread!");
+      return NS_ERROR_FAILURE;
+    }    
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<UnixSocketConsumer> mConsumer;
+  nsString mObjectPath;
+  bool mAuth;
+  bool mEncrypt;
+};
+
 class CreateBluetoothSocketRunnable : public nsRunnable
 {
 public:
   CreateBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
                                 UnixSocketConsumer* aConsumer,
                                 const nsAString& aObjectPath,
                                 const nsAString& aServiceUUID,
                                 BluetoothSocketType aType,
@@ -2213,16 +2293,40 @@ private:
   nsString mObjectPath;
   nsString mServiceUUID;
   BluetoothSocketType mType;
   bool mAuth;
   bool mEncrypt;
 };
 
 nsresult
+BluetoothDBusService::GetScoSocket(const nsAString& aObjectPath,
+                                   bool aAuth,
+                                   bool aEncrypt,
+                                   mozilla::ipc::UnixSocketConsumer* aConsumer)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
+  if (!mConnection || !gThreadConnection) {
+    NS_ERROR("Bluetooth service not started yet!");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<nsRunnable> func(new CreateBluetoothScoSocket(aConsumer,
+                                                         aObjectPath,
+                                                         aAuth,
+                                                         aEncrypt));
+  if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Cannot dispatch firmware loading task!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK; 
+}
+
+nsresult
 BluetoothDBusService::GetSocketViaService(const nsAString& aObjectPath,
                                           const nsAString& aService,
                                           BluetoothSocketType aType,
                                           bool aAuth,
                                           bool aEncrypt,
                                           mozilla::ipc::UnixSocketConsumer* aConsumer,
                                           BluetoothReplyRunnable* aRunnable)
 {
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -63,16 +63,22 @@ public:
                               const nsTArray<uint32_t>& aServices,
                               nsTArray<uint32_t>& aServiceHandlesContainer);
 
   static bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<uint32_t>& aServiceHandles);
 
   virtual nsresult
+  GetScoSocket(const nsAString& aObjectPath,
+               bool aAuth,
+               bool aEncrypt,
+               mozilla::ipc::UnixSocketConsumer* aConsumer);
+
+  virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
                       BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
                       mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable);
 
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -27,34 +27,37 @@ using namespace mozilla::hal;
 using namespace mozilla;
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args) 
 
 #define HEADPHONES_STATUS_CHANGED "headphones-status-changed"
 #define HEADPHONES_STATUS_ON      NS_LITERAL_STRING("on").get()
 #define HEADPHONES_STATUS_OFF     NS_LITERAL_STRING("off").get()
 #define HEADPHONES_STATUS_UNKNOWN NS_LITERAL_STRING("unknown").get()
+#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
 
 // A bitwise variable for recording what kind of headset is attached.
 static int sHeadsetState;
+static const char* sDeviceAddress;
+static int kBtSampleRate = 8000;
 
 static bool
 IsFmRadioAudioOn()
 {
   if (static_cast<
       audio_policy_dev_state_t (*) (audio_devices_t, const char *)
       >(AudioSystem::getDeviceConnectionState)) {
     return AudioSystem::getDeviceConnectionState(AUDIO_DEVICE_OUT_FM, "") == 
            AUDIO_POLICY_DEVICE_STATE_AVAILABLE ? true : false;
   } else {
     return false;
   }
 }
 
-NS_IMPL_ISUPPORTS1(AudioManager, nsIAudioManager)
+NS_IMPL_ISUPPORTS2(AudioManager, nsIAudioManager, nsIObserver)
 
 static AudioSystem::audio_devices
 GetRoutingMode(int aType) {
   if (aType == nsIAudioManager::FORCE_SPEAKER) {
     return AudioSystem::DEVICE_OUT_SPEAKER;
   } else if (aType == nsIAudioManager::FORCE_HEADPHONES) {
     return AudioSystem::DEVICE_OUT_WIRED_HEADSET;
   } else if (aType == nsIAudioManager::FORCE_BT_SCO) {
@@ -72,16 +75,20 @@ InternalSetAudioRoutesICS(SwitchState aS
   if (aState == SWITCH_STATE_HEADSET) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
                                           AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
     sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET;
   } else if (aState == SWITCH_STATE_HEADPHONE) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                           AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
     sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+  } else if (aState == SWITCH_STATE_BLUETOOTH_SCO) {
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE, sDeviceAddress);
+    sHeadsetState |= AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
   } else if (aState == SWITCH_STATE_OFF) {
     AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(sHeadsetState),
                                           AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
     sHeadsetState = 0;
   }
 
   // The audio volume is not consistent when we plug and unplug the headset.
   // Set the fm volume again here.
@@ -117,16 +124,33 @@ InternalSetAudioRoutes(SwitchState aStat
     InternalSetAudioRoutesICS(aState);
   } else if (static_cast<
     audio_io_handle_t (*)(AudioSystem::stream_type, uint32_t, uint32_t, uint32_t, AudioSystem::output_flags)
     >(AudioSystem::getOutput)) {
     InternalSetAudioRoutesGB(aState);
   }
 }
 
+nsresult
+AudioManager::Observe(nsISupports* aSubject,
+                      const char* aTopic,
+                      const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED)) {
+    String8 cmd;
+    cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
+    AudioSystem::setParameters(0, cmd);
+
+    sDeviceAddress = NS_ConvertUTF16toUTF8(nsDependentString(aData)).get();
+    InternalSetAudioRoutes(SwitchState::SWITCH_STATE_BLUETOOTH_SCO);
+    return NS_OK;
+  }
+  return NS_ERROR_UNEXPECTED;
+}
+
 static void
 NotifyHeadphonesStatus(SwitchState aState)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     if (aState == SWITCH_STATE_ON) {
       obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_ON);
     } else if (aState == SWITCH_STATE_OFF) {
@@ -148,20 +172,30 @@ public:
 
 AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT),
                  mObserver(new HeadphoneSwitchObserver())
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
 
   InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
   NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES));
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED, false))) {
+    NS_WARNING("Failed to add bluetooth-sco-status-changed oberver!");
+  }
 }
 
 AudioManager::~AudioManager() {
   UnregisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED))) {
+    NS_WARNING("Failed to add bluetooth-sco-status-changed oberver!");
+  }
 }
 
 NS_IMETHODIMP
 AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
 {
   if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -14,16 +14,17 @@
  */
 
 #ifndef mozilla_dom_system_b2g_audiomanager_h__
 #define mozilla_dom_system_b2g_audiomanager_h__
 
 #include "mozilla/Observer.h"
 #include "nsAutoPtr.h"
 #include "nsIAudioManager.h"
+#include "nsIObserver.h"
 
 // {b2b51423-502d-4d77-89b3-7786b562b084}
 #define NS_AUDIOMANAGER_CID {0x94f6fd70, 0x7615, 0x4af9, \
       {0x89, 0x10, 0xf9, 0x3c, 0x55, 0xe6, 0x62, 0xec}}
 #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
 
 
 namespace mozilla {
@@ -31,20 +32,22 @@ namespace hal {
 class SwitchEvent;
 typedef Observer<SwitchEvent> SwitchObserver;
 } // namespace hal
 
 namespace dom {
 namespace gonk {
 
 class AudioManager : public nsIAudioManager
+                   , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAUDIOMANAGER
+  NS_DECL_NSIOBSERVER
 
   AudioManager();
   ~AudioManager();
 
 protected:
   int32_t mPhoneState;
 
 private:
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -49,16 +49,17 @@ enum SwitchDevice {
 };
 
 enum SwitchState {
   SWITCH_STATE_UNKNOWN = -1,
   SWITCH_STATE_ON,
   SWITCH_STATE_OFF,
   SWITCH_STATE_HEADSET,          // Headphone with microphone
   SWITCH_STATE_HEADPHONE,        // without microphone
+  SWITCH_STATE_BLUETOOTH_SCO,
   NUM_SWITCH_STATE
 };
 
 typedef Observer<SwitchEvent> SwitchObserver;
 
 enum ProcessPriority {
   PROCESS_PRIORITY_BACKGROUND,
   PROCESS_PRIORITY_FOREGROUND,