Bug 928362: Support ad-hoc mode for Wifi, r=vchang, r=mrbkap
authorThomas Zimmermann <tdz@users.sourceforge.net>
Mon, 20 Oct 2014 14:22:47 +0200
changeset 211322 eea1504453bfa5f23ae58958e853fbe7b900952e
parent 211321 1c2a8e82b4dd51bb36a078ef3d964417296583ab
child 211323 a4bcf84cfc9472c6140d37319108fd4656ebae37
push id50691
push userkwierso@gmail.com
push dateTue, 21 Oct 2014 02:08:21 +0000
treeherdermozilla-inbound@0808729b24e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvchang, mrbkap
bugs928362
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 928362: Support ad-hoc mode for Wifi, r=vchang, r=mrbkap This patch adds support for Wifi networks that run in ad-hoc mode. The network attributes 'frequency' and 'mode' are required by wpa_supplicant to connect to an ad-hoc network. 'Frequency' is just the frequency of the channel in use. 'Mode' is the network's mode, with 0 being infrastructure mode, 1 being IBSS mode (aka ad-hoc mode), and 2 being access-point mode. If ad-hoc mode is not supported on your device, you can set the environment property 'ro.moz.wifi.ibss_supported' to false to disable it in Gecko.
dom/webidl/MozWifiManager.webidl
dom/wifi/WifiWorker.js
--- a/dom/webidl/MozWifiManager.webidl
+++ b/dom/webidl/MozWifiManager.webidl
@@ -23,16 +23,18 @@ enum ConnectionStatus {
 dictionary WifiWPSInfo {
   WifiWPSMethod method;
   DOMString? pin;
   DOMString? bssid;
 };
 
 dictionary NetworkProperties {
   DOMString ssid;
+  long mode;
+  long frequency;
   sequence<DOMString>? security;
   sequence<DOMString>? capabilities;
   boolean known;
   boolean connected;
   boolean hidden;
   DOMString bssid;
   DOMString signalStrength;
   long relSignalStrength;
@@ -58,16 +60,18 @@ dictionary NetworkProperties {
   DOMString subjectMatch;
 };
 
 [Constructor(optional NetworkProperties properties),
  JSImplementation="@mozilla.org/mozwifinetwork;1",
  Func="Navigator::HasWifiManagerSupport"]
 interface MozWifiNetwork {
   readonly attribute DOMString ssid;
+  readonly attribute long mode;
+  readonly attribute long frequency;
   [Constant, Cached] readonly attribute sequence<DOMString>? security;
   [Constant, Cached] readonly attribute sequence<DOMString>? capabilities;
   readonly attribute boolean known;
   readonly attribute boolean connected;
   readonly attribute boolean hidden;
 
            attribute DOMString? bssid;
            attribute DOMString? signalStrength;
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -77,16 +77,19 @@ const DEFAULT_WLAN_INTERFACE = "wlan0";
 
 const DRIVER_READY_WAIT = 2000;
 
 const SUPP_PROP = "init.svc.wpa_supplicant";
 const WPA_SUPPLICANT = "wpa_supplicant";
 const DHCP_PROP = "init.svc.dhcpcd";
 const DHCP = "dhcpcd";
 
+const MODE_ESS = 0;
+const MODE_IBSS = 1;
+
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
                                    "@mozilla.org/network/service;1",
                                    "nsINetworkService");
 
@@ -107,32 +110,37 @@ var WifiManager = (function() {
   function getStartupPrefs() {
     return {
       sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
       unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
       schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
       driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
       p2pSupported: libcutils.property_get("ro.moz.wifi.p2p_supported") === "1",
       eapSimSupported: libcutils.property_get("ro.moz.wifi.eapsim_supported") === "1",
+      ibssSupported: libcutils.property_get("ro.moz.wifi.ibss_supported", "true") === "true",
       ifname: libcutils.property_get("wifi.interface")
     };
   }
 
   let {sdkVersion, unloadDriverEnabled, schedScanRecovery,
-       driverDelay, p2pSupported, eapSimSupported, ifname} = getStartupPrefs();
+       driverDelay, p2pSupported, eapSimSupported, ibssSupported, ifname} = getStartupPrefs();
 
   let capabilities = {
     security: ["OPEN", "WEP", "WPA-PSK", "WPA-EAP"],
     eapMethod: ["PEAP", "TTLS"],
     eapPhase2: ["MSCHAPV2"],
-    certificate: ["SERVER"]
+    certificate: ["SERVER"],
+    mode: [MODE_ESS]
   };
   if (eapSimSupported) {
     capabilities.eapMethod.unshift("SIM");
   }
+  if (ibssSupported) {
+    capabilities.mode.push(MODE_IBSS);
+  }
 
   let wifiListener = {
     onWaitEvent: function(event, iface) {
       if (manager.ifname === iface && handleEvent(event)) {
         waitForEvent(iface);
       } else if (p2pSupported) {
         // Please refer to
         // http://androidxref.com/4.4.2_r1/xref/frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java#519
@@ -1183,17 +1191,23 @@ var WifiManager = (function() {
     {name: "password",      type: "string"},
     {name: "auth_alg",      type: "string"},
     {name: "phase1",        type: "string"},
     {name: "phase2",        type: "string"},
     {name: "eap",           type: "string"},
     {name: "pin",           type: "string"},
     {name: "pcsc",          type: "string"},
     {name: "ca_cert",       type: "string"},
-    {name: "subject_match", type: "string"}
+    {name: "subject_match", type: "string"},
+    {name: "frequency",     type: "integer"},
+    {name: "mode",          type: "integer"}
+  ];
+  // These fields are only handled in IBSS (aka ad-hoc) mode
+  var ibssNetworkConfigurationFields = [
+    "frequency", "mode"
   ];
 
   manager.getNetworkConfiguration = function(config, callback) {
     var netId = config.netId;
     var done = 0;
     for (var n = 0; n < networkConfigurationFields.length; ++n) {
       let fieldName = networkConfigurationFields[n].name;
       let fieldType = networkConfigurationFields[n].type;
@@ -1218,19 +1232,33 @@ var WifiManager = (function() {
 
     function hasValidProperty(name) {
       return ((name in config) &&
                config[name] != null &&
                (["password", "wep_key0", "psk"].indexOf(name) === -1 ||
                 config[name] !== '*'));
     }
 
+    function getModeFromConfig() {
+      /* we use the mode from the config, or ESS as default */
+      return hasValidProperty("mode") ? config["mode"] : MODE_ESS;
+    }
+
+    var mode = getModeFromConfig();
+
+    function validForMode(name, mode) {
+            /* all fields are valid for IBSS */
+      return (mode == MODE_IBSS) ||
+            /* IBSS fields are not valid for ESS */
+            ((mode == MODE_ESS) && !(name in ibssNetworkConfigurationFields));
+    }
+
     for (var n = 0; n < networkConfigurationFields.length; ++n) {
       let fieldName = networkConfigurationFields[n].name;
-      if (!hasValidProperty(fieldName)) {
+      if (!hasValidProperty(fieldName) || !validForMode(fieldName, mode)) {
         ++done;
       } else {
         wifiCommand.setNetworkVariable(netId, fieldName, config[fieldName], function(ok) {
           if (!ok)
             ++errors;
           if (++done == networkConfigurationFields.length)
             callback(errors == 0);
         });
@@ -1551,16 +1579,28 @@ function getNetworkKey(network)
 
   // ssid here must be dequoted, and it's safer to esacpe it.
   // encryption won't be empty and always be assigned one of the followings :
   // "OPEN"/"WEP"/"WPA-PSK"/"WPA-EAP".
   // So for a invalid network object, the returned key will be "OPEN".
   return escape(ssid) + encryption;
 }
 
+function getMode(flags) {
+  if (!flags)
+    return -1;
+
+  if (/\[ESS/.test(flags))
+    return MODE_ESS;
+  if (/\[IBSS/.test(flags))
+    return MODE_IBSS;
+
+  return -1;
+}
+
 function getKeyManagement(flags) {
   var types = [];
   if (!flags)
     return types;
 
   if (/\[WPA2?-PSK/.test(flags))
     types.push("WPA-PSK");
   if (/\[WPA2?-EAP/.test(flags))
@@ -1597,31 +1637,35 @@ function calculateSignal(strength) {
 
   if (strength <= MIN_RSSI)
     return 0;
   if (strength >= MAX_RSSI)
     return 100;
   return Math.floor(((strength - MIN_RSSI) / (MAX_RSSI - MIN_RSSI)) * 100);
 }
 
-function Network(ssid, security, password, capabilities) {
+function Network(ssid, mode, frequency, security, password, capabilities) {
   this.ssid = ssid;
+  this.mode = mode;
+  this.frequency = frequency;
   this.security = security;
 
   if (typeof password !== "undefined")
     this.password = password;
   if (capabilities !== undefined)
     this.capabilities = capabilities;
   // TODO connected here as well?
 
   this.__exposedProps__ = Network.api;
 }
 
 Network.api = {
   ssid: "r",
+  mode: "r",
+  frequency: "r",
   security: "r",
   capabilities: "r",
   known: "r",
 
   password: "rw",
   keyManagement: "rw",
   psk: "rw",
   identity: "rw",
@@ -1631,19 +1675,19 @@ Network.api = {
   pin: "rw",
   phase1: "rw",
   phase2: "rw",
   serverCertificate: "rw"
 };
 
 // Note: We never use ScanResult.prototype, so the fact that it's unrelated to
 // Network.prototype is OK.
-function ScanResult(ssid, bssid, flags, signal) {
-  Network.call(this, ssid, getKeyManagement(flags), undefined,
-               getCapabilities(flags));
+function ScanResult(ssid, bssid, frequency, flags, signal) {
+  Network.call(this, ssid, getMode(flags), frequency,
+               getKeyManagement(flags), undefined, getCapabilities(flags));
   this.bssid = bssid;
   this.signalStrength = signal;
   this.relSignalStrength = calculateSignal(Number(signal));
 
   this.__exposedProps__ = ScanResult.api;
 }
 
 // XXX This should probably live in the DOM-facing side, but it's hard to do
@@ -1845,27 +1889,29 @@ function WifiWorker() {
 
   // Given a connection status network, takes a network from
   // self.configuredNetworks and prepares it for the DOM.
   netToDOM = function(net) {
     if (!net) {
       return null;
     }
     var ssid = dequote(net.ssid);
+    var mode = net.mode;
+    var frequency = net.frequency;
     var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
                    (net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt.split(" ")[0]] :
                    [];
     var password;
     if (("psk" in net && net.psk) ||
         ("password" in net && net.password) ||
         ("wep_key0" in net && net.wep_key0)) {
       password = "*";
     }
 
-    var pub = new Network(ssid, security, password);
+    var pub = new Network(ssid, mode, frequency, security, password);
     if (net.identity)
       pub.identity = dequote(net.identity);
     if ("netId" in net)
       pub.known = true;
     if (net.scan_ssid === 1)
       pub.hidden = true;
     if ("ca_cert" in net && net.ca_cert &&
         net.ca_cert.indexOf("keystore://WIFI_SERVERCERT_" === 0)) {
@@ -2057,20 +2103,22 @@ function WifiWorker() {
         // automatically connect. Try to knock us out of it. We only
         // hit this state when we've failed to run DHCP, so trying
         // again isn't the worst thing we can do. Eventually, we'll
         // need to detect if we're looping in this state and bail out.
         WifiManager.reconnect(function(){});
         break;
       case "ASSOCIATING":
         // id has not yet been filled in, so we can only report the ssid and
-        // bssid.
+        // bssid. mode and frequency are simply made up.
         self.currentNetwork =
           { bssid: WifiManager.connectionInfo.bssid,
-            ssid: quote(WifiManager.connectionInfo.ssid) };
+            ssid: quote(WifiManager.connectionInfo.ssid),
+            mode: MODE_ESS,
+            frequency: 0};
         self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
         break;
       case "ASSOCIATED":
         if (!self.currentNetwork) {
           self.currentNetwork =
             { bssid: WifiManager.connectionInfo.bssid,
               ssid: quote(WifiManager.connectionInfo.ssid) };
         }
@@ -2256,40 +2304,43 @@ function WifiWorker() {
     WifiManager.getScanResults(function(r) {
       // Failure.
       if (!r) {
         self.wantScanResults.forEach(function(callback) { callback(null) });
         self.wantScanResults = [];
         return;
       }
 
+      let capabilities = WifiManager.getCapabilities();
+
       // Now that we have scan results, there's no more need to continue
       // scanning. Ignore any errors from this command.
       WifiManager.setScanMode("inactive", function() {});
       let lines = r.split("\n");
       // NB: Skip the header line.
       self.networksArray = [];
       for (let i = 1; i < lines.length; ++i) {
         // bssid / frequency / signal level / flags / ssid
         var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s(.*)/.exec(lines[i]);
 
         if (match && match[5]) {
           let ssid = match[5],
               bssid = match[1],
+              frequency = match[2],
               signalLevel = match[3],
               flags = match[4];
 
-          // Skip ad-hoc networks which aren't supported (bug 811635).
-          if (flags && flags.indexOf("[IBSS]") >= 0)
+          /* Skip networks with unknown or unsupported modes. */
+          if (capabilities.mode.indexOf(getMode(flags)) == -1)
             continue;
 
           // If this is the first time that we've seen this SSID in the scan
           // results, add it to the list along with any other information.
           // Also, we use the highest signal strength that we see.
-          let network = new ScanResult(ssid, bssid, flags, signalLevel);
+          let network = new ScanResult(ssid, bssid, frequency, flags, signalLevel);
 
           let networkKey = getNetworkKey(network);
           let eapIndex = -1;
           if (networkKey in self.configuredNetworks) {
             let known = self.configuredNetworks[networkKey];
             network.known = true;
 
             if ("identity" in known && known.identity)