dom/wifi/WifiCommand.jsm

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a9e696a8bde4
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 };

mercurial