Bug 899014 - [Bluetooth][HID] HID dom bluetooth implementation, r=echou

This commit is contained in:
Ben Tian 2013-08-12 17:32:25 +08:00
Родитель 68800c9375
Коммит 79f2e1f7bf
11 изменённых файлов: 393 добавлений и 5 удалений

Просмотреть файл

@ -57,6 +57,7 @@ extern bool gBluetoothDebugFlag;
*/
#define BLUETOOTH_A2DP_STATUS_CHANGED_ID "bluetooth-a2dp-status-changed"
#define BLUETOOTH_HFP_STATUS_CHANGED_ID "bluetooth-hfp-status-changed"
#define BLUETOOTH_HID_STATUS_CHANGED_ID "bluetooth-hid-status-changed"
#define BLUETOOTH_SCO_STATUS_CHANGED_ID "bluetooth-sco-status-changed"
/**

Просмотреть файл

@ -0,0 +1,207 @@
/* -*- 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 "BluetoothHidManager.h"
#include "BluetoothCommon.h"
#include "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsIObserverService.h"
using namespace mozilla;
USING_BLUETOOTH_NAMESPACE
namespace {
StaticRefPtr<BluetoothHidManager> sBluetoothHidManager;
bool sInShutdown = false;
} // anonymous namespace
NS_IMETHODIMP
BluetoothHidManager::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
MOZ_ASSERT(sBluetoothHidManager);
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
HandleShutdown();
return NS_OK;
}
MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
BluetoothHidManager::BluetoothHidManager()
: mConnected(false)
{
}
bool
BluetoothHidManager::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, false);
if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
BT_WARNING("Failed to add shutdown observer!");
return false;
}
return true;
}
BluetoothHidManager::~BluetoothHidManager()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE_VOID(obs);
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
BT_WARNING("Failed to remove shutdown observer!");
}
}
//static
BluetoothHidManager*
BluetoothHidManager::Get()
{
MOZ_ASSERT(NS_IsMainThread());
// If we already exist, exit early
if (sBluetoothHidManager) {
return sBluetoothHidManager;
}
// If we're in shutdown, don't create a new instance
NS_ENSURE_FALSE(sInShutdown, nullptr);
// Create a new instance, register, and return
BluetoothHidManager* manager = new BluetoothHidManager();
NS_ENSURE_TRUE(manager->Init(), nullptr);
sBluetoothHidManager = manager;
return sBluetoothHidManager;
}
void
BluetoothHidManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
sInShutdown = true;
Disconnect();
sBluetoothHidManager = nullptr;
}
bool
BluetoothHidManager::Connect(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
NS_ENSURE_FALSE(sInShutdown, false);
NS_ENSURE_FALSE(mConnected, false);
mDeviceAddress = aDeviceAddress;
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, false);
nsresult rv = bs->SendInputMessage(aDeviceAddress,
NS_LITERAL_STRING("Connect"),
aRunnable);
return NS_SUCCEEDED(rv);
}
void
BluetoothHidManager::Disconnect()
{
NS_ENSURE_TRUE_VOID(mConnected);
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE_VOID(bs);
bs->SendInputMessage(mDeviceAddress,
NS_LITERAL_STRING("Disconnect"),
nullptr);
}
bool BluetoothHidManager::IsConnected()
{
return mConnected;
}
void BluetoothHidManager::HandleInputPropertyChanged(const BluetoothSignal& aSignal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
const InfallibleTArray<BluetoothNamedValue>& arr =
aSignal.value().get_ArrayOfBluetoothNamedValue();
MOZ_ASSERT(arr.Length() == 1);
const nsString& name = arr[0].name();
const BluetoothValue& value = arr[0].value();
if (name.EqualsLiteral("Connected")) {
MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
MOZ_ASSERT(mConnected != value.get_bool());
mConnected = value.get_bool();
NotifyStatusChanged();
}
}
void
BluetoothHidManager::NotifyStatusChanged()
{
MOZ_ASSERT(NS_IsMainThread());
NS_NAMED_LITERAL_STRING(type, BLUETOOTH_HID_STATUS_CHANGED_ID);
InfallibleTArray<BluetoothNamedValue> parameters;
BluetoothValue v = mConnected;
parameters.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("connected"), v));
v = mDeviceAddress;
parameters.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), v));
if (!BroadcastSystemMessage(type, parameters)) {
NS_WARNING("Failed to broadcast system message to settings");
return;
}
}
void
BluetoothHidManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel)
{
// Do nothing here as bluez acquires service channel and connects for us
}
void
BluetoothHidManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
{
// Do nothing here as bluez acquires service channel and connects for us
}
void
BluetoothHidManager::GetAddress(nsAString& aDeviceAddress)
{
aDeviceAddress = mDeviceAddress;
}
NS_IMPL_ISUPPORTS1(BluetoothHidManager, nsIObserver)

Просмотреть файл

@ -0,0 +1,53 @@
/* -*- 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/. */
#ifndef mozilla_dom_bluetooth_bluetoothhidmanager_h__
#define mozilla_dom_bluetooth_bluetoothhidmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothProfileManagerBase.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothReplyRunnable;
class BluetoothHidManager : public BluetoothProfileManagerBase
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static BluetoothHidManager* Get();
~BluetoothHidManager();
virtual void OnGetServiceChannel(const nsAString& aDeviceAddress,
const nsAString& aServiceUuid,
int aChannel) MOZ_OVERRIDE;
virtual void OnUpdateSdpRecords(const nsAString& aDeviceAddress) MOZ_OVERRIDE;
virtual void GetAddress(nsAString& aDeviceAddress) MOZ_OVERRIDE;
virtual bool IsConnected() MOZ_OVERRIDE;
bool Connect(const nsAString& aDeviceAddress,
BluetoothReplyRunnable* aRunnable);
void Disconnect();
void HandleInputPropertyChanged(const BluetoothSignal& aSignal);
private:
BluetoothHidManager();
bool Init();
void Cleanup();
void HandleShutdown();
void NotifyStatusChanged();
// data member
bool mConnected;
nsString mDeviceAddress;
};
END_BLUETOOTH_NAMESPACE
#endif //#ifndef mozilla_dom_bluetooth_bluetoothhidmanager_h__

