Bug 800695 - Final version: Audio route is set to sco before connecting to a bluetooth headset, r=qdot

This commit is contained in:
Gina Yeh 2012-10-22 18:34:15 +08:00
parent 814606cd60
commit 0c2ef57514
6 changed files with 335 additions and 280 deletions

View File

@ -21,12 +21,10 @@
#include "nsIAudioManager.h"
#include "nsIObserverService.h"
#include "nsIRadioInterfaceLayer.h"
#include "nsVariant.h"
#include <unistd.h> /* usleep() */
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
#define AUDIO_VOLUME_MASTER "audio.volume.master"
#define HANDSFREE_UUID mozilla::dom::bluetooth::BluetoothServiceUuidStr::Handsfree
#define HEADSET_UUID mozilla::dom::bluetooth::BluetoothServiceUuidStr::Headset
@ -35,6 +33,66 @@ using namespace mozilla;
using namespace mozilla::ipc;
USING_BLUETOOTH_NAMESPACE
/* CallState for sCINDItems[CINDType::CALL].value
* - NO_CALL: there are no calls in progress
* - IN_PROGRESS: at least one call is in progress
*/
enum CallState {
NO_CALL,
IN_PROGRESS
};
/* CallSetupState for sCINDItems[CINDType::CALLSETUP].value
* - NO_CALLSETUP: not currently in call set up
* - INCOMING: an incoming call process ongoing
* - OUTGOING: an outgoing call set up is ongoing
* - OUTGOING_ALERTING: remote party being alerted in an outgoing call
*/
enum CallSetupState {
NO_CALLSETUP,
INCOMING,
OUTGOING,
OUTGOING_ALERTING
};
/* CallHeldState for sCINDItems[CINDType::CALLHELD].value
* - NO_CALLHELD: no calls held
* - ONHOLD_ACTIVE: both an active and a held call
* - ONHOLD_NOACTIVE: call on hold, no active call
*/
enum CallHeldState {
NO_CALLHELD,
ONHOLD_ACTIVE,
ONHOLD_NOACTIVE
};
typedef struct {
const char* name;
const char* range;
int value;
} CINDItem;
enum CINDType {
BATTCHG = 1,
SIGNAL,
SERVICE,
CALL,
CALLSETUP,
CALLHELD,
ROAM,
};
static CINDItem sCINDItems[] = {
{},
{"battchg", "0-5", 5},
{"signal", "0-5", 5},
{"service", "0,1", 1},
{"call", "0,1", CallState::NO_CALL},
{"callsetup", "0-3", CallSetupState::NO_CALLSETUP},
{"callheld", "0-2", CallHeldState::NO_CALLHELD},
{"roam", "0,1", 0}
};
class mozilla::dom::bluetooth::BluetoothHfpManagerObserver : public nsIObserver
{
public:
@ -98,6 +156,7 @@ BluetoothHfpManagerObserver::Observe(nsISupports* aSubject,
const PRUnichar* aData)
{
MOZ_ASSERT(gBluetoothHfpManager);
if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
return gBluetoothHfpManager->HandleVolumeChanged(nsDependentString(aData));
} else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
@ -131,7 +190,7 @@ public:
};
void
OpenScoSocket(const nsAString& aDevicePath)
OpenScoSocket(const nsAString& aDeviceAddress)
{
MOZ_ASSERT(NS_IsMainThread());
@ -141,7 +200,7 @@ OpenScoSocket(const nsAString& aDevicePath)
return;
}
if (!sco->Connect(aDevicePath)) {
if (!sco->Connect(aDeviceAddress)) {
NS_WARNING("Failed to create a sco socket!");
}
}
@ -151,39 +210,22 @@ CloseScoSocket()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIAudioManager> am = do_GetService("@mozilla.org/telephony/audiomanager;1");
if (!am) {
NS_WARNING("Failed to get AudioManager Service!");
return;
}
am->SetForceForUse(am->USE_COMMUNICATION, am->FORCE_NONE);
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (!sco) {
NS_WARNING("BluetoothScoManager is not available!");
return;
}
if (sco->GetConnected()) {
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (obs) {
if (NS_FAILED(obs->NotifyObservers(nullptr, BLUETOOTH_SCO_STATUS_CHANGED, nullptr))) {
NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
return;
}
}
sco->Disconnect();
}
sco->Disconnect();
}
BluetoothHfpManager::BluetoothHfpManager()
: mCurrentVgs(-1)
, mCurrentCallIndex(0)
, mCurrentCallState(nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED)
, mCall(0)
, mCallSetup(0)
, mCallHeld(0)
{
sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
}
bool
@ -266,7 +308,7 @@ BluetoothHfpManager::Get()
}
void
BluetoothHfpManager::NotifySettings(const bool aConnected)
BluetoothHfpManager::NotifySettings()
{
nsString type, name;
BluetoothValue v;
@ -274,11 +316,14 @@ BluetoothHfpManager::NotifySettings(const bool aConnected)
type.AssignLiteral("bluetooth-hfp-status-changed");
name.AssignLiteral("connected");
v = aConnected;
uint32_t status = GetConnectionStatus();
v = status;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("address");
v = GetAddressFromObjectPath(mDevicePath);
nsString address;
GetSocketAddr(address);
v = address;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
@ -309,6 +354,10 @@ BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (GetConnectionStatus() != SocketConnectionStatus::SOCKET_CONNECTED) {
return NS_OK;
}
// The string that we're interested in will be a JSON string that looks like:
// {"key":"volumeup", "value":1.0}
// {"key":"volumedown", "value":0.2}
@ -360,26 +409,11 @@ BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
}
// AG volume range: [0.0, 1.0]
float volume = value.toNumber();
// HS volume range: [0, 15]
float volume = value.toNumber();
mCurrentVgs = ceil(volume * 15);
nsDiscriminatedUnion du;
du.mType = 0;
du.u.mInt8Value = mCurrentVgs;
nsCString vgs;
if (NS_FAILED(nsVariant::ConvertToACString(du, vgs))) {
NS_WARNING("Failed to convert volume to string");
return NS_ERROR_FAILURE;
}
nsAutoCString newVgs;
newVgs += "+VGS: ";
newVgs += vgs;
SendLine(newVgs.get());
SendCommand("+VGS: ", mCurrentVgs);
return NS_OK;
}
@ -394,23 +428,6 @@ BluetoothHfpManager::HandleShutdown()
return NS_OK;
}
bool
AppendIntegerToString(nsAutoCString& aString, int aValue)
{
nsDiscriminatedUnion du;
du.mType = 0;
du.u.mInt8Value = aValue;
nsCString temp;
if (NS_FAILED(nsVariant::ConvertToACString(du, temp))) {
NS_WARNING("Failed to convert mCall/mCallSetup/mCallHeld to string");
return false;
}
aString += temp;
aString += ",";
return true;
}
// Virtual function of class SocketConsumer
void
BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
@ -422,38 +439,18 @@ BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
// For more information, please refer to 4.34.1 "Bluetooth Defined AT
// Capabilities" in Bluetooth hands-free profile 1.6
if (!strncmp(msg, "AT+BRSF=", 8)) {
SendLine("+BRSF: 23");
SendCommand("+BRSF: ", 23);
SendLine("OK");
} else if (!strncmp(msg, "AT+CIND=?", 9)) {
nsAutoCString cindRange;
cindRange += "+CIND: ";
cindRange += "(\"battchg\",(0-5)),";
cindRange += "(\"signal\",(0-5)),";
cindRange += "(\"service\",(0,1)),";
cindRange += "(\"call\",(0,1)),";
cindRange += "(\"callsetup\",(0-3)),";
cindRange += "(\"callheld\",(0-2)),";
cindRange += "(\"roam\",(0,1))";
SendLine(cindRange.get());
// Asking for CIND range
SendCommand("+CIND: ", 0);
SendLine("OK");
} else if (!strncmp(msg, "AT+CIND?", 8)) {
nsAutoCString cind;
cind += "+CIND: 5,5,1,";
if (!AppendIntegerToString(cind, mCall) ||
!AppendIntegerToString(cind, mCallSetup) ||
!AppendIntegerToString(cind, mCallHeld)) {
NS_WARNING("Failed to convert mCall/mCallSetup/mCallHeld to string");
}
cind += "0";
SendLine(cind.get());
// Asking for CIND value
SendCommand("+CIND: ", 1);
SendLine("OK");
} else if (!strncmp(msg, "AT+CMER=", 8)) {
// SLC establishment
NotifySettings(true);
SendLine("OK");
} else if (!strncmp(msg, "AT+CHLD=?", 9)) {
SendLine("+CHLD: (0,1,2,3)");
@ -461,7 +458,7 @@ BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
} else if (!strncmp(msg, "AT+CHLD=", 8)) {
SendLine("OK");
} else if (!strncmp(msg, "AT+VGS=", 7)) {
// VGS range: [0, 15]
// HS volume range: [0, 15]
int newVgs = msg[7] - '0';
if (strlen(msg) > 8) {
@ -529,7 +526,7 @@ BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
}
bool
BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
BluetoothHfpManager::Connect(const nsAString& aDevicePath,
const bool aIsHandsfree,
BluetoothReplyRunnable* aRunnable)
{
@ -540,6 +537,11 @@ BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
return false;
}
if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
NS_WARNING("BluetoothHfpManager has connected to a headset/handsfree!");
return false;
}
CloseSocket();
BluetoothService* bs = BluetoothService::Get();
@ -547,7 +549,6 @@ BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
NS_WARNING("BluetoothService not available!");
return false;
}
mDevicePath = aDeviceObjectPath;
nsString serviceUuidStr;
if (aIsHandsfree) {
@ -558,12 +559,14 @@ BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
nsCOMPtr<nsIRILContentHelper> ril =
do_GetService("@mozilla.org/ril/content-helper;1");
NS_ENSURE_TRUE(ril, false);
if (!ril) {
MOZ_ASSERT("Failed to get RIL Content Helper");
}
ril->EnumerateCalls(mListener->GetCallback());
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
nsresult rv = bs->GetSocketViaService(aDeviceObjectPath,
nsresult rv = bs->GetSocketViaService(aDevicePath,
serviceUuidStr,
BluetoothSocketType::RFCOMM,
true,
@ -604,11 +607,12 @@ BluetoothHfpManager::Listen()
void
BluetoothHfpManager::Disconnect()
{
NotifySettings(false);
if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_DISCONNECTED) {
NS_WARNING("BluetoothHfpManager has disconnected!");
return;
}
CloseSocket();
mCall = 0;
mCallSetup = 0;
mCallHeld = 0;
Listen();
}
@ -625,76 +629,74 @@ BluetoothHfpManager::SendLine(const char* aMessage)
return SendSocketData(msg);
}
/*
* EnumerateCallState will be called for each call
*/
void
BluetoothHfpManager::EnumerateCallState(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive)
bool
BluetoothHfpManager::SendCommand(const char* aCommand, const int aValue)
{
// TODO: enums for mCall, mCallSetup, mCallHeld
/* mCall - 0: there are no calls in progress
* - 1: at least one call is in progress
* mCallSetup - 0: not currently in call set up
* - 1: an incoming call process ongoing
* - 2: an outgoing call set up is ongoing
* - 3: remote party being alerted in an outgoing call
* mCallHeld - 0: no calls held
* - 1: call is placed on hold or active/held calls swapped
* - 2: call o hold, no active call
*/
nsAutoCString message;
int value = aValue;
message += aCommand;
if (mCallHeld == 2 && aIsActive) {
mCallHeld = 1;
if (!strcmp(aCommand, "+CIEV: ")) {
message.AppendInt(aValue);
message += ",";
switch (aValue) {
case CINDType::CALL:
message.AppendInt(sCINDItems[CINDType::CALL].value);
break;
case CINDType::CALLSETUP:
message.AppendInt(sCINDItems[CINDType::CALLSETUP].value);
break;
case CINDType::CALLHELD:
message.AppendInt(sCINDItems[CINDType::CALLHELD].value);
break;
default:
#ifdef DEBUG
NS_WARNING("unexpected CINDType for CIEV command");
#endif
return false;
}
} else if (!strcmp(aCommand, "+CIND: ")) {
if (!aValue) {
for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
message += "(\"";
message += sCINDItems[i].name;
message += "\",(";
message += sCINDItems[i].range;
message += ")";
if (i == (ArrayLength(sCINDItems) - 1)) {
message +=")";
break;
}
message += "),";
}
} else {
for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
message.AppendInt(sCINDItems[i].value);
if (i == (ArrayLength(sCINDItems) - 1)) {
break;
}
message += ",";
}
}
} else {
message.AppendInt(value);
}
if (mCall || mCallSetup) {
return;
}
switch (aCallState) {
case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
mCall = 1;
mCallSetup = 3;
break;
case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
mCall = 1;
mCallSetup = 1;
break;
case nsIRadioInterfaceLayer::CALL_STATE_CONNECTING:
case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTING:
case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED:
case nsIRadioInterfaceLayer::CALL_STATE_BUSY:
mCall = 1;
mCallSetup = 2;
break;
case nsIRadioInterfaceLayer::CALL_STATE_HOLDING:
case nsIRadioInterfaceLayer::CALL_STATE_HELD:
case nsIRadioInterfaceLayer::CALL_STATE_RESUMING:
mCall = 1;
mCallHeld = 2;
default:
mCall = 0;
mCallSetup = 0;
}
return SendLine(message.get());
}
/*
* CallStateChanged will be called whenever call status is changed, and it
* also means we need to notify HS about the change. For more information,
* please refer to 4.13 ~ 4.15 in Bluetooth hands-free profile 1.6.
*/
void
BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive)
BluetoothHfpManager::SetupCIND(int aCallIndex, int aCallState, bool aInitial)
{
nsRefPtr<nsRunnable> sendRingTask;
nsString address;
switch (aCallState) {
case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
// Send "CallSetup = 1"
SendLine("+CIEV: 5,1");
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::INCOMING;
if (!aInitial) {
SendCommand("+CIEV: ", CINDType::CALLSETUP);
}
// Start sending RING indicator to HF
sStopSendingRingFlag = false;
@ -706,63 +708,77 @@ BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
};
break;
case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
// Send "CallSetup = 2"
SendLine("+CIEV: 5,2");
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::OUTGOING;
if (!aInitial) {
SendCommand("+CIEV: ", CINDType::CALLSETUP);
GetSocketAddr(address);
OpenScoSocket(address);
}
break;
case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
// Send "CallSetup = 3"
if (mCurrentCallIndex == nsIRadioInterfaceLayer::CALL_STATE_DIALING) {
SendLine("+CIEV: 5,3");
} else {
#ifdef DEBUG
NS_WARNING("Not handling state changed");
#endif
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::OUTGOING_ALERTING;
if (!aInitial) {
SendCommand("+CIEV: ", CINDType::CALLSETUP);
}
break;
case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
switch (mCurrentCallState) {
case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
sStopSendingRingFlag = true;
// Continue executing, no break
case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
// Send "Call = 1, CallSetup = 0"
SendLine("+CIEV: 4,1");
SendLine("+CIEV: 5,0");
break;
default:
if (aInitial) {
sCINDItems[CINDType::CALL].value = CallState::IN_PROGRESS;
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
} else {
switch (mCurrentCallState) {
case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
// Incoming call, no break
sStopSendingRingFlag = true;
GetSocketAddr(address);
OpenScoSocket(address);
case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
// Outgoing call
sCINDItems[CINDType::CALL].value = CallState::IN_PROGRESS;
SendCommand("+CIEV: ", CINDType::CALL);
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
SendCommand("+CIEV: ", CINDType::CALLSETUP);
break;
default:
#ifdef DEBUG
NS_WARNING("Not handling state changed");
NS_WARNING("Not handling state changed");
#endif
break;
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:
// Send "CallSetup = 0"
SendLine("+CIEV: 5,0");
break;
case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
// Send "Call = 0"
SendLine("+CIEV: 4,0");
break;
default:
if (!aInitial) {
switch (mCurrentCallState) {
case nsIRadioInterfaceLayer::CALL_STATE_INCOMING:
// Incoming call, no break
sStopSendingRingFlag = true;
case nsIRadioInterfaceLayer::CALL_STATE_DIALING:
case nsIRadioInterfaceLayer::CALL_STATE_ALERTING:
// Outgoing call
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
SendCommand("+CIEV: ", CINDType::CALLSETUP);
break;
case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED:
sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
SendCommand("+CIEV: ", CINDType::CALL);
break;
default:
#ifdef DEBUG
NS_WARNING("Not handling state changed");
NS_WARNING("Not handling state changed");
#endif
break;
break;
}
CloseScoSocket();
}
CloseScoSocket();
break;
default:
#ifdef DEBUG
NS_WARNING("Not handling state changed");
sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
#endif
break;
}
@ -771,9 +787,53 @@ BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
mCurrentCallState = aCallState;
}
/*
* EnumerateCallState will be called for each call
*/
void
BluetoothHfpManager::EnumerateCallState(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive)
{
if (aIsActive &&
(sCINDItems[CINDType::CALLHELD].value == CallHeldState::ONHOLD_NOACTIVE)) {
sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
}
if (sCINDItems[CINDType::CALL].value || sCINDItems[CINDType::CALLSETUP].value) {
return;
}
SetupCIND(aCallIndex, aCallState, true);
}
/*
* CallStateChanged will be called whenever call status is changed, and it
* also means we need to notify HS about the change. For more information,
* please refer to 4.13 ~ 4.15 in Bluetooth hands-free profile 1.6.
*/
void
BluetoothHfpManager::CallStateChanged(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive)
{
if (GetConnectionStatus() != SocketConnectionStatus::SOCKET_CONNECTED) {
return;
}
SetupCIND(aCallIndex, aCallState, false);
}
void
BluetoothHfpManager::OnConnectSuccess()
{
if (mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED ||
mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_DIALING ||
mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_ALERTING) {
nsString address;
GetSocketAddr(address);
OpenScoSocket(address);
}
NotifySettings();
}
void
@ -787,5 +847,8 @@ BluetoothHfpManager::OnConnectError()
void
BluetoothHfpManager::OnDisconnect()
{
NS_WARNING("GOT DISCONNECT!");
NotifySettings();
sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
}

View File

@ -29,10 +29,12 @@ public:
BluetoothReplyRunnable* aRunnable);
void Disconnect();
bool SendLine(const char* aMessage);
bool SendCommand(const char* aCommand, const int aValue);
void CallStateChanged(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive);
void EnumerateCallState(int aCallIndex, int aCallState,
const char* aNumber, bool aIsActive);
void SetupCIND(int aCallIndex, int aCallState, bool aInitial);
bool Listen();
private:
@ -43,7 +45,7 @@ private:
bool Init();
void Cleanup();
void NotifyDialer(const nsAString& aCommand);
void NotifySettings(const bool aConnected);
void NotifySettings();
virtual void OnConnectSuccess() MOZ_OVERRIDE;
virtual void OnConnectError() MOZ_OVERRIDE;
virtual void OnDisconnect() MOZ_OVERRIDE;
@ -51,11 +53,7 @@ private:
int mCurrentVgs;
int mCurrentCallIndex;
int mCurrentCallState;
int mCall;
int mCallSetup;
int mCallHeld;
nsAutoPtr<BluetoothRilListener> mListener;
nsString mDevicePath;
};
END_BLUETOOTH_NAMESPACE

View File

@ -11,15 +11,16 @@
#include "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothServiceUuid.h"
#include "BluetoothUtils.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "nsContentUtils.h"
#include "nsIDOMDOMRequest.h"
#include "nsIAudioManager.h"
#include "nsIObserverService.h"
#include "nsISystemMessagesInternal.h"
#include "nsVariant.h"
#define BLUETOOTH_SCO_STATUS_CHANGED "bluetooth-sco-status-changed"
using namespace mozilla;
using namespace mozilla::ipc;
@ -64,6 +65,36 @@ public:
}
};
void
BluetoothScoManager::NotifyAudioManager(const nsAString& aAddress) {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (!obs) {
NS_WARNING("Failed to get observser service!");
return;
}
if (aAddress.IsEmpty()) {
if (NS_FAILED(obs->NotifyObservers(nullptr, BLUETOOTH_SCO_STATUS_CHANGED, nullptr))) {
NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
return;
}
} else {
if (NS_FAILED(obs->NotifyObservers(nullptr, BLUETOOTH_SCO_STATUS_CHANGED, aAddress.BeginReading()))) {
NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
return;
}
}
nsCOMPtr<nsIAudioManager> am = do_GetService("@mozilla.org/telephony/audiomanager;1");
if (!am) {
NS_WARNING("Failed to get AudioManager service!");
return;
}
am->SetForceForUse(am->USE_COMMUNICATION, am->FORCE_BT_SCO);
}
NS_IMPL_ISUPPORTS1(BluetoothScoManagerObserver, nsIObserver)
namespace {
@ -87,7 +118,6 @@ BluetoothScoManagerObserver::Observe(nsISupports* aSubject,
}
BluetoothScoManager::BluetoothScoManager()
: mConnected(false)
{
}
@ -161,7 +191,7 @@ BluetoothScoManager::HandleShutdown()
}
bool
BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
BluetoothScoManager::Connect(const nsAString& aDeviceAddress)
{
MOZ_ASSERT(NS_IsMainThread());
@ -170,9 +200,9 @@ BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
return false;
}
if (mConnected) {
NS_WARNING("Sco socket has been ready");
return true;
if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
NS_WARNING("Sco socket has been connected");
return false;
}
BluetoothService* bs = BluetoothService::Get();
@ -181,7 +211,7 @@ BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
return false;
}
nsresult rv = bs->GetScoSocket(aDeviceObjectPath,
nsresult rv = bs->GetScoSocket(aDeviceAddress,
true,
false,
this);
@ -192,34 +222,30 @@ BluetoothScoManager::Connect(const nsAString& aDeviceObjectPath)
void
BluetoothScoManager::Disconnect()
{
if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_DISCONNECTED) {
return;
}
CloseSocket();
mConnected = false;
}
// FIXME: detect connection in UnixSocketConsumer
bool
BluetoothScoManager::GetConnected()
{
return mConnected;
}
void
BluetoothScoManager::SetConnected(bool aConnected)
{
mConnected = aConnected;
}
void
BluetoothScoManager::OnConnectSuccess()
{
nsString address;
GetSocketAddr(address);
NotifyAudioManager(address);
}
void
BluetoothScoManager::OnConnectError()
{
CloseSocket();
}
void
BluetoothScoManager::OnDisconnect()
{
nsString address = NS_LITERAL_STRING("");
NotifyAudioManager(address);
}

