mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1298 lines
34 KiB
C++
1298 lines
34 KiB
C++
/* -*- 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 "BluetoothHfpManager.h"
|
|
#include "BluetoothProfileController.h"
|
|
#include "BluetoothServiceBluedroid.h"
|
|
#include "BluetoothUtils.h"
|
|
|
|
#include "jsapi.h"
|
|
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIAudioManager.h"
|
|
#include "nsIDOMIccInfo.h"
|
|
#include "nsIDOMMobileConnection.h"
|
|
#include "nsIIccProvider.h"
|
|
#include "nsIMobileConnectionProvider.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsISettingsService.h"
|
|
#include "nsITelephonyProvider.h"
|
|
#include "nsRadioInterfaceLayer.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
|
|
#define AUDIO_VOLUME_BT_SCO_ID "audio.volume.bt_sco"
|
|
|
|
/**
|
|
* Dispatch task with arguments to main thread.
|
|
*/
|
|
#define BT_HF_DISPATCH_MAIN(args...) \
|
|
NS_DispatchToMainThread(new MainThreadTask(args))
|
|
|
|
/**
|
|
* Process bluedroid callbacks with corresponding handlers.
|
|
*/
|
|
#define BT_HF_PROCESS_CB(func, args...) \
|
|
do { \
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpManager); \
|
|
sBluetoothHfpManager->func(args); \
|
|
} while(0)
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::ipc;
|
|
USING_BLUETOOTH_NAMESPACE
|
|
|
|
namespace {
|
|
StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
|
|
static const bthf_interface_t* sBluetoothHfpInterface = nullptr;
|
|
|
|
bool sInShutdown = false;
|
|
|
|
// Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a
|
|
// magic number. The mechanism should be revised once we can get call history.
|
|
static int sWaitingForDialingInterval = 2000; //unit: ms
|
|
|
|
// Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the
|
|
// time window set in Dialer and the extra '0.7' second is a magic number.
|
|
// The mechanism should be revised once we know the exact time at which
|
|
// Dialer stops playing.
|
|
static int sBusyToneInterval = 3700; //unit: ms
|
|
} // anonymous namespace
|
|
|
|
// Main thread task commands
|
|
enum MainThreadTaskCmd {
|
|
NOTIFY_CONN_STATE_CHANGED,
|
|
NOTIFY_DIALER,
|
|
POST_TASK_RESPOND_TO_BLDN,
|
|
POST_TASK_CLOSE_SCO
|
|
};
|
|
|
|
static void
|
|
ConnectionStateCallback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessConnectionState, state, bd_addr);
|
|
}
|
|
|
|
static void
|
|
AudioStateCallback(bthf_audio_state_t state, bt_bdaddr_t* bd_addr)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAudioState, state, bd_addr);
|
|
}
|
|
|
|
static void
|
|
VoiceRecognitionCallback(bthf_vr_state_t state)
|
|
{
|
|
// No support
|
|
}
|
|
|
|
static void
|
|
AnswerCallCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAnswerCall);
|
|
}
|
|
|
|
static void
|
|
HangupCallCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessHangupCall);
|
|
}
|
|
|
|
static void
|
|
VolumeControlCallback(bthf_volume_type_t type, int volume)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessVolumeControl, type, volume);
|
|
}
|
|
|
|
static void
|
|
DialCallCallback(char *number)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessDialCall, number);
|
|
}
|
|
|
|
static void
|
|
DtmfCmdCallback(char dtmf)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessDtmfCmd, dtmf);
|
|
}
|
|
|
|
static void
|
|
NoiceReductionCallback(bthf_nrec_t nrec)
|
|
{
|
|
// No support
|
|
}
|
|
|
|
static void
|
|
AtChldCallback(bthf_chld_type_t chld)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAtChld, chld);
|
|
}
|
|
|
|
static void
|
|
AtCnumCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAtCnum);
|
|
}
|
|
|
|
static void
|
|
AtCindCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAtCind);
|
|
}
|
|
|
|
static void
|
|
AtCopsCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAtCops);
|
|
}
|
|
|
|
static void
|
|
AtClccCallback()
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessAtClcc);
|
|
}
|
|
|
|
static void
|
|
UnknownAtCallback(char *at_string)
|
|
{
|
|
BT_HF_PROCESS_CB(ProcessUnknownAt, at_string);
|
|
}
|
|
|
|
static void
|
|
KeyPressedCallback()
|
|
{
|
|
// No support
|
|
}
|
|
|
|
static bthf_callbacks_t sBluetoothHfpCallbacks = {
|
|
sizeof(sBluetoothHfpCallbacks),
|
|
ConnectionStateCallback,
|
|
AudioStateCallback,
|
|
VoiceRecognitionCallback,
|
|
AnswerCallCallback,
|
|
HangupCallCallback,
|
|
VolumeControlCallback,
|
|
DialCallCallback,
|
|
DtmfCmdCallback,
|
|
NoiceReductionCallback,
|
|
AtChldCallback,
|
|
AtCnumCallback,
|
|
AtCindCallback,
|
|
AtCopsCallback,
|
|
AtClccCallback,
|
|
UnknownAtCallback,
|
|
KeyPressedCallback
|
|
};
|
|
|
|
static bool
|
|
IsValidDtmf(const char aChar) {
|
|
// Valid DTMF: [*#0-9ABCD]
|
|
return (aChar == '*' || aChar == '#') ||
|
|
(aChar >= '0' && aChar <= '9') ||
|
|
(aChar >= 'A' && aChar <= 'D');
|
|
}
|
|
|
|
class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_IMETHOD
|
|
Handle(const nsAString& aName, const JS::Value& aResult)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
|
NS_ENSURE_TRUE(cx, NS_OK);
|
|
|
|
if (!aResult.isNumber()) {
|
|
BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!");
|
|
return NS_OK;
|
|
}
|
|
|
|
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
|
hfp->mCurrentVgs = aResult.toNumber();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD
|
|
HandleError(const nsAString& aName)
|
|
{
|
|
BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'");
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class BluetoothHfpManager::CloseScoTask : public Task
|
|
{
|
|
private:
|
|
void Run() MOZ_OVERRIDE
|
|
{
|
|
MOZ_ASSERT(sBluetoothHfpManager);
|
|
sBluetoothHfpManager->DisconnectSco();
|
|
}
|
|
};
|
|
|
|
class BluetoothHfpManager::RespondToBLDNTask : public Task
|
|
{
|
|
private:
|
|
void Run() MOZ_OVERRIDE
|
|
{
|
|
MOZ_ASSERT(sBluetoothHfpManager);
|
|
|
|
if (!sBluetoothHfpManager->mDialingRequestProcessed) {
|
|
sBluetoothHfpManager->mDialingRequestProcessed = true;
|
|
sBluetoothHfpManager->SendResponse(BTHF_AT_RESPONSE_ERROR);
|
|
}
|
|
}
|
|
};
|
|
|
|
class BluetoothHfpManager::MainThreadTask : public nsRunnable
|
|
{
|
|
public:
|
|
MainThreadTask(const int aCommand,
|
|
const nsAString& aParameter = EmptyString())
|
|
: mCommand(aCommand), mParameter(aParameter)
|
|
{
|
|
}
|
|
|
|
nsresult Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(sBluetoothHfpManager);
|
|
|
|
switch (mCommand) {
|
|
case MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED:
|
|
sBluetoothHfpManager->NotifyConnectionStateChanged(mParameter);
|
|
break;
|
|
case MainThreadTaskCmd::NOTIFY_DIALER:
|
|
sBluetoothHfpManager->NotifyDialer(mParameter);
|
|
break;
|
|
case MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN:
|
|
MessageLoop::current()->
|
|
PostDelayedTask(FROM_HERE, new RespondToBLDNTask(),
|
|
sWaitingForDialingInterval);
|
|
break;
|
|
case MainThreadTaskCmd::POST_TASK_CLOSE_SCO:
|
|
MessageLoop::current()->
|
|
PostDelayedTask(FROM_HERE, new CloseScoTask(),
|
|
sBusyToneInterval);
|
|
break;
|
|
default:
|
|
BT_WARNING("MainThreadTask: Unknown command %d", mCommand);
|
|
break;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
int mCommand;
|
|
nsString mParameter;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS1(BluetoothHfpManager::GetVolumeTask,
|
|
nsISettingsServiceCallback);
|
|
|
|
/**
|
|
* Call
|
|
*/
|
|
Call::Call()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void
|
|
Call::Reset()
|
|
{
|
|
mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
|
|
mDirection = BTHF_CALL_DIRECTION_OUTGOING;
|
|
mNumber.Truncate();
|
|
mType = BTHF_CALL_ADDRTYPE_UNKNOWN;
|
|
}
|
|
|
|
bool
|
|
Call::IsActive()
|
|
{
|
|
return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
|
|
}
|
|
|
|
/**
|
|
* BluetoothHfpManager
|
|
*/
|
|
BluetoothHfpManager::BluetoothHfpManager()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ResetCallArray()
|
|
{
|
|
mCurrentCallArray.Clear();
|
|
// Append a call object at the beginning of mCurrentCallArray since call
|
|
// index from RIL starts at 1.
|
|
Call call;
|
|
mCurrentCallArray.AppendElement(call);
|
|
|
|
if (mPhoneType == PhoneType::CDMA) {
|
|
mCdmaSecondCall.Reset();
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::Reset()
|
|
{
|
|
mReceiveVgsFlag = false;
|
|
mDialingRequestProcessed = true;
|
|
|
|
mConnectionState = BTHF_CONNECTION_STATE_DISCONNECTED;
|
|
mAudioState = BTHF_AUDIO_STATE_DISCONNECTED;
|
|
|
|
// Phone & Device CIND
|
|
ResetCallArray();
|
|
mCallSetupState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
|
|
mBattChg = 5;
|
|
mService = 0;
|
|
mRoam = 0;
|
|
mSignal = 0;
|
|
|
|
mController = nullptr;
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::Init()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
NS_ENSURE_TRUE(InitHfpInterface(), false);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
NS_ENSURE_TRUE(obs, false);
|
|
|
|
if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) ||
|
|
NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
|
|
BT_WARNING("Failed to add observers!");
|
|
return false;
|
|
}
|
|
|
|
hal::RegisterBatteryObserver(this);
|
|
|
|
mListener = new BluetoothRilListener();
|
|
NS_ENSURE_TRUE(mListener->StartListening(), false);
|
|
|
|
nsCOMPtr<nsISettingsService> settings =
|
|
do_GetService("@mozilla.org/settingsService;1");
|
|
NS_ENSURE_TRUE(settings, false);
|
|
|
|
nsCOMPtr<nsISettingsServiceLock> settingsLock;
|
|
nsresult rv = settings->CreateLock(getter_AddRefs(settingsLock));
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
nsRefPtr<GetVolumeTask> callback = new GetVolumeTask();
|
|
rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::InitHfpInterface()
|
|
{
|
|
const bt_interface_t* btInf = GetBluetoothInterface();
|
|
NS_ENSURE_TRUE(btInf, false);
|
|
|
|
if (sBluetoothHfpInterface) {
|
|
sBluetoothHfpInterface->cleanup();
|
|
sBluetoothHfpInterface = nullptr;
|
|
}
|
|
|
|
bthf_interface_t *interface = (bthf_interface_t *)
|
|
btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID);
|
|
NS_ENSURE_TRUE(interface, false);
|
|
|
|
NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
|
|
interface->init(&sBluetoothHfpCallbacks), false);
|
|
sBluetoothHfpInterface = interface;
|
|
|
|
return true;
|
|
}
|
|
|
|
BluetoothHfpManager::~BluetoothHfpManager()
|
|
{
|
|
if (!mListener->StopListening()) {
|
|
BT_WARNING("Failed to stop listening RIL");
|
|
}
|
|
mListener = nullptr;
|
|
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
NS_ENSURE_TRUE_VOID(obs);
|
|
|
|
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
|
|
NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) {
|
|
BT_WARNING("Failed to remove observers!");
|
|
}
|
|
|
|
hal::UnregisterBatteryObserver(this);
|
|
DeinitHfpInterface();
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::DeinitHfpInterface()
|
|
{
|
|
NS_ENSURE_TRUE_VOID(GetBluetoothInterface());
|
|
|
|
if (sBluetoothHfpInterface) {
|
|
sBluetoothHfpInterface->cleanup();
|
|
sBluetoothHfpInterface = nullptr;
|
|
}
|
|
}
|
|
|
|
//static
|
|
BluetoothHfpManager*
|
|
BluetoothHfpManager::Get()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// If sBluetoothHfpManager already exists, exit early
|
|
if (sBluetoothHfpManager) {
|
|
return sBluetoothHfpManager;
|
|
}
|
|
|
|
// If we're in shutdown, don't create a new instance
|
|
NS_ENSURE_FALSE(sInShutdown, nullptr);
|
|
|
|
// Create a new instance, register, and return
|
|
BluetoothHfpManager* manager = new BluetoothHfpManager();
|
|
NS_ENSURE_TRUE(manager->Init(), nullptr);
|
|
|
|
sBluetoothHfpManager = manager;
|
|
return sBluetoothHfpManager;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
BluetoothHfpManager::Observe(nsISupports* aSubject,
|
|
const char* aTopic,
|
|
const PRUnichar* aData)
|
|
{
|
|
if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
|
|
HandleVolumeChanged(nsDependentString(aData));
|
|
} else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
|
HandleShutdown();
|
|
} else {
|
|
MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo)
|
|
{
|
|
// Range of battery level: [0, 1], double
|
|
// Range of CIND::BATTCHG: [0, 5], int
|
|
mBattChg = (int) ceil(aBatteryInfo.level() * 5.0);
|
|
UpdateDeviceCIND();
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessConnectionState(bthf_connection_state_t aState,
|
|
bt_bdaddr_t* aBdAddress)
|
|
{
|
|
BT_LOGR("%s: state %d", __FUNCTION__, aState);
|
|
|
|
mConnectionState = aState;
|
|
|
|
if (aState == BTHF_CONNECTION_STATE_CONNECTED) {
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
|
|
NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
|
|
} else if (aState == BTHF_CONNECTION_STATE_DISCONNECTED) {
|
|
DisconnectSco();
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
|
|
NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
|
|
Reset();
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAudioState(bthf_audio_state_t aState,
|
|
bt_bdaddr_t* aBdAddress)
|
|
{
|
|
BT_LOGR("%s: state %d", __FUNCTION__, aState);
|
|
|
|
mAudioState = aState;
|
|
|
|
if (aState == BTHF_AUDIO_STATE_CONNECTED ||
|
|
aState == BTHF_AUDIO_STATE_DISCONNECTED) {
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
|
|
NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAnswerCall()
|
|
{
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_LITERAL_STRING("ATA"));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessHangupCall()
|
|
{
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_LITERAL_STRING("CHUP"));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessVolumeControl(bthf_volume_type_t aType, int aVolume)
|
|
{
|
|
NS_ENSURE_TRUE_VOID(aVolume >= 0 && aVolume <= 15);
|
|
|
|
if (aType == BTHF_VOLUME_TYPE_MIC) {
|
|
mCurrentVgm = aVolume;
|
|
} else if (aType == BTHF_VOLUME_TYPE_SPK) {
|
|
// Adjust volume by headset
|
|
mReceiveVgsFlag = true;
|
|
NS_ENSURE_TRUE_VOID(aVolume != mCurrentVgs);
|
|
|
|
nsString data;
|
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
|
data.AppendInt(aVolume);
|
|
os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get());
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessDtmfCmd(char aDtmf)
|
|
{
|
|
NS_ENSURE_TRUE_VOID(IsValidDtmf(aDtmf));
|
|
|
|
nsAutoCString message("VTS=");
|
|
message += aDtmf;
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_ConvertUTF8toUTF16(message));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAtChld(bthf_chld_type_t aChld)
|
|
{
|
|
nsAutoCString message("CHLD=");
|
|
message.AppendInt((int)aChld);
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_ConvertUTF8toUTF16(message));
|
|
}
|
|
|
|
void BluetoothHfpManager::ProcessDialCall(char *aNumber)
|
|
{
|
|
nsAutoCString message(aNumber);
|
|
if (message.IsEmpty()) {
|
|
// Redial: BLDN
|
|
mDialingRequestProcessed = false;
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_LITERAL_STRING("BLDN"));
|
|
} else {
|
|
nsAutoCString newMsg("ATD");
|
|
|
|
if (message[0] == '>') {
|
|
// Memory dial: ATD>xxx
|
|
mDialingRequestProcessed = false;
|
|
newMsg += message;
|
|
} else {
|
|
// Dial number: ATDxxx
|
|
int end = message.FindChar(';');
|
|
if (end < 0) {
|
|
BT_WARNING("Couldn't get the number to dial");
|
|
SendResponse(BTHF_AT_RESPONSE_OK);
|
|
return;
|
|
}
|
|
newMsg += nsDependentCSubstring(message, 0, end);
|
|
}
|
|
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
|
|
NS_ConvertUTF8toUTF16(newMsg));
|
|
}
|
|
|
|
if (!mDialingRequestProcessed) {
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAtCnum()
|
|
{
|
|
NS_ENSURE_TRUE_VOID(!mMsisdn.IsEmpty());
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
nsAutoCString message("+CNUM: ,\"");
|
|
message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
|
|
message.AppendLiteral("\",");
|
|
message.AppendInt(BTHF_CALL_ADDRTYPE_UNKNOWN);
|
|
message.AppendLiteral(",,4");
|
|
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->formatted_at_response(message.get()));
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_OK, 0));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAtCind()
|
|
{
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
|
|
int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
|
|
|
|
bt_status_t status = sBluetoothHfpInterface->cind_response(
|
|
mService,
|
|
numActive,
|
|
numHeld,
|
|
ConvertToBthfCallState(mCallSetupState),
|
|
mSignal,
|
|
mRoam,
|
|
mBattChg);
|
|
NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAtCops()
|
|
{
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->cops_response(
|
|
NS_ConvertUTF16toUTF8(mOperatorName).get()));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessAtClcc()
|
|
{
|
|
uint32_t callNumbers = mCurrentCallArray.Length();
|
|
uint32_t i;
|
|
for (i = 1; i < callNumbers; i++) {
|
|
SendCLCC(mCurrentCallArray[i], i);
|
|
}
|
|
|
|
if (!mCdmaSecondCall.mNumber.IsEmpty()) {
|
|
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
|
MOZ_ASSERT(i == 2);
|
|
|
|
SendCLCC(mCdmaSecondCall, 2);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ProcessUnknownAt(char *aAtString)
|
|
{
|
|
BT_LOGR("%s: [%s]", __FUNCTION__, aAtString);
|
|
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_ERROR, 0));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::NotifyConnectionStateChanged(const nsAString& aType)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Notify Gecko observers
|
|
nsCOMPtr<nsIObserverService> obs =
|
|
do_GetService("@mozilla.org/observer-service;1");
|
|
NS_ENSURE_TRUE_VOID(obs);
|
|
|
|
if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(),
|
|
mDeviceAddress.get()))) {
|
|
BT_WARNING("Failed to notify observsers!");
|
|
}
|
|
|
|
// Dispatch an event of status change
|
|
bool status;
|
|
nsAutoString eventName;
|
|
if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
|
|
status = IsConnected();
|
|
eventName.AssignLiteral(HFP_STATUS_CHANGED_ID);
|
|
} else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
|
|
status = IsScoConnected();
|
|
eventName.AssignLiteral(SCO_STATUS_CHANGED_ID);
|
|
} else {
|
|
MOZ_ASSERT(false);
|
|
return;
|
|
}
|
|
|
|
DispatchStatusChangedEvent(eventName, mDeviceAddress, status);
|
|
|
|
// Notify profile controller
|
|
if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
|
|
if (IsConnected()) {
|
|
MOZ_ASSERT(mListener);
|
|
|
|
// Enumerate current calls
|
|
mListener->EnumerateCalls();
|
|
|
|
OnConnect(EmptyString());
|
|
} else if (mConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) {
|
|
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
|
OnDisconnect(EmptyString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
|
|
{
|
|
BluetoothValue v;
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
NS_NAMED_LITERAL_STRING(type, "bluetooth-dialer-command");
|
|
NS_NAMED_LITERAL_STRING(name, "command");
|
|
|
|
v = nsString(aCommand);
|
|
parameters.AppendElement(BluetoothNamedValue(name, v));
|
|
|
|
if (!BroadcastSystemMessage(type, parameters)) {
|
|
BT_WARNING("Failed to broadcast system message to dialer");
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// The string that we're interested in will be a JSON string that looks like:
|
|
// {"key":"volumeup", "value":10}
|
|
// {"key":"volumedown", "value":2}
|
|
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
|
NS_ENSURE_TRUE_VOID(cx);
|
|
|
|
JS::Rooted<JS::Value> val(cx);
|
|
NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
|
|
NS_ENSURE_TRUE_VOID(val.isObject());
|
|
|
|
JS::Rooted<JSObject*> obj(cx, &val.toObject());
|
|
JS::Rooted<JS::Value> key(cx);
|
|
if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
|
|
return;
|
|
}
|
|
|
|
bool match;
|
|
if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
|
|
!match) {
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> value(cx);
|
|
if (!JS_GetProperty(cx, obj, "value", &value) ||
|
|
!value.isNumber()) {
|
|
return;
|
|
}
|
|
|
|
mCurrentVgs = value.toNumber();
|
|
|
|
// Adjust volume by headset and we don't have to send volume back to headset
|
|
if (mReceiveVgsFlag) {
|
|
mReceiveVgsFlag = false;
|
|
return;
|
|
}
|
|
|
|
// Only send volume back when there's a connected headset
|
|
if (IsConnected()) {
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->volume_control(BTHF_VOLUME_TYPE_SPK,
|
|
mCurrentVgs));
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::HandleVoiceConnectionChanged()
|
|
{
|
|
nsCOMPtr<nsIMobileConnectionProvider> connection =
|
|
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
|
NS_ENSURE_TRUE_VOID(connection);
|
|
|
|
nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
|
|
connection->GetVoiceConnectionInfo(0, getter_AddRefs(voiceInfo));
|
|
NS_ENSURE_TRUE_VOID(voiceInfo);
|
|
|
|
// Roam
|
|
bool roaming;
|
|
voiceInfo->GetRoaming(&roaming);
|
|
mRoam = (roaming) ? 1 : 0;
|
|
|
|
// Service
|
|
nsString regState;
|
|
voiceInfo->GetState(regState);
|
|
mService = (regState.EqualsLiteral("registered")) ? 1 : 0;
|
|
|
|
// Signal
|
|
JS::Value value;
|
|
voiceInfo->GetRelSignalStrength(&value);
|
|
if (!value.isNumber()) {
|
|
BT_WARNING("Failed to get relSignalStrength in BluetoothHfpManager");
|
|
return;
|
|
}
|
|
mSignal = (int)ceil(value.toNumber() / 20.0);
|
|
|
|
UpdateDeviceCIND();
|
|
|
|
// Operator name
|
|
nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
|
|
voiceInfo->GetNetwork(getter_AddRefs(network));
|
|
NS_ENSURE_TRUE_VOID(network);
|
|
network->GetLongName(mOperatorName);
|
|
|
|
// According to GSM 07.07, "<format> indicates if the format is alphanumeric
|
|
// or numeric; long alphanumeric format can be upto 16 characters long and
|
|
// short format up to 8 characters (refer GSM MoU SE.13 [9])..."
|
|
// However, we found that the operator name may sometimes be longer than 16
|
|
// characters. After discussion, we decided to fix this here but not in RIL
|
|
// or modem.
|
|
//
|
|
// Please see Bug 871366 for more information.
|
|
if (mOperatorName.Length() > 16) {
|
|
BT_WARNING("The operator name was longer than 16 characters. We cut it.");
|
|
mOperatorName.Left(mOperatorName, 16);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::HandleIccInfoChanged()
|
|
{
|
|
nsCOMPtr<nsIIccProvider> icc =
|
|
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
|
NS_ENSURE_TRUE_VOID(icc);
|
|
|
|
nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
|
|
icc->GetIccInfo(0, getter_AddRefs(iccInfo));
|
|
NS_ENSURE_TRUE_VOID(iccInfo);
|
|
|
|
nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
|
|
NS_ENSURE_TRUE_VOID(gsmIccInfo);
|
|
gsmIccInfo->GetMsisdn(mMsisdn);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::HandleShutdown()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
sInShutdown = true;
|
|
Disconnect(nullptr);
|
|
DisconnectSco();
|
|
sBluetoothHfpManager = nullptr;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::SendCLCC(Call& aCall, int aIndex)
|
|
{
|
|
NS_ENSURE_TRUE_VOID(aCall.mState !=
|
|
nsITelephonyProvider::CALL_STATE_DISCONNECTED);
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
bthf_call_state_t callState = ConvertToBthfCallState(aCall.mState);
|
|
|
|
if (mPhoneType == PhoneType::CDMA && aIndex == 1 && aCall.IsActive()) {
|
|
callState = (mCdmaSecondCall.IsActive()) ? BTHF_CALL_STATE_HELD :
|
|
BTHF_CALL_STATE_ACTIVE;
|
|
}
|
|
|
|
bt_status_t status = sBluetoothHfpInterface->clcc_response(
|
|
aIndex,
|
|
aCall.mDirection,
|
|
callState,
|
|
BTHF_CALL_TYPE_VOICE,
|
|
BTHF_CALL_MPTY_TYPE_SINGLE,
|
|
NS_ConvertUTF16toUTF8(aCall.mNumber).get(),
|
|
aCall.mType);
|
|
NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::SendLine(const char* aMessage)
|
|
{
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->formatted_at_response(aMessage));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::SendResponse(bthf_at_response_t aResponseCode)
|
|
{
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->at_response(aResponseCode, 0));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex, bool aSend)
|
|
{
|
|
// Update callsetup state
|
|
uint16_t callState = mCurrentCallArray[aCallIndex].mState;
|
|
if (callState == nsITelephonyProvider::CALL_STATE_CONNECTED ||
|
|
callState == nsITelephonyProvider::CALL_STATE_HELD) {
|
|
mCallSetupState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
|
|
} else {
|
|
mCallSetupState = callState;
|
|
}
|
|
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
|
|
int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
|
|
bthf_call_state_t bthfCallState = ConvertToBthfCallState(mCallSetupState);
|
|
nsAutoCString number = NS_ConvertUTF16toUTF8(mCurrentCallArray[aCallIndex].mNumber);
|
|
bthf_call_addrtype_t type = mCurrentCallArray[aCallIndex].mType;
|
|
|
|
BT_LOGR("%s: [%d] state %d => BTHF: active[%d] held[%d] state[%d]",
|
|
__FUNCTION__, aCallIndex, callState, numActive, numHeld, bthfCallState);
|
|
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->phone_state_change(
|
|
numActive, numHeld, bthfCallState, number.get(), type));
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::UpdateDeviceCIND()
|
|
{
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->device_status_notification(
|
|
(bthf_network_state_t) mService,
|
|
(bthf_service_type_t) mRoam,
|
|
mSignal,
|
|
mBattChg));
|
|
}
|
|
|
|
uint32_t
|
|
BluetoothHfpManager::FindFirstCall(uint16_t aState)
|
|
{
|
|
uint32_t callLength = mCurrentCallArray.Length();
|
|
|
|
for (uint32_t i = 1; i < callLength; ++i) {
|
|
if (mCurrentCallArray[i].mState == aState) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t
|
|
BluetoothHfpManager::GetNumberOfCalls(uint16_t aState)
|
|
{
|
|
uint32_t num = 0;
|
|
uint32_t callLength = mCurrentCallArray.Length();
|
|
|
|
for (uint32_t i = 1; i < callLength; ++i) {
|
|
if (mCurrentCallArray[i].mState == aState) {
|
|
++num;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
bthf_call_state_t
|
|
BluetoothHfpManager::ConvertToBthfCallState(int aCallState)
|
|
{
|
|
bthf_call_state_t state;
|
|
|
|
// Refer to AOSP BluetoothPhoneService.convertCallState
|
|
if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) {
|
|
state = (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) ?
|
|
BTHF_CALL_STATE_WAITING :
|
|
BTHF_CALL_STATE_INCOMING;
|
|
} else if (aCallState == nsITelephonyProvider::CALL_STATE_DIALING) {
|
|
state = BTHF_CALL_STATE_DIALING;
|
|
} else if (aCallState == nsITelephonyProvider::CALL_STATE_ALERTING) {
|
|
state = BTHF_CALL_STATE_ALERTING;
|
|
} else if (aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED) {
|
|
state = BTHF_CALL_STATE_ACTIVE;
|
|
} else if (aCallState == nsITelephonyProvider::CALL_STATE_HELD) {
|
|
state = BTHF_CALL_STATE_HELD;
|
|
} else { // disconnected
|
|
state = BTHF_CALL_STATE_IDLE;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
|
|
uint16_t aCallState,
|
|
const nsAString& aError,
|
|
const nsAString& aNumber,
|
|
const bool aIsOutgoing,
|
|
bool aSend)
|
|
{
|
|
if (!IsConnected()) {
|
|
// Normal case. No need to print out warnings.
|
|
return;
|
|
}
|
|
|
|
while (aCallIndex >= mCurrentCallArray.Length()) {
|
|
Call call;
|
|
mCurrentCallArray.AppendElement(call);
|
|
}
|
|
|
|
mCurrentCallArray[aCallIndex].mState = aCallState;
|
|
mCurrentCallArray[aCallIndex].mNumber = aNumber;
|
|
mCurrentCallArray[aCallIndex].mDirection = (aIsOutgoing) ?
|
|
BTHF_CALL_DIRECTION_OUTGOING :
|
|
BTHF_CALL_DIRECTION_INCOMING;
|
|
// Same logic as implementation in ril_worker.js
|
|
if (aNumber.Length() && aNumber[0] == '+') {
|
|
mCurrentCallArray[aCallIndex].mType = BTHF_CALL_ADDRTYPE_INTERNATIONAL;
|
|
}
|
|
|
|
UpdatePhoneCIND(aCallIndex, aSend);
|
|
|
|
switch (aCallState) {
|
|
case nsITelephonyProvider::CALL_STATE_DIALING:
|
|
// We've send Dialer a dialing request and this is the response.
|
|
if (!mDialingRequestProcessed) {
|
|
SendResponse(BTHF_AT_RESPONSE_OK);
|
|
mDialingRequestProcessed = true;
|
|
}
|
|
break;
|
|
|
|
case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
|
|
// -1 is necessary because call 0 is an invalid (padding) call object.
|
|
if (mCurrentCallArray.Length() - 1 ==
|
|
GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) {
|
|
// In order to let user hear busy tone via connected Bluetooth headset,
|
|
// we postpone the timing of dropping SCO.
|
|
if (aError.Equals(NS_LITERAL_STRING("BusyError"))) {
|
|
// FIXME: UpdatePhoneCIND later since it causes SCO close but
|
|
// Dialer is still playing busy tone via HF.
|
|
BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_CLOSE_SCO);
|
|
}
|
|
|
|
ResetCallArray();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
PhoneType
|
|
BluetoothHfpManager::GetPhoneType(const nsAString& aType)
|
|
{
|
|
// FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
|
|
if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
|
|
aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
|
|
aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
|
|
aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
|
|
return PhoneType::GSM;
|
|
} else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
|
|
aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
|
|
aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
|
|
return PhoneType::CDMA;
|
|
}
|
|
|
|
return PhoneType::NONE;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
|
|
{
|
|
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
|
|
|
// Always regard second call as incoming call since v1.2 RIL
|
|
// doesn't support outgoing second call in CDMA.
|
|
mCdmaSecondCall.mDirection = BTHF_CALL_DIRECTION_INCOMING;
|
|
|
|
mCdmaSecondCall.mNumber = aNumber;
|
|
if (aNumber[0] == '+') {
|
|
mCdmaSecondCall.mType = BTHF_CALL_ADDRTYPE_INTERNATIONAL;
|
|
}
|
|
|
|
// FIXME: check CDMA + bluedroid
|
|
//UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::AnswerWaitingCall()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
|
|
|
// Pick up second call. First call is held now.
|
|
mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED;
|
|
// FIXME: check CDMA + bluedroid
|
|
//UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
|
|
|
|
//sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
|
|
//SendCommand("+CIEV: ", CINDType::CALLHELD);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::IgnoreWaitingCall()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
|
|
|
mCdmaSecondCall.Reset();
|
|
// FIXME: check CDMA + bluedroid
|
|
//UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::ToggleCalls()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
|
|
|
// Toggle acitve and held calls
|
|
mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
|
|
nsITelephonyProvider::CALL_STATE_HELD :
|
|
nsITelephonyProvider::CALL_STATE_CONNECTED;
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::ConnectSco()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
NS_ENSURE_TRUE(!sInShutdown, false);
|
|
NS_ENSURE_TRUE(IsConnected() && !IsScoConnected(), false);
|
|
NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
|
|
|
|
bt_bdaddr_t deviceBdAddress;
|
|
StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
|
|
NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->connect_audio(&deviceBdAddress), false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::DisconnectSco()
|
|
{
|
|
NS_ENSURE_TRUE(IsScoConnected(), false);
|
|
NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
|
|
|
|
bt_bdaddr_t deviceBdAddress;
|
|
StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
|
|
NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->disconnect_audio(&deviceBdAddress), false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::IsScoConnected()
|
|
{
|
|
return (mAudioState == BTHF_AUDIO_STATE_CONNECTED);
|
|
}
|
|
|
|
bool
|
|
BluetoothHfpManager::IsConnected()
|
|
{
|
|
return (mConnectionState == BTHF_CONNECTION_STATE_SLC_CONNECTED ||
|
|
mConnectionState == BTHF_CONNECTION_STATE_CONNECTED);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::Connect(const nsAString& aDeviceAddress,
|
|
BluetoothProfileController* aController)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aController && !mController);
|
|
|
|
if (sInShutdown) {
|
|
aController->OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
|
return;
|
|
}
|
|
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
bt_bdaddr_t deviceBdAddress;
|
|
StringToBdAddressType(aDeviceAddress, &deviceBdAddress);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->connect(&deviceBdAddress));
|
|
|
|
mDeviceAddress = aDeviceAddress;
|
|
mController = aController;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
|
|
|
|
bt_bdaddr_t deviceBdAddress;
|
|
StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
|
|
NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
|
|
sBluetoothHfpInterface->disconnect(&deviceBdAddress));
|
|
|
|
MOZ_ASSERT(!mController);
|
|
mController = aController;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
/**
|
|
* On the one hand, notify the controller that we've done for outbound
|
|
* connections. On the other hand, we do nothing for inbound connections.
|
|
*/
|
|
NS_ENSURE_TRUE_VOID(mController);
|
|
|
|
mController->OnConnect(aErrorStr);
|
|
mController = nullptr;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
/**
|
|
* On the one hand, notify the controller that we've done for outbound
|
|
* connections. On the other hand, we do nothing for inbound connections.
|
|
*/
|
|
NS_ENSURE_TRUE_VOID(mController);
|
|
|
|
mController->OnDisconnect(aErrorStr);
|
|
mController = nullptr;
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
|
|
{
|
|
// Bluedroid handles this part
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
|
|
const nsAString& aServiceUuid,
|
|
int aChannel)
|
|
{
|
|
// Bluedroid handles this part
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
void
|
|
BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress)
|
|
{
|
|
aDeviceAddress = mDeviceAddress;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(BluetoothHfpManager, nsIObserver)
|