michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WifiProxyService.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "WifiUtils.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: #ifdef MOZ_TASK_TRACER michael@0: #include "GeckoTaskTracer.h" michael@0: using namespace mozilla::tasktracer; michael@0: #endif michael@0: michael@0: #define NS_WIFIPROXYSERVICE_CID \ michael@0: { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} } michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: namespace mozilla { michael@0: michael@0: // The singleton Wifi service, to be used on the main thread. michael@0: static StaticRefPtr gWifiProxyService; michael@0: michael@0: // The singleton supplicant class, that can be used on any thread. michael@0: static nsAutoPtr gWpaSupplicant; michael@0: michael@0: // Runnable used dispatch the WaitForEvent result on the main thread. michael@0: class WifiEventDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: WifiEventDispatcher(const nsAString& aEvent, const nsACString& aInterface) michael@0: : mEvent(aEvent) michael@0: , mInterface(aInterface) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: gWifiProxyService->DispatchWifiEvent(mEvent, mInterface); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsString mEvent; michael@0: nsCString mInterface; michael@0: }; michael@0: michael@0: // Runnable used to call WaitForEvent on the event thread. michael@0: class EventRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: EventRunnable(const nsACString& aInterface) michael@0: : mInterface(aInterface) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: nsAutoString event; michael@0: gWpaSupplicant->WaitForEvent(event, mInterface); michael@0: if (!event.IsEmpty()) { michael@0: #ifdef MOZ_TASK_TRACER michael@0: // Make wifi initialization events to be the source events of TaskTracer, michael@0: // and originate the rest correlation tasks from here. michael@0: AutoSourceEvent taskTracerEvent(SourceEventType::WIFI); michael@0: AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get()); michael@0: #endif michael@0: nsCOMPtr runnable = new WifiEventDispatcher(event, mInterface); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCString mInterface; michael@0: }; michael@0: michael@0: // Runnable used dispatch the Command result on the main thread. michael@0: class WifiResultDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface) michael@0: : mInterface(aInterface) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: // XXX: is there a better way to copy webidl dictionnaries? michael@0: // the copy constructor is private. michael@0: #define COPY_FIELD(prop) mResult.prop = aResult.prop; michael@0: michael@0: COPY_FIELD(mId) michael@0: COPY_FIELD(mStatus) michael@0: COPY_FIELD(mReply) michael@0: COPY_FIELD(mRoute) michael@0: COPY_FIELD(mError) michael@0: COPY_FIELD(mValue) michael@0: COPY_FIELD(mIpaddr_str) michael@0: COPY_FIELD(mGateway_str) michael@0: COPY_FIELD(mDns1_str) michael@0: COPY_FIELD(mDns2_str) michael@0: COPY_FIELD(mMask_str) michael@0: COPY_FIELD(mServer_str) michael@0: COPY_FIELD(mVendor_str) michael@0: COPY_FIELD(mLease) michael@0: COPY_FIELD(mMask) michael@0: COPY_FIELD(mIpaddr) michael@0: COPY_FIELD(mGateway) michael@0: COPY_FIELD(mDns1) michael@0: COPY_FIELD(mDns2) michael@0: COPY_FIELD(mServer) michael@0: michael@0: #undef COPY_FIELD michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: gWifiProxyService->DispatchWifiResult(mResult, mInterface); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: WifiResultOptions mResult; michael@0: nsCString mInterface; michael@0: }; michael@0: michael@0: // Runnable used to call SendCommand on the control thread. michael@0: class ControlRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: ControlRunnable(CommandOptions aOptions, const nsACString& aInterface) michael@0: : mOptions(aOptions) michael@0: , mInterface(aInterface) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: WifiResultOptions result; michael@0: if (gWpaSupplicant->ExecuteCommand(mOptions, result, mInterface)) { michael@0: nsCOMPtr runnable = new WifiResultDispatcher(result, mInterface); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: CommandOptions mOptions; michael@0: nsCString mInterface; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(WifiProxyService, nsIWifiProxyService) michael@0: michael@0: WifiProxyService::WifiProxyService() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!gWifiProxyService); michael@0: } michael@0: michael@0: WifiProxyService::~WifiProxyService() michael@0: { michael@0: MOZ_ASSERT(!gWifiProxyService); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WifiProxyService::FactoryCreate() michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!gWifiProxyService) { michael@0: gWifiProxyService = new WifiProxyService(); michael@0: ClearOnShutdown(&gWifiProxyService); michael@0: michael@0: gWpaSupplicant = new WpaSupplicant(); michael@0: ClearOnShutdown(&gWpaSupplicant); michael@0: } michael@0: michael@0: nsRefPtr service = gWifiProxyService.get(); michael@0: return service.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WifiProxyService::Start(nsIWifiEventListener* aListener, michael@0: const char ** aInterfaces, michael@0: uint32_t aNumOfInterfaces) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aListener); michael@0: michael@0: nsresult rv; michael@0: michael@0: // Since EventRunnable runs in the manner of blocking, we have to michael@0: // spin a thread for each interface. michael@0: // (See the WpaSupplicant::WaitForEvent) michael@0: mEventThreadList.SetLength(aNumOfInterfaces); michael@0: for (uint32_t i = 0; i < aNumOfInterfaces; i++) { michael@0: mEventThreadList[i].mInterface = aInterfaces[i]; michael@0: rv = NS_NewThread(getter_AddRefs(mEventThreadList[i].mThread)); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Can't create wifi event thread"); michael@0: Shutdown(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: rv = NS_NewThread(getter_AddRefs(mControlThread)); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Can't create wifi control thread"); michael@0: Shutdown(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mListener = aListener; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WifiProxyService::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: for (size_t i = 0; i < mEventThreadList.Length(); i++) { michael@0: if (mEventThreadList[i].mThread) { michael@0: mEventThreadList[i].mThread->Shutdown(); michael@0: mEventThreadList[i].mThread = nullptr; michael@0: } michael@0: } michael@0: mEventThreadList.Clear(); michael@0: if (mControlThread) { michael@0: mControlThread->Shutdown(); michael@0: mControlThread = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WifiProxyService::SendCommand(JS::Handle aOptions, michael@0: const nsACString& aInterface, michael@0: JSContext* aCx) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: WifiCommandOptions options; michael@0: michael@0: if (!options.Init(aCx, aOptions)) { michael@0: NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Dispatch the command to the control thread. michael@0: CommandOptions commandOptions(options); michael@0: nsCOMPtr runnable = new ControlRunnable(commandOptions, aInterface); michael@0: mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WifiProxyService::WaitForEvent(const nsACString& aInterface) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Dispatch to the event thread which has the given interface name michael@0: for (size_t i = 0; i < mEventThreadList.Length(); i++) { michael@0: if (mEventThreadList[i].mInterface.Equals(aInterface)) { michael@0: nsCOMPtr runnable = new EventRunnable(aInterface); michael@0: mEventThreadList[i].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mozilla::AutoSafeJSContext cx; michael@0: JS::Rooted val(cx); michael@0: michael@0: if (!aOptions.ToObject(cx, &val)) { michael@0: return; michael@0: } michael@0: michael@0: // Call the listener with a JS value. michael@0: mListener->OnCommand(val, aInterface); michael@0: } michael@0: michael@0: void michael@0: WifiProxyService::DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsAutoString event; michael@0: if (StringBeginsWith(aEvent, NS_LITERAL_STRING("IFNAME"))) { michael@0: // Jump over IFNAME for gonk-kk. michael@0: event = Substring(aEvent, aEvent.FindChar(' ') + 1); michael@0: } michael@0: else { michael@0: event = aEvent; michael@0: } michael@0: // Call the listener. michael@0: mListener->OnWaitEvent(event, aInterface); michael@0: } michael@0: michael@0: NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService, michael@0: WifiProxyService::FactoryCreate) michael@0: michael@0: NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = { michael@0: { &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = { michael@0: { "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kWifiProxyServiceModule = { michael@0: mozilla::Module::kVersion, michael@0: kWifiProxyServiceCIDs, michael@0: kWifiProxyServiceContracts, michael@0: nullptr michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule;