dom/bluetooth/BluetoothService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8
michael@0 9 #include "BluetoothService.h"
michael@0 10
michael@0 11 #include "BluetoothCommon.h"
michael@0 12 #include "BluetoothA2dpManager.h"
michael@0 13 #include "BluetoothHfpManager.h"
michael@0 14 #include "BluetoothHidManager.h"
michael@0 15 #include "BluetoothManager.h"
michael@0 16 #include "BluetoothOppManager.h"
michael@0 17 #include "BluetoothParent.h"
michael@0 18 #include "BluetoothReplyRunnable.h"
michael@0 19 #include "BluetoothServiceChildProcess.h"
michael@0 20 #include "BluetoothUtils.h"
michael@0 21
michael@0 22 #include "jsapi.h"
michael@0 23 #include "mozilla/Services.h"
michael@0 24 #include "mozilla/StaticPtr.h"
michael@0 25 #include "mozilla/unused.h"
michael@0 26 #include "mozilla/dom/ContentParent.h"
michael@0 27 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
michael@0 28 #include "mozilla/ipc/UnixSocket.h"
michael@0 29 #include "nsContentUtils.h"
michael@0 30 #include "nsCxPusher.h"
michael@0 31 #include "nsIObserverService.h"
michael@0 32 #include "nsISettingsService.h"
michael@0 33 #include "nsISystemMessagesInternal.h"
michael@0 34 #include "nsITimer.h"
michael@0 35 #include "nsServiceManagerUtils.h"
michael@0 36 #include "nsXPCOM.h"
michael@0 37
michael@0 38 #if defined(MOZ_WIDGET_GONK)
michael@0 39 #include "cutils/properties.h"
michael@0 40 #endif
michael@0 41
michael@0 42 #if defined(MOZ_B2G_BT)
michael@0 43 #if defined(MOZ_B2G_BT_BLUEZ)
michael@0 44 /**
michael@0 45 * B2G blueZ:
michael@0 46 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEZ are both defined.
michael@0 47 */
michael@0 48 #include "BluetoothDBusService.h"
michael@0 49 #elif defined(MOZ_B2G_BT_BLUEDROID)
michael@0 50 /**
michael@0 51 * B2G bluedroid:
michael@0 52 * MOZ_B2G_BT and MOZ_B2G_BT_BLUEDROID are both defined;
michael@0 53 * MOZ_B2G_BLUEZ is not defined.
michael@0 54 */
michael@0 55 #include "BluetoothServiceBluedroid.h"
michael@0 56 #endif
michael@0 57 #elif defined(MOZ_BLUETOOTH_DBUS)
michael@0 58 /**
michael@0 59 * Desktop bluetooth:
michael@0 60 * MOZ_B2G_BT is not defined; MOZ_BLUETOOTH_DBUS is defined.
michael@0 61 */
michael@0 62 #include "BluetoothDBusService.h"
michael@0 63 #else
michael@0 64 #error No backend
michael@0 65 #endif
michael@0 66
michael@0 67 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
michael@0 68 #define BLUETOOTH_ENABLED_SETTING "bluetooth.enabled"
michael@0 69 #define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled"
michael@0 70
michael@0 71 #define PROP_BLUETOOTH_ENABLED "bluetooth.isEnabled"
michael@0 72
michael@0 73 #define DEFAULT_SHUTDOWN_TIMER_MS 5000
michael@0 74
michael@0 75 bool gBluetoothDebugFlag = false;
michael@0 76
michael@0 77 using namespace mozilla;
michael@0 78 using namespace mozilla::dom;
michael@0 79 USING_BLUETOOTH_NAMESPACE
michael@0 80
michael@0 81 namespace {
michael@0 82
michael@0 83 StaticRefPtr<BluetoothService> sBluetoothService;
michael@0 84
michael@0 85 bool sInShutdown = false;
michael@0 86 bool sToggleInProgress = false;
michael@0 87
michael@0 88 bool
michael@0 89 IsMainProcess()
michael@0 90 {
michael@0 91 return XRE_GetProcessType() == GeckoProcessType_Default;
michael@0 92 }
michael@0 93
michael@0 94 void
michael@0 95 ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure)
michael@0 96 {
michael@0 97 MOZ_ASSERT(NS_IsMainThread());
michael@0 98 *static_cast<bool*>(aClosure) = true;
michael@0 99 }
michael@0 100
michael@0 101 void
michael@0 102 GetAllBluetoothActors(InfallibleTArray<BluetoothParent*>& aActors)
michael@0 103 {
michael@0 104 MOZ_ASSERT(NS_IsMainThread());
michael@0 105 MOZ_ASSERT(aActors.IsEmpty());
michael@0 106
michael@0 107 nsAutoTArray<ContentParent*, 20> contentActors;
michael@0 108 ContentParent::GetAll(contentActors);
michael@0 109
michael@0 110 for (uint32_t contentIndex = 0;
michael@0 111 contentIndex < contentActors.Length();
michael@0 112 contentIndex++) {
michael@0 113 MOZ_ASSERT(contentActors[contentIndex]);
michael@0 114
michael@0 115 AutoInfallibleTArray<PBluetoothParent*, 5> bluetoothActors;
michael@0 116 contentActors[contentIndex]->ManagedPBluetoothParent(bluetoothActors);
michael@0 117
michael@0 118 for (uint32_t bluetoothIndex = 0;
michael@0 119 bluetoothIndex < bluetoothActors.Length();
michael@0 120 bluetoothIndex++) {
michael@0 121 MOZ_ASSERT(bluetoothActors[bluetoothIndex]);
michael@0 122
michael@0 123 BluetoothParent* actor =
michael@0 124 static_cast<BluetoothParent*>(bluetoothActors[bluetoothIndex]);
michael@0 125 aActors.AppendElement(actor);
michael@0 126 }
michael@0 127 }
michael@0 128 }
michael@0 129
michael@0 130 } // anonymous namespace
michael@0 131
michael@0 132 BluetoothService::ToggleBtAck::ToggleBtAck(bool aEnabled)
michael@0 133 : mEnabled(aEnabled)
michael@0 134 { }
michael@0 135
michael@0 136 NS_METHOD
michael@0 137 BluetoothService::ToggleBtAck::Run()
michael@0 138 {
michael@0 139 MOZ_ASSERT(NS_IsMainThread());
michael@0 140
michael@0 141 // This is requested in Bug 836516. With settings this property, WLAN
michael@0 142 // firmware could be aware of Bluetooth has been turned on/off, so that the
michael@0 143 // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
michael@0 144 //
michael@0 145 // In the future, we may have our own way instead of setting a system
michael@0 146 // property to let firmware developers be able to sense that Bluetooth has
michael@0 147 // been toggled.
michael@0 148 #if defined(MOZ_WIDGET_GONK)
michael@0 149 if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) {
michael@0 150 BT_WARNING("Failed to set bluetooth enabled property");
michael@0 151 }
michael@0 152 #endif
michael@0 153
michael@0 154 NS_ENSURE_TRUE(sBluetoothService, NS_OK);
michael@0 155
michael@0 156 if (sInShutdown) {
michael@0 157 sBluetoothService = nullptr;
michael@0 158 return NS_OK;
michael@0 159 }
michael@0 160
michael@0 161 // Update mEnabled of BluetoothService object since
michael@0 162 // StartInternal/StopInternal have been already done.
michael@0 163 sBluetoothService->SetEnabled(mEnabled);
michael@0 164 sToggleInProgress = false;
michael@0 165
michael@0 166 nsAutoString signalName;
michael@0 167 signalName = mEnabled ? NS_LITERAL_STRING("Enabled")
michael@0 168 : NS_LITERAL_STRING("Disabled");
michael@0 169 BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true);
michael@0 170 sBluetoothService->DistributeSignal(signal);
michael@0 171
michael@0 172 // Event 'AdapterAdded' has to be fired after firing 'Enabled'
michael@0 173 sBluetoothService->TryFiringAdapterAdded();
michael@0 174
michael@0 175 return NS_OK;
michael@0 176 }
michael@0 177
michael@0 178 class BluetoothService::StartupTask : public nsISettingsServiceCallback
michael@0 179 {
michael@0 180 public:
michael@0 181 NS_DECL_ISUPPORTS
michael@0 182
michael@0 183 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
michael@0 184 {
michael@0 185 MOZ_ASSERT(NS_IsMainThread());
michael@0 186
michael@0 187 if (!aResult.isBoolean()) {
michael@0 188 BT_WARNING("Setting for '" BLUETOOTH_ENABLED_SETTING "' is not a boolean!");
michael@0 189 return NS_OK;
michael@0 190 }
michael@0 191
michael@0 192 // It is theoretically possible to shut down before the first settings check
michael@0 193 // has completed (though extremely unlikely).
michael@0 194 if (sBluetoothService) {
michael@0 195 return sBluetoothService->HandleStartupSettingsCheck(aResult.toBoolean());
michael@0 196 }
michael@0 197
michael@0 198 return NS_OK;
michael@0 199 }
michael@0 200
michael@0 201 NS_IMETHOD HandleError(const nsAString& aName)
michael@0 202 {
michael@0 203 BT_WARNING("Unable to get value for '" BLUETOOTH_ENABLED_SETTING "'");
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206 };
michael@0 207
michael@0 208 NS_IMPL_ISUPPORTS(BluetoothService::StartupTask, nsISettingsServiceCallback);
michael@0 209
michael@0 210 NS_IMPL_ISUPPORTS(BluetoothService, nsIObserver)
michael@0 211
michael@0 212 bool
michael@0 213 BluetoothService::IsToggling() const
michael@0 214 {
michael@0 215 return sToggleInProgress;
michael@0 216 }
michael@0 217
michael@0 218 BluetoothService::~BluetoothService()
michael@0 219 {
michael@0 220 Cleanup();
michael@0 221 }
michael@0 222
michael@0 223 PLDHashOperator
michael@0 224 RemoveObserversExceptBluetoothManager
michael@0 225 (const nsAString& key,
michael@0 226 nsAutoPtr<BluetoothSignalObserverList>& value,
michael@0 227 void* arg)
michael@0 228 {
michael@0 229 if (!key.EqualsLiteral(KEY_MANAGER)) {
michael@0 230 return PL_DHASH_REMOVE;
michael@0 231 }
michael@0 232
michael@0 233 return PL_DHASH_NEXT;
michael@0 234 }
michael@0 235
michael@0 236 void
michael@0 237 BluetoothService::RemoveObserverFromTable(const nsAString& key)
michael@0 238 {
michael@0 239 mBluetoothSignalObserverTable.Remove(key);
michael@0 240 }
michael@0 241
michael@0 242 // static
michael@0 243 BluetoothService*
michael@0 244 BluetoothService::Create()
michael@0 245 {
michael@0 246 #if defined(MOZ_B2G_BT)
michael@0 247 if (!IsMainProcess()) {
michael@0 248 return BluetoothServiceChildProcess::Create();
michael@0 249 }
michael@0 250
michael@0 251 #if defined(MOZ_B2G_BT_BLUEZ)
michael@0 252 return new BluetoothDBusService();
michael@0 253 #elif defined(MOZ_B2G_BT_BLUEDROID)
michael@0 254 return new BluetoothServiceBluedroid();
michael@0 255 #endif
michael@0 256 #elif defined(MOZ_BLUETOOTH_DBUS)
michael@0 257 return new BluetoothDBusService();
michael@0 258 #endif
michael@0 259
michael@0 260 BT_WARNING("No platform support for bluetooth!");
michael@0 261 return nullptr;
michael@0 262 }
michael@0 263
michael@0 264 bool
michael@0 265 BluetoothService::Init()
michael@0 266 {
michael@0 267 MOZ_ASSERT(NS_IsMainThread());
michael@0 268
michael@0 269 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 270 NS_ENSURE_TRUE(obs, false);
michael@0 271
michael@0 272 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
michael@0 273 false))) {
michael@0 274 BT_WARNING("Failed to add shutdown observer!");
michael@0 275 return false;
michael@0 276 }
michael@0 277
michael@0 278 // Only the main process should observe bluetooth settings changes.
michael@0 279 if (IsMainProcess() &&
michael@0 280 NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
michael@0 281 BT_WARNING("Failed to add settings change observer!");
michael@0 282 return false;
michael@0 283 }
michael@0 284
michael@0 285 return true;
michael@0 286 }
michael@0 287
michael@0 288 void
michael@0 289 BluetoothService::Cleanup()
michael@0 290 {
michael@0 291 MOZ_ASSERT(NS_IsMainThread());
michael@0 292
michael@0 293 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 294 if (obs &&
michael@0 295 (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
michael@0 296 NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)))) {
michael@0 297 BT_WARNING("Can't unregister observers!");
michael@0 298 }
michael@0 299 }
michael@0 300
michael@0 301 void
michael@0 302 BluetoothService::RegisterBluetoothSignalHandler(
michael@0 303 const nsAString& aNodeName,
michael@0 304 BluetoothSignalObserver* aHandler)
michael@0 305 {
michael@0 306 MOZ_ASSERT(NS_IsMainThread());
michael@0 307 MOZ_ASSERT(aHandler);
michael@0 308
michael@0 309 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
michael@0 310
michael@0 311 BluetoothSignalObserverList* ol;
michael@0 312 if (!mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
michael@0 313 ol = new BluetoothSignalObserverList();
michael@0 314 mBluetoothSignalObserverTable.Put(aNodeName, ol);
michael@0 315 }
michael@0 316
michael@0 317 ol->AddObserver(aHandler);
michael@0 318 }
michael@0 319
michael@0 320 void
michael@0 321 BluetoothService::UnregisterBluetoothSignalHandler(
michael@0 322 const nsAString& aNodeName,
michael@0 323 BluetoothSignalObserver* aHandler)
michael@0 324 {
michael@0 325 MOZ_ASSERT(NS_IsMainThread());
michael@0 326 MOZ_ASSERT(aHandler);
michael@0 327
michael@0 328 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aNodeName).get());
michael@0 329
michael@0 330 BluetoothSignalObserverList* ol;
michael@0 331 if (mBluetoothSignalObserverTable.Get(aNodeName, &ol)) {
michael@0 332 ol->RemoveObserver(aHandler);
michael@0 333 // We shouldn't have duplicate instances in the ObserverList, but there's
michael@0 334 // no appropriate way to do duplication check while registering, so
michael@0 335 // assertions are added here.
michael@0 336 MOZ_ASSERT(!ol->RemoveObserver(aHandler));
michael@0 337 if (ol->Length() == 0) {
michael@0 338 mBluetoothSignalObserverTable.Remove(aNodeName);
michael@0 339 }
michael@0 340 }
michael@0 341 else {
michael@0 342 BT_WARNING("Node was never registered!");
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 PLDHashOperator
michael@0 347 RemoveAllSignalHandlers(const nsAString& aKey,
michael@0 348 nsAutoPtr<BluetoothSignalObserverList>& aData,
michael@0 349 void* aUserArg)
michael@0 350 {
michael@0 351 BluetoothSignalObserver* handler = static_cast<BluetoothSignalObserver*>(aUserArg);
michael@0 352 aData->RemoveObserver(handler);
michael@0 353 // We shouldn't have duplicate instances in the ObserverList, but there's
michael@0 354 // no appropriate way to do duplication check while registering, so
michael@0 355 // assertions are added here.
michael@0 356 MOZ_ASSERT(!aData->RemoveObserver(handler));
michael@0 357 return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
michael@0 358 }
michael@0 359
michael@0 360 void
michael@0 361 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
michael@0 362 {
michael@0 363 MOZ_ASSERT(NS_IsMainThread());
michael@0 364 MOZ_ASSERT(aHandler);
michael@0 365
michael@0 366 mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
michael@0 371 {
michael@0 372 MOZ_ASSERT(NS_IsMainThread());
michael@0 373
michael@0 374 if (aSignal.path().EqualsLiteral(KEY_LOCAL_AGENT)) {
michael@0 375 Notify(aSignal);
michael@0 376 return;
michael@0 377 } else if (aSignal.path().EqualsLiteral(KEY_REMOTE_AGENT)) {
michael@0 378 Notify(aSignal);
michael@0 379 return;
michael@0 380 }
michael@0 381
michael@0 382 BluetoothSignalObserverList* ol;
michael@0 383 if (!mBluetoothSignalObserverTable.Get(aSignal.path(), &ol)) {
michael@0 384 #if DEBUG
michael@0 385 nsAutoCString msg("No observer registered for path ");
michael@0 386 msg.Append(NS_ConvertUTF16toUTF8(aSignal.path()));
michael@0 387 BT_WARNING(msg.get());
michael@0 388 #endif
michael@0 389 return;
michael@0 390 }
michael@0 391 MOZ_ASSERT(ol->Length());
michael@0 392 ol->Broadcast(aSignal);
michael@0 393 }
michael@0 394
michael@0 395 nsresult
michael@0 396 BluetoothService::StartBluetooth(bool aIsStartup)
michael@0 397 {
michael@0 398 MOZ_ASSERT(NS_IsMainThread());
michael@0 399
michael@0 400 if (sInShutdown) {
michael@0 401 // Don't try to start if we're already shutting down.
michael@0 402 MOZ_ASSERT(false, "Start called while in shutdown!");
michael@0 403 return NS_ERROR_FAILURE;
michael@0 404 }
michael@0 405
michael@0 406 mAdapterAddedReceived = false;
michael@0 407
michael@0 408 /* When IsEnabled() is true, we don't switch on Bluetooth but we still
michael@0 409 * send ToggleBtAck task. One special case happens at startup stage. At
michael@0 410 * startup, the initialization of BluetoothService still has to be done
michael@0 411 * even if Bluetooth is already enabled.
michael@0 412 *
michael@0 413 * Please see bug 892392 for more information.
michael@0 414 */
michael@0 415 if (aIsStartup || !sBluetoothService->IsEnabled()) {
michael@0 416 // Switch Bluetooth on
michael@0 417 if (NS_FAILED(sBluetoothService->StartInternal())) {
michael@0 418 BT_WARNING("Bluetooth service failed to start!");
michael@0 419 }
michael@0 420 } else {
michael@0 421 BT_WARNING("Bluetooth has already been enabled before.");
michael@0 422 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true);
michael@0 423 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
michael@0 424 BT_WARNING("Failed to dispatch to main thread!");
michael@0 425 }
michael@0 426 }
michael@0 427
michael@0 428 return NS_OK;
michael@0 429 }
michael@0 430
michael@0 431 nsresult
michael@0 432 BluetoothService::StopBluetooth(bool aIsStartup)
michael@0 433 {
michael@0 434 MOZ_ASSERT(NS_IsMainThread());
michael@0 435
michael@0 436 BluetoothProfileManagerBase* profile;
michael@0 437 profile = BluetoothHfpManager::Get();
michael@0 438 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
michael@0 439 if (profile->IsConnected()) {
michael@0 440 profile->Disconnect(nullptr);
michael@0 441 } else {
michael@0 442 profile->Reset();
michael@0 443 }
michael@0 444
michael@0 445 profile = BluetoothOppManager::Get();
michael@0 446 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
michael@0 447 if (profile->IsConnected()) {
michael@0 448 profile->Disconnect(nullptr);
michael@0 449 }
michael@0 450
michael@0 451 profile = BluetoothA2dpManager::Get();
michael@0 452 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
michael@0 453 if (profile->IsConnected()) {
michael@0 454 profile->Disconnect(nullptr);
michael@0 455 } else {
michael@0 456 profile->Reset();
michael@0 457 }
michael@0 458
michael@0 459 profile = BluetoothHidManager::Get();
michael@0 460 NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
michael@0 461 if (profile->IsConnected()) {
michael@0 462 profile->Disconnect(nullptr);
michael@0 463 } else {
michael@0 464 profile->Reset();
michael@0 465 }
michael@0 466
michael@0 467 mAdapterAddedReceived = false;
michael@0 468
michael@0 469 /* When IsEnabled() is false, we don't switch off Bluetooth but we still
michael@0 470 * send ToggleBtAck task. One special case happens at startup stage. At
michael@0 471 * startup, the initialization of BluetoothService still has to be done
michael@0 472 * even if Bluetooth is disabled.
michael@0 473 *
michael@0 474 * Please see bug 892392 for more information.
michael@0 475 */
michael@0 476 if (aIsStartup || sBluetoothService->IsEnabled()) {
michael@0 477 // Switch Bluetooth off
michael@0 478 if (NS_FAILED(sBluetoothService->StopInternal())) {
michael@0 479 BT_WARNING("Bluetooth service failed to stop!");
michael@0 480 }
michael@0 481 } else {
michael@0 482 BT_WARNING("Bluetooth has already been enabled/disabled before.");
michael@0 483 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false);
michael@0 484 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
michael@0 485 BT_WARNING("Failed to dispatch to main thread!");
michael@0 486 }
michael@0 487 }
michael@0 488
michael@0 489 return NS_OK;
michael@0 490 }
michael@0 491
michael@0 492 nsresult
michael@0 493 BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
michael@0 494 {
michael@0 495 nsresult rv;
michael@0 496 if (aStart) {
michael@0 497 rv = StartBluetooth(aIsStartup);
michael@0 498 } else {
michael@0 499 rv = StopBluetooth(aIsStartup);
michael@0 500 }
michael@0 501 return rv;
michael@0 502 }
michael@0 503
michael@0 504 void
michael@0 505 BluetoothService::SetEnabled(bool aEnabled)
michael@0 506 {
michael@0 507 MOZ_ASSERT(NS_IsMainThread());
michael@0 508
michael@0 509 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
michael@0 510 GetAllBluetoothActors(childActors);
michael@0 511
michael@0 512 for (uint32_t index = 0; index < childActors.Length(); index++) {
michael@0 513 unused << childActors[index]->SendEnabled(aEnabled);
michael@0 514 }
michael@0 515
michael@0 516 if (!aEnabled) {
michael@0 517 /**
michael@0 518 * Remove all handlers except BluetoothManager when turning off bluetooth
michael@0 519 * since it is possible that the event 'onAdapterAdded' would be fired after
michael@0 520 * BluetoothManagers of child process are registered. Please see Bug 827759
michael@0 521 * for more details.
michael@0 522 */
michael@0 523 mBluetoothSignalObserverTable.Enumerate(
michael@0 524 RemoveObserversExceptBluetoothManager, nullptr);
michael@0 525 }
michael@0 526
michael@0 527 /**
michael@0 528 * mEnabled: real status of bluetooth
michael@0 529 * aEnabled: expected status of bluetooth
michael@0 530 */
michael@0 531 if (mEnabled == aEnabled) {
michael@0 532 BT_WARNING("Bluetooth has already been enabled/disabled before "
michael@0 533 "or the toggling is failed.");
michael@0 534 }
michael@0 535
michael@0 536 mEnabled = aEnabled;
michael@0 537 }
michael@0 538
michael@0 539 nsresult
michael@0 540 BluetoothService::HandleStartup()
michael@0 541 {
michael@0 542 MOZ_ASSERT(NS_IsMainThread());
michael@0 543 MOZ_ASSERT(!sToggleInProgress);
michael@0 544
michael@0 545 nsCOMPtr<nsISettingsService> settings =
michael@0 546 do_GetService("@mozilla.org/settingsService;1");
michael@0 547 NS_ENSURE_TRUE(settings, NS_ERROR_UNEXPECTED);
michael@0 548
michael@0 549 nsCOMPtr<nsISettingsServiceLock> settingsLock;
michael@0 550 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
michael@0 551 NS_ENSURE_SUCCESS(rv, rv);
michael@0 552
michael@0 553 nsRefPtr<StartupTask> callback = new StartupTask();
michael@0 554 rv = settingsLock->Get(BLUETOOTH_ENABLED_SETTING, callback);
michael@0 555 NS_ENSURE_SUCCESS(rv, rv);
michael@0 556
michael@0 557 sToggleInProgress = true;
michael@0 558 return NS_OK;
michael@0 559 }
michael@0 560
michael@0 561 nsresult
michael@0 562 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
michael@0 563 {
michael@0 564 MOZ_ASSERT(NS_IsMainThread());
michael@0 565 return StartStopBluetooth(aEnable, true);
michael@0 566 }
michael@0 567
michael@0 568 nsresult
michael@0 569 BluetoothService::HandleSettingsChanged(const nsAString& aData)
michael@0 570 {
michael@0 571 MOZ_ASSERT(NS_IsMainThread());
michael@0 572
michael@0 573 // The string that we're interested in will be a JSON string that looks like:
michael@0 574 // {"key":"bluetooth.enabled","value":true}
michael@0 575
michael@0 576 AutoSafeJSContext cx;
michael@0 577 if (!cx) {
michael@0 578 return NS_OK;
michael@0 579 }
michael@0 580
michael@0 581 JS::Rooted<JS::Value> val(cx);
michael@0 582 if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
michael@0 583 return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
michael@0 584 }
michael@0 585
michael@0 586 if (!val.isObject()) {
michael@0 587 return NS_OK;
michael@0 588 }
michael@0 589
michael@0 590 JS::Rooted<JSObject*> obj(cx, &val.toObject());
michael@0 591
michael@0 592 JS::Rooted<JS::Value> key(cx);
michael@0 593 if (!JS_GetProperty(cx, obj, "key", &key)) {
michael@0 594 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 595 return NS_ERROR_OUT_OF_MEMORY;
michael@0 596 }
michael@0 597
michael@0 598 if (!key.isString()) {
michael@0 599 return NS_OK;
michael@0 600 }
michael@0 601
michael@0 602 // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING
michael@0 603 bool match;
michael@0 604 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
michael@0 605 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 606 return NS_ERROR_OUT_OF_MEMORY;
michael@0 607 }
michael@0 608
michael@0 609 if (match) {
michael@0 610 JS::Rooted<JS::Value> value(cx);
michael@0 611 if (!JS_GetProperty(cx, obj, "value", &value)) {
michael@0 612 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 613 return NS_ERROR_OUT_OF_MEMORY;
michael@0 614 }
michael@0 615
michael@0 616 if (!value.isBoolean()) {
michael@0 617 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
michael@0 618 return NS_ERROR_UNEXPECTED;
michael@0 619 }
michael@0 620
michael@0 621 SWITCH_BT_DEBUG(value.toBoolean());
michael@0 622
michael@0 623 return NS_OK;
michael@0 624 }
michael@0 625
michael@0 626 // Second, check if the string is BLUETOOTH_ENABLED_SETTING
michael@0 627 if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
michael@0 628 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 629 return NS_ERROR_OUT_OF_MEMORY;
michael@0 630 }
michael@0 631
michael@0 632 if (match) {
michael@0 633 JS::Rooted<JS::Value> value(cx);
michael@0 634 if (!JS_GetProperty(cx, obj, "value", &value)) {
michael@0 635 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 636 return NS_ERROR_OUT_OF_MEMORY;
michael@0 637 }
michael@0 638
michael@0 639 if (!value.isBoolean()) {
michael@0 640 MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
michael@0 641 return NS_ERROR_UNEXPECTED;
michael@0 642 }
michael@0 643
michael@0 644 if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
michael@0 645 // Nothing to do here.
michael@0 646 return NS_OK;
michael@0 647 }
michael@0 648
michael@0 649 sToggleInProgress = true;
michael@0 650
michael@0 651 nsresult rv = StartStopBluetooth(value.toBoolean(), false);
michael@0 652 NS_ENSURE_SUCCESS(rv, rv);
michael@0 653 }
michael@0 654
michael@0 655 return NS_OK;
michael@0 656 }
michael@0 657
michael@0 658 nsresult
michael@0 659 BluetoothService::HandleShutdown()
michael@0 660 {
michael@0 661 MOZ_ASSERT(NS_IsMainThread());
michael@0 662
michael@0 663 // This is a two phase shutdown. First we notify all child processes that
michael@0 664 // bluetooth is going away, and then we wait for them to acknowledge. Then we
michael@0 665 // close down all the bluetooth machinery.
michael@0 666
michael@0 667 sInShutdown = true;
michael@0 668
michael@0 669 Cleanup();
michael@0 670
michael@0 671 AutoInfallibleTArray<BluetoothParent*, 10> childActors;
michael@0 672 GetAllBluetoothActors(childActors);
michael@0 673
michael@0 674 if (!childActors.IsEmpty()) {
michael@0 675 // Notify child processes that they should stop using bluetooth now.
michael@0 676 for (uint32_t index = 0; index < childActors.Length(); index++) {
michael@0 677 childActors[index]->BeginShutdown();
michael@0 678 }
michael@0 679
michael@0 680 // Create a timer to ensure that we don't wait forever for a child process
michael@0 681 // or the bluetooth threads to finish. If we don't get a timer or can't use
michael@0 682 // it for some reason then we skip all the waiting entirely since we really
michael@0 683 // can't afford to hang on shutdown.
michael@0 684 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 685 MOZ_ASSERT(timer);
michael@0 686
michael@0 687 if (timer) {
michael@0 688 bool timeExceeded = false;
michael@0 689
michael@0 690 if (NS_SUCCEEDED(timer->InitWithFuncCallback(ShutdownTimeExceeded,
michael@0 691 &timeExceeded,
michael@0 692 DEFAULT_SHUTDOWN_TIMER_MS,
michael@0 693 nsITimer::TYPE_ONE_SHOT))) {
michael@0 694 nsIThread* currentThread = NS_GetCurrentThread();
michael@0 695 MOZ_ASSERT(currentThread);
michael@0 696
michael@0 697 // Wait for those child processes to acknowledge.
michael@0 698 while (!timeExceeded && !childActors.IsEmpty()) {
michael@0 699 if (!NS_ProcessNextEvent(currentThread)) {
michael@0 700 MOZ_ASSERT(false, "Something horribly wrong here!");
michael@0 701 break;
michael@0 702 }
michael@0 703 GetAllBluetoothActors(childActors);
michael@0 704 }
michael@0 705
michael@0 706 if (NS_FAILED(timer->Cancel())) {
michael@0 707 MOZ_CRASH("Failed to cancel shutdown timer, this will crash!");
michael@0 708 }
michael@0 709 }
michael@0 710 else {
michael@0 711 MOZ_ASSERT(false, "Failed to initialize shutdown timer!");
michael@0 712 }
michael@0 713 }
michael@0 714 }
michael@0 715
michael@0 716 if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
michael@0 717 MOZ_ASSERT(false, "Failed to deliver stop message!");
michael@0 718 }
michael@0 719
michael@0 720 return NS_OK;
michael@0 721 }
michael@0 722
michael@0 723 // static
michael@0 724 BluetoothService*
michael@0 725 BluetoothService::Get()
michael@0 726 {
michael@0 727 MOZ_ASSERT(NS_IsMainThread());
michael@0 728
michael@0 729 // If we already exist, exit early
michael@0 730 if (sBluetoothService) {
michael@0 731 return sBluetoothService;
michael@0 732 }
michael@0 733
michael@0 734 // If we're in shutdown, don't create a new instance
michael@0 735 if (sInShutdown) {
michael@0 736 BT_WARNING("BluetoothService can't be created during shutdown");
michael@0 737 return nullptr;
michael@0 738 }
michael@0 739
michael@0 740 // Create new instance, register, return
michael@0 741 nsRefPtr<BluetoothService> service = BluetoothService::Create();
michael@0 742 NS_ENSURE_TRUE(service, nullptr);
michael@0 743
michael@0 744 if (!service->Init()) {
michael@0 745 service->Cleanup();
michael@0 746 return nullptr;
michael@0 747 }
michael@0 748
michael@0 749 sBluetoothService = service;
michael@0 750 return sBluetoothService;
michael@0 751 }
michael@0 752
michael@0 753 nsresult
michael@0 754 BluetoothService::Observe(nsISupports* aSubject, const char* aTopic,
michael@0 755 const char16_t* aData)
michael@0 756 {
michael@0 757 MOZ_ASSERT(NS_IsMainThread());
michael@0 758
michael@0 759 if (!strcmp(aTopic, "profile-after-change")) {
michael@0 760 return HandleStartup();
michael@0 761 }
michael@0 762
michael@0 763 if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
michael@0 764 return HandleSettingsChanged(nsDependentString(aData));
michael@0 765 }
michael@0 766
michael@0 767 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
michael@0 768 return HandleShutdown();
michael@0 769 }
michael@0 770
michael@0 771 MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
michael@0 772 return NS_ERROR_UNEXPECTED;
michael@0 773 }
michael@0 774
michael@0 775 void
michael@0 776 BluetoothService::TryFiringAdapterAdded()
michael@0 777 {
michael@0 778 MOZ_ASSERT(NS_IsMainThread());
michael@0 779
michael@0 780 if (IsToggling() || !mAdapterAddedReceived) {
michael@0 781 return;
michael@0 782 }
michael@0 783
michael@0 784 BluetoothSignal signal(NS_LITERAL_STRING("AdapterAdded"),
michael@0 785 NS_LITERAL_STRING(KEY_MANAGER), true);
michael@0 786 DistributeSignal(signal);
michael@0 787 }
michael@0 788
michael@0 789 void
michael@0 790 BluetoothService::AdapterAddedReceived()
michael@0 791 {
michael@0 792 MOZ_ASSERT(NS_IsMainThread());
michael@0 793
michael@0 794 mAdapterAddedReceived = true;
michael@0 795 }
michael@0 796
michael@0 797 void
michael@0 798 BluetoothService::Notify(const BluetoothSignal& aData)
michael@0 799 {
michael@0 800 nsString type = NS_LITERAL_STRING("bluetooth-pairing-request");
michael@0 801
michael@0 802 AutoSafeJSContext cx;
michael@0 803 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(),
michael@0 804 JS::NullPtr()));
michael@0 805 NS_ENSURE_TRUE_VOID(obj);
michael@0 806
michael@0 807 if (!SetJsObject(cx, aData.value(), obj)) {
michael@0 808 BT_WARNING("Failed to set properties of system message!");
michael@0 809 return;
michael@0 810 }
michael@0 811
michael@0 812 BT_LOGD("[S] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get());
michael@0 813
michael@0 814 if (aData.name().EqualsLiteral("RequestConfirmation")) {
michael@0 815 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 4,
michael@0 816 "RequestConfirmation: Wrong length of parameters");
michael@0 817 } else if (aData.name().EqualsLiteral("RequestPinCode")) {
michael@0 818 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
michael@0 819 "RequestPinCode: Wrong length of parameters");
michael@0 820 } else if (aData.name().EqualsLiteral("RequestPasskey")) {
michael@0 821 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 3,
michael@0 822 "RequestPinCode: Wrong length of parameters");
michael@0 823 } else if (aData.name().EqualsLiteral("Cancel")) {
michael@0 824 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 0,
michael@0 825 "Cancel: Wrong length of parameters");
michael@0 826 type.AssignLiteral("bluetooth-cancel");
michael@0 827 } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID)) {
michael@0 828 MOZ_ASSERT(aData.value().get_ArrayOfBluetoothNamedValue().Length() == 1,
michael@0 829 "pairedstatuschanged: Wrong length of parameters");
michael@0 830 type.AssignLiteral("bluetooth-pairedstatuschanged");
michael@0 831 } else {
michael@0 832 nsCString warningMsg;
michael@0 833 warningMsg.AssignLiteral("Not handling service signal: ");
michael@0 834 warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
michael@0 835 BT_WARNING(warningMsg.get());
michael@0 836 return;
michael@0 837 }
michael@0 838
michael@0 839 nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
michael@0 840 do_GetService("@mozilla.org/system-message-internal;1");
michael@0 841 NS_ENSURE_TRUE_VOID(systemMessenger);
michael@0 842
michael@0 843 JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*obj));
michael@0 844 systemMessenger->BroadcastMessage(type, value,
michael@0 845 JS::UndefinedHandleValue);
michael@0 846 }

mercurial