Bug 936367 - Support wifi hotspot enable API. r=vchang, r=ptheriault, sr=mrbkap
authorDimi Lee <dlee@mozilla.com>
Mon, 11 Aug 2014 17:13:25 +0800
changeset 220485 ca2d0b3441ba3a54b5fe3bf09e2566e45171a704
parent 220484 2c37827e4e5ac906e42dff5c87d7ae48dc432185
child 220486 d88299945b02c28619ad812106b908ca38d51cb1
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvchang, ptheriault, mrbkap
bugs936367
milestone34.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 936367 - Support wifi hotspot enable API. r=vchang, r=ptheriault, sr=mrbkap
b2g/installer/package-manifest.in
dom/moz.build
dom/tethering/TetheringManager.js
dom/tethering/TetheringManager.manifest
dom/tethering/moz.build
dom/webidl/MozTetheringManager.webidl
dom/webidl/moz.build
dom/wifi/WifiWorker.js
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -410,16 +410,22 @@
 @BINPATH@/components/NetworkStatsManager.js
 @BINPATH@/components/NetworkStatsManager.manifest
 @BINPATH@/components/NetworkStatsServiceProxy.js
 @BINPATH@/components/NetworkStatsServiceProxy.manifest
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 #endif // MOZ_WIDGET_GONK
 
+; Tethering
+#ifdef MOZ_WIDGET_GONK
+@BINPATH@/components/TetheringManager.js
+@BINPATH@/components/TetheringManager.manifest
+#endif
+
 ; ResourceStats
 #ifdef MOZ_WIDGET_GONK
 @BINPATH@/components/ResourceStats.js
 @BINPATH@/components/ResourceStats.manifest
 @BINPATH@/components/ResourceStatsManager.js
 @BINPATH@/components/ResourceStatsManager.manifest
 #endif // MOZ_WIDGET_GONK
 
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -91,16 +91,17 @@ DIRS += [
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += [
         'speakermanager',
+        'tethering',
         'wifi',
     ]
 
 if CONFIG['MOZ_B2G_RIL']:
     DIRS += [
         'icc',
         'cellbroadcast',
         'mobileconnection',
new file mode 100644
--- /dev/null
+++ b/dom/tethering/TetheringManager.js
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+const DEBUG = false;
+
+const TETHERING_TYPE_WIFI       = "wifi";
+const TETHERING_TYPE_BLUETOOTH  = "bt";
+const TETHERING_TYPE_USB        = "usb";
+
+function TetheringManager() {
+}
+
+TetheringManager.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
+
+  classDescription: "TetheringManager",
+  classID: Components.ID("{bd8a831c-d8ec-4f00-8803-606e50781097}"),
+  contractID: "@mozilla.org/dom/tetheringmanager;1",
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsIObserver]),
+
+  init: function(aWindow) {
+    const messages = ["WifiManager:setWifiTethering:Return:OK",
+                      "WifiManager:setWifiTethering:Return:NO"];
+    this.initDOMRequestHelper(aWindow, messages);
+  },
+
+  _getPromise: function(aCallback) {
+    let self = this;
+
+    return this.createPromise(function(aResolve, aReject) {
+      let resolverId = self.getPromiseResolverId({
+        resolve: aResolve,
+        reject: aReject
+      });
+
+      aCallback(resolverId);
+    });
+  },
+
+  // TODO : aMessage format may be different after supporting bt/usb.
+  //        for now, use wifi format first.
+  receiveMessage: function(aMessage) {
+    let data = aMessage.data.data;
+
+    let resolver = this.takePromiseResolver(data.resolverId);
+    if (!resolver) {
+      return;
+    }
+
+    switch (aMessage.name) {
+      case "WifiManager:setWifiTethering:Return:OK":
+        resolver.resolve(data);
+        break;
+      case "WifiManager:setWifiTethering:Return:NO":
+        resolver.reject(data.reason);
+        break;
+    }
+  },
+
+  setTetheringEnabled: function setTetheringEnabled(aEnabled, aType, aConfig) {
+    let self = this;
+    switch (aType) {
+      case TETHERING_TYPE_WIFI:
+        return this._getPromise(function(aResolverId) {
+          let data = { resolverId: aResolverId, enabled: aEnabled, config: aConfig };
+          cpmm.sendAsyncMessage("WifiManager:setWifiTethering", { data: data});
+        });
+      case TETHERING_TYPE_BLUETOOTH:
+      case TETHERING_TYPE_USB:
+      default:
+        debug("tethering type(" + aType + ") doesn't support");
+        return this._getPromise(function(aResolverId) {
+          self.takePromiseResolver(aResolverId).reject();
+        });
+    }
+  },
+};
+
+this.NSGetFactory =
+  XPCOMUtils.generateNSGetFactory([TetheringManager]);
+
+let debug;
+if (DEBUG) {
+  debug = function (s) {
+    dump("-*- TetheringManager component: " + s + "\n");
+  };
+} else {
+  debug = function (s) {};
+}
new file mode 100644
--- /dev/null
+++ b/dom/tethering/TetheringManager.manifest
@@ -0,0 +1,4 @@
+#TetheringManager.js
+component {bd8a831c-d8ec-4f00-8803-606e50781097} TetheringManager.js
+contract @mozilla.org/tetheringmanager;1 {bd8a831c-d8ec-4f00-8803-606e50781097}
+
new file mode 100644
--- /dev/null
+++ b/dom/tethering/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXTRA_COMPONENTS += [
+    'TetheringManager.js',
+    'TetheringManager.manifest',
+]
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MozTetheringManager.webidl
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+enum TetheringType {
+  "bluetooth",
+  "usb",
+  "wifi"
+};
+
+enum SecurityType {
+  "open",
+  "wpa-psk",
+  "wpa2-psk"
+};
+
+dictionary WifiTetheringConfig {
+  DOMString ssid;
+  SecurityType security;
+  DOMString key;
+};
+
+dictionary TetheringConfiguration {
+  DOMString ip;
+  DOMString prefix;
+  DOMString startIp;
+  DOMString endIp;
+  DOMString dns1;
+  DOMString dns2;
+  WifiTetheringConfig wifiConfig;
+};
+
+[JSImplementation="@mozilla.org/tetheringmanager;1",
+ NavigatorProperty="mozTetheringManager",
+ AvailableIn="CertifiedApps"]
+interface MozTetheringManager {
+  /**
+   * Enable/Disable tethering.
+   * @param enabled True to enable tethering, False to disable tethering.
+   * @param type Tethering type to enable/disable.
+   * @param config Configuration should have following fields when enable is True:
+   *               - ip ip address.
+   *               - prefix mask length.
+   *               - startIp start ip address allocated by DHCP server for tethering.
+   *               - endIp end ip address allocated by DHCP server for tethering.
+   *               - dns1 first DNS server address.
+   *               - dns2 second DNS server address.
+   *               - wifiConfig wifi tethering configuration
+   *                  - ssid SSID network name.
+   *                  - security open, wpa-psk or wpa2-psk.
+   *                  - key password for wpa-psk or wpa2-psk.
+   *               config should not be set when enabled is False.
+   */
+  Promise<any> setTetheringEnabled(boolean enabled,
+                                   TetheringType type,
+                                   optional TetheringConfiguration config);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -258,16 +258,17 @@ WEBIDL_FILES = [
     'MouseEvent.webidl',
     'MouseScrollEvent.webidl',
     'MozActivity.webidl',
     'MozMmsMessage.webidl',
     'MozMobileMessageManager.webidl',
     'MozNamedAttrMap.webidl',
     'MozPowerManager.webidl',
     'MozSelfSupport.webidl',
+    'MozTetheringManager.webidl',
     'MozTimeManager.webidl',
     'MozWakeLock.webidl',
     'MutationEvent.webidl',
     'MutationObserver.webidl',
     'NativeOSFileInternals.webidl',
     'NetDashboard.webidl',
     'NetworkInformation.webidl',
     'NetworkOptions.webidl',
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1760,16 +1760,17 @@ function WifiWorker() {
                     "WifiManager:wps", "WifiManager:getState",
                     "WifiManager:setPowerSavingMode",
                     "WifiManager:setHttpProxy",
                     "WifiManager:setStaticIpMode",
                     "WifiManager:importCert",
                     "WifiManager:getImportedCerts",
                     "WifiManager:deleteCert",
                     "WifiManager:setWifiEnabled",
+                    "WifiManager:setWifiTethering",
                     "child-process-shutdown"];
 
   messages.forEach((function(msgName) {
     this._mm.addMessageListener(msgName, this);
   }).bind(this));
 
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   Services.obs.addObserver(this, "xpcom-shutdown", false);
@@ -2746,16 +2747,19 @@ WifiWorker.prototype = {
         this.importCert(msg);
         break;
       case "WifiManager:getImportedCerts":
         this.getImportedCerts(msg);
         break;
       case "WifiManager:deleteCert":
         this.deleteCert(msg);
         break;
+      case "WifiManager:setWifiTethering":
+        this.setWifiTethering(msg);
+        break;
       case "WifiManager:getState": {
         let i;
         if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
           this._domManagers.push(msg.manager);
         }
 
         let net = this.currentNetwork ? netToDOM(this.currentNetwork) : null;
         return { network: net,
@@ -2981,16 +2985,70 @@ WifiWorker.prototype = {
       data: data,
       callback: callback
     });
 
     this.nextRequest();
   },
 
   getWifiTetheringParameters: function getWifiTetheringParameters(enable) {
+    if (this.useTetheringAPI) {
+      return this.getWifiTetheringConfiguration(enable);
+    } else {
+      return this.getWifiTetheringParametersBySetting(enable);
+    }
+  },
+
+  getWifiTetheringConfiguration: function getWifiTetheringConfiguration(enable) {
+    let config = {};
+    let params = this.tetheringConfig;
+
+    let check = function(field, _default) {
+      config[field] = field in params ? params[field] : _default;
+    };
+
+    check("ssid", DEFAULT_WIFI_SSID);
+    check("security", DEFAULT_WIFI_SECURITY_TYPE);
+    check("key", DEFAULT_WIFI_SECURITY_PASSWORD);
+    check("ip", DEFAULT_WIFI_IP);
+    check("prefix", DEFAULT_WIFI_PREFIX);
+    check("wifiStartIp", DEFAULT_WIFI_DHCPSERVER_STARTIP);
+    check("wifiEndIp", DEFAULT_WIFI_DHCPSERVER_ENDIP);
+    check("usbStartIp", DEFAULT_USB_DHCPSERVER_STARTIP);
+    check("usbEndIp", DEFAULT_USB_DHCPSERVER_ENDIP);
+    check("dns1", DEFAULT_DNS1);
+    check("dns2", DEFAULT_DNS2);
+
+    config.enable = enable;
+    config.mode = enable ? WIFI_FIRMWARE_AP : WIFI_FIRMWARE_STATION;
+    config.link = enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN;
+
+    // Check the format to prevent netd from crash.
+    if (enable && (!config.ssid || config.ssid == "")) {
+      debug("Invalid SSID value.");
+      return null;
+    }
+
+    if (enable && (config.security != WIFI_SECURITY_TYPE_NONE && !config.key)) {
+      debug("Invalid security password.");
+      return null;
+    }
+
+    // Using the default values here until application supports these settings.
+    if (config.ip == "" || config.prefix == "" ||
+        config.wifiStartIp == "" || config.wifiEndIp == "" ||
+        config.usbStartIp == "" || config.usbEndIp == "") {
+      debug("Invalid subnet information.");
+      return null;
+    }
+
+    return config;
+  },
+
+  getWifiTetheringParametersBySetting: function getWifiTetheringParametersBySetting(enable) {
     let ssid;
     let securityType;
     let securityId;
     let interfaceIp;
     let prefix;
     let wifiDhcpStartIp;
     let wifiDhcpEndIp;
     let usbDhcpStartIp;
@@ -3395,16 +3453,45 @@ WifiWorker.prototype = {
       return;
     }
 
     WifiManager.deleteCert(msg.data, function(data) {
       self._sendMessage(message, data.status === 0, "Delete Cert failed", msg);
     });
   },
 
+  // TODO : These two variables should be removed once GAIA uses tethering API.
+  useTetheringAPI : false,
+  tetheringConfig : {},
+
+  setWifiTethering: function setWifiTethering(msg) {
+    const message = "WifiManager:setWifiTethering:Return";
+    let self = this;
+    let enabled = msg.data.enabled;
+
+    this.useTetheringAPI = true;
+    this.tetheringConfig = msg.data.config;
+
+    if (WifiManager.enabled) {
+      this._sendMessage(message, false, "Wifi is enabled", msg);
+      return;
+    }
+
+    this.setWifiApEnabled(enabled, function() {
+      if ((enabled && WifiManager.tetheringState == "COMPLETED") ||
+          (!enabled && WifiManager.tetheringState == "UNINITIALIZED")) {
+        self._sendMessage(message, true, msg.data, msg);
+      } else {
+        msg.data.reason = enabled ?
+          "Enable WIFI tethering faild" : "Disable WIFI tethering faild";
+        self._sendMessage(message, false, msg.data, msg);
+      }
+    });
+  },
+
   // This is a bit ugly, but works. In particular, this depends on the fact
   // that RadioManager never actually tries to get the worker from us.
   get worker() { throw "Not implemented"; },
 
   shutdown: function() {
     debug("shutting down ...");
     this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
       this._setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
@@ -3592,16 +3679,22 @@ WifiWorker.prototype = {
       case SETTINGS_WIFI_IP:
       case SETTINGS_WIFI_PREFIX:
       case SETTINGS_WIFI_DHCPSERVER_STARTIP:
       case SETTINGS_WIFI_DHCPSERVER_ENDIP:
       case SETTINGS_WIFI_DNS1:
       case SETTINGS_WIFI_DNS2:
       case SETTINGS_USB_DHCPSERVER_STARTIP:
       case SETTINGS_USB_DHCPSERVER_ENDIP:
+        // TODO: code related to wifi-tethering setting should be removed after GAIA
+        //       use tethering API
+        if (this.useTetheringAPI) {
+          break;
+        }
+
         if (aResult !== null) {
           this.tetheringSettings[aName] = aResult;
         }
         debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
         let index = this._wifiTetheringSettingsToRead.indexOf(aName);
 
         if (index != -1) {
           this._wifiTetheringSettingsToRead.splice(index, 1);