зеркало из https://github.com/mozilla/gecko-dev.git
Bug 800695 - Final version: Audio route is set to sco before connecting to a bluetooth headset, r=qdot, a=blocking-basecamp
This commit is contained in:
Родитель
eb8b19c829
Коммит
7805d67b55
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче