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