View File

@ -27,8 +27,6 @@ public:
bool Connect(const nsAString& aDeviceObjectPath);
void Disconnect();
void SetConnected(bool aConnected);
bool GetConnected();
private:
friend class BluetoothScoManagerObserver;
@ -36,11 +34,10 @@ private:
bool Init();
void Cleanup();
nsresult HandleShutdown();
void CreateScoSocket(const nsAString& aDevicePath);
void NotifyAudioManager(const nsAString& aAddress);
virtual void OnConnectSuccess() MOZ_OVERRIDE;
virtual void OnConnectError() MOZ_OVERRIDE;
virtual void OnDisconnect() MOZ_OVERRIDE;
bool mConnected;
};
END_BLUETOOTH_NAMESPACE

View File

@ -191,6 +191,6 @@ BluetoothUnixSocketConnector::GetSocketAddr(const sockaddr& aAddr,
nsAString& aAddrStr)
{
char addr[18];
get_bdaddr_as_string((bdaddr_t*)&aAddr, addr);
get_bdaddr_as_string((bdaddr_t*)aAddr.sa_data, addr);
aAddrStr.AssignASCII(addr);
}

View File

@ -75,7 +75,6 @@ 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;
@ -174,37 +173,6 @@ 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");
if (!am) {
NS_WARNING("Failed to get AudioManager service!");
}
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) :
@ -2263,14 +2231,26 @@ BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
{
NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
nsString errorStr;
BluetoothValue v = true;
if (aProfileId == (uint16_t)(BluetoothServiceUuid::Handsfree >> 32)) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
return hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress), true,
aRunnable);
if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
true, aRunnable)) {
errorStr.AssignLiteral("Failed to connect with device.");
DispatchBluetoothReply(aRunnable, v, errorStr);
return false;
}
return true;
} else if (aProfileId == (uint16_t)(BluetoothServiceUuid::Headset >> 32)) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
return hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress), false,
aRunnable);
if (!hfp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
false, aRunnable)) {
errorStr.AssignLiteral("Failed to connect with device.");
DispatchBluetoothReply(aRunnable, v, errorStr);
return false;
}
return true;
} else if (aProfileId == (uint16_t)(BluetoothServiceUuid::ObjectPush >> 32)) {
BluetoothOppManager* opp = BluetoothOppManager::Get();
return opp->Connect(GetObjectPathFromAddress(aAdapterPath, aDeviceAddress),
@ -2306,13 +2286,13 @@ BluetoothDBusService::Disconnect(const uint16_t aProfileId,
class CreateBluetoothScoSocket : public nsRunnable
{
public:
public:
CreateBluetoothScoSocket(UnixSocketConsumer* aConsumer,
const nsAString& aObjectPath,
const nsAString& aAddress,
bool aAuth,
bool aEncrypt)
: mConsumer(aConsumer),
mObjectPath(aObjectPath),
mAddress(aAddress),
mAuth(aAuth),
mEncrypt(aEncrypt)
{
@ -2323,31 +2303,22 @@ public:
{
MOZ_ASSERT(!NS_IsMainThread());
nsString address = GetAddressFromObjectPath(mObjectPath);
nsString replyError;
BluetoothUnixSocketConnector* c =
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
mAuth, mEncrypt);
BluetoothScoManager* sco = BluetoothScoManager::Get();
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(mAddress).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;
nsString mAddress;
bool mAuth;
bool mEncrypt;
};
@ -2498,7 +2469,7 @@ BluetoothDBusService::GetSocketViaService(const nsAString& aObjectPath,
}
nsresult
BluetoothDBusService::GetScoSocket(const nsAString& aObjectPath,
BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
bool aAuth,
bool aEncrypt,
mozilla::ipc::UnixSocketConsumer* aConsumer)
@ -2510,7 +2481,7 @@ BluetoothDBusService::GetScoSocket(const nsAString& aObjectPath,
}
nsRefPtr<nsRunnable> func(new CreateBluetoothScoSocket(aConsumer,
aObjectPath,
aAddress,
aAuth,
aEncrypt));
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {