mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
74ddad6337
|INIT_AT| works around missing compiler features for gcc before version 4.7. All B2G targets have been lifted to gcc 4.8, so the workaround is not required any longer.
507 lines
12 KiB
C++
507 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=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 "BluetoothDaemonA2dpInterface.h"
|
|
#include "BluetoothDaemonSetupInterface.h"
|
|
#include "mozilla/unused.h"
|
|
|
|
BEGIN_BLUETOOTH_NAMESPACE
|
|
|
|
using namespace mozilla::ipc;
|
|
|
|
//
|
|
// A2DP module
|
|
//
|
|
|
|
const int BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS = 1;
|
|
|
|
BluetoothA2dpNotificationHandler*
|
|
BluetoothDaemonA2dpModule::sNotificationHandler;
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::SetNotificationHandler(
|
|
BluetoothA2dpNotificationHandler* aNotificationHandler)
|
|
{
|
|
sNotificationHandler = aNotificationHandler;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDaemonA2dpModule::Send(DaemonSocketPDU* aPDU,
|
|
BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
nsRefPtr<BluetoothA2dpResultHandler> res(aRes);
|
|
nsresult rv = Send(aPDU, static_cast<void*>(res.get()));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
unused << res.forget(); // Keep reference for response
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::HandleSvc(const DaemonSocketPDUHeader& aHeader,
|
|
DaemonSocketPDU& aPDU, void* aUserData)
|
|
{
|
|
static void (BluetoothDaemonA2dpModule::* const HandleOp[])(
|
|
const DaemonSocketPDUHeader&, DaemonSocketPDU&, void*) = {
|
|
[0] = &BluetoothDaemonA2dpModule::HandleRsp,
|
|
[1] = &BluetoothDaemonA2dpModule::HandleNtf
|
|
};
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
// negate twice to map bit to 0/1
|
|
unsigned int isNtf = !!(aHeader.mOpcode & 0x80);
|
|
|
|
(this->*(HandleOp[isNtf]))(aHeader, aPDU, aUserData);
|
|
}
|
|
|
|
// Commands
|
|
//
|
|
|
|
nsresult
|
|
BluetoothDaemonA2dpModule::ConnectCmd(
|
|
const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsAutoPtr<DaemonSocketPDU> pdu(new DaemonSocketPDU(SERVICE_ID,
|
|
OPCODE_CONNECT,
|
|
6)); // Address
|
|
nsresult rv = PackPDU(
|
|
PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = Send(pdu, aRes);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
unused << pdu.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
BluetoothDaemonA2dpModule::DisconnectCmd(
|
|
const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsAutoPtr<DaemonSocketPDU> pdu(new DaemonSocketPDU(SERVICE_ID,
|
|
OPCODE_DISCONNECT,
|
|
6)); // Address
|
|
nsresult rv = PackPDU(
|
|
PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = Send(pdu, aRes);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
unused << pdu.forget();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Responses
|
|
//
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::ErrorRsp(
|
|
const DaemonSocketPDUHeader& aHeader,
|
|
DaemonSocketPDU& aPDU, BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
ErrorRunnable::Dispatch(
|
|
aRes, &BluetoothA2dpResultHandler::OnError, UnpackPDUInitOp(aPDU));
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::ConnectRsp(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
|
|
BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
ResultRunnable::Dispatch(
|
|
aRes, &BluetoothA2dpResultHandler::Connect, UnpackPDUInitOp(aPDU));
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::DisconnectRsp(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
|
|
BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
ResultRunnable::Dispatch(
|
|
aRes, &BluetoothA2dpResultHandler::Disconnect, UnpackPDUInitOp(aPDU));
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::HandleRsp(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
|
|
void* aUserData)
|
|
{
|
|
static void (BluetoothDaemonA2dpModule::* const HandleRsp[])(
|
|
const DaemonSocketPDUHeader&,
|
|
DaemonSocketPDU&,
|
|
BluetoothA2dpResultHandler*) = {
|
|
[OPCODE_ERROR] = &BluetoothDaemonA2dpModule::ErrorRsp,
|
|
[OPCODE_CONNECT] = &BluetoothDaemonA2dpModule::ConnectRsp,
|
|
[OPCODE_DISCONNECT] = &BluetoothDaemonA2dpModule::DisconnectRsp
|
|
};
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
|
|
|
|
if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) ||
|
|
NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) {
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<BluetoothA2dpResultHandler> res =
|
|
already_AddRefed<BluetoothA2dpResultHandler>(
|
|
static_cast<BluetoothA2dpResultHandler*>(aUserData));
|
|
|
|
if (!res) {
|
|
return; // Return early if no result handler has been set for response
|
|
}
|
|
|
|
(this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res);
|
|
}
|
|
|
|
// Notifications
|
|
//
|
|
|
|
// Returns the current notification handler to a notification runnable
|
|
class BluetoothDaemonA2dpModule::NotificationHandlerWrapper final
|
|
{
|
|
public:
|
|
typedef BluetoothA2dpNotificationHandler ObjectType;
|
|
|
|
static ObjectType* GetInstance()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
return sNotificationHandler;
|
|
}
|
|
};
|
|
|
|
// Init operator class for ConnectionStateNotification
|
|
class BluetoothDaemonA2dpModule::ConnectionStateInitOp final
|
|
: private PDUInitOp
|
|
{
|
|
public:
|
|
ConnectionStateInitOp(DaemonSocketPDU& aPDU)
|
|
: PDUInitOp(aPDU)
|
|
{ }
|
|
|
|
nsresult
|
|
operator () (BluetoothA2dpConnectionState& aArg1, nsString& aArg2) const
|
|
{
|
|
DaemonSocketPDU& pdu = GetPDU();
|
|
|
|
/* Read state */
|
|
nsresult rv = UnpackPDU(pdu, aArg1);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
/* Read address */
|
|
rv = UnpackPDU(
|
|
pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
WarnAboutTrailingData();
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::ConnectionStateNtf(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
|
|
{
|
|
ConnectionStateNotification::Dispatch(
|
|
&BluetoothA2dpNotificationHandler::ConnectionStateNotification,
|
|
ConnectionStateInitOp(aPDU));
|
|
}
|
|
|
|
// Init operator class for AudioStateNotification
|
|
class BluetoothDaemonA2dpModule::AudioStateInitOp final
|
|
: private PDUInitOp
|
|
{
|
|
public:
|
|
AudioStateInitOp(DaemonSocketPDU& aPDU)
|
|
: PDUInitOp(aPDU)
|
|
{ }
|
|
|
|
nsresult
|
|
operator () (BluetoothA2dpAudioState& aArg1,
|
|
nsString& aArg2) const
|
|
{
|
|
DaemonSocketPDU& pdu = GetPDU();
|
|
|
|
/* Read state */
|
|
nsresult rv = UnpackPDU(pdu, aArg1);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
/* Read address */
|
|
rv = UnpackPDU(
|
|
pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
WarnAboutTrailingData();
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::AudioStateNtf(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
|
|
{
|
|
AudioStateNotification::Dispatch(
|
|
&BluetoothA2dpNotificationHandler::AudioStateNotification,
|
|
AudioStateInitOp(aPDU));
|
|
}
|
|
|
|
// Init operator class for AudioConfigNotification
|
|
class BluetoothDaemonA2dpModule::AudioConfigInitOp final
|
|
: private PDUInitOp
|
|
{
|
|
public:
|
|
AudioConfigInitOp(DaemonSocketPDU& aPDU)
|
|
: PDUInitOp(aPDU)
|
|
{ }
|
|
|
|
nsresult
|
|
operator () (nsString& aArg1, uint32_t aArg2, uint8_t aArg3) const
|
|
{
|
|
DaemonSocketPDU& pdu = GetPDU();
|
|
|
|
/* Read address */
|
|
nsresult rv = UnpackPDU(
|
|
pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
/* Read sample rate */
|
|
rv = UnpackPDU(pdu, aArg2);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
/* Read channel count */
|
|
rv = UnpackPDU(pdu, aArg3);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
WarnAboutTrailingData();
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::AudioConfigNtf(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU)
|
|
{
|
|
AudioConfigNotification::Dispatch(
|
|
&BluetoothA2dpNotificationHandler::AudioConfigNotification,
|
|
AudioConfigInitOp(aPDU));
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpModule::HandleNtf(
|
|
const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU,
|
|
void* aUserData)
|
|
{
|
|
static void (BluetoothDaemonA2dpModule::* const HandleNtf[])(
|
|
const DaemonSocketPDUHeader&, DaemonSocketPDU&) = {
|
|
[0] = &BluetoothDaemonA2dpModule::ConnectionStateNtf,
|
|
[1] = &BluetoothDaemonA2dpModule::AudioStateNtf,
|
|
#if ANDROID_VERSION >= 21
|
|
[2] = &BluetoothDaemonA2dpModule::AudioConfigNtf
|
|
#endif
|
|
};
|
|
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
uint8_t index = aHeader.mOpcode - 0x81;
|
|
|
|
if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) ||
|
|
NS_WARN_IF(!HandleNtf[index])) {
|
|
return;
|
|
}
|
|
|
|
(this->*(HandleNtf[index]))(aHeader, aPDU);
|
|
}
|
|
|
|
//
|
|
// A2DP interface
|
|
//
|
|
|
|
BluetoothDaemonA2dpInterface::BluetoothDaemonA2dpInterface(
|
|
BluetoothDaemonA2dpModule* aModule)
|
|
: mModule(aModule)
|
|
{ }
|
|
|
|
BluetoothDaemonA2dpInterface::~BluetoothDaemonA2dpInterface()
|
|
{ }
|
|
|
|
class BluetoothDaemonA2dpInterface::InitResultHandler final
|
|
: public BluetoothSetupResultHandler
|
|
{
|
|
public:
|
|
InitResultHandler(BluetoothA2dpResultHandler* aRes)
|
|
: mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mRes);
|
|
}
|
|
|
|
void OnError(BluetoothStatus aStatus) override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mRes->OnError(aStatus);
|
|
}
|
|
|
|
void RegisterModule() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mRes->Init();
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<BluetoothA2dpResultHandler> mRes;
|
|
};
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::Init(
|
|
BluetoothA2dpNotificationHandler* aNotificationHandler,
|
|
BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
// Set notification handler _before_ registering the module. It could
|
|
// happen that we receive notifications, before the result handler runs.
|
|
mModule->SetNotificationHandler(aNotificationHandler);
|
|
|
|
InitResultHandler* res;
|
|
|
|
if (aRes) {
|
|
res = new InitResultHandler(aRes);
|
|
} else {
|
|
// We don't need a result handler if the caller is not interested.
|
|
res = nullptr;
|
|
}
|
|
|
|
nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
|
|
0x00, BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS, res);
|
|
if (NS_FAILED(rv) && aRes) {
|
|
DispatchError(aRes, rv);
|
|
}
|
|
}
|
|
|
|
class BluetoothDaemonA2dpInterface::CleanupResultHandler final
|
|
: public BluetoothSetupResultHandler
|
|
{
|
|
public:
|
|
CleanupResultHandler(BluetoothDaemonA2dpModule* aModule,
|
|
BluetoothA2dpResultHandler* aRes)
|
|
: mModule(aModule)
|
|
, mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mModule);
|
|
}
|
|
|
|
void OnError(BluetoothStatus aStatus) override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mRes) {
|
|
mRes->OnError(aStatus);
|
|
}
|
|
}
|
|
|
|
void UnregisterModule() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Clear notification handler _after_ module has been
|
|
// unregistered. While unregistering the module, we might
|
|
// still receive notifications.
|
|
mModule->SetNotificationHandler(nullptr);
|
|
|
|
if (mRes) {
|
|
mRes->Cleanup();
|
|
}
|
|
}
|
|
|
|
private:
|
|
BluetoothDaemonA2dpModule* mModule;
|
|
nsRefPtr<BluetoothA2dpResultHandler> mRes;
|
|
};
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::Cleanup(
|
|
BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
nsresult rv = mModule->UnregisterModule(
|
|
BluetoothDaemonA2dpModule::SERVICE_ID,
|
|
new CleanupResultHandler(mModule, aRes));
|
|
if (NS_FAILED(rv)) {
|
|
DispatchError(aRes, rv);
|
|
}
|
|
}
|
|
|
|
/* Connect / Disconnect */
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::Connect(
|
|
const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(mModule);
|
|
|
|
nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
|
|
if (NS_FAILED(rv)) {
|
|
DispatchError(aRes, rv);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::Disconnect(
|
|
const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(mModule);
|
|
|
|
nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
|
|
if (NS_FAILED(rv)) {
|
|
DispatchError(aRes, rv);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::DispatchError(
|
|
BluetoothA2dpResultHandler* aRes, BluetoothStatus aStatus)
|
|
{
|
|
DaemonResultRunnable1<BluetoothA2dpResultHandler, void,
|
|
BluetoothStatus, BluetoothStatus>::Dispatch(
|
|
aRes, &BluetoothA2dpResultHandler::OnError,
|
|
ConstantInitOp1<BluetoothStatus>(aStatus));
|
|
}
|
|
|
|
void
|
|
BluetoothDaemonA2dpInterface::DispatchError(
|
|
BluetoothA2dpResultHandler* aRes, nsresult aRv)
|
|
{
|
|
BluetoothStatus status;
|
|
|
|
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
|
|
status = STATUS_FAIL;
|
|
}
|
|
DispatchError(aRes, status);
|
|
}
|
|
|
|
END_BLUETOOTH_NAMESPACE
|