michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: #include "BluetoothHidManager.h" michael@0: michael@0: #include "BluetoothCommon.h" michael@0: #include "BluetoothService.h" michael@0: #include "BluetoothUtils.h" michael@0: michael@0: #include "mozilla/dom/bluetooth/BluetoothTypes.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "nsIObserverService.h" michael@0: #include "MainThreadUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: USING_BLUETOOTH_NAMESPACE michael@0: michael@0: namespace { michael@0: StaticRefPtr sBluetoothHidManager; michael@0: bool sInShutdown = false; michael@0: } // anonymous namespace michael@0: michael@0: NS_IMETHODIMP michael@0: BluetoothHidManager::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: MOZ_ASSERT(sBluetoothHidManager); michael@0: michael@0: if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { michael@0: HandleShutdown(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: BluetoothHidManager::BluetoothHidManager() michael@0: { michael@0: Reset(); michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::Reset() michael@0: { michael@0: mConnected = false; michael@0: mController = nullptr; michael@0: } michael@0: michael@0: bool michael@0: BluetoothHidManager::Init() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: NS_ENSURE_TRUE(obs, false); michael@0: if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) { michael@0: BT_WARNING("Failed to add shutdown observer!"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: BluetoothHidManager::~BluetoothHidManager() michael@0: { michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: NS_ENSURE_TRUE_VOID(obs); michael@0: if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) { michael@0: BT_WARNING("Failed to remove shutdown observer!"); michael@0: } michael@0: } michael@0: michael@0: //static michael@0: BluetoothHidManager* michael@0: BluetoothHidManager::Get() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // If we already exist, exit early michael@0: if (sBluetoothHidManager) { michael@0: return sBluetoothHidManager; michael@0: } michael@0: michael@0: // If we're in shutdown, don't create a new instance michael@0: NS_ENSURE_FALSE(sInShutdown, nullptr); michael@0: michael@0: // Create a new instance, register, and return michael@0: BluetoothHidManager* manager = new BluetoothHidManager(); michael@0: NS_ENSURE_TRUE(manager->Init(), nullptr); michael@0: michael@0: sBluetoothHidManager = manager; michael@0: return sBluetoothHidManager; michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::HandleShutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: sInShutdown = true; michael@0: Disconnect(nullptr); michael@0: sBluetoothHidManager = nullptr; michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::Connect(const nsAString& aDeviceAddress, michael@0: BluetoothProfileController* aController) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!aDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(aController && !mController); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: if (!bs || sInShutdown) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); michael@0: return; michael@0: } michael@0: michael@0: if (mConnected) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED)); michael@0: return; michael@0: } michael@0: michael@0: mDeviceAddress = aDeviceAddress; michael@0: mController = aController; michael@0: michael@0: if (NS_FAILED(bs->SendInputMessage(aDeviceAddress, michael@0: NS_LITERAL_STRING("Connect")))) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::Disconnect(BluetoothProfileController* aController) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: if (!bs) { michael@0: if (aController) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (!mConnected) { michael@0: if (aController) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED)); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(!mDeviceAddress.IsEmpty()); michael@0: MOZ_ASSERT(!mController); michael@0: michael@0: mController = aController; michael@0: michael@0: if (NS_FAILED(bs->SendInputMessage(mDeviceAddress, michael@0: NS_LITERAL_STRING("Disconnect")))) { michael@0: aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::OnConnect(const nsAString& aErrorStr) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: /** michael@0: * On the one hand, notify the controller that we've done for outbound michael@0: * connections. On the other hand, we do nothing for inbound connections. michael@0: */ michael@0: NS_ENSURE_TRUE_VOID(mController); michael@0: michael@0: nsRefPtr controller = mController.forget(); michael@0: controller->NotifyCompletion(aErrorStr); michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::OnDisconnect(const nsAString& aErrorStr) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: /** michael@0: * On the one hand, notify the controller that we've done for outbound michael@0: * connections. On the other hand, we do nothing for inbound connections. michael@0: */ michael@0: NS_ENSURE_TRUE_VOID(mController); michael@0: michael@0: nsRefPtr controller = mController.forget(); michael@0: controller->NotifyCompletion(aErrorStr); michael@0: } michael@0: michael@0: bool michael@0: BluetoothHidManager::IsConnected() michael@0: { michael@0: return mConnected; michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::HandleInputPropertyChanged(const BluetoothSignal& aSignal) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue); michael@0: michael@0: const InfallibleTArray& arr = michael@0: aSignal.value().get_ArrayOfBluetoothNamedValue(); michael@0: MOZ_ASSERT(arr.Length() == 1); michael@0: michael@0: const nsString& name = arr[0].name(); michael@0: const BluetoothValue& value = arr[0].value(); michael@0: michael@0: if (name.EqualsLiteral("Connected")) { michael@0: MOZ_ASSERT(value.type() == BluetoothValue::Tbool); michael@0: MOZ_ASSERT(mConnected != value.get_bool()); michael@0: michael@0: mConnected = value.get_bool(); michael@0: NotifyStatusChanged(); michael@0: if (mConnected) { michael@0: OnConnect(EmptyString()); michael@0: } else { michael@0: OnDisconnect(EmptyString()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::NotifyStatusChanged() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: NS_NAMED_LITERAL_STRING(type, BLUETOOTH_HID_STATUS_CHANGED_ID); michael@0: InfallibleTArray parameters; michael@0: michael@0: BT_APPEND_NAMED_VALUE(parameters, "connected", mConnected); michael@0: BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress); michael@0: michael@0: BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::OnGetServiceChannel(const nsAString& aDeviceAddress, michael@0: const nsAString& aServiceUuid, michael@0: int aChannel) michael@0: { michael@0: // Do nothing here as bluez acquires service channel and connects for us michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress) michael@0: { michael@0: // Do nothing here as bluez acquires service channel and connects for us michael@0: } michael@0: michael@0: void michael@0: BluetoothHidManager::GetAddress(nsAString& aDeviceAddress) michael@0: { michael@0: aDeviceAddress = mDeviceAddress; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(BluetoothHidManager, nsIObserver)