Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/Services.h"
24 #include "mozilla/StaticPtr.h"
25 #include "mozilla/unused.h"
26 #include "mozilla/dom/ContentParent.h"
27 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
28 #include "mozilla/ipc/UnixSocket.h"
29 #include "nsContentUtils.h"
30 #include "nsCxPusher.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.
47 */
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.
54 */
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.
61 */
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()
90 {
91 return XRE_GetProcessType() == GeckoProcessType_Default;
92 }
94 void
95 ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
96 {
97 MOZ_ASSERT(NS_IsMainThread());
98 *static_cast<bool*>(aClosure) = true;
99 }
101 void
102 GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors)
103 {
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);
126 }
127 }
128 }
130 } // anonymous namespace
132 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
133 : mEnabled(aEnabled)
134 { }
136 NS_METHOD
137 BluetoothService::ToggleBtAck::Run()
138 {
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.
144 //
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");
151 }
152 #endif
154 NS_ENSURE_TRUE(sBluetoothService, NS_OK);
156 if (sInShutdown) {
157 sBluetoothService = nullptr;
158 return NS_OK;
159 }
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;
176 }
178 class BluetoothService::StartupTask : public nsISettingsServiceCallback
179 {
180 public:
181 NS_DECL_ISUPPORTS
183 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
184 {
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;
190 }
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());
196 }
198 return NS_OK;
199 }
201 NS_IMETHOD HandleError(const nsAString& aName)
202 {
203 BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'");
204 return NS_OK;
205 }
206 };
208 NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback);
210 NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver)
212 bool
213 BluetoothService::IsToggling() const
214 {
215 return sToggleInProgress;
216 }
218 BluetoothService::~BluetoothService()
219 {
220 Cleanup();
221 }
223 PLDHashOperator
224 RemoveObserversExceptBluetoothManager
225 (const nsAString& key,
226 nsAutoPtr<BluetoothSignalObserverList>& value,
227 void* arg)
228 {
229 if (!key.EqualsLiteral(KEY_MANAGER)) {
230 return PL_DHASH_REMOVE;
231 }
233 return PL_DHASH_NEXT;
234 }
236 void
237 BluetoothService::RemoveObserverFromTable(const nsAString& key)
238 {
239 mBluetoothSignalObserverTable.Remove(key);
240 }
242 // static
243 BluetoothService*
244 BluetoothService::Create()
245 {
246 #if defined(MOZ_B2G_BT)
247 if (!IsMainProcess()) {
248 return BluetoothServiceChildProcess::Create();
249 }
251 #if defined(MOZ_B2G_BT_BLUEZ)
252 return new BluetoothDBusService();
253 #elif defined(MOZ_B2G_BT_BLUEDROID)
254 return new BluetoothServiceBluedroid();
255 #endif
256 #elif defined(MOZ_BLUETOOTH_DBUS)
257 return new BluetoothDBusService();
258 #endif
260 BT_WARNING("No platform support for bluetooth!");
261 return nullptr;
262 }
264 bool
265 BluetoothService::Init()
266 {
267 MOZ_ASSERT(NS_IsMainThread());
269 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
270 NS_ENSURE_TRUE(obs, false);
272 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
273 false))) {
274 BT_WARNING("Failed to add shutdown observer!");
275 return false;
276 }
278 // Only the main process should observe bluetooth settings changes.
279 if (IsMainProcess() &&
280 NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
281 BT_WARNING("Failed to add settings change observer!");
282 return false;
283 }
285 return true;
286 }
288 void
289 BluetoothService::Cleanup()
290 {
291 MOZ_ASSERT(NS_IsMainThread());
293 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
294 if (obs &&
295 (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
296 NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) {
297 BT_WARNING("Can't unregister observers!");
298 }
299 }
301 void
302 BluetoothService::RegisterBluetoothSignalHandler(
303 const nsAString& aNodeName,
304 BluetoothSignalObserver* aHandler)
305 {
306 MOZ_ASSERT(NS_IsMainThread());
307 MOZ_ASSERT(aHandler);
309 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
311 BluetoothSignalObserverList* ol;
312 if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
313 ol = new BluetoothSignalObserverList();
314 mBluetoothSignalObserverTable.Put(aNodeName, ol);
315 }
317 ol->AddObserver(aHandler);
318 }
320 void
321 BluetoothService::UnregisterBluetoothSignalHandler(
322 const nsAString& aNodeName,
323 BluetoothSignalObserver* aHandler)
324 {
325 MOZ_ASSERT(NS_IsMainThread());
326 MOZ_ASSERT(aHandler);
328 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
330 BluetoothSignalObserverList* ol;
331 if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
332 ol->RemoveObserver(aHandler);
333 // We shouldn't have duplicate instances in the ObserverList, but there's
334 // no appropriate way to do duplication check while registering, so
335 // assertions are added here.
336 MOZ_ASSERT(!ol->RemoveObserver(aHandler));
337 if (ol->Length() == 0) {
338 mBluetoothSignalObserverTable.Remove(aNodeName);
339 }
340 }
341 else {
342 BT_WARNING("Node was never registered!");
343 }
344 }
346 PLDHashOperator
347 RemoveAllSignalHandlers(const nsAString& aKey,
348 nsAutoPtr<BluetoothSignalObserverList>& aData,
349 void* aUserArg)
350 {
351 BluetoothSignalObserver* handler = static_cast<BluetoothSignalObserver*>(aUserArg);
352 aData->RemoveObserver(handler);
353 // We shouldn't have duplicate instances in the ObserverList, but there's
354 // no appropriate way to do duplication check while registering, so
355 // assertions are added here.
356 MOZ_ASSERT(!aData->RemoveObserver(handler));
357 return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
358 }
360 void
361 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
362 {
363 MOZ_ASSERT(NS_IsMainThread());
364 MOZ_ASSERT(aHandler);
366 mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
367 }
369 void
370 BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
371 {
372 MOZ_ASSERT(NS_IsMainThread());
374 if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) {
375 Notify(aSignal);
376 return;
377 } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) {
378 Notify(aSignal);
379 return;
380 }
382 BluetoothSignalObserverList* ol;
383 if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
384 #if DEBUG
385 nsAutoCString msg("No observer registered for path ");
386 msg.Append(NS_ConvertUTF16toUTF8(aSignal.path()));
387 BT_WARNING(msg.get());
388 #endif
389 return;
390 }
391 MOZ_ASSERT(ol->Length());
392 ol->Broadcast(aSignal);
393 }
395 nsresult
396 BluetoothService::StartBluetooth(bool aIsStartup)
397 {
398 MOZ_ASSERT(NS_IsMainThread());
400 if (sInShutdown) {
401 // Don't try to start if we're already shutting down.
402 MOZ_ASSERT(false, "Start called while in shutdown!");
403 return NS_ERROR_FAILURE;
404 }
406 mAdapterAddedReceived = false;
408 /* When IsEnabled() is true, we don't switch on Bluetooth but we still
409 * send ToggleBtAck task. One special case happens at startup stage. At
410 * startup, the initialization of BluetoothService still has to be done
411 * even if Bluetooth is already enabled.
412 *
413 * Please see bug 892392 for more information.
414 */
415 if (aIsStartup || !sBluetoothService->IsEnabled()) {
416 // Switch Bluetooth on
417 if (NS_FAILED(sBluetoothService->StartInternal())) {
418 BT_WARNING("Bluetooth service failed to start!");
419 }
420 } else {
421 BT_WARNING("Bluetooth has already been enabled before.");
422 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
423 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
424 BT_WARNING("Failed to dispatch to main thread!");
425 }
426 }
428 return NS_OK;
429 }
431 nsresult
432 BluetoothService::StopBluetooth(bool aIsStartup)
433 {
434 MOZ_ASSERT(NS_IsMainThread());
436 BluetoothProfileManagerBase* profile;
437 profile = BluetoothHfpManager::Get();
438 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
439 if (profile->IsConnected()) {
440 profile->Disconnect(nullptr);
441 } else {
442 profile->Reset();
443 }
445 profile = BluetoothOppManager::Get();
446 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
447 if (profile->IsConnected()) {
448 profile->Disconnect(nullptr);
449 }
451 profile = BluetoothA2dpManager::Get();
452 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
453 if (profile->IsConnected()) {
454 profile->Disconnect(nullptr);
455 } else {
456 profile->Reset();
457 }
459 profile = BluetoothHidManager::Get();
460 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
461 if (profile->IsConnected()) {
462 profile->Disconnect(nullptr);
463 } else {
464 profile->Reset();
465 }
467 mAdapterAddedReceived = false;
469 /* When IsEnabled() is false, we don't switch off Bluetooth but we still
470 * send ToggleBtAck task. One special case happens at startup stage. At
471 * startup, the initialization of BluetoothService still has to be done
472 * even if Bluetooth is disabled.
473 *
474 * Please see bug 892392 for more information.
475 */
476 if (aIsStartup || sBluetoothService->IsEnabled()) {
477 // Switch Bluetooth off
478 if (NS_FAILED(sBluetoothService->StopInternal())) {
479 BT_WARNING("Bluetooth service failed to stop!");
480 }
481 } else {
482 BT_WARNING("Bluetooth has already been enabled/disabled before.");
483 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
484 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
485 BT_WARNING("Failed to dispatch to main thread!");
486 }
487 }
489 return NS_OK;
490 }
492 nsresult
493 BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
494 {
495 nsresult rv;
496 if (aStart) {
497 rv = StartBluetooth(aIsStartup);
498 } else {
499 rv = StopBluetooth(aIsStartup);
500 }
501 return rv;
502 }
504 void
505 BluetoothService::SetEnabled(bool aEnabled)
506 {
507 MOZ_ASSERT(NS_IsMainThread());
509 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
510 GetAllBluetoothActors(childActors);
512 for (uint32_t index = 0; index < childActors.Length(); index++) {
513 unused << childActors[index]->SendEnabled(aEnabled);
514 }
516 if (!aEnabled) {
517 /**
518 * Remove all handlers except BluetoothManager when turning off bluetooth
519 * since it is possible that the event 'onAdapterAdded' would be fired after
520 * BluetoothManagers of child process are registered. Please see Bug 827759
521 * for more details.
522 */
523 mBluetoothSignalObserverTable.Enumerate(
524 RemoveObserversExceptBluetoothManager, nullptr);
525 }
527 /**
528 * mEnabled: real status of bluetooth
529 * aEnabled: expected status of bluetooth
530 */
531 if (mEnabled == aEnabled) {
532 BT_WARNING("Bluetooth has already been enabled/disabled before "
533 "or the toggling is failed.");
534 }
536 mEnabled = aEnabled;
537 }
539 nsresult
540 BluetoothService::HandleStartup()
541 {
542 MOZ_ASSERT(NS_IsMainThread());
543 MOZ_ASSERT(!sToggleInProgress);
545 nsCOMPtr<nsISettingsService> settings =
546 do_GetService("@mozilla.org/settingsService;1");
547 NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED);
549 nsCOMPtr<nsISettingsServiceLock> settingsLock;
550 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
551 NS_ENSURE_SUCCESS(rv, rv);
553 nsRefPtr<StartupTask> callback = new StartupTask();
554 rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback);
555 NS_ENSURE_SUCCESS(rv, rv);
557 sToggleInProgress = true;
558 return NS_OK;
559 }
561 nsresult
562 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
563 {
564 MOZ_ASSERT(NS_IsMainThread());
565 return StartStopBluetooth(aEnable, true);
566 }
568 nsresult
569 BluetoothService::HandleSettingsChanged(const nsAString& aData)
570 {
571 MOZ_ASSERT(NS_IsMainThread());
573 // The string that we're interested in will be a JSON string that looks like:
574 // {"key":"bluetooth.enabled","value":true}
576 AutoSafeJSContext cx;
577 if (!cx) {
578 return NS_OK;
579 }
581 JS::Rooted<JS::Value> val(cx);
582 if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
583 return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
584 }
586 if (!val.isObject()) {
587 return NS_OK;
588 }
590 JS::Rooted<JSObject*> obj(cx, &val.toObject());
592 JS::Rooted<JS::Value> key(cx);
593 if (!JS_GetProperty(cx, obj, "key", &key)) {
594 MOZ_ASSERT(!JS_IsExceptionPending(cx));
595 return NS_ERROR_OUT_OF_MEMORY;
596 }
598 if (!key.isString()) {
599 return NS_OK;
600 }
602 // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING
603 bool match;
604 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
605 MOZ_ASSERT(!JS_IsExceptionPending(cx));
606 return NS_ERROR_OUT_OF_MEMORY;
607 }
609 if (match) {
610 JS::Rooted<JS::Value> value(cx);
611 if (!JS_GetProperty(cx, obj, "value", &value)) {
612 MOZ_ASSERT(!JS_IsExceptionPending(cx));
613 return NS_ERROR_OUT_OF_MEMORY;
614 }
616 if (!value.isBoolean()) {
617 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
618 return NS_ERROR_UNEXPECTED;
619 }
621 SWITCH_BT_DEBUG(value.toBoolean());
623 return NS_OK;
624 }
626 // Second, check if the string is BLUETOOTH_ENABLED_SETTING
627 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
628 MOZ_ASSERT(!JS_IsExceptionPending(cx));
629 return NS_ERROR_OUT_OF_MEMORY;
630 }
632 if (match) {
633 JS::Rooted<JS::Value> value(cx);
634 if (!JS_GetProperty(cx, obj, "value", &value)) {
635 MOZ_ASSERT(!JS_IsExceptionPending(cx));
636 return NS_ERROR_OUT_OF_MEMORY;
637 }
639 if (!value.isBoolean()) {
640 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
641 return NS_ERROR_UNEXPECTED;
642 }
644 if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
645 // Nothing to do here.
646 return NS_OK;
647 }
649 sToggleInProgress = true;
651 nsresult rv = StartStopBluetooth(value.toBoolean(), false);
652 NS_ENSURE_SUCCESS(rv, rv);
653 }
655 return NS_OK;
656 }
658 nsresult
659 BluetoothService::HandleShutdown()
660 {
661 MOZ_ASSERT(NS_IsMainThread());
663 // This is a two phase shutdown. First we notify all child processes that
664 // bluetooth is going away, and then we wait for them to acknowledge. Then we
665 // close down all the bluetooth machinery.
667 sInShutdown = true;
669 Cleanup();
671 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
672 GetAllBluetoothActors(childActors);
674 if (!childActors.IsEmpty()) {
675 // Notify child processes that they should stop using bluetooth now.
676 for (uint32_t index = 0; index < childActors.Length(); index++) {
677 childActors[index]->BeginShutdown();
678 }
680 // Create a timer to ensure that we don't wait forever for a child process
681 // or the bluetooth threads to finish. If we don't get a timer or can't use
682 // it for some reason then we skip all the waiting entirely since we really
683 // can't afford to hang on shutdown.
684 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
685 MOZ_ASSERT(timer);
687 if (timer) {
688 bool timeExceeded = false;
690 if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded,
691 &timeExceeded,
692 DEFAULT_SHUTDOWN_TIMER_MS,
693 nsITimer::TYPE_ONE_SHOT))) {
694 nsIThread* currentThread = NS_GetCurrentThread();
695 MOZ_ASSERT(currentThread);
697 // Wait for those child processes to acknowledge.
698 while (!timeExceeded && !childActors.IsEmpty()) {
699 if (!NS_ProcessNextEvent(currentThread)) {
700 MOZ_ASSERT(false, "Something horribly wrong here!");
701 break;
702 }
703 GetAllBluetoothActors(childActors);
704 }
706 if (NS_FAILED(timer->Cancel())) {
707 MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
708 }
709 }
710 else {
711 MOZ_ASSERT(false, "Failed to initialize shutdown timer!");
712 }
713 }
714 }
716 if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
717 MOZ_ASSERT(false, "Failed to deliver stop message!");
718 }
720 return NS_OK;
721 }
723 // static
724 BluetoothService*
725 BluetoothService::Get()
726 {
727 MOZ_ASSERT(NS_IsMainThread());
729 // If we already exist, exit early
730 if (sBluetoothService) {
731 return sBluetoothService;
732 }
734 // If we're in shutdown, don't create a new instance
735 if (sInShutdown) {
736 BT_WARNING("BluetoothService can't be created during shutdown");
737 return nullptr;
738 }
740 // Create new instance, register, return
741 nsRefPtr<BluetoothService> service = BluetoothService::Create();
742 NS_ENSURE_TRUE(service, nullptr);
744 if (!service->Init()) {
745 service->Cleanup();
746 return nullptr;
747 }
749 sBluetoothService = service;
750 return sBluetoothService;
751 }
753 nsresult
754 BluetoothService::Observe(nsISupports* aSubject, const char* aTopic,
755 const char16_t* aData)
756 {
757 MOZ_ASSERT(NS_IsMainThread());
759 if (!strcmp(aTopic, "profile-after-change")) {
760 return HandleStartup();
761 }
763 if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
764 return HandleSettingsChanged(nsDependentString(aData));
765 }
767 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
768 return HandleShutdown();
769 }
771 MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
772 return NS_ERROR_UNEXPECTED;
773 }
775 void
776 BluetoothService::TryFiringAdapterAdded()
777 {
778 MOZ_ASSERT(NS_IsMainThread());
780 if (IsToggling() || !mAdapterAddedReceived) {
781 return;
782 }
784 BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"),
785 NS_LITERAL_STRING(KEY_MANAGER), true);
786 DistributeSignal(signal);
787 }
789 void
790 BluetoothService::AdapterAddedReceived()
791 {
792 MOZ_ASSERT(NS_IsMainThread());
794 mAdapterAddedReceived = true;
795 }
797 void
798 BluetoothService::Notify(const BluetoothSignal& aData)
799 {
800 nsString type = NS_LITERAL_STRING("bluetooth-pairing-request");
802 AutoSafeJSContext cx;
803 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
804 JS::NullPtr()));
805 NS_ENSURE_TRUE_VOID(obj);
807 if (!SetJsObject(cx, aData.value(), obj)) {
808 BT_WARNING("Failed to set properties of system message!");
809 return;
810 }
812 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
814 if (aData.name().EqualsLiteral("RequestConfirmation")) {
815 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4,
816 "RequestConfirmation: Wrong length of parameters");
817 } else if (aData.name().EqualsLiteral("RequestPinCode")) {
818 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
819 "RequestPinCode: Wrong length of parameters");
820 } else if (aData.name().EqualsLiteral("RequestPasskey")) {
821 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
822 "RequestPinCode: Wrong length of parameters");
823 } else if (aData.name().EqualsLiteral("Cancel")) {
824 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0,
825 "Cancel: Wrong length of parameters");
826 type.AssignLiteral("bluetooth-cancel");
827 } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) {
828 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1,
829 "pairedstatuschanged: Wrong length of parameters");
830 type.AssignLiteral("bluetooth-pairedstatuschanged");
831 } else {
832 nsCString warningMsg;
833 warningMsg.AssignLiteral("Not handling service signal: ");
834 warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
835 BT_WARNING(warningMsg.get());
836 return;
837 }
839 nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
840 do_GetService("@mozilla.org/system-message-internal;1");
841 NS_ENSURE_TRUE_VOID(systemMessenger);
843 JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
844 systemMessenger->BroadcastMessage(type, value,
845 JS::UndefinedHandleValue);
846 }