dom/bluetooth/BluetoothService.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     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 }

mercurial