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 "BluetoothService.h" michael@0: michael@0: #include "BluetoothCommon.h" michael@0: #include "BluetoothA2dpManager.h" michael@0: #include "BluetoothHfpManager.h" michael@0: #include "BluetoothHidManager.h" michael@0: #include "BluetoothManager.h" michael@0: #include "BluetoothOppManager.h" michael@0: #include "BluetoothParent.h" michael@0: #include "BluetoothReplyRunnable.h" michael@0: #include "BluetoothServiceChildProcess.h" michael@0: #include "BluetoothUtils.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/bluetooth/BluetoothTypes.h" michael@0: #include "mozilla/ipc/UnixSocket.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISettingsService.h" michael@0: #include "nsISystemMessagesInternal.h" michael@0: #include "nsITimer.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsXPCOM.h" michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include "cutils/properties.h" michael@0: #endif michael@0: michael@0: #if defined(MOZ_B2G_BT) michael@0: #if defined(MOZ_B2G_BT_BLUEZ) michael@0: /** michael@0: * B2G blueZ: michael@0: * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined. michael@0: */ michael@0: #include "BluetoothDBusService.h" michael@0: #elif defined(MOZ_B2G_BT_BLUEDROID) michael@0: /** michael@0: * B2G bluedroid: michael@0: * MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined; michael@0: * MOZ_B2G_BLUEZ is not defined. michael@0: */ michael@0: #include "BluetoothServiceBluedroid.h" michael@0: #endif michael@0: #elif defined(MOZ_BLUETOOTH_DBUS) michael@0: /** michael@0: * Desktop bluetooth: michael@0: * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined. michael@0: */ michael@0: #include "BluetoothDBusService.h" michael@0: #else michael@0: #error No backend michael@0: #endif michael@0: michael@0: #define MOZSETTINGS_CHANGED_ID "mozsettings-changed" michael@0: #define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled" michael@0: #define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled" michael@0: michael@0: #define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled" michael@0: michael@0: #define DEFAULT_SHUTDOWN_TIMER_MS 5000 michael@0: michael@0: bool gBluetoothDebugFlag = false; michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: USING_BLUETOOTH_NAMESPACE michael@0: michael@0: namespace { michael@0: michael@0: StaticRefPtr sBluetoothService; michael@0: michael@0: bool sInShutdown = false; michael@0: bool sToggleInProgress = false; michael@0: michael@0: bool michael@0: IsMainProcess() michael@0: { michael@0: return XRE_GetProcessType() == GeckoProcessType_Default; michael@0: } michael@0: michael@0: void michael@0: ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: *static_cast(aClosure) = true; michael@0: } michael@0: michael@0: void michael@0: GetAllBluetoothActors(InfallibleTArray& aActors) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aActors.IsEmpty()); michael@0: michael@0: nsAutoTArray contentActors; michael@0: ContentParent::GetAll(contentActors); michael@0: michael@0: for (uint32_t contentIndex = 0; michael@0: contentIndex < contentActors.Length(); michael@0: contentIndex++) { michael@0: MOZ_ASSERT(contentActors[contentIndex]); michael@0: michael@0: AutoInfallibleTArray bluetoothActors; michael@0: contentActors[contentIndex]->ManagedPBluetoothParent(bluetoothActors); michael@0: michael@0: for (uint32_t bluetoothIndex = 0; michael@0: bluetoothIndex < bluetoothActors.Length(); michael@0: bluetoothIndex++) { michael@0: MOZ_ASSERT(bluetoothActors[bluetoothIndex]); michael@0: michael@0: BluetoothParent* actor = michael@0: static_cast(bluetoothActors[bluetoothIndex]); michael@0: aActors.AppendElement(actor); michael@0: } michael@0: } michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled) michael@0: : mEnabled(aEnabled) michael@0: { } michael@0: michael@0: NS_METHOD michael@0: BluetoothService::ToggleBtAck::Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // This is requested in Bug 836516. With settings this property, WLAN michael@0: // firmware could be aware of Bluetooth has been turned on/off, so that the michael@0: // mecahnism of handling coexistence of WIFI and Bluetooth could be started. michael@0: // michael@0: // In the future, we may have our own way instead of setting a system michael@0: // property to let firmware developers be able to sense that Bluetooth has michael@0: // been toggled. michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) { michael@0: BT_WARNING("Failed to set bluetooth enabled property"); michael@0: } michael@0: #endif michael@0: michael@0: NS_ENSURE_TRUE(sBluetoothService, NS_OK); michael@0: michael@0: if (sInShutdown) { michael@0: sBluetoothService = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Update mEnabled of BluetoothService object since michael@0: // StartInternal/StopInternal have been already done. michael@0: sBluetoothService->SetEnabled(mEnabled); michael@0: sToggleInProgress = false; michael@0: michael@0: nsAutoString signalName; michael@0: signalName = mEnabled ? NS_LITERAL_STRING("Enabled") michael@0: : NS_LITERAL_STRING("Disabled"); michael@0: BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true); michael@0: sBluetoothService->DistributeSignal(signal); michael@0: michael@0: // Event 'AdapterAdded' has to be fired after firing 'Enabled' michael@0: sBluetoothService->TryFiringAdapterAdded(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class BluetoothService::StartupTask : public nsISettingsServiceCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_IMETHOD Handle(const nsAString& aName, JS::Handle aResult) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!aResult.isBoolean()) { michael@0: BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING "' is not a boolean!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // It is theoretically possible to shut down before the first settings check michael@0: // has completed (though extremely unlikely). michael@0: if (sBluetoothService) { michael@0: return sBluetoothService->HandleStartupSettingsCheck(aResult.toBoolean()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD HandleError(const nsAString& aName) michael@0: { michael@0: BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'"); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback); michael@0: michael@0: NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver) michael@0: michael@0: bool michael@0: BluetoothService::IsToggling() const michael@0: { michael@0: return sToggleInProgress; michael@0: } michael@0: michael@0: BluetoothService::~BluetoothService() michael@0: { michael@0: Cleanup(); michael@0: } michael@0: michael@0: PLDHashOperator michael@0: RemoveObserversExceptBluetoothManager michael@0: (const nsAString& key, michael@0: nsAutoPtr& value, michael@0: void* arg) michael@0: { michael@0: if (!key.EqualsLiteral(KEY_MANAGER)) { michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::RemoveObserverFromTable(const nsAString& key) michael@0: { michael@0: mBluetoothSignalObserverTable.Remove(key); michael@0: } michael@0: michael@0: // static michael@0: BluetoothService* michael@0: BluetoothService::Create() michael@0: { michael@0: #if defined(MOZ_B2G_BT) michael@0: if (!IsMainProcess()) { michael@0: return BluetoothServiceChildProcess::Create(); michael@0: } michael@0: michael@0: #if defined(MOZ_B2G_BT_BLUEZ) michael@0: return new BluetoothDBusService(); michael@0: #elif defined(MOZ_B2G_BT_BLUEDROID) michael@0: return new BluetoothServiceBluedroid(); michael@0: #endif michael@0: #elif defined(MOZ_BLUETOOTH_DBUS) michael@0: return new BluetoothDBusService(); michael@0: #endif michael@0: michael@0: BT_WARNING("No platform support for bluetooth!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: BluetoothService::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: michael@0: if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, michael@0: false))) { michael@0: BT_WARNING("Failed to add shutdown observer!"); michael@0: return false; michael@0: } michael@0: michael@0: // Only the main process should observe bluetooth settings changes. michael@0: if (IsMainProcess() && michael@0: NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) { michael@0: BT_WARNING("Failed to add settings change observer!"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::Cleanup() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs && michael@0: (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) || michael@0: NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) { michael@0: BT_WARNING("Can't unregister observers!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothService::RegisterBluetoothSignalHandler( michael@0: const nsAString& aNodeName, michael@0: BluetoothSignalObserver* aHandler) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aHandler); michael@0: michael@0: BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get()); michael@0: michael@0: BluetoothSignalObserverList* ol; michael@0: if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) { michael@0: ol = new BluetoothSignalObserverList(); michael@0: mBluetoothSignalObserverTable.Put(aNodeName, ol); michael@0: } michael@0: michael@0: ol->AddObserver(aHandler); michael@0: } michael@0: michael@0: void michael@0: BluetoothService::UnregisterBluetoothSignalHandler( michael@0: const nsAString& aNodeName, michael@0: BluetoothSignalObserver* aHandler) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aHandler); michael@0: michael@0: BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get()); michael@0: michael@0: BluetoothSignalObserverList* ol; michael@0: if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) { michael@0: ol->RemoveObserver(aHandler); michael@0: // We shouldn't have duplicate instances in the ObserverList, but there's michael@0: // no appropriate way to do duplication check while registering, so michael@0: // assertions are added here. michael@0: MOZ_ASSERT(!ol->RemoveObserver(aHandler)); michael@0: if (ol->Length() == 0) { michael@0: mBluetoothSignalObserverTable.Remove(aNodeName); michael@0: } michael@0: } michael@0: else { michael@0: BT_WARNING("Node was never registered!"); michael@0: } michael@0: } michael@0: michael@0: PLDHashOperator michael@0: RemoveAllSignalHandlers(const nsAString& aKey, michael@0: nsAutoPtr& aData, michael@0: void* aUserArg) michael@0: { michael@0: BluetoothSignalObserver* handler = static_cast(aUserArg); michael@0: aData->RemoveObserver(handler); michael@0: // We shouldn't have duplicate instances in the ObserverList, but there's michael@0: // no appropriate way to do duplication check while registering, so michael@0: // assertions are added here. michael@0: MOZ_ASSERT(!aData->RemoveObserver(handler)); michael@0: return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aHandler); michael@0: michael@0: mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler); michael@0: } michael@0: michael@0: void michael@0: BluetoothService::DistributeSignal(const BluetoothSignal& aSignal) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) { michael@0: Notify(aSignal); michael@0: return; michael@0: } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) { michael@0: Notify(aSignal); michael@0: return; michael@0: } michael@0: michael@0: BluetoothSignalObserverList* ol; michael@0: if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) { michael@0: #if DEBUG michael@0: nsAutoCString msg("No observer registered for path "); michael@0: msg.Append(NS_ConvertUTF16toUTF8(aSignal.path())); michael@0: BT_WARNING(msg.get()); michael@0: #endif michael@0: return; michael@0: } michael@0: MOZ_ASSERT(ol->Length()); michael@0: ol->Broadcast(aSignal); michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::StartBluetooth(bool aIsStartup) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (sInShutdown) { michael@0: // Don't try to start if we're already shutting down. michael@0: MOZ_ASSERT(false, "Start called while in shutdown!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mAdapterAddedReceived = false; michael@0: michael@0: /* When IsEnabled() is true, we don't switch on Bluetooth but we still michael@0: * send ToggleBtAck task. One special case happens at startup stage. At michael@0: * startup, the initialization of BluetoothService still has to be done michael@0: * even if Bluetooth is already enabled. michael@0: * michael@0: * Please see bug 892392 for more information. michael@0: */ michael@0: if (aIsStartup || !sBluetoothService->IsEnabled()) { michael@0: // Switch Bluetooth on michael@0: if (NS_FAILED(sBluetoothService->StartInternal())) { michael@0: BT_WARNING("Bluetooth service failed to start!"); michael@0: } michael@0: } else { michael@0: BT_WARNING("Bluetooth has already been enabled before."); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(true); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::StopBluetooth(bool aIsStartup) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: BluetoothProfileManagerBase* profile; michael@0: profile = BluetoothHfpManager::Get(); michael@0: NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); michael@0: if (profile->IsConnected()) { michael@0: profile->Disconnect(nullptr); michael@0: } else { michael@0: profile->Reset(); michael@0: } michael@0: michael@0: profile = BluetoothOppManager::Get(); michael@0: NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); michael@0: if (profile->IsConnected()) { michael@0: profile->Disconnect(nullptr); michael@0: } michael@0: michael@0: profile = BluetoothA2dpManager::Get(); michael@0: NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); michael@0: if (profile->IsConnected()) { michael@0: profile->Disconnect(nullptr); michael@0: } else { michael@0: profile->Reset(); michael@0: } michael@0: michael@0: profile = BluetoothHidManager::Get(); michael@0: NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); michael@0: if (profile->IsConnected()) { michael@0: profile->Disconnect(nullptr); michael@0: } else { michael@0: profile->Reset(); michael@0: } michael@0: michael@0: mAdapterAddedReceived = false; michael@0: michael@0: /* When IsEnabled() is false, we don't switch off Bluetooth but we still michael@0: * send ToggleBtAck task. One special case happens at startup stage. At michael@0: * startup, the initialization of BluetoothService still has to be done michael@0: * even if Bluetooth is disabled. michael@0: * michael@0: * Please see bug 892392 for more information. michael@0: */ michael@0: if (aIsStartup || sBluetoothService->IsEnabled()) { michael@0: // Switch Bluetooth off michael@0: if (NS_FAILED(sBluetoothService->StopInternal())) { michael@0: BT_WARNING("Bluetooth service failed to stop!"); michael@0: } michael@0: } else { michael@0: BT_WARNING("Bluetooth has already been enabled/disabled before."); michael@0: nsRefPtr runnable = new BluetoothService::ToggleBtAck(false); michael@0: if (NS_FAILED(NS_DispatchToMainThread(runnable))) { michael@0: BT_WARNING("Failed to dispatch to main thread!"); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup) michael@0: { michael@0: nsresult rv; michael@0: if (aStart) { michael@0: rv = StartBluetooth(aIsStartup); michael@0: } else { michael@0: rv = StopBluetooth(aIsStartup); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::SetEnabled(bool aEnabled) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: AutoInfallibleTArray childActors; michael@0: GetAllBluetoothActors(childActors); michael@0: michael@0: for (uint32_t index = 0; index < childActors.Length(); index++) { michael@0: unused << childActors[index]->SendEnabled(aEnabled); michael@0: } michael@0: michael@0: if (!aEnabled) { michael@0: /** michael@0: * Remove all handlers except BluetoothManager when turning off bluetooth michael@0: * since it is possible that the event 'onAdapterAdded' would be fired after michael@0: * BluetoothManagers of child process are registered. Please see Bug 827759 michael@0: * for more details. michael@0: */ michael@0: mBluetoothSignalObserverTable.Enumerate( michael@0: RemoveObserversExceptBluetoothManager, nullptr); michael@0: } michael@0: michael@0: /** michael@0: * mEnabled: real status of bluetooth michael@0: * aEnabled: expected status of bluetooth michael@0: */ michael@0: if (mEnabled == aEnabled) { michael@0: BT_WARNING("Bluetooth has already been enabled/disabled before " michael@0: "or the toggling is failed."); michael@0: } michael@0: michael@0: mEnabled = aEnabled; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::HandleStartup() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!sToggleInProgress); michael@0: michael@0: nsCOMPtr settings = michael@0: do_GetService("@mozilla.org/settingsService;1"); michael@0: NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr settingsLock; michael@0: nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr callback = new StartupTask(); michael@0: rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: sToggleInProgress = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::HandleStartupSettingsCheck(bool aEnable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: return StartStopBluetooth(aEnable, true); michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::HandleSettingsChanged(const nsAString& aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // The string that we're interested in will be a JSON string that looks like: michael@0: // {"key":"bluetooth.enabled","value":true} michael@0: michael@0: AutoSafeJSContext cx; michael@0: if (!cx) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: JS::Rooted val(cx); michael@0: if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) { michael@0: return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (!val.isObject()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: JS::Rooted obj(cx, &val.toObject()); michael@0: michael@0: JS::Rooted key(cx); michael@0: if (!JS_GetProperty(cx, obj, "key", &key)) { michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (!key.isString()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING michael@0: bool match; michael@0: if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) { michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (match) { michael@0: JS::Rooted value(cx); michael@0: if (!JS_GetProperty(cx, obj, "value", &value)) { michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (!value.isBoolean()) { michael@0: MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: SWITCH_BT_DEBUG(value.toBoolean()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Second, check if the string is BLUETOOTH_ENABLED_SETTING michael@0: if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) { michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (match) { michael@0: JS::Rooted value(cx); michael@0: if (!JS_GetProperty(cx, obj, "value", &value)) { michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (!value.isBoolean()) { michael@0: MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (sToggleInProgress || value.toBoolean() == IsEnabled()) { michael@0: // Nothing to do here. michael@0: return NS_OK; michael@0: } michael@0: michael@0: sToggleInProgress = true; michael@0: michael@0: nsresult rv = StartStopBluetooth(value.toBoolean(), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::HandleShutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // This is a two phase shutdown. First we notify all child processes that michael@0: // bluetooth is going away, and then we wait for them to acknowledge. Then we michael@0: // close down all the bluetooth machinery. michael@0: michael@0: sInShutdown = true; michael@0: michael@0: Cleanup(); michael@0: michael@0: AutoInfallibleTArray childActors; michael@0: GetAllBluetoothActors(childActors); michael@0: michael@0: if (!childActors.IsEmpty()) { michael@0: // Notify child processes that they should stop using bluetooth now. michael@0: for (uint32_t index = 0; index < childActors.Length(); index++) { michael@0: childActors[index]->BeginShutdown(); michael@0: } michael@0: michael@0: // Create a timer to ensure that we don't wait forever for a child process michael@0: // or the bluetooth threads to finish. If we don't get a timer or can't use michael@0: // it for some reason then we skip all the waiting entirely since we really michael@0: // can't afford to hang on shutdown. michael@0: nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: MOZ_ASSERT(timer); michael@0: michael@0: if (timer) { michael@0: bool timeExceeded = false; michael@0: michael@0: if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded, michael@0: &timeExceeded, michael@0: DEFAULT_SHUTDOWN_TIMER_MS, michael@0: nsITimer::TYPE_ONE_SHOT))) { michael@0: nsIThread* currentThread = NS_GetCurrentThread(); michael@0: MOZ_ASSERT(currentThread); michael@0: michael@0: // Wait for those child processes to acknowledge. michael@0: while (!timeExceeded && !childActors.IsEmpty()) { michael@0: if (!NS_ProcessNextEvent(currentThread)) { michael@0: MOZ_ASSERT(false, "Something horribly wrong here!"); michael@0: break; michael@0: } michael@0: GetAllBluetoothActors(childActors); michael@0: } michael@0: michael@0: if (NS_FAILED(timer->Cancel())) { michael@0: MOZ_CRASH("Failed to cancel shutdown timer, this will crash!"); michael@0: } michael@0: } michael@0: else { michael@0: MOZ_ASSERT(false, "Failed to initialize shutdown timer!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (IsEnabled() && NS_FAILED(StopBluetooth(false))) { michael@0: MOZ_ASSERT(false, "Failed to deliver stop message!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: BluetoothService* michael@0: BluetoothService::Get() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // If we already exist, exit early michael@0: if (sBluetoothService) { michael@0: return sBluetoothService; michael@0: } michael@0: michael@0: // If we're in shutdown, don't create a new instance michael@0: if (sInShutdown) { michael@0: BT_WARNING("BluetoothService can't be created during shutdown"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Create new instance, register, return michael@0: nsRefPtr service = BluetoothService::Create(); michael@0: NS_ENSURE_TRUE(service, nullptr); michael@0: michael@0: if (!service->Init()) { michael@0: service->Cleanup(); michael@0: return nullptr; michael@0: } michael@0: michael@0: sBluetoothService = service; michael@0: return sBluetoothService; michael@0: } michael@0: michael@0: nsresult michael@0: BluetoothService::Observe(nsISupports* aSubject, const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!strcmp(aTopic, "profile-after-change")) { michael@0: return HandleStartup(); michael@0: } michael@0: michael@0: if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) { michael@0: return HandleSettingsChanged(nsDependentString(aData)); michael@0: } michael@0: michael@0: if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { michael@0: return HandleShutdown(); michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "BluetoothService got unexpected topic!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::TryFiringAdapterAdded() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (IsToggling() || !mAdapterAddedReceived) { michael@0: return; michael@0: } michael@0: michael@0: BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"), michael@0: NS_LITERAL_STRING(KEY_MANAGER), true); michael@0: DistributeSignal(signal); michael@0: } michael@0: michael@0: void michael@0: BluetoothService::AdapterAddedReceived() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mAdapterAddedReceived = true; michael@0: } michael@0: michael@0: void michael@0: BluetoothService::Notify(const BluetoothSignal& aData) michael@0: { michael@0: nsString type = NS_LITERAL_STRING("bluetooth-pairing-request"); michael@0: michael@0: AutoSafeJSContext cx; michael@0: JS::Rooted obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), michael@0: JS::NullPtr())); michael@0: NS_ENSURE_TRUE_VOID(obj); michael@0: michael@0: if (!SetJsObject(cx, aData.value(), obj)) { michael@0: BT_WARNING("Failed to set properties of system message!"); michael@0: return; michael@0: } michael@0: michael@0: BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get()); michael@0: michael@0: if (aData.name().EqualsLiteral("RequestConfirmation")) { michael@0: MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4, michael@0: "RequestConfirmation: Wrong length of parameters"); michael@0: } else if (aData.name().EqualsLiteral("RequestPinCode")) { michael@0: MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3, michael@0: "RequestPinCode: Wrong length of parameters"); michael@0: } else if (aData.name().EqualsLiteral("RequestPasskey")) { michael@0: MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3, michael@0: "RequestPinCode: Wrong length of parameters"); michael@0: } else if (aData.name().EqualsLiteral("Cancel")) { michael@0: MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0, michael@0: "Cancel: Wrong length of parameters"); michael@0: type.AssignLiteral("bluetooth-cancel"); michael@0: } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) { michael@0: MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1, michael@0: "pairedstatuschanged: Wrong length of parameters"); michael@0: type.AssignLiteral("bluetooth-pairedstatuschanged"); michael@0: } else { michael@0: nsCString warningMsg; michael@0: warningMsg.AssignLiteral("Not handling service signal: "); michael@0: warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name())); michael@0: BT_WARNING(warningMsg.get()); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr systemMessenger = michael@0: do_GetService("@mozilla.org/system-message-internal;1"); michael@0: NS_ENSURE_TRUE_VOID(systemMessenger); michael@0: michael@0: JS::Rooted value(cx, JS::ObjectValue(*obj)); michael@0: systemMessenger->BroadcastMessage(type, value, michael@0: JS::UndefinedHandleValue); michael@0: }