Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "WifiProxyService.h" |
michael@0 | 6 | #include "nsServiceManagerUtils.h" |
michael@0 | 7 | #include "mozilla/ModuleUtils.h" |
michael@0 | 8 | #include "mozilla/ClearOnShutdown.h" |
michael@0 | 9 | #include "nsXULAppAPI.h" |
michael@0 | 10 | #include "WifiUtils.h" |
michael@0 | 11 | #include "nsCxPusher.h" |
michael@0 | 12 | |
michael@0 | 13 | #ifdef MOZ_TASK_TRACER |
michael@0 | 14 | #include "GeckoTaskTracer.h" |
michael@0 | 15 | using namespace mozilla::tasktracer; |
michael@0 | 16 | #endif |
michael@0 | 17 | |
michael@0 | 18 | #define NS_WIFIPROXYSERVICE_CID \ |
michael@0 | 19 | { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} } |
michael@0 | 20 | |
michael@0 | 21 | using namespace mozilla; |
michael@0 | 22 | using namespace mozilla::dom; |
michael@0 | 23 | |
michael@0 | 24 | namespace mozilla { |
michael@0 | 25 | |
michael@0 | 26 | // The singleton Wifi service, to be used on the main thread. |
michael@0 | 27 | static StaticRefPtr<WifiProxyService> gWifiProxyService; |
michael@0 | 28 | |
michael@0 | 29 | // The singleton supplicant class, that can be used on any thread. |
michael@0 | 30 | static nsAutoPtr<WpaSupplicant> gWpaSupplicant; |
michael@0 | 31 | |
michael@0 | 32 | // Runnable used dispatch the WaitForEvent result on the main thread. |
michael@0 | 33 | class WifiEventDispatcher : public nsRunnable |
michael@0 | 34 | { |
michael@0 | 35 | public: |
michael@0 | 36 | WifiEventDispatcher(const nsAString& aEvent, const nsACString& aInterface) |
michael@0 | 37 | : mEvent(aEvent) |
michael@0 | 38 | , mInterface(aInterface) |
michael@0 | 39 | { |
michael@0 | 40 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | NS_IMETHOD Run() |
michael@0 | 44 | { |
michael@0 | 45 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 46 | gWifiProxyService->DispatchWifiEvent(mEvent, mInterface); |
michael@0 | 47 | return NS_OK; |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | private: |
michael@0 | 51 | nsString mEvent; |
michael@0 | 52 | nsCString mInterface; |
michael@0 | 53 | }; |
michael@0 | 54 | |
michael@0 | 55 | // Runnable used to call WaitForEvent on the event thread. |
michael@0 | 56 | class EventRunnable : public nsRunnable |
michael@0 | 57 | { |
michael@0 | 58 | public: |
michael@0 | 59 | EventRunnable(const nsACString& aInterface) |
michael@0 | 60 | : mInterface(aInterface) |
michael@0 | 61 | { |
michael@0 | 62 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 63 | } |
michael@0 | 64 | |
michael@0 | 65 | NS_IMETHOD Run() |
michael@0 | 66 | { |
michael@0 | 67 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 68 | nsAutoString event; |
michael@0 | 69 | gWpaSupplicant->WaitForEvent(event, mInterface); |
michael@0 | 70 | if (!event.IsEmpty()) { |
michael@0 | 71 | #ifdef MOZ_TASK_TRACER |
michael@0 | 72 | // Make wifi initialization events to be the source events of TaskTracer, |
michael@0 | 73 | // and originate the rest correlation tasks from here. |
michael@0 | 74 | AutoSourceEvent taskTracerEvent(SourceEventType::WIFI); |
michael@0 | 75 | AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get()); |
michael@0 | 76 | #endif |
michael@0 | 77 | nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface); |
michael@0 | 78 | NS_DispatchToMainThread(runnable); |
michael@0 | 79 | } |
michael@0 | 80 | return NS_OK; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | private: |
michael@0 | 84 | nsCString mInterface; |
michael@0 | 85 | }; |
michael@0 | 86 | |
michael@0 | 87 | // Runnable used dispatch the Command result on the main thread. |
michael@0 | 88 | class WifiResultDispatcher : public nsRunnable |
michael@0 | 89 | { |
michael@0 | 90 | public: |
michael@0 | 91 | WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface) |
michael@0 | 92 | : mInterface(aInterface) |
michael@0 | 93 | { |
michael@0 | 94 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 95 | |
michael@0 | 96 | // XXX: is there a better way to copy webidl dictionnaries? |
michael@0 | 97 | // the copy constructor is private. |
michael@0 | 98 | #define COPY_FIELD(prop) mResult.prop = aResult.prop; |
michael@0 | 99 | |
michael@0 | 100 | COPY_FIELD(mId) |
michael@0 | 101 | COPY_FIELD(mStatus) |
michael@0 | 102 | COPY_FIELD(mReply) |
michael@0 | 103 | COPY_FIELD(mRoute) |
michael@0 | 104 | COPY_FIELD(mError) |
michael@0 | 105 | COPY_FIELD(mValue) |
michael@0 | 106 | COPY_FIELD(mIpaddr_str) |
michael@0 | 107 | COPY_FIELD(mGateway_str) |
michael@0 | 108 | COPY_FIELD(mDns1_str) |
michael@0 | 109 | COPY_FIELD(mDns2_str) |
michael@0 | 110 | COPY_FIELD(mMask_str) |
michael@0 | 111 | COPY_FIELD(mServer_str) |
michael@0 | 112 | COPY_FIELD(mVendor_str) |
michael@0 | 113 | COPY_FIELD(mLease) |
michael@0 | 114 | COPY_FIELD(mMask) |
michael@0 | 115 | COPY_FIELD(mIpaddr) |
michael@0 | 116 | COPY_FIELD(mGateway) |
michael@0 | 117 | COPY_FIELD(mDns1) |
michael@0 | 118 | COPY_FIELD(mDns2) |
michael@0 | 119 | COPY_FIELD(mServer) |
michael@0 | 120 | |
michael@0 | 121 | #undef COPY_FIELD |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | NS_IMETHOD Run() |
michael@0 | 125 | { |
michael@0 | 126 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 127 | gWifiProxyService->DispatchWifiResult(mResult, mInterface); |
michael@0 | 128 | return NS_OK; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | private: |
michael@0 | 132 | WifiResultOptions mResult; |
michael@0 | 133 | nsCString mInterface; |
michael@0 | 134 | }; |
michael@0 | 135 | |
michael@0 | 136 | // Runnable used to call SendCommand on the control thread. |
michael@0 | 137 | class ControlRunnable : public nsRunnable |
michael@0 | 138 | { |
michael@0 | 139 | public: |
michael@0 | 140 | ControlRunnable(CommandOptions aOptions, const nsACString& aInterface) |
michael@0 | 141 | : mOptions(aOptions) |
michael@0 | 142 | , mInterface(aInterface) |
michael@0 | 143 | { |
michael@0 | 144 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | NS_IMETHOD Run() |
michael@0 | 148 | { |
michael@0 | 149 | WifiResultOptions result; |
michael@0 | 150 | if (gWpaSupplicant->ExecuteCommand(mOptions, result, mInterface)) { |
michael@0 | 151 | nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result, mInterface); |
michael@0 | 152 | NS_DispatchToMainThread(runnable); |
michael@0 | 153 | } |
michael@0 | 154 | return NS_OK; |
michael@0 | 155 | } |
michael@0 | 156 | private: |
michael@0 | 157 | CommandOptions mOptions; |
michael@0 | 158 | nsCString mInterface; |
michael@0 | 159 | }; |
michael@0 | 160 | |
michael@0 | 161 | NS_IMPL_ISUPPORTS(WifiProxyService, nsIWifiProxyService) |
michael@0 | 162 | |
michael@0 | 163 | WifiProxyService::WifiProxyService() |
michael@0 | 164 | { |
michael@0 | 165 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 166 | MOZ_ASSERT(!gWifiProxyService); |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | WifiProxyService::~WifiProxyService() |
michael@0 | 170 | { |
michael@0 | 171 | MOZ_ASSERT(!gWifiProxyService); |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | already_AddRefed<WifiProxyService> |
michael@0 | 175 | WifiProxyService::FactoryCreate() |
michael@0 | 176 | { |
michael@0 | 177 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 178 | return nullptr; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 182 | |
michael@0 | 183 | if (!gWifiProxyService) { |
michael@0 | 184 | gWifiProxyService = new WifiProxyService(); |
michael@0 | 185 | ClearOnShutdown(&gWifiProxyService); |
michael@0 | 186 | |
michael@0 | 187 | gWpaSupplicant = new WpaSupplicant(); |
michael@0 | 188 | ClearOnShutdown(&gWpaSupplicant); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | nsRefPtr<WifiProxyService> service = gWifiProxyService.get(); |
michael@0 | 192 | return service.forget(); |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | NS_IMETHODIMP |
michael@0 | 196 | WifiProxyService::Start(nsIWifiEventListener* aListener, |
michael@0 | 197 | const char ** aInterfaces, |
michael@0 | 198 | uint32_t aNumOfInterfaces) |
michael@0 | 199 | { |
michael@0 | 200 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 201 | MOZ_ASSERT(aListener); |
michael@0 | 202 | |
michael@0 | 203 | nsresult rv; |
michael@0 | 204 | |
michael@0 | 205 | // Since EventRunnable runs in the manner of blocking, we have to |
michael@0 | 206 | // spin a thread for each interface. |
michael@0 | 207 | // (See the WpaSupplicant::WaitForEvent) |
michael@0 | 208 | mEventThreadList.SetLength(aNumOfInterfaces); |
michael@0 | 209 | for (uint32_t i = 0; i < aNumOfInterfaces; i++) { |
michael@0 | 210 | mEventThreadList[i].mInterface = aInterfaces[i]; |
michael@0 | 211 | rv = NS_NewThread(getter_AddRefs(mEventThreadList[i].mThread)); |
michael@0 | 212 | if (NS_FAILED(rv)) { |
michael@0 | 213 | NS_WARNING("Can't create wifi event thread"); |
michael@0 | 214 | Shutdown(); |
michael@0 | 215 | return NS_ERROR_FAILURE; |
michael@0 | 216 | } |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | rv = NS_NewThread(getter_AddRefs(mControlThread)); |
michael@0 | 220 | if (NS_FAILED(rv)) { |
michael@0 | 221 | NS_WARNING("Can't create wifi control thread"); |
michael@0 | 222 | Shutdown(); |
michael@0 | 223 | return NS_ERROR_FAILURE; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | mListener = aListener; |
michael@0 | 227 | |
michael@0 | 228 | return NS_OK; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | NS_IMETHODIMP |
michael@0 | 232 | WifiProxyService::Shutdown() |
michael@0 | 233 | { |
michael@0 | 234 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 235 | for (size_t i = 0; i < mEventThreadList.Length(); i++) { |
michael@0 | 236 | if (mEventThreadList[i].mThread) { |
michael@0 | 237 | mEventThreadList[i].mThread->Shutdown(); |
michael@0 | 238 | mEventThreadList[i].mThread = nullptr; |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | mEventThreadList.Clear(); |
michael@0 | 242 | if (mControlThread) { |
michael@0 | 243 | mControlThread->Shutdown(); |
michael@0 | 244 | mControlThread = nullptr; |
michael@0 | 245 | } |
michael@0 | 246 | return NS_OK; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | NS_IMETHODIMP |
michael@0 | 250 | WifiProxyService::SendCommand(JS::Handle<JS::Value> aOptions, |
michael@0 | 251 | const nsACString& aInterface, |
michael@0 | 252 | JSContext* aCx) |
michael@0 | 253 | { |
michael@0 | 254 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 255 | WifiCommandOptions options; |
michael@0 | 256 | |
michael@0 | 257 | if (!options.Init(aCx, aOptions)) { |
michael@0 | 258 | NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand"); |
michael@0 | 259 | return NS_ERROR_FAILURE; |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | // Dispatch the command to the control thread. |
michael@0 | 263 | CommandOptions commandOptions(options); |
michael@0 | 264 | nsCOMPtr<nsIRunnable> runnable = new ControlRunnable(commandOptions, aInterface); |
michael@0 | 265 | mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); |
michael@0 | 266 | return NS_OK; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | NS_IMETHODIMP |
michael@0 | 270 | WifiProxyService::WaitForEvent(const nsACString& aInterface) |
michael@0 | 271 | { |
michael@0 | 272 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 273 | |
michael@0 | 274 | // Dispatch to the event thread which has the given interface name |
michael@0 | 275 | for (size_t i = 0; i < mEventThreadList.Length(); i++) { |
michael@0 | 276 | if (mEventThreadList[i].mInterface.Equals(aInterface)) { |
michael@0 | 277 | nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface); |
michael@0 | 278 | mEventThreadList[i].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); |
michael@0 | 279 | return NS_OK; |
michael@0 | 280 | } |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | return NS_ERROR_FAILURE; |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | void |
michael@0 | 287 | WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface) |
michael@0 | 288 | { |
michael@0 | 289 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 290 | |
michael@0 | 291 | mozilla::AutoSafeJSContext cx; |
michael@0 | 292 | JS::Rooted<JS::Value> val(cx); |
michael@0 | 293 | |
michael@0 | 294 | if (!aOptions.ToObject(cx, &val)) { |
michael@0 | 295 | return; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | // Call the listener with a JS value. |
michael@0 | 299 | mListener->OnCommand(val, aInterface); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | void |
michael@0 | 303 | WifiProxyService::DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface) |
michael@0 | 304 | { |
michael@0 | 305 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 306 | nsAutoString event; |
michael@0 | 307 | if (StringBeginsWith(aEvent, NS_LITERAL_STRING("IFNAME"))) { |
michael@0 | 308 | // Jump over IFNAME for gonk-kk. |
michael@0 | 309 | event = Substring(aEvent, aEvent.FindChar(' ') + 1); |
michael@0 | 310 | } |
michael@0 | 311 | else { |
michael@0 | 312 | event = aEvent; |
michael@0 | 313 | } |
michael@0 | 314 | // Call the listener. |
michael@0 | 315 | mListener->OnWaitEvent(event, aInterface); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService, |
michael@0 | 319 | WifiProxyService::FactoryCreate) |
michael@0 | 320 | |
michael@0 | 321 | NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID); |
michael@0 | 322 | |
michael@0 | 323 | static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = { |
michael@0 | 324 | { &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor }, |
michael@0 | 325 | { nullptr } |
michael@0 | 326 | }; |
michael@0 | 327 | |
michael@0 | 328 | static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = { |
michael@0 | 329 | { "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID }, |
michael@0 | 330 | { nullptr } |
michael@0 | 331 | }; |
michael@0 | 332 | |
michael@0 | 333 | static const mozilla::Module kWifiProxyServiceModule = { |
michael@0 | 334 | mozilla::Module::kVersion, |
michael@0 | 335 | kWifiProxyServiceCIDs, |
michael@0 | 336 | kWifiProxyServiceContracts, |
michael@0 | 337 | nullptr |
michael@0 | 338 | }; |
michael@0 | 339 | |
michael@0 | 340 | } // namespace mozilla |
michael@0 | 341 | |
michael@0 | 342 | NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule; |