1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bluetooth/BluetoothService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,846 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "base/basictypes.h" 1.11 + 1.12 +#include "BluetoothService.h" 1.13 + 1.14 +#include "BluetoothCommon.h" 1.15 +#include "BluetoothA2dpManager.h" 1.16 +#include "BluetoothHfpManager.h" 1.17 +#include "BluetoothHidManager.h" 1.18 +#include "BluetoothManager.h" 1.19 +#include "BluetoothOppManager.h" 1.20 +#include "BluetoothParent.h" 1.21 +#include "BluetoothReplyRunnable.h" 1.22 +#include "BluetoothServiceChildProcess.h" 1.23 +#include "BluetoothUtils.h" 1.24 + 1.25 +#include "jsapi.h" 1.26 +#include "mozilla/Services.h" 1.27 +#include "mozilla/StaticPtr.h" 1.28 +#include "mozilla/unused.h" 1.29 +#include "mozilla/dom/ContentParent.h" 1.30 +#include "mozilla/dom/bluetooth/BluetoothTypes.h" 1.31 +#include "mozilla/ipc/UnixSocket.h" 1.32 +#include "nsContentUtils.h" 1.33 +#include "nsCxPusher.h" 1.34 +#include "nsIObserverService.h" 1.35 +#include "nsISettingsService.h" 1.36 +#include "nsISystemMessagesInternal.h" 1.37 +#include "nsITimer.h" 1.38 +#include "nsServiceManagerUtils.h" 1.39 +#include "nsXPCOM.h" 1.40 + 1.41 +#if defined(MOZ_WIDGET_GONK) 1.42 +#include "cutils/properties.h" 1.43 +#endif 1.44 + 1.45 +#if defined(MOZ_B2G_BT) 1.46 +#if defined(MOZ_B2G_BT_BLUEZ) 1.47 +/** 1.48 + * B2G blueZ: 1.49 + * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined. 1.50 + */ 1.51 +#include "BluetoothDBusService.h" 1.52 +#elif defined(MOZ_B2G_BT_BLUEDROID) 1.53 +/** 1.54 + * B2G bluedroid: 1.55 + * MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined; 1.56 + * MOZ_B2G_BLUEZ is not defined. 1.57 + */ 1.58 +#include "BluetoothServiceBluedroid.h" 1.59 +#endif 1.60 +#elif defined(MOZ_BLUETOOTH_DBUS) 1.61 +/** 1.62 + * Desktop bluetooth: 1.63 + * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined. 1.64 + */ 1.65 +#include "BluetoothDBusService.h" 1.66 +#else 1.67 +#error No backend 1.68 +#endif 1.69 + 1.70 +#define MOZSETTINGS_CHANGED_ID "mozsettings-changed" 1.71 +#define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled" 1.72 +#define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled" 1.73 + 1.74 +#define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled" 1.75 + 1.76 +#define DEFAULT_SHUTDOWN_TIMER_MS 5000 1.77 + 1.78 +bool gBluetoothDebugFlag = false; 1.79 + 1.80 +using namespace mozilla; 1.81 +using namespace mozilla::dom; 1.82 +USING_BLUETOOTH_NAMESPACE 1.83 + 1.84 +namespace { 1.85 + 1.86 +StaticRefPtr<BluetoothService> sBluetoothService; 1.87 + 1.88 +bool sInShutdown = false; 1.89 +bool sToggleInProgress = false; 1.90 + 1.91 +bool 1.92 +IsMainProcess() 1.93 +{ 1.94 + return XRE_GetProcessType() == GeckoProcessType_Default; 1.95 +} 1.96 + 1.97 +void 1.98 +ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure) 1.99 +{ 1.100 + MOZ_ASSERT(NS_IsMainThread()); 1.101 + *static_cast<bool*>(aClosure) = true; 1.102 +} 1.103 + 1.104 +void 1.105 +GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors) 1.106 +{ 1.107 + MOZ_ASSERT(NS_IsMainThread()); 1.108 + MOZ_ASSERT(aActors.IsEmpty()); 1.109 + 1.110 + nsAutoTArray<ContentParent*, 20> contentActors; 1.111 + ContentParent::GetAll(contentActors); 1.112 + 1.113 + for (uint32_t contentIndex = 0; 1.114 + contentIndex < contentActors.Length(); 1.115 + contentIndex++) { 1.116 + MOZ_ASSERT(contentActors[contentIndex]); 1.117 + 1.118 + AutoInfallibleTArray<PBluetoothParent*, 5> bluetoothActors; 1.119 + contentActors[contentIndex]->ManagedPBluetoothParent(bluetoothActors); 1.120 + 1.121 + for (uint32_t bluetoothIndex = 0; 1.122 + bluetoothIndex < bluetoothActors.Length(); 1.123 + bluetoothIndex++) { 1.124 + MOZ_ASSERT(bluetoothActors[bluetoothIndex]); 1.125 + 1.126 + BluetoothParent* actor = 1.127 + static_cast<BluetoothParent*>(bluetoothActors[bluetoothIndex]); 1.128 + aActors.AppendElement(actor); 1.129 + } 1.130 + } 1.131 +} 1.132 + 1.133 +} // anonymous namespace 1.134 + 1.135 +BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled) 1.136 + : mEnabled(aEnabled) 1.137 +{ } 1.138 + 1.139 +NS_METHOD 1.140 +BluetoothService::ToggleBtAck::Run() 1.141 +{ 1.142 + MOZ_ASSERT(NS_IsMainThread()); 1.143 + 1.144 + // This is requested in Bug 836516. With settings this property, WLAN 1.145 + // firmware could be aware of Bluetooth has been turned on/off, so that the 1.146 + // mecahnism of handling coexistence of WIFI and Bluetooth could be started. 1.147 + // 1.148 + // In the future, we may have our own way instead of setting a system 1.149 + // property to let firmware developers be able to sense that Bluetooth has 1.150 + // been toggled. 1.151 +#if defined(MOZ_WIDGET_GONK) 1.152 + if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) { 1.153 + BT_WARNING("Failed to set bluetooth enabled property"); 1.154 + } 1.155 +#endif 1.156 + 1.157 + NS_ENSURE_TRUE(sBluetoothService, NS_OK); 1.158 + 1.159 + if (sInShutdown) { 1.160 + sBluetoothService = nullptr; 1.161 + return NS_OK; 1.162 + } 1.163 + 1.164 + // Update mEnabled of BluetoothService object since 1.165 + // StartInternal/StopInternal have been already done. 1.166 + sBluetoothService->SetEnabled(mEnabled); 1.167 + sToggleInProgress = false; 1.168 + 1.169 + nsAutoString signalName; 1.170 + signalName = mEnabled ? NS_LITERAL_STRING("Enabled") 1.171 + : NS_LITERAL_STRING("Disabled"); 1.172 + BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true); 1.173 + sBluetoothService->DistributeSignal(signal); 1.174 + 1.175 + // Event 'AdapterAdded' has to be fired after firing 'Enabled' 1.176 + sBluetoothService->TryFiringAdapterAdded(); 1.177 + 1.178 + return NS_OK; 1.179 +} 1.180 + 1.181 +class BluetoothService::StartupTask : public nsISettingsServiceCallback 1.182 +{ 1.183 +public: 1.184 + NS_DECL_ISUPPORTS 1.185 + 1.186 + NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) 1.187 + { 1.188 + MOZ_ASSERT(NS_IsMainThread()); 1.189 + 1.190 + if (!aResult.isBoolean()) { 1.191 + BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING "' is not a boolean!"); 1.192 + return NS_OK; 1.193 + } 1.194 + 1.195 + // It is theoretically possible to shut down before the first settings check 1.196 + // has completed (though extremely unlikely). 1.197 + if (sBluetoothService) { 1.198 + return sBluetoothService->HandleStartupSettingsCheck(aResult.toBoolean()); 1.199 + } 1.200 + 1.201 + return NS_OK; 1.202 + } 1.203 + 1.204 + NS_IMETHOD HandleError(const nsAString& aName) 1.205 + { 1.206 + BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'"); 1.207 + return NS_OK; 1.208 + } 1.209 +}; 1.210 + 1.211 +NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback); 1.212 + 1.213 +NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver) 1.214 + 1.215 +bool 1.216 +BluetoothService::IsToggling() const 1.217 +{ 1.218 + return sToggleInProgress; 1.219 +} 1.220 + 1.221 +BluetoothService::~BluetoothService() 1.222 +{ 1.223 + Cleanup(); 1.224 +} 1.225 + 1.226 +PLDHashOperator 1.227 +RemoveObserversExceptBluetoothManager 1.228 + (const nsAString& key, 1.229 + nsAutoPtr<BluetoothSignalObserverList>& value, 1.230 + void* arg) 1.231 +{ 1.232 + if (!key.EqualsLiteral(KEY_MANAGER)) { 1.233 + return PL_DHASH_REMOVE; 1.234 + } 1.235 + 1.236 + return PL_DHASH_NEXT; 1.237 +} 1.238 + 1.239 +void 1.240 +BluetoothService::RemoveObserverFromTable(const nsAString& key) 1.241 +{ 1.242 + mBluetoothSignalObserverTable.Remove(key); 1.243 +} 1.244 + 1.245 +// static 1.246 +BluetoothService* 1.247 +BluetoothService::Create() 1.248 +{ 1.249 +#if defined(MOZ_B2G_BT) 1.250 + if (!IsMainProcess()) { 1.251 + return BluetoothServiceChildProcess::Create(); 1.252 + } 1.253 + 1.254 +#if defined(MOZ_B2G_BT_BLUEZ) 1.255 + return new BluetoothDBusService(); 1.256 +#elif defined(MOZ_B2G_BT_BLUEDROID) 1.257 + return new BluetoothServiceBluedroid(); 1.258 +#endif 1.259 +#elif defined(MOZ_BLUETOOTH_DBUS) 1.260 + return new BluetoothDBusService(); 1.261 +#endif 1.262 + 1.263 + BT_WARNING("No platform support for bluetooth!"); 1.264 + return nullptr; 1.265 +} 1.266 + 1.267 +bool 1.268 +BluetoothService::Init() 1.269 +{ 1.270 + MOZ_ASSERT(NS_IsMainThread()); 1.271 + 1.272 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.273 + NS_ENSURE_TRUE(obs, false); 1.274 + 1.275 + if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, 1.276 + false))) { 1.277 + BT_WARNING("Failed to add shutdown observer!"); 1.278 + return false; 1.279 + } 1.280 + 1.281 + // Only the main process should observe bluetooth settings changes. 1.282 + if (IsMainProcess() && 1.283 + NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) { 1.284 + BT_WARNING("Failed to add settings change observer!"); 1.285 + return false; 1.286 + } 1.287 + 1.288 + return true; 1.289 +} 1.290 + 1.291 +void 1.292 +BluetoothService::Cleanup() 1.293 +{ 1.294 + MOZ_ASSERT(NS_IsMainThread()); 1.295 + 1.296 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.297 + if (obs && 1.298 + (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) || 1.299 + NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) { 1.300 + BT_WARNING("Can't unregister observers!"); 1.301 + } 1.302 +} 1.303 + 1.304 +void 1.305 +BluetoothService::RegisterBluetoothSignalHandler( 1.306 + const nsAString& aNodeName, 1.307 + BluetoothSignalObserver* aHandler) 1.308 +{ 1.309 + MOZ_ASSERT(NS_IsMainThread()); 1.310 + MOZ_ASSERT(aHandler); 1.311 + 1.312 + BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get()); 1.313 + 1.314 + BluetoothSignalObserverList* ol; 1.315 + if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) { 1.316 + ol = new BluetoothSignalObserverList(); 1.317 + mBluetoothSignalObserverTable.Put(aNodeName, ol); 1.318 + } 1.319 + 1.320 + ol->AddObserver(aHandler); 1.321 +} 1.322 + 1.323 +void 1.324 +BluetoothService::UnregisterBluetoothSignalHandler( 1.325 + const nsAString& aNodeName, 1.326 + BluetoothSignalObserver* aHandler) 1.327 +{ 1.328 + MOZ_ASSERT(NS_IsMainThread()); 1.329 + MOZ_ASSERT(aHandler); 1.330 + 1.331 + BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get()); 1.332 + 1.333 + BluetoothSignalObserverList* ol; 1.334 + if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) { 1.335 + ol->RemoveObserver(aHandler); 1.336 + // We shouldn't have duplicate instances in the ObserverList, but there's 1.337 + // no appropriate way to do duplication check while registering, so 1.338 + // assertions are added here. 1.339 + MOZ_ASSERT(!ol->RemoveObserver(aHandler)); 1.340 + if (ol->Length() == 0) { 1.341 + mBluetoothSignalObserverTable.Remove(aNodeName); 1.342 + } 1.343 + } 1.344 + else { 1.345 + BT_WARNING("Node was never registered!"); 1.346 + } 1.347 +} 1.348 + 1.349 +PLDHashOperator 1.350 +RemoveAllSignalHandlers(const nsAString& aKey, 1.351 + nsAutoPtr<BluetoothSignalObserverList>& aData, 1.352 + void* aUserArg) 1.353 +{ 1.354 + BluetoothSignalObserver* handler = static_cast<BluetoothSignalObserver*>(aUserArg); 1.355 + aData->RemoveObserver(handler); 1.356 + // We shouldn't have duplicate instances in the ObserverList, but there's 1.357 + // no appropriate way to do duplication check while registering, so 1.358 + // assertions are added here. 1.359 + MOZ_ASSERT(!aData->RemoveObserver(handler)); 1.360 + return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE; 1.361 +} 1.362 + 1.363 +void 1.364 +BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler) 1.365 +{ 1.366 + MOZ_ASSERT(NS_IsMainThread()); 1.367 + MOZ_ASSERT(aHandler); 1.368 + 1.369 + mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler); 1.370 +} 1.371 + 1.372 +void 1.373 +BluetoothService::DistributeSignal(const BluetoothSignal& aSignal) 1.374 +{ 1.375 + MOZ_ASSERT(NS_IsMainThread()); 1.376 + 1.377 + if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) { 1.378 + Notify(aSignal); 1.379 + return; 1.380 + } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) { 1.381 + Notify(aSignal); 1.382 + return; 1.383 + } 1.384 + 1.385 + BluetoothSignalObserverList* ol; 1.386 + if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) { 1.387 +#if DEBUG 1.388 + nsAutoCString msg("No observer registered for path "); 1.389 + msg.Append(NS_ConvertUTF16toUTF8(aSignal.path())); 1.390 + BT_WARNING(msg.get()); 1.391 +#endif 1.392 + return; 1.393 + } 1.394 + MOZ_ASSERT(ol->Length()); 1.395 + ol->Broadcast(aSignal); 1.396 +} 1.397 + 1.398 +nsresult 1.399 +BluetoothService::StartBluetooth(bool aIsStartup) 1.400 +{ 1.401 + MOZ_ASSERT(NS_IsMainThread()); 1.402 + 1.403 + if (sInShutdown) { 1.404 + // Don't try to start if we're already shutting down. 1.405 + MOZ_ASSERT(false, "Start called while in shutdown!"); 1.406 + return NS_ERROR_FAILURE; 1.407 + } 1.408 + 1.409 + mAdapterAddedReceived = false; 1.410 + 1.411 + /* When IsEnabled() is true, we don't switch on Bluetooth but we still 1.412 + * send ToggleBtAck task. One special case happens at startup stage. At 1.413 + * startup, the initialization of BluetoothService still has to be done 1.414 + * even if Bluetooth is already enabled. 1.415 + * 1.416 + * Please see bug 892392 for more information. 1.417 + */ 1.418 + if (aIsStartup || !sBluetoothService->IsEnabled()) { 1.419 + // Switch Bluetooth on 1.420 + if (NS_FAILED(sBluetoothService->StartInternal())) { 1.421 + BT_WARNING("Bluetooth service failed to start!"); 1.422 + } 1.423 + } else { 1.424 + BT_WARNING("Bluetooth has already been enabled before."); 1.425 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true); 1.426 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.427 + BT_WARNING("Failed to dispatch to main thread!"); 1.428 + } 1.429 + } 1.430 + 1.431 + return NS_OK; 1.432 +} 1.433 + 1.434 +nsresult 1.435 +BluetoothService::StopBluetooth(bool aIsStartup) 1.436 +{ 1.437 + MOZ_ASSERT(NS_IsMainThread()); 1.438 + 1.439 + BluetoothProfileManagerBase* profile; 1.440 + profile = BluetoothHfpManager::Get(); 1.441 + NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); 1.442 + if (profile->IsConnected()) { 1.443 + profile->Disconnect(nullptr); 1.444 + } else { 1.445 + profile->Reset(); 1.446 + } 1.447 + 1.448 + profile = BluetoothOppManager::Get(); 1.449 + NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); 1.450 + if (profile->IsConnected()) { 1.451 + profile->Disconnect(nullptr); 1.452 + } 1.453 + 1.454 + profile = BluetoothA2dpManager::Get(); 1.455 + NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); 1.456 + if (profile->IsConnected()) { 1.457 + profile->Disconnect(nullptr); 1.458 + } else { 1.459 + profile->Reset(); 1.460 + } 1.461 + 1.462 + profile = BluetoothHidManager::Get(); 1.463 + NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE); 1.464 + if (profile->IsConnected()) { 1.465 + profile->Disconnect(nullptr); 1.466 + } else { 1.467 + profile->Reset(); 1.468 + } 1.469 + 1.470 + mAdapterAddedReceived = false; 1.471 + 1.472 + /* When IsEnabled() is false, we don't switch off Bluetooth but we still 1.473 + * send ToggleBtAck task. One special case happens at startup stage. At 1.474 + * startup, the initialization of BluetoothService still has to be done 1.475 + * even if Bluetooth is disabled. 1.476 + * 1.477 + * Please see bug 892392 for more information. 1.478 + */ 1.479 + if (aIsStartup || sBluetoothService->IsEnabled()) { 1.480 + // Switch Bluetooth off 1.481 + if (NS_FAILED(sBluetoothService->StopInternal())) { 1.482 + BT_WARNING("Bluetooth service failed to stop!"); 1.483 + } 1.484 + } else { 1.485 + BT_WARNING("Bluetooth has already been enabled/disabled before."); 1.486 + nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); 1.487 + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { 1.488 + BT_WARNING("Failed to dispatch to main thread!"); 1.489 + } 1.490 + } 1.491 + 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +nsresult 1.496 +BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup) 1.497 +{ 1.498 + nsresult rv; 1.499 + if (aStart) { 1.500 + rv = StartBluetooth(aIsStartup); 1.501 + } else { 1.502 + rv = StopBluetooth(aIsStartup); 1.503 + } 1.504 + return rv; 1.505 +} 1.506 + 1.507 +void 1.508 +BluetoothService::SetEnabled(bool aEnabled) 1.509 +{ 1.510 + MOZ_ASSERT(NS_IsMainThread()); 1.511 + 1.512 + AutoInfallibleTArray<BluetoothParent*, 10> childActors; 1.513 + GetAllBluetoothActors(childActors); 1.514 + 1.515 + for (uint32_t index = 0; index < childActors.Length(); index++) { 1.516 + unused << childActors[index]->SendEnabled(aEnabled); 1.517 + } 1.518 + 1.519 + if (!aEnabled) { 1.520 + /** 1.521 + * Remove all handlers except BluetoothManager when turning off bluetooth 1.522 + * since it is possible that the event 'onAdapterAdded' would be fired after 1.523 + * BluetoothManagers of child process are registered. Please see Bug 827759 1.524 + * for more details. 1.525 + */ 1.526 + mBluetoothSignalObserverTable.Enumerate( 1.527 + RemoveObserversExceptBluetoothManager, nullptr); 1.528 + } 1.529 + 1.530 + /** 1.531 + * mEnabled: real status of bluetooth 1.532 + * aEnabled: expected status of bluetooth 1.533 + */ 1.534 + if (mEnabled == aEnabled) { 1.535 + BT_WARNING("Bluetooth has already been enabled/disabled before " 1.536 + "or the toggling is failed."); 1.537 + } 1.538 + 1.539 + mEnabled = aEnabled; 1.540 +} 1.541 + 1.542 +nsresult 1.543 +BluetoothService::HandleStartup() 1.544 +{ 1.545 + MOZ_ASSERT(NS_IsMainThread()); 1.546 + MOZ_ASSERT(!sToggleInProgress); 1.547 + 1.548 + nsCOMPtr<nsISettingsService> settings = 1.549 + do_GetService("@mozilla.org/settingsService;1"); 1.550 + NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED); 1.551 + 1.552 + nsCOMPtr<nsISettingsServiceLock> settingsLock; 1.553 + nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock)); 1.554 + NS_ENSURE_SUCCESS(rv, rv); 1.555 + 1.556 + nsRefPtr<StartupTask> callback = new StartupTask(); 1.557 + rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback); 1.558 + NS_ENSURE_SUCCESS(rv, rv); 1.559 + 1.560 + sToggleInProgress = true; 1.561 + return NS_OK; 1.562 +} 1.563 + 1.564 +nsresult 1.565 +BluetoothService::HandleStartupSettingsCheck(bool aEnable) 1.566 +{ 1.567 + MOZ_ASSERT(NS_IsMainThread()); 1.568 + return StartStopBluetooth(aEnable, true); 1.569 +} 1.570 + 1.571 +nsresult 1.572 +BluetoothService::HandleSettingsChanged(const nsAString& aData) 1.573 +{ 1.574 + MOZ_ASSERT(NS_IsMainThread()); 1.575 + 1.576 + // The string that we're interested in will be a JSON string that looks like: 1.577 + // {"key":"bluetooth.enabled","value":true} 1.578 + 1.579 + AutoSafeJSContext cx; 1.580 + if (!cx) { 1.581 + return NS_OK; 1.582 + } 1.583 + 1.584 + JS::Rooted<JS::Value> val(cx); 1.585 + if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) { 1.586 + return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.587 + } 1.588 + 1.589 + if (!val.isObject()) { 1.590 + return NS_OK; 1.591 + } 1.592 + 1.593 + JS::Rooted<JSObject*> obj(cx, &val.toObject()); 1.594 + 1.595 + JS::Rooted<JS::Value> key(cx); 1.596 + if (!JS_GetProperty(cx, obj, "key", &key)) { 1.597 + MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1.598 + return NS_ERROR_OUT_OF_MEMORY; 1.599 + } 1.600 + 1.601 + if (!key.isString()) { 1.602 + return NS_OK; 1.603 + } 1.604 + 1.605 + // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING 1.606 + bool match; 1.607 + if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) { 1.608 + MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1.609 + return NS_ERROR_OUT_OF_MEMORY; 1.610 + } 1.611 + 1.612 + if (match) { 1.613 + JS::Rooted<JS::Value> value(cx); 1.614 + if (!JS_GetProperty(cx, obj, "value", &value)) { 1.615 + MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1.616 + return NS_ERROR_OUT_OF_MEMORY; 1.617 + } 1.618 + 1.619 + if (!value.isBoolean()) { 1.620 + MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!"); 1.621 + return NS_ERROR_UNEXPECTED; 1.622 + } 1.623 + 1.624 + SWITCH_BT_DEBUG(value.toBoolean()); 1.625 + 1.626 + return NS_OK; 1.627 + } 1.628 + 1.629 + // Second, check if the string is BLUETOOTH_ENABLED_SETTING 1.630 + if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) { 1.631 + MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1.632 + return NS_ERROR_OUT_OF_MEMORY; 1.633 + } 1.634 + 1.635 + if (match) { 1.636 + JS::Rooted<JS::Value> value(cx); 1.637 + if (!JS_GetProperty(cx, obj, "value", &value)) { 1.638 + MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1.639 + return NS_ERROR_OUT_OF_MEMORY; 1.640 + } 1.641 + 1.642 + if (!value.isBoolean()) { 1.643 + MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!"); 1.644 + return NS_ERROR_UNEXPECTED; 1.645 + } 1.646 + 1.647 + if (sToggleInProgress || value.toBoolean() == IsEnabled()) { 1.648 + // Nothing to do here. 1.649 + return NS_OK; 1.650 + } 1.651 + 1.652 + sToggleInProgress = true; 1.653 + 1.654 + nsresult rv = StartStopBluetooth(value.toBoolean(), false); 1.655 + NS_ENSURE_SUCCESS(rv, rv); 1.656 + } 1.657 + 1.658 + return NS_OK; 1.659 +} 1.660 + 1.661 +nsresult 1.662 +BluetoothService::HandleShutdown() 1.663 +{ 1.664 + MOZ_ASSERT(NS_IsMainThread()); 1.665 + 1.666 + // This is a two phase shutdown. First we notify all child processes that 1.667 + // bluetooth is going away, and then we wait for them to acknowledge. Then we 1.668 + // close down all the bluetooth machinery. 1.669 + 1.670 + sInShutdown = true; 1.671 + 1.672 + Cleanup(); 1.673 + 1.674 + AutoInfallibleTArray<BluetoothParent*, 10> childActors; 1.675 + GetAllBluetoothActors(childActors); 1.676 + 1.677 + if (!childActors.IsEmpty()) { 1.678 + // Notify child processes that they should stop using bluetooth now. 1.679 + for (uint32_t index = 0; index < childActors.Length(); index++) { 1.680 + childActors[index]->BeginShutdown(); 1.681 + } 1.682 + 1.683 + // Create a timer to ensure that we don't wait forever for a child process 1.684 + // or the bluetooth threads to finish. If we don't get a timer or can't use 1.685 + // it for some reason then we skip all the waiting entirely since we really 1.686 + // can't afford to hang on shutdown. 1.687 + nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.688 + MOZ_ASSERT(timer); 1.689 + 1.690 + if (timer) { 1.691 + bool timeExceeded = false; 1.692 + 1.693 + if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded, 1.694 + &timeExceeded, 1.695 + DEFAULT_SHUTDOWN_TIMER_MS, 1.696 + nsITimer::TYPE_ONE_SHOT))) { 1.697 + nsIThread* currentThread = NS_GetCurrentThread(); 1.698 + MOZ_ASSERT(currentThread); 1.699 + 1.700 + // Wait for those child processes to acknowledge. 1.701 + while (!timeExceeded && !childActors.IsEmpty()) { 1.702 + if (!NS_ProcessNextEvent(currentThread)) { 1.703 + MOZ_ASSERT(false, "Something horribly wrong here!"); 1.704 + break; 1.705 + } 1.706 + GetAllBluetoothActors(childActors); 1.707 + } 1.708 + 1.709 + if (NS_FAILED(timer->Cancel())) { 1.710 + MOZ_CRASH("Failed to cancel shutdown timer, this will crash!"); 1.711 + } 1.712 + } 1.713 + else { 1.714 + MOZ_ASSERT(false, "Failed to initialize shutdown timer!"); 1.715 + } 1.716 + } 1.717 + } 1.718 + 1.719 + if (IsEnabled() && NS_FAILED(StopBluetooth(false))) { 1.720 + MOZ_ASSERT(false, "Failed to deliver stop message!"); 1.721 + } 1.722 + 1.723 + return NS_OK; 1.724 +} 1.725 + 1.726 +// static 1.727 +BluetoothService* 1.728 +BluetoothService::Get() 1.729 +{ 1.730 + MOZ_ASSERT(NS_IsMainThread()); 1.731 + 1.732 + // If we already exist, exit early 1.733 + if (sBluetoothService) { 1.734 + return sBluetoothService; 1.735 + } 1.736 + 1.737 + // If we're in shutdown, don't create a new instance 1.738 + if (sInShutdown) { 1.739 + BT_WARNING("BluetoothService can't be created during shutdown"); 1.740 + return nullptr; 1.741 + } 1.742 + 1.743 + // Create new instance, register, return 1.744 + nsRefPtr<BluetoothService> service = BluetoothService::Create(); 1.745 + NS_ENSURE_TRUE(service, nullptr); 1.746 + 1.747 + if (!service->Init()) { 1.748 + service->Cleanup(); 1.749 + return nullptr; 1.750 + } 1.751 + 1.752 + sBluetoothService = service; 1.753 + return sBluetoothService; 1.754 +} 1.755 + 1.756 +nsresult 1.757 +BluetoothService::Observe(nsISupports* aSubject, const char* aTopic, 1.758 + const char16_t* aData) 1.759 +{ 1.760 + MOZ_ASSERT(NS_IsMainThread()); 1.761 + 1.762 + if (!strcmp(aTopic, "profile-after-change")) { 1.763 + return HandleStartup(); 1.764 + } 1.765 + 1.766 + if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) { 1.767 + return HandleSettingsChanged(nsDependentString(aData)); 1.768 + } 1.769 + 1.770 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.771 + return HandleShutdown(); 1.772 + } 1.773 + 1.774 + MOZ_ASSERT(false, "BluetoothService got unexpected topic!"); 1.775 + return NS_ERROR_UNEXPECTED; 1.776 +} 1.777 + 1.778 +void 1.779 +BluetoothService::TryFiringAdapterAdded() 1.780 +{ 1.781 + MOZ_ASSERT(NS_IsMainThread()); 1.782 + 1.783 + if (IsToggling() || !mAdapterAddedReceived) { 1.784 + return; 1.785 + } 1.786 + 1.787 + BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"), 1.788 + NS_LITERAL_STRING(KEY_MANAGER), true); 1.789 + DistributeSignal(signal); 1.790 +} 1.791 + 1.792 +void 1.793 +BluetoothService::AdapterAddedReceived() 1.794 +{ 1.795 + MOZ_ASSERT(NS_IsMainThread()); 1.796 + 1.797 + mAdapterAddedReceived = true; 1.798 +} 1.799 + 1.800 +void 1.801 +BluetoothService::Notify(const BluetoothSignal& aData) 1.802 +{ 1.803 + nsString type = NS_LITERAL_STRING("bluetooth-pairing-request"); 1.804 + 1.805 + AutoSafeJSContext cx; 1.806 + JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), 1.807 + JS::NullPtr())); 1.808 + NS_ENSURE_TRUE_VOID(obj); 1.809 + 1.810 + if (!SetJsObject(cx, aData.value(), obj)) { 1.811 + BT_WARNING("Failed to set properties of system message!"); 1.812 + return; 1.813 + } 1.814 + 1.815 + BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get()); 1.816 + 1.817 + if (aData.name().EqualsLiteral("RequestConfirmation")) { 1.818 + MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4, 1.819 + "RequestConfirmation: Wrong length of parameters"); 1.820 + } else if (aData.name().EqualsLiteral("RequestPinCode")) { 1.821 + MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3, 1.822 + "RequestPinCode: Wrong length of parameters"); 1.823 + } else if (aData.name().EqualsLiteral("RequestPasskey")) { 1.824 + MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3, 1.825 + "RequestPinCode: Wrong length of parameters"); 1.826 + } else if (aData.name().EqualsLiteral("Cancel")) { 1.827 + MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0, 1.828 + "Cancel: Wrong length of parameters"); 1.829 + type.AssignLiteral("bluetooth-cancel"); 1.830 + } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) { 1.831 + MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1, 1.832 + "pairedstatuschanged: Wrong length of parameters"); 1.833 + type.AssignLiteral("bluetooth-pairedstatuschanged"); 1.834 + } else { 1.835 + nsCString warningMsg; 1.836 + warningMsg.AssignLiteral("Not handling service signal: "); 1.837 + warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name())); 1.838 + BT_WARNING(warningMsg.get()); 1.839 + return; 1.840 + } 1.841 + 1.842 + nsCOMPtr<nsISystemMessagesInternal> systemMessenger = 1.843 + do_GetService("@mozilla.org/system-message-internal;1"); 1.844 + NS_ENSURE_TRUE_VOID(systemMessenger); 1.845 + 1.846 + JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj)); 1.847 + systemMessenger->BroadcastMessage(type, value, 1.848 + JS::UndefinedHandleValue); 1.849 +}