Просмотреть файл

@ -294,6 +294,11 @@ public:
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) = 0;
virtual nsresult
SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable) = 0;
bool
IsEnabled() const
{

Просмотреть файл

@ -26,7 +26,8 @@ enum BluetoothServiceClass
HEADSET_AG = 0x1112,
HANDSFREE = 0x111E,
HANDSFREE_AG = 0x111F,
OBJECT_PUSH = 0x1105
OBJECT_PUSH = 0x1105,
HID = 0x1124,
};
class BluetoothUuidHelper

Просмотреть файл

@ -415,6 +415,14 @@ BluetoothServiceChildProcess::SendSinkMessage(const nsAString& aDeviceAddresses,
MOZ_CRASH("This should never be called!");
}
nsresult
BluetoothServiceChildProcess::SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable)
{
MOZ_CRASH("This should never be called!");
}
void
BluetoothServiceChildProcess::UpdatePlayStatus(uint32_t aDuration,
uint32_t aPosition,

Просмотреть файл

@ -176,6 +176,11 @@ public:
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) MOZ_OVERRIDE;
virtual nsresult
SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
protected:
BluetoothServiceChildProcess();
virtual ~BluetoothServiceChildProcess();

Просмотреть файл

@ -20,6 +20,7 @@
#include "BluetoothDBusService.h"
#include "BluetoothA2dpManager.h"
#include "BluetoothHfpManager.h"
#include "BluetoothHidManager.h"
#include "BluetoothOppManager.h"
#include "BluetoothReplyRunnable.h"
#include "BluetoothUnixSocketConnector.h"
@ -69,6 +70,7 @@ USING_BLUETOOTH_NAMESPACE
#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
#define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink"
#define DBUS_CTL_IFACE BLUEZ_DBUS_BASE_IFC ".Control"
#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
#define BLUEZ_DBUS_BASE_PATH "/org/bluez"
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
#define BLUEZ_ERROR_IFC "org.bluez.Error"
@ -139,6 +141,10 @@ static Properties sControlProperties[] = {
{"Connected", DBUS_TYPE_BOOLEAN}
};
static Properties sInputProperties[] = {
{"Connected", DBUS_TYPE_BOOLEAN}
};
static const char* sBluetoothDBusIfaces[] =
{
DBUS_MANAGER_IFACE,
@ -175,7 +181,6 @@ static Monitor sStopBluetoothMonitor("BluetoothService.sStopBluetoothMonitor");
typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
typedef bool (*FilterFunc)(const BluetoothValue&);
typedef void (*SinkCallback)(DBusMessage*, void*);
static bool
GetConnectedDevicesFilter(const BluetoothValue& aValue)
@ -290,6 +295,35 @@ private:
BluetoothSignal mSignal;
};
class InputPropertyChangedHandler : public nsRunnable
{
public:
InputPropertyChangedHandler(const BluetoothSignal& aSignal)
: mSignal(aSignal)
{
}
NS_IMETHOD
Run()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged"));
MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue);
// Replace object path with device address
nsString address = GetAddressFromObjectPath(mSignal.path());
mSignal.path() = address;
BluetoothHidManager* hid = BluetoothHidManager::Get();
NS_ENSURE_TRUE(hid, NS_ERROR_FAILURE);
hid->HandleInputPropertyChanged(mSignal);
return NS_OK;
}
private:
BluetoothSignal mSignal;
};
static bool
IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
{
@ -489,6 +523,15 @@ CheckForError(DBusMessage* aMsg, void *aParam, const nsAString& aError)
}
#endif
static void
InputDisconnectCallback(DBusMessage* aMsg, void* aParam)
{
#ifdef DEBUG
NS_NAMED_LITERAL_STRING(errorStr, "Failed to disconnect input device");
CheckForError(aMsg, aParam, errorStr);
#endif
}
static void
SinkConnectCallback(DBusMessage* aMsg, void* aParam)
{
@ -503,7 +546,7 @@ SinkDisconnectCallback(DBusMessage* aMsg, void* aParam)
{
#ifdef DEBUG
NS_NAMED_LITERAL_STRING(errorStr, "Failed to disconnect sink");
CheckForError(false, aMsg, errorStr);
CheckForError(aMsg, aParam, errorStr);
#endif
}
@ -1558,6 +1601,13 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
errorStr,
sControlProperties,
ArrayLength(sControlProperties));
} else if (dbus_message_is_signal(aMsg, DBUS_INPUT_IFACE,
"PropertyChanged")) {
ParsePropertyChange(aMsg,
v,
errorStr,
sInputProperties,
ArrayLength(sInputProperties));
} else {
errorStr = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg));
errorStr.AppendLiteral(" Signal not handled!");
@ -1574,6 +1624,8 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
task = new SinkPropertyChangedHandler(signal);
} else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) {
task = new ControlPropertyChangedHandler(signal);
} else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) {
task = new InputPropertyChangedHandler(signal);
} else {
task = new DistributeBluetoothSignalTask(signal);
}
@ -1882,6 +1934,43 @@ BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName,
return NS_OK;
}
nsresult
BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mConnection);
MOZ_ASSERT(aMessage.EqualsLiteral("Connect") ||
aMessage.EqualsLiteral("Disconnect"));
NS_ENSURE_TRUE(IsReady(), NS_ERROR_FAILURE);
DBusCallback callback;
if (aMessage.EqualsLiteral("Connect")) {
callback = GetVoidCallback;
} else if (aMessage.EqualsLiteral("Disconnect")) {
callback = InputDisconnectCallback;
}
nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress);
bool ret = dbus_func_args_async(mConnection,
-1,
callback,
static_cast<void*>(runnable.get()),
NS_ConvertUTF16toUTF8(objectPath).get(),
DBUS_INPUT_IFACE,
NS_ConvertUTF16toUTF8(aMessage).get(),
DBUS_TYPE_INVALID);
NS_ENSURE_TRUE(ret, NS_ERROR_FAILURE);
runnable.forget();
return NS_OK;
}
nsresult
BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
const nsAString& aMessage)
@ -1890,7 +1979,7 @@ BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
MOZ_ASSERT(mConnection);
MOZ_ASSERT(IsEnabled());
SinkCallback callback;
DBusCallback callback;
if (aMessage.EqualsLiteral("Connect")) {
callback = SinkConnectCallback;
} else if (aMessage.EqualsLiteral("Disconnect")) {
@ -1909,8 +1998,8 @@ BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
DBUS_SINK_IFACE,
NS_ConvertUTF16toUTF8(aMessage).get(),
DBUS_TYPE_INVALID);
NS_ENSURE_TRUE(ret, NS_ERROR_FAILURE);
return NS_OK;
}
@ -2022,6 +2111,8 @@ BluetoothDBusService::GetConnectedDevicePropertiesInternal(uint16_t aProfileId,
if (aProfileId == BluetoothServiceClass::HANDSFREE ||
aProfileId == BluetoothServiceClass::HEADSET) {
profile = BluetoothHfpManager::Get();
} else if (aProfileId == BluetoothServiceClass::HID) {
profile = BluetoothHidManager::Get();
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
profile = BluetoothOppManager::Get();
} else {
@ -2489,6 +2580,9 @@ BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
} else if (aProfileId == BluetoothServiceClass::HEADSET) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
hfp->Connect(aDeviceAddress, false, aRunnable);
} else if (aProfileId == BluetoothServiceClass::HID) {
BluetoothHidManager* hid = BluetoothHidManager::Get();
hid->Connect(aDeviceAddress, aRunnable);
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
BluetoothOppManager* opp = BluetoothOppManager::Get();
opp->Connect(aDeviceAddress, aRunnable);
@ -2508,6 +2602,9 @@ BluetoothDBusService::Disconnect(const uint16_t aProfileId,
aProfileId == BluetoothServiceClass::HEADSET) {
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
hfp->Disconnect();
} else if (aProfileId == BluetoothServiceClass::HID) {
BluetoothHidManager* hid = BluetoothHidManager::Get();
hid->Disconnect();
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
BluetoothOppManager* opp = BluetoothOppManager::Get();
opp->Disconnect();
@ -2531,6 +2628,8 @@ BluetoothDBusService::IsConnected(const uint16_t aProfileId)
if (aProfileId == BluetoothServiceClass::HANDSFREE ||
aProfileId == BluetoothServiceClass::HEADSET) {
profile = BluetoothHfpManager::Get();
} else if (aProfileId == BluetoothServiceClass::HID) {
profile = BluetoothHidManager::Get();
} else if (aProfileId == BluetoothServiceClass::OBJECT_PUSH) {
profile = BluetoothOppManager::Get();
} else {

Просмотреть файл

@ -162,6 +162,11 @@ public:
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) MOZ_OVERRIDE;
virtual nsresult
SendInputMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
private:
/**
* For DBus Control method of "UpdateNotification", event id should be

Просмотреть файл

@ -37,6 +37,7 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothUnixSocketConnector.cpp',
'BluetoothA2dpManager.cpp',
'BluetoothHfpManager.cpp',
'BluetoothHidManager.cpp',
'BluetoothOppManager.cpp',
'ObexBase.cpp',
'BluetoothUuid.cpp',

Просмотреть файл

@ -49,6 +49,9 @@ this.SystemMessagePermissionsTable = {
"bluetooth-hfp-status-changed": {
"bluetooth": []
},
"bluetooth-hid-status-changed": {
"bluetooth": []
},
"bluetooth-sco-status-changed": {
"bluetooth": []
},