gecko/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp

2832 lines
83 KiB
C++

/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "BluetoothServiceBluedroid.h"
#include "BluetoothA2dpManager.h"
#ifndef MOZ_B2G_BT_API_V1
#include "BluetoothGattManager.h"
#else
// TODO: Support GATT
#endif
#include "BluetoothHfpManager.h"
#ifndef MOZ_B2G_BT_API_V1
#include "BluetoothHidManager.h"
#else
// TODO: Support HID
#endif
#include "BluetoothOppManager.h"
#include "BluetoothPbapManager.h"
#include "BluetoothProfileController.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothUtils.h"
#include "BluetoothUuid.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/ipc/SocketBase.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/unused.h"
#ifndef MOZ_B2G_BT_API_V1
#include "nsDataHashtable.h"
#endif
#define ENSURE_BLUETOOTH_IS_READY(runnable, result) \
do { \
if (!sBtInterface || !IsEnabled()) { \
DispatchReplyError(runnable, \
NS_LITERAL_STRING("Bluetooth is not ready")); \
return result; \
} \
} while(0)
#define ENSURE_BLUETOOTH_IS_READY_VOID(runnable) \
do { \
if (!sBtInterface || !IsEnabled()) { \
DispatchReplyError(runnable, \
NS_LITERAL_STRING("Bluetooth is not ready")); \
return; \
} \
} while(0)
#ifndef MOZ_B2G_BT_API_V1
#define ENSURE_GATT_MGR_IS_READY_VOID(gatt, runnable) \
do { \
if (!gatt) { \
DispatchReplyError(runnable, \
NS_LITERAL_STRING("GattManager is not ready")); \
return; \
} \
} while(0)
#else
// Missing in Bluetooth v1
#endif
#ifndef MOZ_B2G_BT_API_V1
// Missing in Bluetooth v2
#else
// Audio: Major service class = 0x100 (Bit 21 is set)
#define SET_AUDIO_BIT(cod) (cod |= 0x200000)
// Rendering: Major service class = 0x20 (Bit 18 is set)
#define SET_RENDERING_BIT(cod) (cod |= 0x40000)
#endif
using namespace mozilla;
using namespace mozilla::ipc;
USING_BLUETOOTH_NAMESPACE
static nsString sAdapterBdAddress;
static nsString sAdapterBdName;
static bool sAdapterDiscoverable(false);
static bool sAdapterDiscovering(false);
// InfallibleTArray is an alias for nsTArray.
static InfallibleTArray<nsString> sAdapterBondedAddressArray;
static BluetoothInterface* sBtInterface;
static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
static nsTArray<int> sRequestedDeviceCountArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
static bool sIsRestart(false);
static bool sIsFirstTimeToggleOffBt(false);
#ifndef MOZ_B2G_BT_API_V1
static bool sAdapterEnabled(false);
// Use a static hash table to keep the name of remote device during the pairing
// procedure. In this manner, BT service and adapter can get the name of paired
// device name when bond state changed.
// The hash Key is BD address, the Value is remote BD name.
static nsDataHashtable<nsStringHashKey, nsString> sPairingNameTable;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sFetchUuidsRunnableArray;
#else
// Missing in Bluetooth v1
#endif
#ifndef MOZ_B2G_BT_API_V1
// Missing in Bluetooth v2
#else
static uint32_t sAdapterDiscoverableTimeout(0);
#endif
/*
* Static methods
*/
ControlPlayStatus
BluetoothServiceBluedroid::PlayStatusStringToControlPlayStatus(
const nsAString& aPlayStatus)
{
ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
if (aPlayStatus.EqualsLiteral("STOPPED")) {
playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
} else if (aPlayStatus.EqualsLiteral("PLAYING")) {
playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
} else if (aPlayStatus.EqualsLiteral("PAUSED")) {
playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED;
} else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) {
playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK;
} else if (aPlayStatus.EqualsLiteral("REV_SEEK")) {
playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK;
} else if (aPlayStatus.EqualsLiteral("ERROR")) {
playStatus = ControlPlayStatus::PLAYSTATUS_ERROR;
}
return playStatus;
}
#ifndef MOZ_B2G_BT_API_V1
// Missing in Bluetooth v2
#else
void
BluetoothServiceBluedroid::ClassToIcon(uint32_t aClass, nsAString& aRetIcon)
{
switch ((aClass & 0x1f00) >> 8) {
case 0x01:
aRetIcon.AssignLiteral("computer");
break;
case 0x02:
switch ((aClass & 0xfc) >> 2) {
case 0x01:
case 0x02:
case 0x03:
case 0x05:
aRetIcon.AssignLiteral("phone");
break;
case 0x04:
aRetIcon.AssignLiteral("modem");
break;
}
break;
case 0x03:
aRetIcon.AssignLiteral("network-wireless");
break;
case 0x04:
switch ((aClass & 0xfc) >> 2) {
case 0x01:
case 0x02:
case 0x06:
aRetIcon.AssignLiteral("audio-card");
break;
case 0x0b:
case 0x0c:
case 0x0d:
aRetIcon.AssignLiteral("camera-video");
break;
default:
aRetIcon.AssignLiteral("audio-card");
break;
}
break;
case 0x05:
switch ((aClass & 0xc0) >> 6) {
case 0x00:
switch ((aClass && 0x1e) >> 2) {
case 0x01:
case 0x02:
aRetIcon.AssignLiteral("input-gaming");
break;
}
break;
case 0x01:
aRetIcon.AssignLiteral("input-keyboard");
break;
case 0x02:
switch ((aClass && 0x1e) >> 2) {
case 0x05:
aRetIcon.AssignLiteral("input-tablet");
break;
default:
aRetIcon.AssignLiteral("input-mouse");
break;
}
}
break;
case 0x06:
if (aClass & 0x80) {
aRetIcon.AssignLiteral("printer");
break;
}
if (aClass & 0x20) {
aRetIcon.AssignLiteral("camera-photo");
break;
}
break;
}
if (aRetIcon.IsEmpty()) {
if (HAS_AUDIO(aClass)) {
/**
* Property 'Icon' may be missed due to CoD of major class is TOY(0x08).
* But we need to assign Icon as audio-card if service class is 'Audio'.
* This is for PTS test case TC_AG_COD_BV_02_I. As HFP specification
* defines that service class is 'Audio' can be considered as HFP HF.
*/
aRetIcon.AssignLiteral("audio-card");
} else {
BT_LOGR("No icon to match class: %x", aClass);
}
}
}
static void
ReplyStatusError(BluetoothReplyRunnable* aBluetoothReplyRunnable,
BluetoothStatus aStatusCode, const nsAString& aCustomMsg)
{
MOZ_ASSERT(aBluetoothReplyRunnable, "Reply runnable is nullptr");
BT_LOGR("error code(%d)", aStatusCode);
nsAutoString replyError;
replyError.Assign(aCustomMsg);
if (aStatusCode == STATUS_BUSY) {
replyError.AppendLiteral(":BT_STATUS_BUSY");
} else if (aStatusCode == STATUS_NOT_READY) {
replyError.AppendLiteral(":BT_STATUS_NOT_READY");
} else if (aStatusCode == STATUS_DONE) {
replyError.AppendLiteral(":BT_STATUS_DONE");
} else if (aStatusCode == STATUS_AUTH_FAILURE) {
replyError.AppendLiteral(":BT_STATUS_AUTH_FAILURE");
} else if (aStatusCode == STATUS_RMT_DEV_DOWN) {
replyError.AppendLiteral(":BT_STATUS_RMT_DEV_DOWN");
} else if (aStatusCode == STATUS_FAIL) {
replyError.AppendLiteral(":BT_STATUS_FAIL");
}
DispatchReplyError(aBluetoothReplyRunnable, replyError);
}
#endif
class BluetoothServiceBluedroid::EnableResultHandler final
: public BluetoothResultHandler
{
public:
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BluetoothInterface::Enable failed: %d", aStatus);
BluetoothService::AcknowledgeToggleBt(false);
}
};
/* |ProfileInitResultHandler| collects the results of all profile
* result handlers and calls |Proceed| after all results handlers
* have been run.
*/
class BluetoothServiceBluedroid::ProfileInitResultHandler final
: public BluetoothProfileResultHandler
{
public:
ProfileInitResultHandler(unsigned char aNumProfiles)
: mNumProfiles(aNumProfiles)
{
MOZ_ASSERT(mNumProfiles);
}
void Init() override
{
if (!(--mNumProfiles)) {
Proceed();
}
}
void OnError(nsresult aResult) override
{
if (!(--mNumProfiles)) {
Proceed();
}
}
private:
void Proceed() const
{
sBtInterface->Enable(new EnableResultHandler());
}
unsigned char mNumProfiles;
};
class BluetoothServiceBluedroid::InitResultHandler final
: public BluetoothResultHandler
{
public:
void Init() override
{
static void (* const sInitManager[])(BluetoothProfileResultHandler*) = {
BluetoothHfpManager::InitHfpInterface,
BluetoothA2dpManager::InitA2dpInterface,
#ifndef MOZ_B2G_BT_API_V1
BluetoothGattManager::InitGattInterface
#else
// Missing in Bluetooth v1
#endif
};
MOZ_ASSERT(NS_IsMainThread());
// Register all the bluedroid callbacks before enable() get called
// It is required to register a2dp callbacks before a2dp media task starts up.
// If any interface cannot be initialized, turn on bluetooth core anyway.
nsRefPtr<ProfileInitResultHandler> res =
new ProfileInitResultHandler(MOZ_ARRAY_LENGTH(sInitManager));
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sInitManager); ++i) {
sInitManager[i](res);
}
}
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BluetoothInterface::Init failed: %d", aStatus);
sBtInterface = nullptr;
BluetoothService::AcknowledgeToggleBt(false);
}
};
nsresult
BluetoothServiceBluedroid::StartGonkBluetooth()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
if (bs->IsEnabled()) {
// Keep current enable status
BluetoothService::AcknowledgeToggleBt(true);
return NS_OK;
}
sBtInterface->Init(reinterpret_cast<BluetoothServiceBluedroid*>(bs),
new InitResultHandler());
return NS_OK;
}
class BluetoothServiceBluedroid::DisableResultHandler final
: public BluetoothResultHandler
{
public:
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BluetoothInterface::Disable failed: %d", aStatus);
// Always make progress; even on failures
BluetoothService::AcknowledgeToggleBt(false);
}
};
nsresult
BluetoothServiceBluedroid::StopGonkBluetooth()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
if (!bs->IsEnabled()) {
// Keep current enable status
BluetoothService::AcknowledgeToggleBt(false);
return NS_OK;
}
sBtInterface->Disable(new DisableResultHandler());
return NS_OK;
}
/*
* Member functions
*/
BluetoothServiceBluedroid::BluetoothServiceBluedroid()
{
sBtInterface = BluetoothInterface::GetInstance();
if (!sBtInterface) {
BT_LOGR("Error! Failed to get instance of bluetooth interface");
}
}
BluetoothServiceBluedroid::~BluetoothServiceBluedroid()
{
}
#ifndef MOZ_B2G_BT_API_V1
nsresult
BluetoothServiceBluedroid::StartInternal(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// aRunnable will be a nullptr while startup
if (aRunnable) {
sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
}
nsresult ret = StartGonkBluetooth();
if (NS_FAILED(ret)) {
BluetoothService::AcknowledgeToggleBt(false);
// Reject Promise
if (aRunnable) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("StartBluetoothError"));
sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
}
BT_LOGR("Error");
}
return ret;
}
nsresult
BluetoothServiceBluedroid::StopInternal(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
static BluetoothProfileManagerBase* sProfiles[] = {
BluetoothHfpManager::Get(),
BluetoothA2dpManager::Get(),
BluetoothOppManager::Get(),
BluetoothPbapManager::Get(),
BluetoothHidManager::Get()
};
// Disconnect all connected profiles
for (uint8_t i = 0; i < MOZ_ARRAY_LENGTH(sProfiles); i++) {
nsCString profileName;
sProfiles[i]->GetName(profileName);
if (NS_WARN_IF(!sProfiles[i])) {
BT_LOGR("Profile manager [%s] is null", profileName.get());
return NS_ERROR_FAILURE;
}
if (sProfiles[i]->IsConnected()) {
sProfiles[i]->Disconnect(nullptr);
} else if (!profileName.EqualsLiteral("OPP") &&
!profileName.EqualsLiteral("PBAP")) {
sProfiles[i]->Reset();
}
}
// aRunnable will be a nullptr during starup and shutdown
if (aRunnable) {
sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
}
nsresult ret = StopGonkBluetooth();
if (NS_FAILED(ret)) {
BluetoothService::AcknowledgeToggleBt(true);
// Reject Promise
if (aRunnable) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("StopBluetoothError"));
sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
}
BT_LOGR("Error");
}
return ret;
}
#else
nsresult
BluetoothServiceBluedroid::StartInternal()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult ret = StartGonkBluetooth();
if (NS_FAILED(ret)) {
BluetoothService::AcknowledgeToggleBt(false);
BT_LOGR("Error");
}
return ret;
}
nsresult
BluetoothServiceBluedroid::StopInternal()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult ret = StopGonkBluetooth();
if (NS_FAILED(ret)) {
BluetoothService::AcknowledgeToggleBt(true);
BT_LOGR("Error");
}
return ret;
}
#endif
//
// GATT Client
//
#ifndef MOZ_B2G_BT_API_V1
void
BluetoothServiceBluedroid::StartLeScanInternal(
const nsTArray<nsString>& aServiceUuids,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->StartLeScan(aServiceUuids, aRunnable);
}
void
BluetoothServiceBluedroid::StopLeScanInternal(
const nsAString& aScanUuid,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->StopLeScan(aScanUuid, aRunnable);
}
void
BluetoothServiceBluedroid::ConnectGattClientInternal(
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->Connect(aAppUuid, aDeviceAddress, aRunnable);
}
void
BluetoothServiceBluedroid::DisconnectGattClientInternal(
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->Disconnect(aAppUuid, aDeviceAddress, aRunnable);
}
void
BluetoothServiceBluedroid::DiscoverGattServicesInternal(
const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->Discover(aAppUuid, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientStartNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->RegisterNotifications(aAppUuid, aServId, aCharId, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientStopNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->DeregisterNotifications(aAppUuid, aServId, aCharId, aRunnable);
}
void
BluetoothServiceBluedroid::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->UnregisterClient(aClientIf, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientReadRemoteRssiInternal(
int aClientIf, const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->ReadRemoteRssi(aClientIf, aDeviceAddress, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientReadCharacteristicValueInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharacteristicId,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->ReadCharacteristicValue(aAppUuid, aServiceId, aCharacteristicId,
aRunnable);
}
void
BluetoothServiceBluedroid::GattClientWriteCharacteristicValueInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharacteristicId,
const BluetoothGattWriteType& aWriteType,
const nsTArray<uint8_t>& aValue,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->WriteCharacteristicValue(aAppUuid, aServiceId, aCharacteristicId,
aWriteType, aValue, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientReadDescriptorValueInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharacteristicId,
const BluetoothGattId& aDescriptorId,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->ReadDescriptorValue(aAppUuid, aServiceId, aCharacteristicId,
aDescriptorId, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientWriteDescriptorValueInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharacteristicId,
const BluetoothGattId& aDescriptorId,
const nsTArray<uint8_t>& aValue,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->WriteDescriptorValue(aAppUuid, aServiceId, aCharacteristicId,
aDescriptorId, aValue, aRunnable);
}
#else
// Missing in Bluetooth v1
#endif
#ifndef MOZ_B2G_BT_API_V1
nsresult
BluetoothServiceBluedroid::GetAdaptersInternal(
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
/**
* Wrap BluetoothValue =
* BluetoothNamedValue[]
* |
* |__ BluetoothNamedValue =
* | {"Adapter", BluetoothValue = BluetoothNamedValue[]}
* |
* |__ BluetoothNamedValue =
* | {"Adapter", BluetoothValue = BluetoothNamedValue[]}
* ...
*/
BluetoothValue adaptersProperties = InfallibleTArray<BluetoothNamedValue>();
uint32_t numAdapters = 1; // Bluedroid supports single adapter only
for (uint32_t i = 0; i < numAdapters; i++) {
BluetoothValue properties = InfallibleTArray<BluetoothNamedValue>();
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"State", sAdapterEnabled);
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"Address", sAdapterBdAddress);
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"Name", sAdapterBdName);
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"Discoverable", sAdapterDiscoverable);
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"Discovering", sAdapterDiscovering);
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
"PairedDevices", sAdapterBondedAddressArray);
BT_APPEND_NAMED_VALUE(adaptersProperties.get_ArrayOfBluetoothNamedValue(),
"Adapter", properties);
}
DispatchReplySuccess(aRunnable, adaptersProperties);
return NS_OK;
}
#else
nsresult
BluetoothServiceBluedroid::GetDefaultAdapterPathInternal(
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// Since Atomic<*> is not acceptable for BT_APPEND_NAMED_VALUE(),
// create another variable to store data.
bool discoverable = sAdapterDiscoverable;
uint32_t discoverableTimeout = sAdapterDiscoverableTimeout;
BluetoothValue v = InfallibleTArray<BluetoothNamedValue>();
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Address", sAdapterBdAddress);
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Name", sAdapterBdName);
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Discoverable", discoverable);
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"DiscoverableTimeout", discoverableTimeout);
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Devices", sAdapterBondedAddressArray);
DispatchReplySuccess(aRunnable, v);
return NS_OK;
}
#endif
class BluetoothServiceBluedroid::GetRemoteDevicePropertiesResultHandler
final
: public BluetoothResultHandler
{
public:
GetRemoteDevicePropertiesResultHandler(const nsAString& aDeviceAddress)
: mDeviceAddress(aDeviceAddress)
{ }
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("GetRemoteDeviceProperties(%s) failed: %d",
NS_ConvertUTF16toUTF8(mDeviceAddress).get(), aStatus);
/* dispatch result after final pending operation */
if (--sRequestedDeviceCountArray[0] == 0) {
if (!sGetDeviceRunnableArray.IsEmpty()) {
#ifndef MOZ_B2G_BT_API_V1
DispatchReplyError(sGetDeviceRunnableArray[0],
NS_LITERAL_STRING("GetRemoteDeviceProperties failed"));
#else
DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
#endif
sGetDeviceRunnableArray.RemoveElementAt(0);
}
sRequestedDeviceCountArray.RemoveElementAt(0);
sRemoteDevicesPack.Clear();
}
}
private:
nsString mDeviceAddress;
};
nsresult
BluetoothServiceBluedroid::GetConnectedDevicePropertiesInternal(
uint16_t aServiceUuid, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
BluetoothProfileManagerBase* profile =
BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
if (!profile) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
return NS_OK;
}
nsTArray<nsString> deviceAddresses;
if (profile->IsConnected()) {
nsString address;
profile->GetAddress(address);
deviceAddresses.AppendElement(address);
}
int requestedDeviceCount = deviceAddresses.Length();
if (requestedDeviceCount == 0) {
InfallibleTArray<BluetoothNamedValue> emptyArr;
DispatchReplySuccess(aRunnable, emptyArr);
return NS_OK;
}
sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
sGetDeviceRunnableArray.AppendElement(aRunnable);
for (int i = 0; i < requestedDeviceCount; i++) {
// Retrieve all properties of devices
sBtInterface->GetRemoteDeviceProperties(deviceAddresses[i],
new GetRemoteDevicePropertiesResultHandler(deviceAddresses[i]));
}
return NS_OK;
}
nsresult
BluetoothServiceBluedroid::GetPairedDevicePropertiesInternal(
const nsTArray<nsString>& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
int requestedDeviceCount = aDeviceAddress.Length();
#ifndef MOZ_B2G_BT_API_V1
if (requestedDeviceCount == 0) {
DispatchReplySuccess(aRunnable);
return NS_OK;
}
#else
if (requestedDeviceCount == 0) {
DispatchReplySuccess(aRunnable, InfallibleTArray<BluetoothNamedValue>());
return NS_OK;
}
sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
sGetDeviceRunnableArray.AppendElement(aRunnable);
#endif
for (int i = 0; i < requestedDeviceCount; i++) {
// Retrieve all properties of devices
sBtInterface->GetRemoteDeviceProperties(aDeviceAddress[i],
new GetRemoteDevicePropertiesResultHandler(aDeviceAddress[i]));
}
return NS_OK;
}
class BluetoothServiceBluedroid::StartDiscoveryResultHandler final
: public BluetoothResultHandler
{
public:
StartDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
#ifndef MOZ_B2G_BT_API_V1
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
sChangeDiscoveryRunnableArray.RemoveElement(mRunnable);
DispatchReplyError(mRunnable, aStatus);
}
#else
void StartDiscovery() override
{
MOZ_ASSERT(NS_IsMainThread());
DispatchReplySuccess(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("StartDiscovery"));
}
#endif
private:
BluetoothReplyRunnable* mRunnable;
};
void
BluetoothServiceBluedroid::StartDiscoveryInternal(
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
#ifndef MOZ_B2G_BT_API_V1
sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
#else
// Missing in bluetooth1
#endif
sBtInterface->StartDiscovery(new StartDiscoveryResultHandler(aRunnable));
}
class BluetoothServiceBluedroid::CancelDiscoveryResultHandler final
: public BluetoothResultHandler
{
public:
CancelDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
#ifndef MOZ_B2G_BT_API_V1
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
sChangeDiscoveryRunnableArray.RemoveElement(mRunnable);
DispatchReplyError(mRunnable, aStatus);
}
#else
void CancelDiscovery() override
{
MOZ_ASSERT(NS_IsMainThread());
DispatchReplySuccess(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("StopDiscovery"));
}
#endif
private:
BluetoothReplyRunnable* mRunnable;
};
#ifndef MOZ_B2G_BT_API_V1
class BluetoothServiceBluedroid::GetRemoteServicesResultHandler final
: public BluetoothResultHandler
{
public:
GetRemoteServicesResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
sFetchUuidsRunnableArray.RemoveElement(mRunnable);
DispatchReplyError(mRunnable, aStatus);
}
private:
BluetoothReplyRunnable* mRunnable;
};
nsresult
BluetoothServiceBluedroid::FetchUuidsInternal(
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
/*
* get_remote_services request will not be performed by bluedroid
* if it is currently discovering nearby remote devices.
*/
if (sAdapterDiscovering) {
sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
}
sFetchUuidsRunnableArray.AppendElement(aRunnable);
sBtInterface->GetRemoteServices(aDeviceAddress,
new GetRemoteServicesResultHandler(aRunnable));
return NS_OK;
}
#else
// Missing in bluetooth1
#endif
void
BluetoothServiceBluedroid::StopDiscoveryInternal(
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
#ifndef MOZ_B2G_BT_API_V1
sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
#else
// Missing in bluetooth1
#endif
sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
}
class BluetoothServiceBluedroid::SetAdapterPropertyResultHandler final
: public BluetoothResultHandler
{
public:
SetAdapterPropertyResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
#ifndef MOZ_B2G_BT_API_V1
sSetPropertyRunnableArray.RemoveElement(mRunnable);
DispatchReplyError(mRunnable, aStatus);
#else
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("SetProperty"));
#endif
}
private:
BluetoothReplyRunnable* mRunnable;
};
nsresult
BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
const BluetoothNamedValue& aValue,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
sSetPropertyRunnableArray.AppendElement(aRunnable);
sBtInterface->SetAdapterProperty(aValue,
new SetAdapterPropertyResultHandler(aRunnable));
return NS_OK;
}
nsresult
BluetoothServiceBluedroid::GetServiceChannel(
const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
BluetoothProfileManagerBase* aManager)
{
return NS_OK;
}
bool
BluetoothServiceBluedroid::UpdateSdpRecords(
const nsAString& aDeviceAddress,
BluetoothProfileManagerBase* aManager)
{
return true;
}
class BluetoothServiceBluedroid::CreateBondResultHandler final
: public BluetoothResultHandler
{
public:
CreateBondResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{
MOZ_ASSERT(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
sBondingRunnableArray.RemoveElement(mRunnable);
#ifndef MOZ_B2G_BT_API_V1
DispatchReplyError(mRunnable, aStatus);
#else
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("CreatedPairedDevice"));
#endif
}
private:
nsRefPtr<BluetoothReplyRunnable> mRunnable;
};
nsresult
BluetoothServiceBluedroid::CreatePairedDeviceInternal(
const nsAString& aDeviceAddress, int aTimeout,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
sBondingRunnableArray.AppendElement(aRunnable);
sBtInterface->CreateBond(aDeviceAddress, TRANSPORT_AUTO,
new CreateBondResultHandler(aRunnable));
return NS_OK;
}
class BluetoothServiceBluedroid::RemoveBondResultHandler final
: public BluetoothResultHandler
{
public:
RemoveBondResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{
MOZ_ASSERT(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
sUnbondingRunnableArray.RemoveElement(mRunnable);
#ifndef MOZ_B2G_BT_API_V1
DispatchReplyError(mRunnable, aStatus);
#else
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("RemoveDevice"));
#endif
}
private:
nsRefPtr<BluetoothReplyRunnable> mRunnable;
};
nsresult
BluetoothServiceBluedroid::RemoveDeviceInternal(
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
sUnbondingRunnableArray.AppendElement(aRunnable);
sBtInterface->RemoveBond(aDeviceAddress,
new RemoveBondResultHandler(aRunnable));
return NS_OK;
}
class BluetoothServiceBluedroid::PinReplyResultHandler final
: public BluetoothResultHandler
{
public:
PinReplyResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
void PinReply() override
{
DispatchReplySuccess(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
#ifndef MOZ_B2G_BT_API_V1
DispatchReplyError(mRunnable, aStatus);
#else
ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("SetPinCode"));
#endif
}
private:
BluetoothReplyRunnable* mRunnable;
};
#ifndef MOZ_B2G_BT_API_V1
void
BluetoothServiceBluedroid::PinReplyInternal(
const nsAString& aDeviceAddress, bool aAccept,
const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
sBtInterface->PinReply(aDeviceAddress, aAccept, aPinCode,
new PinReplyResultHandler(aRunnable));
}
void
BluetoothServiceBluedroid::SetPinCodeInternal(
const nsAString& aDeviceAddress, const nsAString& aPinCode,
BluetoothReplyRunnable* aRunnable)
{
// Lecagy method used by BlueZ only.
}
#else
bool
BluetoothServiceBluedroid::SetPinCodeInternal(
const nsAString& aDeviceAddress, const nsAString& aPinCode,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
sBtInterface->PinReply(aDeviceAddress, true, aPinCode,
new PinReplyResultHandler(aRunnable));
return true;
}
#endif
#ifndef MOZ_B2G_BT_API_V1
void
BluetoothServiceBluedroid::SetPasskeyInternal(
const nsAString& aDeviceAddress, uint32_t aPasskey,
BluetoothReplyRunnable* aRunnable)
{
// Lecagy method used by BlueZ only.
}
#else
bool
BluetoothServiceBluedroid::SetPasskeyInternal(
const nsAString& aDeviceAddress, uint32_t aPasskey,
BluetoothReplyRunnable* aRunnable)
{
return true;
}
#endif
class BluetoothServiceBluedroid::SspReplyResultHandler final
: public BluetoothResultHandler
{
public:
SspReplyResultHandler(BluetoothReplyRunnable* aRunnable)
: mRunnable(aRunnable)
{ }
void SspReply() override
{
DispatchReplySuccess(mRunnable);
}
void OnError(BluetoothStatus aStatus) override
{
#ifndef MOZ_B2G_BT_API_V1
DispatchReplyError(mRunnable, aStatus);
#else
ReplyStatusError(mRunnable, aStatus,
NS_LITERAL_STRING("SetPairingConfirmation"));
#endif
}
private:
BluetoothReplyRunnable* mRunnable;
};
#ifndef MOZ_B2G_BT_API_V1
void
BluetoothServiceBluedroid::SspReplyInternal(
const nsAString& aDeviceAddress, BluetoothSspVariant aVariant,
bool aAccept, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
sBtInterface->SspReply(aDeviceAddress, aVariant, aAccept, 0 /* passkey */,
new SspReplyResultHandler(aRunnable));
}
void
BluetoothServiceBluedroid::SetPairingConfirmationInternal(
const nsAString& aDeviceAddress, bool aConfirm,
BluetoothReplyRunnable* aRunnable)
{
// Lecagy method used by BlueZ only.
}
#else
bool
BluetoothServiceBluedroid::SetPairingConfirmationInternal(
const nsAString& aDeviceAddress, bool aConfirm,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
sBtInterface->SspReply(aDeviceAddress,
SSP_VARIANT_PASSKEY_CONFIRMATION,
aConfirm, 0, new SspReplyResultHandler(aRunnable));
return true;
}
#endif
#ifndef MOZ_B2G_BT_API_V1
// Missing in bluetooth2
#else
bool
BluetoothServiceBluedroid::SetAuthorizationInternal(
const nsAString& aDeviceAddress, bool aAllow,
BluetoothReplyRunnable* aRunnable)
{
return true;
}
nsresult
BluetoothServiceBluedroid::PrepareAdapterInternal()
{
return NS_OK;
}
#endif
void
BluetoothServiceBluedroid::NextBluetoothProfileController()
{
MOZ_ASSERT(NS_IsMainThread());
// Remove the completed task at the head
NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty());
sControllerArray.RemoveElementAt(0);
// Start the next task if task array is not empty
if (!sControllerArray.IsEmpty()) {
sControllerArray[0]->StartSession();
}
}
void
BluetoothServiceBluedroid::ConnectDisconnect(
bool aConnect, const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable,
uint16_t aServiceUuid, uint32_t aCod)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRunnable);
BluetoothProfileController* controller =
new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable,
NextBluetoothProfileController,
aServiceUuid, aCod);
sControllerArray.AppendElement(controller);
/**
* If the request is the first element of the quene, start from here. Note
* that other request is pushed into the quene and is popped out after the
* first one is completed. See NextBluetoothProfileController() for details.
*/
if (sControllerArray.Length() == 1) {
sControllerArray[0]->StartSession();
}
}
void
BluetoothServiceBluedroid::Connect(const nsAString& aDeviceAddress,
uint32_t aCod,
uint16_t aServiceUuid,
BluetoothReplyRunnable* aRunnable)
{
ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod);
}
void
BluetoothServiceBluedroid::Disconnect(
const nsAString& aDeviceAddress, uint16_t aServiceUuid,
BluetoothReplyRunnable* aRunnable)
{
ConnectDisconnect(false, aDeviceAddress, aRunnable, aServiceUuid);
}
#ifndef MOZ_B2G_BT_API_V1
bool
BluetoothServiceBluedroid::IsConnected(uint16_t aProfileId)
{
return true;
}
#else
void
BluetoothServiceBluedroid::IsConnected(const uint16_t aServiceUuid,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRunnable);
BluetoothProfileManagerBase* profile =
BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
if (profile) {
DispatchReplySuccess(aRunnable, profile->IsConnected());
} else {
BT_WARNING("Can't find profile manager with uuid: %x", aServiceUuid);
DispatchReplySuccess(aRunnable, false);
}
}
#endif
void
BluetoothServiceBluedroid::SendFile(const nsAString& aDeviceAddress,
BlobParent* aBlobParent,
BlobChild* aBlobChild,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// Currently we only support one device sending one file at a time,
// so we don't need aDeviceAddress here because the target device
// has been determined when calling 'Connect()'. Nevertheless, keep
// it for future use.
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("SendFile failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::SendFile(const nsAString& aDeviceAddress,
Blob* aBlob,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// Currently we only support one device sending one file at a time,
// so we don't need aDeviceAddress here because the target device
// has been determined when calling 'Connect()'. Nevertheless, keep
// it for future use.
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (!opp || !opp->SendFile(aDeviceAddress, aBlob)) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("SendFile failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::StopSendingFile(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// Currently we only support one device sending one file at a time,
// so we don't need aDeviceAddress here because the target device
// has been determined when calling 'Connect()'. Nevertheless, keep
// it for future use.
BluetoothOppManager* opp = BluetoothOppManager::Get();
nsAutoString errorStr;
if (!opp || !opp->StopSendingFile()) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("StopSendingFile failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::ConfirmReceivingFile(
const nsAString& aDeviceAddress, bool aConfirm,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
// Currently we only support one device sending one file at a time,
// so we don't need aDeviceAddress here because the target device
// has been determined when calling 'Connect()'. Nevertheless, keep
// it for future use.
BluetoothOppManager* opp = BluetoothOppManager::Get();
nsAutoString errorStr;
if (!opp || !opp->ConfirmReceivingFile(aConfirm)) {
DispatchReplyError(aRunnable,
NS_LITERAL_STRING("ConfirmReceivingFile failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::ConnectSco(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (!hfp || !hfp->ConnectSco()) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("ConnectSco failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::DisconnectSco(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (!hfp || !hfp->DisconnectSco()) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("DisconnectSco failed"));
return;
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::IsScoConnected(BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
if (!hfp) {
DispatchReplyError(aRunnable, NS_LITERAL_STRING("IsScoConnected failed"));
return;
}
DispatchReplySuccess(aRunnable, BluetoothValue(hfp->IsScoConnected()));
}
void
BluetoothServiceBluedroid::SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
int64_t aMediaNumber,
int64_t aTotalMediaCount,
int64_t aDuration,
BluetoothReplyRunnable* aRunnable)
{
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
if (a2dp) {
a2dp->UpdateMetaData(aTitle, aArtist, aAlbum, aMediaNumber,
aTotalMediaCount, aDuration);
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::SendPlayStatus(
int64_t aDuration, int64_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable)
{
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
if (a2dp) {
ControlPlayStatus playStatus =
PlayStatusStringToControlPlayStatus(aPlayStatus);
a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus);
}
DispatchReplySuccess(aRunnable);
}
void
BluetoothServiceBluedroid::UpdatePlayStatus(
uint32_t aDuration, uint32_t aPosition, ControlPlayStatus aPlayStatus)
{
// We don't need this function for bluedroid.
// In bluez, it only calls dbus api
// But it does not update BluetoothA2dpManager member fields
MOZ_ASSERT(false);
}
nsresult
BluetoothServiceBluedroid::SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage)
{
return NS_OK;
}
nsresult
BluetoothServiceBluedroid::SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage)
{
return NS_OK;
}
void
BluetoothServiceBluedroid::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable)
{
}
void
BluetoothServiceBluedroid::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable)
{
}
void
BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
{
}
#ifndef MOZ_B2G_BT_API_V1
// Missing in bluetooth2
#else
uint16_t
BluetoothServiceBluedroid::UuidToServiceClassInt(const BluetoothUuid& mUuid)
{
// extract short UUID 0000xxxx-0000-1000-8000-00805f9b34fb
uint16_t shortUuid;
memcpy(&shortUuid, mUuid.mUuid + 2, sizeof(uint16_t));
return ntohs(shortUuid);
}
bool
BluetoothServiceBluedroid::IsConnected(const nsAString& aRemoteBdAddr)
{
MOZ_ASSERT(NS_IsMainThread());
nsString connectedAddress;
// Check whether HFP/HSP are connected.
BluetoothProfileManagerBase* profile;
profile = BluetoothHfpManager::Get();
if (profile && profile->IsConnected()) {
profile->GetAddress(connectedAddress);
if (aRemoteBdAddr.Equals(connectedAddress)) {
return true;
}
}
// Check whether OPP is connected.
profile = BluetoothOppManager::Get();
if (profile->IsConnected()) {
profile->GetAddress(connectedAddress);
if (aRemoteBdAddr.Equals(connectedAddress)) {
return true;
}
}
// Check whether A2DP is connected.
profile = BluetoothA2dpManager::Get();
if (profile->IsConnected()) {
profile->GetAddress(connectedAddress);
if (aRemoteBdAddr.Equals(connectedAddress)) {
return true;
}
}
return false;
}
#endif
//
// Bluetooth notifications
//
/* |ProfileDeinitResultHandler| collects the results of all profile
* result handlers and calls |Proceed| after all results handlers
* have been run.
*/
class BluetoothServiceBluedroid::ProfileDeinitResultHandler final
: public BluetoothProfileResultHandler
{
public:
ProfileDeinitResultHandler(unsigned char aNumProfiles)
: mNumProfiles(aNumProfiles)
{
MOZ_ASSERT(mNumProfiles);
}
void Deinit() override
{
if (!(--mNumProfiles)) {
Proceed();
}
}
void OnError(nsresult aResult) override
{
if (!(--mNumProfiles)) {
Proceed();
}
}
private:
void Proceed() const
{
if (!sIsRestart) {
sBtInterface->Cleanup(nullptr);
} else {
BT_LOGR("ProfileDeinitResultHandler::Proceed cancel cleanup() ");
}
}
unsigned char mNumProfiles;
};
class BluetoothServiceBluedroid::SetAdapterPropertyDiscoverableResultHandler
final
: public BluetoothResultHandler
{
public:
void OnError(BluetoothStatus aStatus) override
{
BT_LOGR("Fail to set: BT_SCAN_MODE_CONNECTABLE");
}
};
void
BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BT_STATE: %d", aState);
if (sIsRestart && aState) {
// daemon restarted, reset flag
BT_LOGR("daemon restarted, reset flag");
sIsRestart = false;
sIsFirstTimeToggleOffBt = false;
}
sAdapterEnabled = aState;
if (!sAdapterEnabled) {
static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
BluetoothHfpManager::DeinitHfpInterface,
BluetoothA2dpManager::DeinitA2dpInterface,
BluetoothGattManager::DeinitGattInterface
};
// Return error if BluetoothService is unavailable
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
// Cleanup static adapter properties and notify adapter.
sAdapterBdAddress.Truncate();
sAdapterBdName.Truncate();
InfallibleTArray<BluetoothNamedValue> props;
BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName);
BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress);
if (sAdapterDiscoverable) {
sAdapterDiscoverable = false;
BT_APPEND_NAMED_VALUE(props, "Discoverable", false);
}
if (sAdapterDiscovering) {
sAdapterDiscovering = false;
BT_APPEND_NAMED_VALUE(props, "Discovering", false);
}
bs->DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(props));
// Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
nsRefPtr<ProfileDeinitResultHandler> res =
new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
sDeinitManager[i](res);
}
}
BluetoothService::AcknowledgeToggleBt(sAdapterEnabled);
if (sAdapterEnabled) {
// Bluetooth just enabled, clear profile controllers and runnable arrays.
sControllerArray.Clear();
sChangeDiscoveryRunnableArray.Clear();
sSetPropertyRunnableArray.Clear();
sGetDeviceRunnableArray.Clear();
sFetchUuidsRunnableArray.Clear();
sBondingRunnableArray.Clear();
sUnbondingRunnableArray.Clear();
sPairingNameTable.Clear();
// Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., it should
// be connectable and non-discoverable.
NS_ENSURE_TRUE_VOID(sBtInterface);
sBtInterface->SetAdapterProperty(
BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
new SetAdapterPropertyDiscoverableResultHandler());
// Trigger OPP & PBAP managers to listen
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (!opp || !opp->Listen()) {
BT_LOGR("Fail to start BluetoothOppManager listening");
}
BluetoothPbapManager* pbap = BluetoothPbapManager::Get();
if (!pbap || !pbap->Listen()) {
BT_LOGR("Fail to start BluetoothPbapManager listening");
}
}
// Resolve promise if existed
if (!sChangeAdapterStateRunnableArray.IsEmpty()) {
DispatchReplySuccess(sChangeAdapterStateRunnableArray[0]);
sChangeAdapterStateRunnableArray.RemoveElementAt(0);
}
// After ProfileManagers deinit and cleanup, now restarts bluetooth daemon
if (sIsRestart && !aState) {
BT_LOGR("sIsRestart and off, now restart");
StartBluetooth(false, nullptr);
}
#else
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("BT_STATE: %d", aState);
if (sIsRestart && aState) {
// daemon restarted, reset flag
BT_LOGR("daemon restarted, reset flag");
sIsRestart = false;
sIsFirstTimeToggleOffBt = false;
}
bool isBtEnabled = (aState == true);
if (!isBtEnabled) {
static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
BluetoothHfpManager::DeinitHfpInterface,
BluetoothA2dpManager::DeinitA2dpInterface
};
// Set discoverable cache to default value after state becomes BT_STATE_OFF.
if (sAdapterDiscoverable) {
sAdapterDiscoverable = false;
}
// Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
nsRefPtr<ProfileDeinitResultHandler> res =
new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
sDeinitManager[i](res);
}
}
BluetoothService::AcknowledgeToggleBt(isBtEnabled);
if (isBtEnabled) {
// Bluetooth just enabled, clear profile controllers and runnable arrays.
sControllerArray.Clear();
sBondingRunnableArray.Clear();
sGetDeviceRunnableArray.Clear();
sSetPropertyRunnableArray.Clear();
sUnbondingRunnableArray.Clear();
// Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., It should
// be connectable and non-discoverable.
NS_ENSURE_TRUE_VOID(sBtInterface);
sBtInterface->SetAdapterProperty(
BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
new SetAdapterPropertyDiscoverableResultHandler());
// Try to fire event 'AdapterAdded' to fit the original behaviour when
// we used BlueZ as backend.
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
bs->AdapterAddedReceived();
bs->TryFiringAdapterAdded();
// Trigger OPP & PBAP managers to listen
BluetoothOppManager* opp = BluetoothOppManager::Get();
if (!opp || !opp->Listen()) {
BT_LOGR("Fail to start BluetoothOppManager listening");
}
BluetoothPbapManager* pbap = BluetoothPbapManager::Get();
if (!pbap || !pbap->Listen()) {
BT_LOGR("Fail to start BluetoothPbapManager listening");
}
}
// After ProfileManagers deinit and cleanup, now restarts bluetooth daemon
if (sIsRestart && !aState) {
BT_LOGR("sIsRestart and off, now restart");
StartBluetooth(false);
}
#endif
}
/**
* AdapterPropertiesNotification will be called after enable() but
* before AdapterStateChangeCallback is called. At that moment, both
* BluetoothManager and BluetoothAdapter, do not register observer
* yet.
*/
void
BluetoothServiceBluedroid::AdapterPropertiesNotification(
BluetoothStatus aStatus, int aNumProperties,
const BluetoothProperty* aProperties)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> propertiesArray;
for (int i = 0; i < aNumProperties; i++) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDADDR) {
sAdapterBdAddress = p.mString;
BT_APPEND_NAMED_VALUE(propertiesArray, "Address", sAdapterBdAddress);
} else if (p.mType == PROPERTY_BDNAME) {
sAdapterBdName = p.mString;
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", sAdapterBdName);
} else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
// If BT is not enabled, Bluetooth scan mode should be non-discoverable
// by defalut. 'AdapterStateChangedNotification' would set the default
// properties to bluetooth backend once Bluetooth is enabled.
if (IsEnabled()) {
sAdapterDiscoverable =
(p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
BT_APPEND_NAMED_VALUE(propertiesArray, "Discoverable",
sAdapterDiscoverable);
}
} else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
// We have to cache addresses of bonded devices. Unlike BlueZ,
// Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
// event after bond completed.
BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d",
p.mStringArray.Length());
// Whenever reloading paired devices, force refresh
sAdapterBondedAddressArray.Clear();
sAdapterBondedAddressArray.AppendElements(p.mStringArray);
BT_APPEND_NAMED_VALUE(propertiesArray, "PairedDevices",
sAdapterBondedAddressArray);
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
} else {
BT_LOGD("Unhandled adapter property type: %d", p.mType);
continue;
}
}
NS_ENSURE_TRUE_VOID(propertiesArray.Length() > 0);
DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray));
// Send reply for SetProperty
if (!sSetPropertyRunnableArray.IsEmpty()) {
DispatchReplySuccess(sSetPropertyRunnableArray[0]);
sSetPropertyRunnableArray.RemoveElementAt(0);
}
#else
MOZ_ASSERT(NS_IsMainThread());
BluetoothValue propertyValue;
InfallibleTArray<BluetoothNamedValue> props;
for (int i = 0; i < aNumProperties; i++) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDADDR) {
sAdapterBdAddress = p.mString;
propertyValue = sAdapterBdAddress;
BT_APPEND_NAMED_VALUE(props, "Address", propertyValue);
} else if (p.mType == PROPERTY_BDNAME) {
sAdapterBdName = p.mString;
propertyValue = sAdapterBdName;
BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
} else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
BluetoothScanMode newMode = p.mScanMode;
// If BT is not enabled, Bluetooth scan mode should be non-discoverable
// by defalut. 'AdapterStateChangedNotification' would set the default
// properties to bluetooth backend once Bluetooth is enabled.
if (newMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE && IsEnabled()) {
propertyValue = sAdapterDiscoverable = true;
} else {
propertyValue = sAdapterDiscoverable = false;
}
BT_APPEND_NAMED_VALUE(props, "Discoverable", propertyValue);
} else if (p.mType == PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
propertyValue = sAdapterDiscoverableTimeout = p.mUint32;
BT_APPEND_NAMED_VALUE(props, "DiscoverableTimeout", propertyValue);
} else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
// We have to cache addresses of bonded devices. Unlike BlueZ,
// Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
// event after bond completed.
BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d",
p.mStringArray.Length());
// Whenever reloading paired devices, force refresh
sAdapterBondedAddressArray.Clear();
for (size_t index = 0; index < p.mStringArray.Length(); index++) {
sAdapterBondedAddressArray.AppendElement(p.mStringArray[index]);
}
propertyValue = sAdapterBondedAddressArray;
BT_APPEND_NAMED_VALUE(props, "Devices", propertyValue);
} else if (p.mType == PROPERTY_UUIDS) {
//FIXME: This will be implemented in the later patchset
continue;
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
continue;
} else {
BT_LOGD("Unhandled adapter property type: %d", p.mType);
continue;
}
}
NS_ENSURE_TRUE_VOID(props.Length() > 0);
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(props)));
// Send reply for SetProperty
if (!sSetPropertyRunnableArray.IsEmpty()) {
DispatchReplySuccess(sSetPropertyRunnableArray[0]);
sSetPropertyRunnableArray.RemoveElementAt(0);
}
#endif
}
/**
* RemoteDevicePropertiesNotification will be called
*
* (1) automatically by Bluedroid when BT is turning on, or
* (2) as result of remote device properties update during discovery, or
* (3) as result of CreateBond, or
* (4) as result of GetRemoteDeviceProperties, or
* (5) as result of GetRemoteServices.
*/
void
BluetoothServiceBluedroid::RemoteDevicePropertiesNotification(
BluetoothStatus aStatus, const nsAString& aBdAddr,
int aNumProperties, const BluetoothProperty* aProperties)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "Address", nsString(aBdAddr));
for (int i = 0; i < aNumProperties; ++i) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDNAME) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", p.mString);
} else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = p.mUint32;
BT_APPEND_NAMED_VALUE(propertiesArray, "Cod", cod);
} else if (p.mType == PROPERTY_UUIDS) {
nsTArray<nsString> uuids;
// Construct a sorted uuid set
for (uint32_t index = 0; index < p.mUuidArray.Length(); ++index) {
nsAutoString uuid;
UuidToString(p.mUuidArray[index], uuid);
if (!uuids.Contains(uuid)) { // filter out duplicate uuids
uuids.InsertElementSorted(uuid);
}
}
BT_APPEND_NAMED_VALUE(propertiesArray, "UUIDs", uuids);
} else if (p.mType == PROPERTY_TYPE_OF_DEVICE) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Type",
static_cast<uint32_t>(p.mTypeOfDevice));
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
} else {
BT_LOGD("Other non-handled device properties. Type: %d", p.mType);
}
}
// The order of operations below is
//
// (1) modify global state (i.e., the variables starting with 's'),
// (2) distribute the signal, and finally
// (3) send any pending Bluetooth replies.
//
// |DispatchReplySuccess| creates its own internal runnable, which is
// always run after we completed the current method. This means that we
// can exchange |DispatchReplySuccess| with other operations without
// changing the order of (1,2) and (3).
// Update to registered BluetoothDevice objects
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
nsString(aBdAddr), propertiesArray);
// FetchUuids task
if (!sFetchUuidsRunnableArray.IsEmpty()) {
// propertiesArray contains Address and Uuids only
DispatchReplySuccess(sFetchUuidsRunnableArray[0],
propertiesArray[1].value()); /* Uuids */
sFetchUuidsRunnableArray.RemoveElementAt(0);
DistributeSignal(signal);
return;
}
// GetDevices task
if (sRequestedDeviceCountArray.IsEmpty()) {
// This is possible because the callback would be called after turning
// Bluetooth on.
DistributeSignal(signal);
return;
}
// Use address as the index
sRemoteDevicesPack.AppendElement(
BluetoothNamedValue(nsString(aBdAddr), propertiesArray));
if (--sRequestedDeviceCountArray[0] == 0) {
if (!sGetDeviceRunnableArray.IsEmpty()) {
DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
sGetDeviceRunnableArray.RemoveElementAt(0);
}
sRequestedDeviceCountArray.RemoveElementAt(0);
sRemoteDevicesPack.Clear();
}
DistributeSignal(signal);
#else
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> props;
BT_APPEND_NAMED_VALUE(props, "Address", BluetoothValue(nsString(aBdAddr)));
bool isCodInvalid = false;
for (int i = 0; i < aNumProperties; ++i) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDNAME) {
BT_APPEND_NAMED_VALUE(props, "Name", p.mString);
} else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = p.mUint32;
nsString icon;
ClassToIcon(cod, icon);
if (!icon.IsEmpty()) {
// Valid CoD
BT_APPEND_NAMED_VALUE(props, "Class", cod);
BT_APPEND_NAMED_VALUE(props, "Icon", icon);
} else {
// If Cod is invalid, fallback to check UUIDs. It usually happens due to
// NFC directly trigger pairing. bluedroid sends wrong CoD due to missing
// EIR query records.
isCodInvalid = true;
}
} else if (p.mType == PROPERTY_UUIDS) {
InfallibleTArray<nsString> uuidsArray;
uint32_t cod = 0;
for (size_t i = 0; i < p.mUuidArray.Length(); i++) {
uint16_t uuidServiceClass = UuidToServiceClassInt(p.mUuidArray[i]);
BluetoothServiceClass serviceClass =
BluetoothUuidHelper::GetBluetoothServiceClass(uuidServiceClass);
// Get Uuid string from BluetoothServiceClass
nsString uuid;
BluetoothUuidHelper::GetString(serviceClass, uuid);
uuidsArray.AppendElement(uuid);
// Restore CoD value
if (isCodInvalid) {
if (serviceClass == BluetoothServiceClass::HANDSFREE ||
serviceClass == BluetoothServiceClass::HEADSET) {
BT_LOGD("Restore Class Of Device to Audio bit");
SET_AUDIO_BIT(cod);
} else if (serviceClass == BluetoothServiceClass::A2DP_SINK) {
BT_LOGD("Restore Class of Device to Rendering bit");
SET_RENDERING_BIT(cod);
}
}
}
if (isCodInvalid) {
BT_APPEND_NAMED_VALUE(props, "Class", cod);
// 'audio-card' refers to 'Audio' device
BT_APPEND_NAMED_VALUE(props, "Icon", NS_LITERAL_STRING("audio-card"));
}
BT_APPEND_NAMED_VALUE(props, "UUIDS", uuidsArray);
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
} else {
BT_LOGD("Other non-handled device properties. Type: %d", p.mType);
}
}
// BlueDroid wouldn't notify the status of connection, therefore, query the
// connection state and append to properties array
BT_APPEND_NAMED_VALUE(props, "Connected", IsConnected(aBdAddr));
if (sRequestedDeviceCountArray.IsEmpty()) {
/**
* This is possible when
*
* (1) the callback is called when BT is turning on, or
* (2) remote device properties get updated during discovery, or
* (3) as result of CreateBond
*/
if (sAdapterDiscovering) {
// Fire 'devicefound' again to update device name for (2).
// See bug 1076553 for more information.
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("DeviceFound"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(props)));
}
return;
}
// Use address as the index
sRemoteDevicesPack.AppendElement(
BluetoothNamedValue(nsString(aBdAddr), props));
if (--sRequestedDeviceCountArray[0] == 0) {
if (!sGetDeviceRunnableArray.IsEmpty()) {
DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
sGetDeviceRunnableArray.RemoveElementAt(0);
}
sRequestedDeviceCountArray.RemoveElementAt(0);
sRemoteDevicesPack.Clear();
}
// Update to registered BluetoothDevice objects
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
nsString(aBdAddr),
BluetoothValue(props)));
#endif
}
void
BluetoothServiceBluedroid::DeviceFoundNotification(
int aNumProperties, const BluetoothProperty* aProperties)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
BluetoothValue propertyValue;
InfallibleTArray<BluetoothNamedValue> propertiesArray;
for (int i = 0; i < aNumProperties; i++) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDADDR) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Address", p.mString);
} else if (p.mType == PROPERTY_BDNAME) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", p.mString);
} else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Cod", p.mUint32);
} else if (p.mType == PROPERTY_UUIDS) {
nsTArray<nsString> uuids;
// Construct a sorted uuid set
for (uint32_t index = 0; index < p.mUuidArray.Length(); ++index) {
nsAutoString uuid;
UuidToString(p.mUuidArray[index], uuid);
if (!uuids.Contains(uuid)) { // filter out duplicate uuids
uuids.InsertElementSorted(uuid);
}
}
BT_APPEND_NAMED_VALUE(propertiesArray, "UUIDs", uuids);
} else if (p.mType == PROPERTY_TYPE_OF_DEVICE) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Type",
static_cast<uint32_t>(p.mTypeOfDevice));
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
} else {
BT_LOGD("Not handled remote device property: %d", p.mType);
}
}
DistributeSignal(NS_LITERAL_STRING("DeviceFound"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray));
#else
MOZ_ASSERT(NS_IsMainThread());
BluetoothValue propertyValue;
InfallibleTArray<BluetoothNamedValue> propertiesArray;
for (int i = 0; i < aNumProperties; i++) {
const BluetoothProperty& p = aProperties[i];
if (p.mType == PROPERTY_BDADDR) {
propertyValue = p.mString;
BT_APPEND_NAMED_VALUE(propertiesArray, "Address", propertyValue);
} else if (p.mType == PROPERTY_BDNAME) {
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", p.mString);
} else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = p.mUint32;
propertyValue = cod;
BT_APPEND_NAMED_VALUE(propertiesArray, "Class", propertyValue);
nsString icon;
ClassToIcon(cod, icon);
propertyValue = icon;
BT_APPEND_NAMED_VALUE(propertiesArray, "Icon", propertyValue);
} else if (p.mType == PROPERTY_UNKNOWN) {
/* Bug 1065999: working around unknown properties */
} else {
BT_LOGD("Not handled remote device property: %d", p.mType);
}
}
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("DeviceFound"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray)));
#endif
}
void
BluetoothServiceBluedroid::DiscoveryStateChangedNotification(bool aState)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
sAdapterDiscovering = aState;
// Fire PropertyChanged of Discovering
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "Discovering", sAdapterDiscovering);
DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray));
// Reply that Promise is resolved
if (!sChangeDiscoveryRunnableArray.IsEmpty()) {
DispatchReplySuccess(sChangeDiscoveryRunnableArray[0]);
sChangeDiscoveryRunnableArray.RemoveElementAt(0);
}
#else
MOZ_ASSERT(NS_IsMainThread());
sAdapterDiscovering = aState;
DistributeSignal(
BluetoothSignal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID),
NS_LITERAL_STRING(KEY_ADAPTER), sAdapterDiscovering));
// Distribute "PropertyChanged" signal to notice adapter this change since
// Bluedroid don' treat "discovering" as a property of adapter.
InfallibleTArray<BluetoothNamedValue> props;
BT_APPEND_NAMED_VALUE(props, "Discovering",
BluetoothValue(sAdapterDiscovering));
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(props)));
#endif
}
void
BluetoothServiceBluedroid::PinRequestNotification(const nsAString& aRemoteBdAddr,
const nsAString& aBdName,
uint32_t aCod)
{
#ifndef MOZ_B2G_BT_API_V1
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName));
BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", EmptyString());
BT_APPEND_NAMED_VALUE(propertiesArray, "type",
NS_LITERAL_STRING(PAIRING_REQ_TYPE_ENTERPINCODE));
sPairingNameTable.Put(nsString(aRemoteBdAddr), nsString(aBdName));
DistributeSignal(NS_LITERAL_STRING("PairingRequest"),
NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
BluetoothValue(propertiesArray));
#else
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
BT_APPEND_NAMED_VALUE(propertiesArray, "method",
NS_LITERAL_STRING("pincode"));
BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName));
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("RequestPinCode"),
NS_LITERAL_STRING(KEY_LOCAL_AGENT),
BluetoothValue(propertiesArray)));
#endif
}
void
BluetoothServiceBluedroid::SspRequestNotification(
const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod,
BluetoothSspVariant aPairingVariant, uint32_t aPassKey)
{
MOZ_ASSERT(NS_IsMainThread());
#ifndef MOZ_B2G_BT_API_V1
InfallibleTArray<BluetoothNamedValue> propertiesArray;
nsAutoString passkey;
nsAutoString pairingType;
/**
* Assign pairing request type and passkey based on the pairing variant.
*
* passkey value based on pairing request type:
* 1) aPasskey: PAIRING_REQ_TYPE_CONFIRMATION and
* PAIRING_REQ_TYPE_DISPLAYPASSKEY
* 2) empty string: PAIRING_REQ_TYPE_CONSENT
*/
switch (aPairingVariant) {
case SSP_VARIANT_PASSKEY_CONFIRMATION:
pairingType.AssignLiteral(PAIRING_REQ_TYPE_CONFIRMATION);
passkey.AppendInt(aPassKey);
break;
case SSP_VARIANT_PASSKEY_NOTIFICATION:
pairingType.AssignLiteral(PAIRING_REQ_TYPE_DISPLAYPASSKEY);
passkey.AppendInt(aPassKey);
break;
case SSP_VARIANT_CONSENT:
pairingType.AssignLiteral(PAIRING_REQ_TYPE_CONSENT);
break;
default:
BT_WARNING("Unhandled SSP Bonding Variant: %d", aPairingVariant);
return;
}
BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName));
BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", passkey);
BT_APPEND_NAMED_VALUE(propertiesArray, "type", pairingType);
sPairingNameTable.Put(nsString(aRemoteBdAddr), nsString(aBdName));
DistributeSignal(NS_LITERAL_STRING("PairingRequest"),
NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
BluetoothValue(propertiesArray));
#else
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
BT_APPEND_NAMED_VALUE(propertiesArray, "method",
NS_LITERAL_STRING("confirmation"));
BT_APPEND_NAMED_VALUE(propertiesArray, "name", nsString(aBdName));
BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", aPassKey);
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("RequestConfirmation"),
NS_LITERAL_STRING(KEY_LOCAL_AGENT),
BluetoothValue(propertiesArray)));
#endif
}
void
BluetoothServiceBluedroid::BondStateChangedNotification(
BluetoothStatus aStatus, const nsAString& aRemoteBdAddr,
BluetoothBondState aState)
{
MOZ_ASSERT(NS_IsMainThread());
if (aState == BOND_STATE_BONDING) {
// No need to handle bonding state
return;
}
BT_LOGR("Bond state: %d status: %d", aState, aStatus);
#ifndef MOZ_B2G_BT_API_V1
bool bonded = (aState == BOND_STATE_BONDED);
if (aStatus != STATUS_SUCCESS) {
if (!bonded) { // Active/passive pair failed
BT_LOGR("Pair failed! Abort pairing.");
// Notify adapter of pairing aborted
DistributeSignal(NS_LITERAL_STRING(PAIRING_ABORTED_ID),
NS_LITERAL_STRING(KEY_ADAPTER));
// Reject pair promise
if (!sBondingRunnableArray.IsEmpty()) {
DispatchReplyError(sBondingRunnableArray[0], aStatus);
sBondingRunnableArray.RemoveElementAt(0);
}
} else if (!sUnbondingRunnableArray.IsEmpty()) { // Active unpair failed
// Reject unpair promise
DispatchReplyError(sUnbondingRunnableArray[0], aStatus);
sUnbondingRunnableArray.RemoveElementAt(0);
}
return;
}
// Retrieve and remove pairing device name from hash table
nsString deviceName;
bool nameExists = sPairingNameTable.Get(aRemoteBdAddr, &deviceName);
if (nameExists) {
sPairingNameTable.Remove(aRemoteBdAddr);
}
// Update bonded address array and append pairing device name
InfallibleTArray<BluetoothNamedValue> propertiesArray;
nsString remoteBdAddr = nsString(aRemoteBdAddr);
if (!bonded) {
sAdapterBondedAddressArray.RemoveElement(remoteBdAddr);
} else {
if (!sAdapterBondedAddressArray.Contains(remoteBdAddr)) {
sAdapterBondedAddressArray.AppendElement(remoteBdAddr);
}
// We don't assert |!deviceName.IsEmpty()| here since empty string is
// also a valid name. According to Bluetooth Core Spec. v3.0 - Sec. 6.22,
// "a valid Bluetooth name is a UTF-8 encoding string which is up to 248
// bytes in length."
// Furthermore, we don't assert |nameExists| here since it's expected to be
// 'false' if remote device is using "SSP just works without user
// interaction" or "legacy pairing with auto-pairing".
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", deviceName);
}
// Notify device of attribute changed
BT_APPEND_NAMED_VALUE(propertiesArray, "Paired", bonded);
DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
aRemoteBdAddr,
BluetoothValue(propertiesArray));
// Notify adapter of device paired/unpaired
BT_INSERT_NAMED_VALUE(propertiesArray, 0, "Address", remoteBdAddr);
DistributeSignal(bonded ? NS_LITERAL_STRING(DEVICE_PAIRED_ID)
: NS_LITERAL_STRING(DEVICE_UNPAIRED_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray));
// Resolve existing pair/unpair promise
if (bonded && !sBondingRunnableArray.IsEmpty()) {
DispatchReplySuccess(sBondingRunnableArray[0]);
sBondingRunnableArray.RemoveElementAt(0);
} else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
DispatchReplySuccess(sUnbondingRunnableArray[0]);
sUnbondingRunnableArray.RemoveElementAt(0);
}
#else
if (aState == BOND_STATE_BONDED &&
sAdapterBondedAddressArray.Contains(aRemoteBdAddr)) {
// See bug 940271 for more details about this case.
return;
}
switch (aStatus) {
case STATUS_SUCCESS:
{
bool bonded;
if (aState == BOND_STATE_NONE) {
bonded = false;
sAdapterBondedAddressArray.RemoveElement(aRemoteBdAddr);
} else if (aState == BOND_STATE_BONDED) {
bonded = true;
sAdapterBondedAddressArray.AppendElement(aRemoteBdAddr);
} else {
return;
}
// Update bonded address list to BluetoothAdapter
InfallibleTArray<BluetoothNamedValue> propertiesChangeArray;
BT_APPEND_NAMED_VALUE(propertiesChangeArray, "Devices",
sAdapterBondedAddressArray);
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesChangeArray)));
if (bonded && !sBondingRunnableArray.IsEmpty()) {
DispatchReplySuccess(sBondingRunnableArray[0]);
sBondingRunnableArray.RemoveElementAt(0);
} else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
DispatchReplySuccess(sUnbondingRunnableArray[0]);
sUnbondingRunnableArray.RemoveElementAt(0);
}
// Update bonding status to gaia
InfallibleTArray<BluetoothNamedValue> propertiesArray;
BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
BT_APPEND_NAMED_VALUE(propertiesArray, "status", bonded);
DistributeSignal(
BluetoothSignal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray)));
break;
}
case STATUS_BUSY:
case STATUS_AUTH_FAILURE:
case STATUS_RMT_DEV_DOWN:
{
InfallibleTArray<BluetoothNamedValue> propertiesArray;
DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("Cancel"),
NS_LITERAL_STRING(KEY_LOCAL_AGENT),
BluetoothValue(propertiesArray)));
if (!sBondingRunnableArray.IsEmpty()) {
DispatchReplyError(sBondingRunnableArray[0],
NS_LITERAL_STRING("Authentication failure"));
sBondingRunnableArray.RemoveElementAt(0);
}
break;
}
default:
BT_WARNING("Got an unhandled status of BondStateChangedCallback!");
// Dispatch a reply to unblock the waiting status of pairing.
if (!sBondingRunnableArray.IsEmpty()) {
DispatchReplyError(sBondingRunnableArray[0],
NS_LITERAL_STRING("Internal failure"));
sBondingRunnableArray.RemoveElementAt(0);
}
break;
}
#endif
}
void
BluetoothServiceBluedroid::AclStateChangedNotification(
BluetoothStatus aStatus, const nsAString& aRemoteBdAddr, bool aState)
{
MOZ_ASSERT(NS_IsMainThread());
// FIXME: This will be implemented in the later patchset
}
void
BluetoothServiceBluedroid::DutModeRecvNotification(uint16_t aOpcode,
const uint8_t* aBuf,
uint8_t aLen)
{
MOZ_ASSERT(NS_IsMainThread());
// FIXME: This will be implemented in the later patchset
}
void
BluetoothServiceBluedroid::LeTestModeNotification(BluetoothStatus aStatus,
uint16_t aNumPackets)
{
MOZ_ASSERT(NS_IsMainThread());
// FIXME: This will be implemented in the later patchset
}
void
BluetoothServiceBluedroid::EnergyInfoNotification(
const BluetoothActivityEnergyInfo& aInfo)
{
MOZ_ASSERT(NS_IsMainThread());
// FIXME: This will be implemented in the later patchset
}
void
BluetoothServiceBluedroid::BackendErrorNotification(bool aCrashed)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aCrashed) {
return;
}
/*
* Reset following profile manager states for unexpected backend crash.
* - HFP: connection state and audio state
* - A2DP: connection state
*/
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
NS_ENSURE_TRUE_VOID(hfp);
hfp->HandleBackendError();
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE_VOID(a2dp);
a2dp->HandleBackendError();
sIsRestart = true;
BT_LOGR("Recovery step2: stop bluetooth");
#ifndef MOZ_B2G_BT_API_V1
StopBluetooth(false, nullptr);
#else
StopBluetooth(false);
#endif
}
void
BluetoothServiceBluedroid::CompleteToggleBt(bool aEnabled)
{
MOZ_ASSERT(NS_IsMainThread());
if (sIsRestart && !aEnabled && sIsFirstTimeToggleOffBt) {
// Both StopBluetooth and AdapterStateChangedNotification
// trigger CompleteToggleBt. We don't need to call CompleteToggleBt again
} else if (sIsRestart && !aEnabled && !sIsFirstTimeToggleOffBt) {
// Recovery step 3: cleanup and deinit Profile managers
BT_LOGR("CompleteToggleBt set sIsFirstTimeToggleOffBt = true");
sIsFirstTimeToggleOffBt = true;
BluetoothService::CompleteToggleBt(aEnabled);
AdapterStateChangedNotification(false);
} else {
BluetoothService::CompleteToggleBt(aEnabled);
}
}