Bug 1059702 - Use wifi.tethering.interface for wifi AP if it does exist. r=vchang
[gecko.git] / dom / bluetooth / BluetoothService.cpp
blob4784ae3608b19ad04607af1c3093e9d69d3a9b94
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
9 #include "BluetoothService.h"
11 #include "BluetoothCommon.h"
12 #include "BluetoothA2dpManager.h"
13 #include "BluetoothHfpManager.h"
14 #include "BluetoothHidManager.h"
15 #include "BluetoothManager.h"
16 #include "BluetoothOppManager.h"
17 #include "BluetoothParent.h"
18 #include "BluetoothReplyRunnable.h"
19 #include "BluetoothServiceChildProcess.h"
20 #include "BluetoothUtils.h"
22 #include "jsapi.h"
23 #include "mozilla/ClearOnShutdown.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/StaticPtr.h"
26 #include "mozilla/unused.h"
27 #include "mozilla/dom/ContentParent.h"
28 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
29 #include "mozilla/ipc/UnixSocket.h"
30 #include "nsContentUtils.h"
31 #include "nsIObserverService.h"
32 #include "nsISettingsService.h"
33 #include "nsISystemMessagesInternal.h"
34 #include "nsITimer.h"
35 #include "nsServiceManagerUtils.h"
36 #include "nsXPCOM.h"
38 #if defined(MOZ_WIDGET_GONK)
39 #include "cutils/properties.h"
40 #endif
42 #if defined(MOZ_B2G_BT)
43 #if defined(MOZ_B2G_BT_BLUEZ)
44 /**
45 * B2G blueZ:
46 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined.
48 #include "BluetoothDBusService.h"
49 #elif defined(MOZ_B2G_BT_BLUEDROID)
50 /**
51 * B2G bluedroid:
52 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined;
53 * MOZ_B2G_BLUEZ is not defined.
55 #include "BluetoothServiceBluedroid.h"
56 #endif
57 #elif defined(MOZ_BLUETOOTH_DBUS)
58 /**
59 * Desktop bluetooth:
60 * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined.
62 #include "BluetoothDBusService.h"
63 #else
64 #error No backend
65 #endif
67 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
68 #define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled"
69 #define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled"
71 #define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
73 #define DEFAULT_SHUTDOWN_TIMER_MS 5000
75 bool gBluetoothDebugFlag = false;
77 using namespace mozilla;
78 using namespace mozilla::dom;
79 USING_BLUETOOTH_NAMESPACE
81 namespace {
83 StaticRefPtr<BluetoothService> sBluetoothService;
85 bool sInShutdown = false;
86 bool sToggleInProgress = false;
88 bool
89 IsMainProcess()
91 return XRE_GetProcessType() == GeckoProcessType_Default;
94 void
95 ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
97 MOZ_ASSERT(NS_IsMainThread());
98 *static_cast<bool*>(aClosure) = true;
101 void
102 GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors)
104 MOZ_ASSERT(NS_IsMainThread());
105 MOZ_ASSERT(aActors.IsEmpty());
107 nsAutoTArray<ContentParent*, 20> contentActors;
108 ContentParent::GetAll(contentActors);
110 for (uint32_t contentIndex = 0;
111 contentIndex < contentActors.Length();
112 contentIndex++) {
113 MOZ_ASSERT(contentActors[contentIndex]);
115 AutoInfallibleTArray<PBluetoothParent*, 5> bluetoothActors;
116 contentActors[contentIndex]->ManagedPBluetoothParent(bluetoothActors);
118 for (uint32_t bluetoothIndex = 0;
119 bluetoothIndex < bluetoothActors.Length();
120 bluetoothIndex++) {
121 MOZ_ASSERT(bluetoothActors[bluetoothIndex]);
123 BluetoothParent* actor =
124 static_cast<BluetoothParent*>(bluetoothActors[bluetoothIndex]);
125 aActors.AppendElement(actor);
130 } // anonymous namespace
132 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
133 : mEnabled(aEnabled)
136 NS_METHOD
137 BluetoothService::ToggleBtAck::Run()
139 MOZ_ASSERT(NS_IsMainThread());
141 // This is requested in Bug 836516. With settings this property, WLAN
142 // firmware could be aware of Bluetooth has been turned on/off, so that the
143 // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
145 // In the future, we may have our own way instead of setting a system
146 // property to let firmware developers be able to sense that Bluetooth has
147 // been toggled.
148 #if defined(MOZ_WIDGET_GONK)
149 if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) {
150 BT_WARNING("Failed to set bluetooth enabled property");
152 #endif
154 NS_ENSURE_TRUE(sBluetoothService, NS_OK);
156 if (sInShutdown) {
157 sBluetoothService = nullptr;
158 return NS_OK;
161 // Update mEnabled of BluetoothService object since
162 // StartInternal/StopInternal have been already done.
163 sBluetoothService->SetEnabled(mEnabled);
164 sToggleInProgress = false;
166 nsAutoString signalName;
167 signalName = mEnabled ? NS_LITERAL_STRING("Enabled")
168 : NS_LITERAL_STRING("Disabled");
169 BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true);
170 sBluetoothService->DistributeSignal(signal);
172 // Event 'AdapterAdded' has to be fired after firing 'Enabled'
173 sBluetoothService->TryFiringAdapterAdded();
175 return NS_OK;
178 class BluetoothService::StartupTask : public nsISettingsServiceCallback
180 public:
181 NS_DECL_ISUPPORTS
183 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
185 MOZ_ASSERT(NS_IsMainThread());
187 if (!aResult.isBoolean()) {
188 BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING "' is not a boolean!");
189 return NS_OK;
192 // It is theoretically possible to shut down before the first settings check
193 // has completed (though extremely unlikely).
194 if (sBluetoothService) {
195 return sBluetoothService->HandleStartupSettingsCheck(aResult.toBoolean());
198 return NS_OK;
201 NS_IMETHOD HandleError(const nsAString& aName)
203 BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'");
204 return NS_OK;
208 NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback);
210 NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver)
212 bool
213 BluetoothService::IsToggling() const
215 return sToggleInProgress;
218 BluetoothService::~BluetoothService()
220 Cleanup();
223 PLDHashOperator
224 RemoveObserversExceptBluetoothManager
225 (const nsAString& key,
226 nsAutoPtr<BluetoothSignalObserverList>& value,
227 void* arg)
229 if (!key.EqualsLiteral(KEY_MANAGER)) {
230 return PL_DHASH_REMOVE;
233 return PL_DHASH_NEXT;
236 // static
237 BluetoothService*
238 BluetoothService::Create()
240 #if defined(MOZ_B2G_BT)
241 if (!IsMainProcess()) {
242 return BluetoothServiceChildProcess::Create();
245 #if defined(MOZ_B2G_BT_BLUEZ)
246 return new BluetoothDBusService();
247 #elif defined(MOZ_B2G_BT_BLUEDROID)
248 return new BluetoothServiceBluedroid();
249 #endif
250 #elif defined(MOZ_BLUETOOTH_DBUS)
251 return new BluetoothDBusService();
252 #endif
254 BT_WARNING("No platform support for bluetooth!");
255 return nullptr;
258 bool
259 BluetoothService::Init()
261 MOZ_ASSERT(NS_IsMainThread());
263 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
264 NS_ENSURE_TRUE(obs, false);
266 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
267 false))) {
268 BT_WARNING("Failed to add shutdown observer!");
269 return false;
272 // Only the main process should observe bluetooth settings changes.
273 if (IsMainProcess() &&
274 NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
275 BT_WARNING("Failed to add settings change observer!");
276 return false;
279 return true;
282 void
283 BluetoothService::Cleanup()
285 MOZ_ASSERT(NS_IsMainThread());
287 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
288 if (obs &&
289 (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
290 NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) {
291 BT_WARNING("Can't unregister observers!");
295 void
296 BluetoothService::RegisterBluetoothSignalHandler(
297 const nsAString& aNodeName,
298 BluetoothSignalObserver* aHandler)
300 MOZ_ASSERT(NS_IsMainThread());
301 MOZ_ASSERT(aHandler);
303 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
305 BluetoothSignalObserverList* ol;
306 if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
307 ol = new BluetoothSignalObserverList();
308 mBluetoothSignalObserverTable.Put(aNodeName, ol);
311 ol->AddObserver(aHandler);
314 void
315 BluetoothService::UnregisterBluetoothSignalHandler(
316 const nsAString& aNodeName,
317 BluetoothSignalObserver* aHandler)
319 MOZ_ASSERT(NS_IsMainThread());
320 MOZ_ASSERT(aHandler);
322 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
324 BluetoothSignalObserverList* ol;
325 if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
326 ol->RemoveObserver(aHandler);
327 // We shouldn't have duplicate instances in the ObserverList, but there's
328 // no appropriate way to do duplication check while registering, so
329 // assertions are added here.
330 MOZ_ASSERT(!ol->RemoveObserver(aHandler));
331 if (ol->Length() == 0) {
332 mBluetoothSignalObserverTable.Remove(aNodeName);
335 else {
336 BT_WARNING("Node was never registered!");
340 PLDHashOperator
341 RemoveAllSignalHandlers(const nsAString& aKey,
342 nsAutoPtr<BluetoothSignalObserverList>& aData,
343 void* aUserArg)
345 BluetoothSignalObserver* handler = static_cast<BluetoothSignalObserver*>(aUserArg);
346 aData->RemoveObserver(handler);
347 // We shouldn't have duplicate instances in the ObserverList, but there's
348 // no appropriate way to do duplication check while registering, so
349 // assertions are added here.
350 MOZ_ASSERT(!aData->RemoveObserver(handler));
351 return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
354 void
355 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
357 MOZ_ASSERT(NS_IsMainThread());
358 MOZ_ASSERT(aHandler);
360 mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
363 void
364 BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
366 MOZ_ASSERT(NS_IsMainThread());
368 if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) {
369 Notify(aSignal);
370 return;
371 } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) {
372 Notify(aSignal);
373 return;
376 BluetoothSignalObserverList* ol;
377 if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
378 #if DEBUG
379 nsAutoCString msg("No observer registered for path ");
380 msg.Append(NS_ConvertUTF16toUTF8(aSignal.path()));
381 BT_WARNING(msg.get());
382 #endif
383 return;
385 MOZ_ASSERT(ol->Length());
386 ol->Broadcast(aSignal);
389 nsresult
390 BluetoothService::StartBluetooth(bool aIsStartup)
392 MOZ_ASSERT(NS_IsMainThread());
394 if (sInShutdown) {
395 // Don't try to start if we're already shutting down.
396 MOZ_ASSERT(false, "Start called while in shutdown!");
397 return NS_ERROR_FAILURE;
400 mAdapterAddedReceived = false;
402 /* When IsEnabled() is true, we don't switch on Bluetooth but we still
403 * send ToggleBtAck task. One special case happens at startup stage. At
404 * startup, the initialization of BluetoothService still has to be done
405 * even if Bluetooth is already enabled.
407 * Please see bug 892392 for more information.
409 if (aIsStartup || !sBluetoothService->IsEnabled()) {
410 // Switch Bluetooth on
411 if (NS_FAILED(sBluetoothService->StartInternal())) {
412 BT_WARNING("Bluetooth service failed to start!");
414 } else {
415 BT_WARNING("Bluetooth has already been enabled before.");
416 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
417 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
418 BT_WARNING("Failed to dispatch to main thread!");
422 return NS_OK;
425 nsresult
426 BluetoothService::StopBluetooth(bool aIsStartup)
428 MOZ_ASSERT(NS_IsMainThread());
430 BluetoothProfileManagerBase* profile;
431 profile = BluetoothHfpManager::Get();
432 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
433 if (profile->IsConnected()) {
434 profile->Disconnect(nullptr);
435 } else {
436 profile->Reset();
439 profile = BluetoothOppManager::Get();
440 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
441 if (profile->IsConnected()) {
442 profile->Disconnect(nullptr);
445 profile = BluetoothA2dpManager::Get();
446 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
447 if (profile->IsConnected()) {
448 profile->Disconnect(nullptr);
449 } else {
450 profile->Reset();
453 profile = BluetoothHidManager::Get();
454 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
455 if (profile->IsConnected()) {
456 profile->Disconnect(nullptr);
457 } else {
458 profile->Reset();
461 mAdapterAddedReceived = false;
463 /* When IsEnabled() is false, we don't switch off Bluetooth but we still
464 * send ToggleBtAck task. One special case happens at startup stage. At
465 * startup, the initialization of BluetoothService still has to be done
466 * even if Bluetooth is disabled.
468 * Please see bug 892392 for more information.
470 if (aIsStartup || sBluetoothService->IsEnabled()) {
471 // Switch Bluetooth off
472 if (NS_FAILED(sBluetoothService->StopInternal())) {
473 BT_WARNING("Bluetooth service failed to stop!");
475 } else {
476 BT_WARNING("Bluetooth has already been enabled/disabled before.");
477 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
478 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
479 BT_WARNING("Failed to dispatch to main thread!");
483 return NS_OK;
486 nsresult
487 BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
489 nsresult rv;
490 if (aStart) {
491 rv = StartBluetooth(aIsStartup);
492 } else {
493 rv = StopBluetooth(aIsStartup);
495 return rv;
498 void
499 BluetoothService::SetEnabled(bool aEnabled)
501 MOZ_ASSERT(NS_IsMainThread());
503 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
504 GetAllBluetoothActors(childActors);
506 for (uint32_t index = 0; index < childActors.Length(); index++) {
507 unused << childActors[index]->SendEnabled(aEnabled);
510 if (!aEnabled) {
512 * Remove all handlers except BluetoothManager when turning off bluetooth
513 * since it is possible that the event 'onAdapterAdded' would be fired after
514 * BluetoothManagers of child process are registered. Please see Bug 827759
515 * for more details.
517 mBluetoothSignalObserverTable.Enumerate(
518 RemoveObserversExceptBluetoothManager, nullptr);
522 * mEnabled: real status of bluetooth
523 * aEnabled: expected status of bluetooth
525 if (mEnabled == aEnabled) {
526 BT_WARNING("Bluetooth has already been enabled/disabled before "
527 "or the toggling is failed.");
530 mEnabled = aEnabled;
533 nsresult
534 BluetoothService::HandleStartup()
536 MOZ_ASSERT(NS_IsMainThread());
537 MOZ_ASSERT(!sToggleInProgress);
539 nsCOMPtr<nsISettingsService> settings =
540 do_GetService("@mozilla.org/settingsService;1");
541 NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED);
543 nsCOMPtr<nsISettingsServiceLock> settingsLock;
544 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
545 NS_ENSURE_SUCCESS(rv, rv);
547 nsRefPtr<StartupTask> callback = new StartupTask();
548 rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback);
549 NS_ENSURE_SUCCESS(rv, rv);
551 sToggleInProgress = true;
552 return NS_OK;
555 nsresult
556 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
558 MOZ_ASSERT(NS_IsMainThread());
559 return StartStopBluetooth(aEnable, true);
562 nsresult
563 BluetoothService::HandleSettingsChanged(const nsAString& aData)
565 MOZ_ASSERT(NS_IsMainThread());
567 // The string that we're interested in will be a JSON string that looks like:
568 // {"key":"bluetooth.enabled","value":true}
570 AutoSafeJSContext cx;
571 if (!cx) {
572 return NS_OK;
575 JS::Rooted<JS::Value> val(cx);
576 if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
577 return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
580 if (!val.isObject()) {
581 return NS_OK;
584 JS::Rooted<JSObject*> obj(cx, &val.toObject());
586 JS::Rooted<JS::Value> key(cx);
587 if (!JS_GetProperty(cx, obj, "key", &key)) {
588 MOZ_ASSERT(!JS_IsExceptionPending(cx));
589 return NS_ERROR_OUT_OF_MEMORY;
592 if (!key.isString()) {
593 return NS_OK;
596 // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING
597 bool match;
598 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
599 MOZ_ASSERT(!JS_IsExceptionPending(cx));
600 return NS_ERROR_OUT_OF_MEMORY;
603 if (match) {
604 JS::Rooted<JS::Value> value(cx);
605 if (!JS_GetProperty(cx, obj, "value", &value)) {
606 MOZ_ASSERT(!JS_IsExceptionPending(cx));
607 return NS_ERROR_OUT_OF_MEMORY;
610 if (!value.isBoolean()) {
611 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
612 return NS_ERROR_UNEXPECTED;
615 SWITCH_BT_DEBUG(value.toBoolean());
617 return NS_OK;
620 // Second, check if the string is BLUETOOTH_ENABLED_SETTING
621 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
622 MOZ_ASSERT(!JS_IsExceptionPending(cx));
623 return NS_ERROR_OUT_OF_MEMORY;
626 if (match) {
627 JS::Rooted<JS::Value> value(cx);
628 if (!JS_GetProperty(cx, obj, "value", &value)) {
629 MOZ_ASSERT(!JS_IsExceptionPending(cx));
630 return NS_ERROR_OUT_OF_MEMORY;
633 if (!value.isBoolean()) {
634 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
635 return NS_ERROR_UNEXPECTED;
638 if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
639 // Nothing to do here.
640 return NS_OK;
643 sToggleInProgress = true;
645 nsresult rv = StartStopBluetooth(value.toBoolean(), false);
646 NS_ENSURE_SUCCESS(rv, rv);
649 return NS_OK;
652 nsresult
653 BluetoothService::HandleShutdown()
655 MOZ_ASSERT(NS_IsMainThread());
657 // This is a two phase shutdown. First we notify all child processes that
658 // bluetooth is going away, and then we wait for them to acknowledge. Then we
659 // close down all the bluetooth machinery.
661 sInShutdown = true;
663 Cleanup();
665 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
666 GetAllBluetoothActors(childActors);
668 if (!childActors.IsEmpty()) {
669 // Notify child processes that they should stop using bluetooth now.
670 for (uint32_t index = 0; index < childActors.Length(); index++) {
671 childActors[index]->BeginShutdown();
674 // Create a timer to ensure that we don't wait forever for a child process
675 // or the bluetooth threads to finish. If we don't get a timer or can't use
676 // it for some reason then we skip all the waiting entirely since we really
677 // can't afford to hang on shutdown.
678 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
679 MOZ_ASSERT(timer);
681 if (timer) {
682 bool timeExceeded = false;
684 if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded,
685 &timeExceeded,
686 DEFAULT_SHUTDOWN_TIMER_MS,
687 nsITimer::TYPE_ONE_SHOT))) {
688 nsIThread* currentThread = NS_GetCurrentThread();
689 MOZ_ASSERT(currentThread);
691 // Wait for those child processes to acknowledge.
692 while (!timeExceeded && !childActors.IsEmpty()) {
693 if (!NS_ProcessNextEvent(currentThread)) {
694 MOZ_ASSERT(false, "Something horribly wrong here!");
695 break;
697 GetAllBluetoothActors(childActors);
700 if (NS_FAILED(timer->Cancel())) {
701 MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
704 else {
705 MOZ_ASSERT(false, "Failed to initialize shutdown timer!");
710 if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
711 MOZ_ASSERT(false, "Failed to deliver stop message!");
714 return NS_OK;
717 // static
718 BluetoothService*
719 BluetoothService::Get()
721 MOZ_ASSERT(NS_IsMainThread());
723 // If we already exist, exit early
724 if (sBluetoothService) {
725 return sBluetoothService;
728 // If we're in shutdown, don't create a new instance
729 if (sInShutdown) {
730 BT_WARNING("BluetoothService can't be created during shutdown");
731 return nullptr;
734 // Create new instance, register, return
735 sBluetoothService = BluetoothService::Create();
736 NS_ENSURE_TRUE(sBluetoothService, nullptr);
738 if (!sBluetoothService->Init()) {
739 sBluetoothService->Cleanup();
740 return nullptr;
743 ClearOnShutdown(&sBluetoothService);
744 return sBluetoothService;
747 nsresult
748 BluetoothService::Observe(nsISupports* aSubject, const char* aTopic,
749 const char16_t* aData)
751 MOZ_ASSERT(NS_IsMainThread());
753 if (!strcmp(aTopic, "profile-after-change")) {
754 return HandleStartup();
757 if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
758 return HandleSettingsChanged(nsDependentString(aData));
761 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
762 return HandleShutdown();
765 MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
766 return NS_ERROR_UNEXPECTED;
769 void
770 BluetoothService::TryFiringAdapterAdded()
772 MOZ_ASSERT(NS_IsMainThread());
774 if (IsToggling() || !mAdapterAddedReceived) {
775 return;
778 BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"),
779 NS_LITERAL_STRING(KEY_MANAGER), true);
780 DistributeSignal(signal);
783 void
784 BluetoothService::AdapterAddedReceived()
786 MOZ_ASSERT(NS_IsMainThread());
788 mAdapterAddedReceived = true;
791 void
792 BluetoothService::Notify(const BluetoothSignal& aData)
794 nsString type = NS_LITERAL_STRING("bluetooth-pairing-request");
796 AutoSafeJSContext cx;
797 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
798 JS::NullPtr()));
799 NS_ENSURE_TRUE_VOID(obj);
801 if (!SetJsObject(cx, aData.value(), obj)) {
802 BT_WARNING("Failed to set properties of system message!");
803 return;
806 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
808 if (aData.name().EqualsLiteral("RequestConfirmation")) {
809 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4,
810 "RequestConfirmation: Wrong length of parameters");
811 } else if (aData.name().EqualsLiteral("RequestPinCode")) {
812 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
813 "RequestPinCode: Wrong length of parameters");
814 } else if (aData.name().EqualsLiteral("RequestPasskey")) {
815 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
816 "RequestPinCode: Wrong length of parameters");
817 } else if (aData.name().EqualsLiteral("Cancel")) {
818 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0,
819 "Cancel: Wrong length of parameters");
820 type.AssignLiteral("bluetooth-cancel");
821 } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) {
822 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1,
823 "pairedstatuschanged: Wrong length of parameters");
824 type.AssignLiteral("bluetooth-pairedstatuschanged");
825 } else {
826 nsCString warningMsg;
827 warningMsg.AssignLiteral("Not handling service signal: ");
828 warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
829 BT_WARNING(warningMsg.get());
830 return;
833 nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
834 do_GetService("@mozilla.org/system-message-internal;1");
835 NS_ENSURE_TRUE_VOID(systemMessenger);
837 JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
838 systemMessenger->BroadcastMessage(type, value,
839 JS::UndefinedHandleValue);