diff --git a/dom/bluetooth/BluetoothCommon.h b/dom/bluetooth/BluetoothCommon.h index 271de34689ce..0fd4100dfc40 100644 --- a/dom/bluetooth/BluetoothCommon.h +++ b/dom/bluetooth/BluetoothCommon.h @@ -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" /** diff --git a/dom/bluetooth/BluetoothHidManager.cpp b/dom/bluetooth/BluetoothHidManager.cpp new file mode 100644 index 000000000000..ec70e9d06b30 --- /dev/null +++ b/dom/bluetooth/BluetoothHidManager.cpp @@ -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 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 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 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& 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 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) diff --git a/dom/bluetooth/BluetoothHidManager.h b/dom/bluetooth/BluetoothHidManager.h new file mode 100644 index 000000000000..48095be58eb1 --- /dev/null +++ b/dom/bluetooth/BluetoothHidManager.h @@ -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__ diff --git a/dom/bluetooth/BluetoothService.h b/dom/bluetooth/BluetoothService.h index addfc0bece09..63c6d872f014 100644 --- a/dom/bluetooth/BluetoothService.h +++ b/dom/bluetooth/BluetoothService.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 { diff --git a/dom/bluetooth/BluetoothUuid.h b/dom/bluetooth/BluetoothUuid.h index 1cc3760aa08d..2cbf11b2e5ee 100644 --- a/dom/bluetooth/BluetoothUuid.h +++ b/dom/bluetooth/BluetoothUuid.h @@ -26,7 +26,8 @@ enum BluetoothServiceClass HEADSET_AG = 0x1112, HANDSFREE = 0x111E, HANDSFREE_AG = 0x111F, - OBJECT_PUSH = 0x1105 + OBJECT_PUSH = 0x1105, + HID = 0x1124, }; class BluetoothUuidHelper diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp index 9184e84dc86d..61f8d02f8cd4 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp @@ -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, diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h index e8af2460feda..7b55c5b4eb25 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h @@ -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(); diff --git a/dom/bluetooth/linux/BluetoothDBusService.cpp b/dom/bluetooth/linux/BluetoothDBusService.cpp index a1de72c739d5..7e0aa2174439 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/linux/BluetoothDBusService.cpp @@ -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 runnable(aRunnable); + + nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); + bool ret = dbus_func_args_async(mConnection, + -1, + callback, + static_cast(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 { diff --git a/dom/bluetooth/linux/BluetoothDBusService.h b/dom/bluetooth/linux/BluetoothDBusService.h index 5da45ecc60b3..c6ba429a6ebe 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.h +++ b/dom/bluetooth/linux/BluetoothDBusService.h @@ -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 diff --git a/dom/bluetooth/moz.build b/dom/bluetooth/moz.build index 6976b7b8f512..6527f4bfdb75 100644 --- a/dom/bluetooth/moz.build +++ b/dom/bluetooth/moz.build @@ -37,6 +37,7 @@ if CONFIG['MOZ_B2G_BT']: 'BluetoothUnixSocketConnector.cpp', 'BluetoothA2dpManager.cpp', 'BluetoothHfpManager.cpp', + 'BluetoothHidManager.cpp', 'BluetoothOppManager.cpp', 'ObexBase.cpp', 'BluetoothUuid.cpp', diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index 655c3adccf98..7cd6f89de302 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -49,6 +49,9 @@ this.SystemMessagePermissionsTable = { "bluetooth-hfp-status-changed": { "bluetooth": [] }, + "bluetooth-hid-status-changed": { + "bluetooth": [] + }, "bluetooth-sco-status-changed": { "bluetooth": [] },