diff options
Diffstat (limited to 'dom/wifi/WifiProxyService.cpp')
-rw-r--r-- | dom/wifi/WifiProxyService.cpp | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/dom/wifi/WifiProxyService.cpp b/dom/wifi/WifiProxyService.cpp new file mode 100644 index 000000000..0ff5097af --- /dev/null +++ b/dom/wifi/WifiProxyService.cpp @@ -0,0 +1,357 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WifiProxyService.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/ModuleUtils.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsXULAppAPI.h" +#include "WifiUtils.h" + +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +using namespace mozilla::tasktracer; +#endif + +#define NS_WIFIPROXYSERVICE_CID \ + { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} } + +using namespace mozilla; +using namespace mozilla::dom; + +namespace mozilla { + +// The singleton Wifi service, to be used on the main thread. +static StaticRefPtr<WifiProxyService> gWifiProxyService; + +// The singleton supplicant class, that can be used on any thread. +static UniquePtr<WpaSupplicant> gWpaSupplicant; + +// Runnable used dispatch the WaitForEvent result on the main thread. +class WifiEventDispatcher : public Runnable +{ +public: + WifiEventDispatcher(const nsAString& aEvent, const nsACString& aInterface) + : mEvent(aEvent) + , mInterface(aInterface) + { + MOZ_ASSERT(!NS_IsMainThread()); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + gWifiProxyService->DispatchWifiEvent(mEvent, mInterface); + return NS_OK; + } + +private: + nsString mEvent; + nsCString mInterface; +}; + +// Runnable used to call WaitForEvent on the event thread. +class EventRunnable : public Runnable +{ +public: + EventRunnable(const nsACString& aInterface) + : mInterface(aInterface) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(!NS_IsMainThread()); + nsAutoString event; + gWpaSupplicant->WaitForEvent(event, mInterface); + if (!event.IsEmpty()) { +#ifdef MOZ_TASK_TRACER + // Make wifi initialization events to be the source events of TaskTracer, + // and originate the rest correlation tasks from here. + AutoSourceEvent taskTracerEvent(SourceEventType::Wifi); + AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get()); +#endif + nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface); + NS_DispatchToMainThread(runnable); + } + return NS_OK; + } + +private: + nsCString mInterface; +}; + +// Runnable used dispatch the Command result on the main thread. +class WifiResultDispatcher : public Runnable +{ +public: + WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface) + : mResult(aResult) + , mInterface(aInterface) + { + MOZ_ASSERT(!NS_IsMainThread()); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + gWifiProxyService->DispatchWifiResult(mResult, mInterface); + return NS_OK; + } + +private: + WifiResultOptions mResult; + nsCString mInterface; +}; + +// Runnable used to call SendCommand on the control thread. +class ControlRunnable : public Runnable +{ +public: + ControlRunnable(CommandOptions aOptions, const nsACString& aInterface) + : mOptions(aOptions) + , mInterface(aInterface) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + NS_IMETHOD Run() override + { + WifiResultOptions result; + if (gWpaSupplicant->ExecuteCommand(mOptions, result, mInterface)) { + nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result, mInterface); + NS_DispatchToMainThread(runnable); + } + return NS_OK; + } +private: + CommandOptions mOptions; + nsCString mInterface; +}; + +NS_IMPL_ISUPPORTS(WifiProxyService, nsIWifiProxyService) + +WifiProxyService::WifiProxyService() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!gWifiProxyService); +} + +WifiProxyService::~WifiProxyService() +{ + MOZ_ASSERT(!gWifiProxyService); +} + +already_AddRefed<WifiProxyService> +WifiProxyService::FactoryCreate() +{ + if (!XRE_IsParentProcess()) { + return nullptr; + } + + MOZ_ASSERT(NS_IsMainThread()); + + if (!gWifiProxyService) { + gWifiProxyService = new WifiProxyService(); + ClearOnShutdown(&gWifiProxyService); + + gWpaSupplicant = MakeUnique<WpaSupplicant>(); + ClearOnShutdown(&gWpaSupplicant); + } + + RefPtr<WifiProxyService> service = gWifiProxyService.get(); + return service.forget(); +} + +NS_IMETHODIMP +WifiProxyService::Start(nsIWifiEventListener* aListener, + const char ** aInterfaces, + uint32_t aNumOfInterfaces) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aListener); + +#if ANDROID_VERSION >= 19 + // KK changes the way mux'ing/demux'ing different supplicant interfaces + // (e.g. wlan0/p2p0) from multi-sockets to single socket embedded with + // prefixed interface name (e.g. IFNAME=wlan0 xxxxxx). Therefore, we use + // the first given interface as the global interface for KK. + aNumOfInterfaces = 1; +#endif + + nsresult rv; + + // Since EventRunnable runs in the manner of blocking, we have to + // spin a thread for each interface. + // (See the WpaSupplicant::WaitForEvent) + mEventThreadList.SetLength(aNumOfInterfaces); + for (uint32_t i = 0; i < aNumOfInterfaces; i++) { + mEventThreadList[i].mInterface = aInterfaces[i]; + rv = NS_NewThread(getter_AddRefs(mEventThreadList[i].mThread)); + if (NS_FAILED(rv)) { + NS_WARNING("Can't create wifi event thread"); + Shutdown(); + return NS_ERROR_FAILURE; + } + } + + rv = NS_NewThread(getter_AddRefs(mControlThread)); + if (NS_FAILED(rv)) { + NS_WARNING("Can't create wifi control thread"); + Shutdown(); + return NS_ERROR_FAILURE; + } + + mListener = aListener; + + return NS_OK; +} + +NS_IMETHODIMP +WifiProxyService::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + for (size_t i = 0; i < mEventThreadList.Length(); i++) { + if (mEventThreadList[i].mThread) { + mEventThreadList[i].mThread->Shutdown(); + mEventThreadList[i].mThread = nullptr; + } + } + + mEventThreadList.Clear(); + + if (mControlThread) { + mControlThread->Shutdown(); + mControlThread = nullptr; + } + + mListener = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +WifiProxyService::SendCommand(JS::Handle<JS::Value> aOptions, + const nsACString& aInterface, + JSContext* aCx) +{ + MOZ_ASSERT(NS_IsMainThread()); + WifiCommandOptions options; + + if (!options.Init(aCx, aOptions)) { + NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand"); + return NS_ERROR_FAILURE; + } + + if (!mControlThread) { + return NS_ERROR_FAILURE; + } + + // Dispatch the command to the control thread. + CommandOptions commandOptions(options); + nsCOMPtr<nsIRunnable> runnable = new ControlRunnable(commandOptions, aInterface); + mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); + return NS_OK; +} + +NS_IMETHODIMP +WifiProxyService::WaitForEvent(const nsACString& aInterface) +{ + MOZ_ASSERT(NS_IsMainThread()); + +#if ANDROID_VERSION >= 19 + // We will only have one global interface for KK. + if (!mEventThreadList.IsEmpty()) { + nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface); + mEventThreadList[0].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); + return NS_OK; + } +#else + // Dispatch to the event thread which has the given interface name + for (size_t i = 0; i < mEventThreadList.Length(); i++) { + if (mEventThreadList[i].mInterface.Equals(aInterface)) { + nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface); + mEventThreadList[i].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); + return NS_OK; + } + } +#endif + + return NS_ERROR_FAILURE; +} + +void +WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface) +{ + MOZ_ASSERT(NS_IsMainThread()); + + mozilla::AutoSafeJSContext cx; + JS::Rooted<JS::Value> val(cx); + + if (!ToJSValue(cx, aOptions, &val)) { + return; + } + + if (mListener) { + // Call the listener with a JS value. + mListener->OnCommand(val, aInterface); + } +} + +void +WifiProxyService::DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface) +{ + MOZ_ASSERT(NS_IsMainThread()); + +#if ANDROID_VERSION < 19 + mListener->OnWaitEvent(aEvent, aInterface); +#else + // The interface might be embedded in the event string such as + // "IFNAME=wlan0 CTRL-EVENT-BSS-ADDED 65 3c:94:d5:7c:11:8b". + // Parse the interface name from the event string and use p2p0 + // as the default interface if "IFNAME" is not found. + nsAutoString event; + nsAutoString embeddedInterface(NS_LITERAL_STRING("p2p0")); + if (StringBeginsWith(aEvent, NS_LITERAL_STRING("IFNAME"))) { + int32_t ifnameFrom = aEvent.FindChar('=') + 1; + int32_t ifnameTo = aEvent.FindChar(' ') - 1; + embeddedInterface = Substring(aEvent, ifnameFrom, ifnameTo - ifnameFrom + 1); + event = Substring(aEvent, aEvent.FindChar(' ') + 1); + } + else { + event = aEvent; + } + mListener->OnWaitEvent(event, NS_ConvertUTF16toUTF8(embeddedInterface)); +#endif +} + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService, + WifiProxyService::FactoryCreate) + +NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID); + +static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = { + { &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = { + { "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kWifiProxyServiceModule = { + mozilla::Module::kVersion, + kWifiProxyServiceCIDs, + kWifiProxyServiceContracts, + nullptr +}; + +} // namespace mozilla + +NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule; |