|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
|
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/. */ |
|
6 |
|
7 "use strict"; |
|
8 |
|
9 this.EXPORTED_SYMBOLS = ["WifiCommand"]; |
|
10 |
|
11 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
12 |
|
13 Cu.import("resource://gre/modules/systemlibs.js"); |
|
14 |
|
15 const SUPP_PROP = "init.svc.wpa_supplicant"; |
|
16 const WPA_SUPPLICANT = "wpa_supplicant"; |
|
17 const DEBUG = false; |
|
18 |
|
19 this.WifiCommand = function(aControlMessage, aInterface) { |
|
20 function debug(msg) { |
|
21 if (DEBUG) { |
|
22 dump('-------------- WifiCommand: ' + msg); |
|
23 } |
|
24 } |
|
25 |
|
26 var command = {}; |
|
27 |
|
28 //------------------------------------------------- |
|
29 // General commands. |
|
30 //------------------------------------------------- |
|
31 |
|
32 command.loadDriver = function (callback) { |
|
33 voidControlMessage("load_driver", function(status) { |
|
34 callback(status); |
|
35 }); |
|
36 }; |
|
37 |
|
38 command.unloadDriver = function (callback) { |
|
39 voidControlMessage("unload_driver", function(status) { |
|
40 callback(status); |
|
41 }); |
|
42 }; |
|
43 |
|
44 command.startSupplicant = function (callback) { |
|
45 voidControlMessage("start_supplicant", callback); |
|
46 }; |
|
47 |
|
48 command.killSupplicant = function (callback) { |
|
49 // It is interesting to note that this function does exactly what |
|
50 // wifi_stop_supplicant does. Unforunately, on the Galaxy S2, Samsung |
|
51 // changed that function in a way that means that it doesn't recognize |
|
52 // wpa_supplicant as already running. Therefore, we have to roll our own |
|
53 // version here. |
|
54 stopProcess(SUPP_PROP, WPA_SUPPLICANT, callback); |
|
55 }; |
|
56 |
|
57 command.terminateSupplicant = function (callback) { |
|
58 doBooleanCommand("TERMINATE", "OK", callback); |
|
59 }; |
|
60 |
|
61 command.stopSupplicant = function (callback) { |
|
62 voidControlMessage("stop_supplicant", callback); |
|
63 }; |
|
64 |
|
65 command.listNetworks = function (callback) { |
|
66 doStringCommand("LIST_NETWORKS", callback); |
|
67 }; |
|
68 |
|
69 command.addNetwork = function (callback) { |
|
70 doIntCommand("ADD_NETWORK", callback); |
|
71 }; |
|
72 |
|
73 command.setNetworkVariable = function (netId, name, value, callback) { |
|
74 doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + |
|
75 value, "OK", callback); |
|
76 }; |
|
77 |
|
78 command.getNetworkVariable = function (netId, name, callback) { |
|
79 doStringCommand("GET_NETWORK " + netId + " " + name, callback); |
|
80 }; |
|
81 |
|
82 command.removeNetwork = function (netId, callback) { |
|
83 doBooleanCommand("REMOVE_NETWORK " + netId, "OK", callback); |
|
84 }; |
|
85 |
|
86 command.enableNetwork = function (netId, disableOthers, callback) { |
|
87 doBooleanCommand((disableOthers ? "SELECT_NETWORK " : "ENABLE_NETWORK ") + |
|
88 netId, "OK", callback); |
|
89 }; |
|
90 |
|
91 command.disableNetwork = function (netId, callback) { |
|
92 doBooleanCommand("DISABLE_NETWORK " + netId, "OK", callback); |
|
93 }; |
|
94 |
|
95 command.status = function (callback) { |
|
96 doStringCommand("STATUS", callback); |
|
97 }; |
|
98 |
|
99 command.ping = function (callback) { |
|
100 doBooleanCommand("PING", "PONG", callback); |
|
101 }; |
|
102 |
|
103 command.scanResults = function (callback) { |
|
104 doStringCommand("SCAN_RESULTS", callback); |
|
105 }; |
|
106 |
|
107 command.disconnect = function (callback) { |
|
108 doBooleanCommand("DISCONNECT", "OK", callback); |
|
109 }; |
|
110 |
|
111 command.reconnect = function (callback) { |
|
112 doBooleanCommand("RECONNECT", "OK", callback); |
|
113 }; |
|
114 |
|
115 command.reassociate = function (callback) { |
|
116 doBooleanCommand("REASSOCIATE", "OK", callback); |
|
117 }; |
|
118 |
|
119 command.setBackgroundScan = function (enable, callback) { |
|
120 doBooleanCommand("SET pno " + (enable ? "1" : "0"), |
|
121 "OK", |
|
122 function(ok) { |
|
123 callback(true, ok); |
|
124 }); |
|
125 }; |
|
126 |
|
127 command.doSetScanMode = function (setActive, callback) { |
|
128 doBooleanCommand(setActive ? |
|
129 "DRIVER SCAN-ACTIVE" : |
|
130 "DRIVER SCAN-PASSIVE", "OK", callback); |
|
131 }; |
|
132 |
|
133 command.scan = function (callback) { |
|
134 doBooleanCommand("SCAN", "OK", callback); |
|
135 }; |
|
136 |
|
137 command.setLogLevel = function (level, callback) { |
|
138 doBooleanCommand("LOG_LEVEL " + level, "OK", callback); |
|
139 }; |
|
140 |
|
141 command.getLogLevel = function (callback) { |
|
142 doStringCommand("LOG_LEVEL", callback); |
|
143 }; |
|
144 |
|
145 command.wpsPbc = function (iface, callback) { |
|
146 doBooleanCommand("WPS_PBC" + (iface ? (" interface=" + iface) : ""), |
|
147 "OK", callback); |
|
148 }; |
|
149 |
|
150 command.wpsPin = function (detail, callback) { |
|
151 doStringCommand("WPS_PIN " + |
|
152 (detail.bssid === undefined ? "any" : detail.bssid) + |
|
153 (detail.pin === undefined ? "" : (" " + detail.pin)) + |
|
154 (detail.iface ? (" interface=" + detail.iface) : ""), |
|
155 callback); |
|
156 }; |
|
157 |
|
158 command.wpsCancel = function (callback) { |
|
159 doBooleanCommand("WPS_CANCEL", "OK", callback); |
|
160 }; |
|
161 |
|
162 command.startDriver = function (callback) { |
|
163 doBooleanCommand("DRIVER START", "OK"); |
|
164 }; |
|
165 |
|
166 command.stopDriver = function (callback) { |
|
167 doBooleanCommand("DRIVER STOP", "OK"); |
|
168 }; |
|
169 |
|
170 command.startPacketFiltering = function (callback) { |
|
171 var commandChain = ["DRIVER RXFILTER-ADD 0", |
|
172 "DRIVER RXFILTER-ADD 1", |
|
173 "DRIVER RXFILTER-ADD 3", |
|
174 "DRIVER RXFILTER-START"]; |
|
175 |
|
176 doBooleanCommandChain(commandChain, callback); |
|
177 }; |
|
178 |
|
179 command.stopPacketFiltering = function (callback) { |
|
180 var commandChain = ["DRIVER RXFILTER-STOP", |
|
181 "DRIVER RXFILTER-REMOVE 3", |
|
182 "DRIVER RXFILTER-REMOVE 1", |
|
183 "DRIVER RXFILTER-REMOVE 0"]; |
|
184 |
|
185 doBooleanCommandChain(commandChain, callback); |
|
186 }; |
|
187 |
|
188 command.doGetRssi = function (cmd, callback) { |
|
189 doCommand(cmd, function(data) { |
|
190 var rssi = -200; |
|
191 |
|
192 if (!data.status) { |
|
193 // If we are associating, the reply is "OK". |
|
194 var reply = data.reply; |
|
195 if (reply !== "OK") { |
|
196 // Format is: <SSID> rssi XX". SSID can contain spaces. |
|
197 var offset = reply.lastIndexOf("rssi "); |
|
198 if (offset !== -1) { |
|
199 rssi = reply.substr(offset + 5) | 0; |
|
200 } |
|
201 } |
|
202 } |
|
203 callback(rssi); |
|
204 }); |
|
205 }; |
|
206 |
|
207 command.getRssi = function (callback) { |
|
208 command.doGetRssi("DRIVER RSSI", callback); |
|
209 }; |
|
210 |
|
211 command.getRssiApprox = function (callback) { |
|
212 command.doGetRssi("DRIVER RSSI-APPROX", callback); |
|
213 }; |
|
214 |
|
215 command.getLinkSpeed = function (callback) { |
|
216 doStringCommand("DRIVER LINKSPEED", function(reply) { |
|
217 if (reply) { |
|
218 reply = reply.split(" ")[1] | 0; // Format: LinkSpeed XX |
|
219 } |
|
220 callback(reply); |
|
221 }); |
|
222 }; |
|
223 |
|
224 command.getConnectionInfoICS = function (callback) { |
|
225 doStringCommand("SIGNAL_POLL", function(reply) { |
|
226 if (!reply) { |
|
227 callback(null); |
|
228 return; |
|
229 } |
|
230 |
|
231 let rval = {}; |
|
232 var lines = reply.split("\n"); |
|
233 for (let i = 0; i < lines.length; ++i) { |
|
234 let [key, value] = lines[i].split("="); |
|
235 switch (key.toUpperCase()) { |
|
236 case "RSSI": |
|
237 rval.rssi = value | 0; |
|
238 break; |
|
239 case "LINKSPEED": |
|
240 rval.linkspeed = value | 0; |
|
241 break; |
|
242 default: |
|
243 // Ignore. |
|
244 } |
|
245 } |
|
246 |
|
247 callback(rval); |
|
248 }); |
|
249 }; |
|
250 |
|
251 command.getMacAddress = function (callback) { |
|
252 doStringCommand("DRIVER MACADDR", function(reply) { |
|
253 if (reply) { |
|
254 reply = reply.split(" ")[2]; // Format: Macaddr = XX.XX.XX.XX.XX.XX |
|
255 } |
|
256 callback(reply); |
|
257 }); |
|
258 }; |
|
259 |
|
260 command.setPowerModeICS = function (mode, callback) { |
|
261 doBooleanCommand("DRIVER POWERMODE " + (mode === "AUTO" ? 0 : 1), "OK", callback); |
|
262 }; |
|
263 |
|
264 command.setPowerModeJB = function (mode, callback) { |
|
265 doBooleanCommand("SET ps " + (mode === "AUTO" ? 1 : 0), "OK", callback); |
|
266 }; |
|
267 |
|
268 command.getPowerMode = function (callback) { |
|
269 doStringCommand("DRIVER GETPOWER", function(reply) { |
|
270 if (reply) { |
|
271 reply = (reply.split()[2]|0); // Format: powermode = XX |
|
272 } |
|
273 callback(reply); |
|
274 }); |
|
275 }; |
|
276 |
|
277 command.setNumAllowedChannels = function (numChannels, callback) { |
|
278 doBooleanCommand("DRIVER SCAN-CHANNELS " + numChannels, "OK", callback); |
|
279 }; |
|
280 |
|
281 command.getNumAllowedChannels = function (callback) { |
|
282 doStringCommand("DRIVER SCAN-CHANNELS", function(reply) { |
|
283 if (reply) { |
|
284 reply = (reply.split()[2]|0); // Format: Scan-Channels = X |
|
285 } |
|
286 callback(reply); |
|
287 }); |
|
288 }; |
|
289 |
|
290 command.setBluetoothCoexistenceMode = function (mode, callback) { |
|
291 doBooleanCommand("DRIVER BTCOEXMODE " + mode, "OK", callback); |
|
292 }; |
|
293 |
|
294 command.setBluetoothCoexistenceScanMode = function (mode, callback) { |
|
295 doBooleanCommand("DRIVER BTCOEXSCAN-" + (mode ? "START" : "STOP"), |
|
296 "OK", callback); |
|
297 }; |
|
298 |
|
299 command.saveConfig = function (callback) { |
|
300 // Make sure we never write out a value for AP_SCAN other than 1. |
|
301 doBooleanCommand("AP_SCAN 1", "OK", function(ok) { |
|
302 doBooleanCommand("SAVE_CONFIG", "OK", callback); |
|
303 }); |
|
304 }; |
|
305 |
|
306 command.reloadConfig = function (callback) { |
|
307 doBooleanCommand("RECONFIGURE", "OK", callback); |
|
308 }; |
|
309 |
|
310 command.setScanResultHandling = function (mode, callback) { |
|
311 doBooleanCommand("AP_SCAN " + mode, "OK", callback); |
|
312 }; |
|
313 |
|
314 command.addToBlacklist = function (bssid, callback) { |
|
315 doBooleanCommand("BLACKLIST " + bssid, "OK", callback); |
|
316 }; |
|
317 |
|
318 command.clearBlacklist = function (callback) { |
|
319 doBooleanCommand("BLACKLIST clear", "OK", callback); |
|
320 }; |
|
321 |
|
322 command.setSuspendOptimizationsICS = function (enabled, callback) { |
|
323 doBooleanCommand("DRIVER SETSUSPENDOPT " + (enabled ? 0 : 1), |
|
324 "OK", callback); |
|
325 }; |
|
326 |
|
327 command.setSuspendOptimizationsJB = function (enabled, callback) { |
|
328 doBooleanCommand("DRIVER SETSUSPENDMODE " + (enabled ? 1 : 0), |
|
329 "OK", callback); |
|
330 }; |
|
331 |
|
332 command.connectToSupplicant = function(callback) { |
|
333 voidControlMessage("connect_to_supplicant", callback); |
|
334 }; |
|
335 |
|
336 command.closeSupplicantConnection = function(callback) { |
|
337 voidControlMessage("close_supplicant_connection", callback); |
|
338 }; |
|
339 |
|
340 command.getMacAddress = function(callback) { |
|
341 doStringCommand("DRIVER MACADDR", function(reply) { |
|
342 if (reply) { |
|
343 reply = reply.split(" ")[2]; // Format: Macaddr = XX.XX.XX.XX.XX.XX |
|
344 } |
|
345 callback(reply); |
|
346 }); |
|
347 }; |
|
348 |
|
349 command.setDeviceName = function(deviceName, callback) { |
|
350 doBooleanCommand("SET device_name " + deviceName, "OK", callback); |
|
351 }; |
|
352 |
|
353 //------------------------------------------------- |
|
354 // P2P commands. |
|
355 //------------------------------------------------- |
|
356 |
|
357 command.p2pProvDiscovery = function(address, wpsMethod, callback) { |
|
358 var command = "P2P_PROV_DISC " + address + " " + wpsMethod; |
|
359 doBooleanCommand(command, "OK", callback); |
|
360 }; |
|
361 |
|
362 command.p2pConnect = function(config, callback) { |
|
363 var command = "P2P_CONNECT " + config.address + " " + config.wpsMethodWithPin + " "; |
|
364 if (config.joinExistingGroup) { |
|
365 command += "join"; |
|
366 } else { |
|
367 command += "go_intent=" + config.goIntent; |
|
368 } |
|
369 |
|
370 debug('P2P connect command: ' + command); |
|
371 doBooleanCommand(command, "OK", callback); |
|
372 }; |
|
373 |
|
374 command.p2pGroupRemove = function(iface, callback) { |
|
375 debug("groupRemove()"); |
|
376 doBooleanCommand("P2P_GROUP_REMOVE " + iface, "OK", callback); |
|
377 }; |
|
378 |
|
379 command.p2pEnable = function(detail, callback) { |
|
380 var commandChain = ["SET device_name " + detail.deviceName, |
|
381 "SET device_type " + detail.deviceType, |
|
382 "SET config_methods " + detail.wpsMethods, |
|
383 "P2P_SET conc_pref sta", |
|
384 "P2P_FLUSH"]; |
|
385 |
|
386 doBooleanCommandChain(commandChain, callback); |
|
387 }; |
|
388 |
|
389 command.p2pDisable = function(callback) { |
|
390 doBooleanCommand("P2P_SET disabled 1", "OK", callback); |
|
391 }; |
|
392 |
|
393 command.p2pEnableScan = function(timeout, callback) { |
|
394 doBooleanCommand("P2P_FIND " + timeout, "OK", callback); |
|
395 }; |
|
396 |
|
397 command.p2pDisableScan = function(callback) { |
|
398 doBooleanCommand("P2P_STOP_FIND", "OK", callback); |
|
399 }; |
|
400 |
|
401 command.p2pGetGroupCapab = function(address, callback) { |
|
402 command.p2pPeer(address, function(reply) { |
|
403 debug('p2p_peer reply: ' + reply); |
|
404 if (!reply) { |
|
405 callback(0); |
|
406 return; |
|
407 } |
|
408 var capab = /group_capab=0x([0-9a-fA-F]+)/.exec(reply)[1]; |
|
409 if (!capab) { |
|
410 callback(0); |
|
411 } else { |
|
412 callback(parseInt(capab, 16)); |
|
413 } |
|
414 }); |
|
415 }; |
|
416 |
|
417 command.p2pPeer = function(address, callback) { |
|
418 doStringCommand("P2P_PEER " + address, callback); |
|
419 }; |
|
420 |
|
421 command.p2pGroupAdd = function(netId, callback) { |
|
422 doBooleanCommand("P2P_GROUP_ADD persistent=" + netId, callback); |
|
423 }; |
|
424 |
|
425 command.p2pReinvoke = function(netId, address, callback) { |
|
426 doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + address, "OK", callback); |
|
427 }; |
|
428 |
|
429 //---------------------------------------------------------- |
|
430 // Private stuff. |
|
431 //---------------------------------------------------------- |
|
432 |
|
433 function voidControlMessage(cmd, callback) { |
|
434 aControlMessage({ cmd: cmd, iface: aInterface }, function (data) { |
|
435 callback(data.status); |
|
436 }); |
|
437 } |
|
438 |
|
439 function doCommand(request, callback) { |
|
440 var msg = { cmd: "command", |
|
441 request: request, |
|
442 iface: aInterface }; |
|
443 |
|
444 aControlMessage(msg, callback); |
|
445 } |
|
446 |
|
447 function doIntCommand(request, callback) { |
|
448 doCommand(request, function(data) { |
|
449 callback(data.status ? -1 : (data.reply|0)); |
|
450 }); |
|
451 } |
|
452 |
|
453 function doBooleanCommand(request, expected, callback) { |
|
454 doCommand(request, function(data) { |
|
455 callback(data.status ? false : (data.reply === expected)); |
|
456 }); |
|
457 } |
|
458 |
|
459 function doStringCommand(request, callback) { |
|
460 doCommand(request, function(data) { |
|
461 callback(data.status ? null : data.reply); |
|
462 }); |
|
463 } |
|
464 |
|
465 function doBooleanCommandChain(commandChain, callback, i) { |
|
466 if (undefined === i) { |
|
467 i = 0; |
|
468 } |
|
469 |
|
470 doBooleanCommand(commandChain[i], "OK", function(ok) { |
|
471 if (!ok) { |
|
472 return callback(false); |
|
473 } |
|
474 i++; |
|
475 if (i === commandChain.length || !commandChain[i]) { |
|
476 // Reach the end or empty command. |
|
477 return callback(true); |
|
478 } |
|
479 doBooleanCommandChain(commandChain, callback, i); |
|
480 }); |
|
481 } |
|
482 |
|
483 //-------------------------------------------------- |
|
484 // Helper functions. |
|
485 //-------------------------------------------------- |
|
486 |
|
487 function stopProcess(service, process, callback) { |
|
488 var count = 0; |
|
489 var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); |
|
490 function tick() { |
|
491 let result = libcutils.property_get(service); |
|
492 if (result === null) { |
|
493 callback(); |
|
494 return; |
|
495 } |
|
496 if (result === "stopped" || ++count >= 5) { |
|
497 // Either we succeeded or ran out of time. |
|
498 timer = null; |
|
499 callback(); |
|
500 return; |
|
501 } |
|
502 |
|
503 // Else it's still running, continue waiting. |
|
504 timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT); |
|
505 } |
|
506 |
|
507 setProperty("ctl.stop", process, tick); |
|
508 } |
|
509 |
|
510 // Wrapper around libcutils.property_set that returns true if setting the |
|
511 // value was successful. |
|
512 // Note that the callback is not called asynchronously. |
|
513 function setProperty(key, value, callback) { |
|
514 let ok = true; |
|
515 try { |
|
516 libcutils.property_set(key, value); |
|
517 } catch(e) { |
|
518 ok = false; |
|
519 } |
|
520 callback(ok); |
|
521 } |
|
522 |
|
523 return command; |
|
524 }; |