Bug 794903 - Final version: BluetoothScoManager implementation, r=qdot

This commit is contained in:
Gina Yeh 2012-09-29 17:39:05 +08:00
parent 07892b9c3a
commit 88282bd920
13 changed files with 362 additions and 3 deletions

View File

@ -4,10 +4,12 @@
* 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"
@ -57,6 +59,37 @@ public:
}
};
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)
@ -355,6 +388,7 @@ BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
NS_WARNING("BluetoothService not available!");
return false;
}
mDevicePath = aDeviceObjectPath;
nsString serviceUuidStr =
NS_ConvertUTF8toUTF16(mozilla::dom::bluetooth::BluetoothServiceUuidStr::Handsfree);
@ -447,7 +481,7 @@ BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
#endif
break;
}
OpenScoSocket(mDevicePath);
break;
case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED:
switch (mCurrentCallState) {
@ -469,6 +503,7 @@ BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
#endif
break;
}
CloseScoSocket();
break;
default:

View File

@ -46,6 +46,7 @@ private:
int mCurrentCallIndex;
int mCurrentCallState;
nsAutoPtr<BluetoothRilListener> mListener;
nsString mDevicePath;
};
END_BLUETOOTH_NAMESPACE

View File

@ -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;
}

View File

@ -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

View File

@ -229,6 +229,12 @@ public:
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,

View File

@ -55,6 +55,7 @@ CPPSRCS += \
BluetoothHfpManager.cpp \
BluetoothOppManager.cpp \
ObexBase.cpp \
BluetoothScoManager.cpp \
$(NULL)
ifdef MOZ_B2G_RIL

View File

@ -201,6 +201,17 @@ BluetoothServiceChildProcess::RemoveDeviceInternal(
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,

View File

@ -86,6 +86,12 @@ public:
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,

View File

@ -18,6 +18,8 @@
#include "base/basictypes.h"
#include "BluetoothDBusService.h"
#include "BluetoothHfpManager.h"
#include "BluetoothScoManager.h"
#include "BluetoothServiceUuid.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothUnixSocketConnector.h"
@ -26,10 +28,13 @@
#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"
@ -68,6 +73,7 @@ USING_BLUETOOTH_NAMESPACE
#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;
@ -200,6 +206,34 @@ public:
}
};
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) :
@ -2164,6 +2198,52 @@ BluetoothDBusService::PrepareAdapterInternal(const nsAString& aPath)
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:
@ -2217,6 +2297,30 @@ private:
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,

View File

@ -67,6 +67,12 @@ public:
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,

View File

@ -32,9 +32,12 @@ using namespace mozilla;
#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()
@ -49,7 +52,7 @@ IsFmRadioAudioOn()
}
}
NS_IMPL_ISUPPORTS1(AudioManager, nsIAudioManager)
NS_IMPL_ISUPPORTS2(AudioManager, nsIAudioManager, nsIObserver)
static AudioSystem::audio_devices
GetRoutingMode(int aType) {
@ -77,6 +80,10 @@ InternalSetAudioRoutesICS(SwitchState aState)
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, "");
@ -122,6 +129,23 @@ InternalSetAudioRoutes(SwitchState 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)
{
@ -153,10 +177,20 @@ AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT),
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

View File

@ -19,6 +19,7 @@
#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, \
@ -36,10 +37,12 @@ namespace dom {
namespace gonk {
class AudioManager : public nsIAudioManager
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUDIOMANAGER
NS_DECL_NSIOBSERVER
AudioManager();
~AudioManager();

View File

@ -54,6 +54,7 @@ enum SwitchState {
SWITCH_STATE_OFF,
SWITCH_STATE_HEADSET, // Headphone with microphone
SWITCH_STATE_HEADPHONE, // without microphone
SWITCH_STATE_BLUETOOTH_SCO,
NUM_SWITCH_STATE
};