1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/wifi/WifiP2pManager.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1622 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +"use strict"; 1.11 + 1.12 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; 1.13 + 1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.15 +Cu.import("resource://gre/modules/StateMachine.jsm"); 1.16 +Cu.import("resource://gre/modules/Services.jsm"); 1.17 +Cu.import("resource://gre/modules/systemlibs.js"); 1.18 + 1.19 +XPCOMUtils.defineLazyServiceGetter(this, "gSysMsgr", 1.20 + "@mozilla.org/system-message-internal;1", 1.21 + "nsISystemMessagesInternal"); 1.22 + 1.23 +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager", 1.24 + "@mozilla.org/network/manager;1", 1.25 + "nsINetworkManager"); 1.26 + 1.27 +const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed"; 1.28 + 1.29 +this.EXPORTED_SYMBOLS = ["WifiP2pManager"]; 1.30 + 1.31 +const EVENT_IGNORED = -1; 1.32 +const EVENT_UNKNOWN = -2; 1.33 + 1.34 +// Events from supplicant for p2p. 1.35 +const EVENT_P2P_DEVICE_FOUND = 0; 1.36 +const EVENT_P2P_DEVICE_LOST = 1; 1.37 +const EVENT_P2P_GROUP_STARTED = 2; 1.38 +const EVENT_P2P_GROUP_REMOVED = 3; 1.39 +const EVENT_P2P_PROV_DISC_PBC_REQ = 4; 1.40 +const EVENT_P2P_PROV_DISC_PBC_RESP = 5; 1.41 +const EVENT_P2P_PROV_DISC_SHOW_PIN = 6; 1.42 +const EVENT_P2P_PROV_DISC_ENTER_PIN = 7; 1.43 +const EVENT_P2P_GO_NEG_REQUEST = 8; 1.44 +const EVENT_P2P_GO_NEG_SUCCESS = 9; 1.45 +const EVENT_P2P_GO_NEG_FAILURE = 10; 1.46 +const EVENT_P2P_GROUP_FORMATION_SUCCESS = 11; 1.47 +const EVENT_P2P_GROUP_FORMATION_FAILURE = 12; 1.48 +const EVENT_P2P_FIND_STOPPED = 13; 1.49 +const EVENT_P2P_INVITATION_RESULT = 14; 1.50 +const EVENT_P2P_INVITATION_RECEIVED = 15; 1.51 +const EVENT_P2P_PROV_DISC_FAILURE = 16; 1.52 + 1.53 +// Events from supplicant but not p2p specific. 1.54 +const EVENT_AP_STA_DISCONNECTED = 100; 1.55 +const EVENT_AP_STA_CONNECTED = 101; 1.56 + 1.57 +// Events from DOM. 1.58 +const EVENT_P2P_SET_PAIRING_CONFIRMATION = 1000; 1.59 +const EVENT_P2P_CMD_CONNECT = 1001; 1.60 +const EVENT_P2P_CMD_DISCONNECT = 1002; 1.61 +const EVENT_P2P_CMD_ENABLE = 1003; 1.62 +const EVENT_P2P_CMD_DISABLE = 1004; 1.63 +const EVENT_P2P_CMD_ENABLE_SCAN = 1005; 1.64 +const EVENT_P2P_CMD_DISABLE_SCAN = 1006; 1.65 +const EVENT_P2P_CMD_BLOCK_SCAN = 1007; 1.66 +const EVENT_P2P_CMD_UNBLOCK_SCAN = 1008; 1.67 + 1.68 +// Internal events. 1.69 +const EVENT_TIMEOUT_PAIRING_CONFIRMATION = 10000; 1.70 +const EVENT_TIMEOUT_NEG_REQ = 10001; 1.71 +const EVENT_TIMEOUT_CONNECTING = 10002; 1.72 +const EVENT_P2P_ENABLE_SUCCESS = 10003; 1.73 +const EVENT_P2P_ENABLE_FAILED = 10004; 1.74 +const EVENT_P2P_DISABLE_SUCCESS = 10005; 1.75 + 1.76 +// WPS method string. 1.77 +const WPS_METHOD_PBC = "pbc"; 1.78 +const WPS_METHOD_DISPLAY = "display"; 1.79 +const WPS_METHOD_KEYPAD = "keypad"; 1.80 + 1.81 +// Role string. 1.82 +const P2P_ROLE_GO = "GO"; 1.83 +const P2P_ROLE_CLIENT = "client"; 1.84 + 1.85 +// System message for pairing request. 1.86 +const PAIRING_REQUEST_SYS_MSG = "wifip2p-pairing-request"; 1.87 + 1.88 +// Configuration. 1.89 +const P2P_INTERFACE_NAME = "p2p0"; 1.90 +const DEFAULT_GO_INTENT = 15; 1.91 +const DEFAULT_P2P_DEVICE_NAME = "FirefoxPhone"; 1.92 +const P2P_SCAN_TIMEOUT_SEC = 120; 1.93 +const DEFAULT_P2P_WPS_METHODS = "virtual_push_button physical_display keypad"; // For wpa_supplicant. 1.94 +const DEFAULT_P2P_DEVICE_TYPE = "10-0050F204-5"; // For wpa_supplicant. 1.95 + 1.96 +const GO_NETWORK_INTERFACE = { 1.97 + ip: "192.168.2.1", 1.98 + maskLength: 24, 1.99 + gateway: "192.168.2.1", 1.100 + dns1: "0.0.0.0", 1.101 + dns2: "0.0.0.0", 1.102 + dhcpServer: "192.168.2.1" 1.103 +}; 1.104 + 1.105 +const GO_DHCP_SERVER_IP_RANGE = { 1.106 + startIp: "192.168.2.10", 1.107 + endIp: "192.168.2.30" 1.108 +}; 1.109 + 1.110 +let gDebug = false; 1.111 + 1.112 +// Device Capability bitmap 1.113 +const DEVICE_CAPAB_SERVICE_DISCOVERY = 1; 1.114 +const DEVICE_CAPAB_CLIENT_DISCOVERABILITY = 1<<1; 1.115 +const DEVICE_CAPAB_CONCURRENT_OPER = 1<<2; 1.116 +const DEVICE_CAPAB_INFRA_MANAGED = 1<<3; 1.117 +const DEVICE_CAPAB_DEVICE_LIMIT = 1<<4; 1.118 +const DEVICE_CAPAB_INVITATION_PROCEDURE = 1<<5; 1.119 + 1.120 +// Group Capability bitmap 1.121 +const GROUP_CAPAB_GROUP_OWNER = 1; 1.122 +const GROUP_CAPAB_PERSISTENT_GROUP = 1<<1; 1.123 +const GROUP_CAPAB_GROUP_LIMIT = 1<<2; 1.124 +const GROUP_CAPAB_INTRA_BSS_DIST = 1<<3; 1.125 +const GROUP_CAPAB_CROSS_CONN = 1<<4; 1.126 +const GROUP_CAPAB_PERSISTENT_RECONN = 1<<5; 1.127 +const GROUP_CAPAB_GROUP_FORMATION = 1<<6; 1.128 + 1.129 +// Constants defined in wpa_supplicants. 1.130 +const DEV_PW_REGISTRAR_SPECIFIED = 5; 1.131 +const DEV_PW_USER_SPECIFIED = 1; 1.132 +const DEV_PW_PUSHBUTTON = 4; 1.133 + 1.134 +this.WifiP2pManager = function (aP2pCommand, aNetUtil) { 1.135 + function debug(aMsg) { 1.136 + if (gDebug) { 1.137 + dump('-------------- WifiP2pManager: ' + aMsg); 1.138 + } 1.139 + } 1.140 + 1.141 + let manager = {}; 1.142 + 1.143 + let _stateMachine = P2pStateMachine(aP2pCommand, aNetUtil); 1.144 + 1.145 + // Set debug flag to true or false. 1.146 + // 1.147 + // @param aDebug Boolean to indicate enabling or disabling the debug flag. 1.148 + manager.setDebug = function(aDebug) { 1.149 + gDebug = aDebug; 1.150 + }; 1.151 + 1.152 + // Set observer of observing internal state machine events. 1.153 + // 1.154 + // @param aObserver Used to notify WifiWorker what's happening 1.155 + // in the internal p2p state machine. 1.156 + manager.setObserver = function(aObserver) { 1.157 + _stateMachine.setObserver(aObserver); 1.158 + }; 1.159 + 1.160 + // Handle wpa_supplicant events. 1.161 + // 1.162 + // @param aEventString string from wpa_supplicant. 1.163 + manager.handleEvent = function(aEventString) { 1.164 + let event = parseEventString(aEventString); 1.165 + if (EVENT_UNKNOWN === event.id || EVENT_IGNORED === event.id) { 1.166 + debug('Unknow or ignored event: ' + aEventString); 1.167 + return false; 1.168 + } 1.169 + return _stateMachine.sendEvent(event); 1.170 + }; 1.171 + 1.172 + // Set the confirmation of pairing request. 1.173 + // 1.174 + // @param aResult Object of confirmation result which contains: 1.175 + // .accepted: user granted. 1.176 + // .pin: pin code which is displaying or input by user. 1.177 + // .wpsMethod: string of "pbc" or "display" or "keypad". 1.178 + manager.setPairingConfirmation = function(aResult) { 1.179 + let event = { 1.180 + id: EVENT_P2P_SET_PAIRING_CONFIRMATION, 1.181 + info: { 1.182 + accepted: aResult.accepted, 1.183 + pin: aResult.pin 1.184 + } 1.185 + }; 1.186 + _stateMachine.sendEvent(event); 1.187 + }; 1.188 + 1.189 + // Connect to a known peer. 1.190 + // 1.191 + // @param aAddress MAC address of the peer to connect. 1.192 + // @param aWpsMethod String of "pbc" or "display" or "keypad". 1.193 + // @param aGoIntent Number from 0 to 15. 1.194 + // @param aCallback Callback |true| on attempting to connect. 1.195 + // |false| on failed to connect. 1.196 + manager.connect = function(aAddress, aWpsMethod, aGoIntent, aCallback) { 1.197 + let event = { 1.198 + id: EVENT_P2P_CMD_CONNECT, 1.199 + info: { 1.200 + wpsMethod: aWpsMethod, 1.201 + address: aAddress, 1.202 + goIntent: aGoIntent, 1.203 + onDoConnect: aCallback 1.204 + } 1.205 + }; 1.206 + _stateMachine.sendEvent(event); 1.207 + }; 1.208 + 1.209 + // Disconnect with a known peer. 1.210 + // 1.211 + // @param aAddress The address the user desires to disconect. 1.212 + // @param aCallback Callback |true| on "attempting" to disconnect. 1.213 + // |false| on failed to disconnect. 1.214 + manager.disconnect = function(aAddress, aCallback) { 1.215 + let event = { 1.216 + id: EVENT_P2P_CMD_DISCONNECT, 1.217 + info: { 1.218 + address: aAddress, 1.219 + onDoDisconnect: aCallback 1.220 + } 1.221 + }; 1.222 + _stateMachine.sendEvent(event); 1.223 + }; 1.224 + 1.225 + // Enable/disable wifi p2p. 1.226 + // 1.227 + // @param aEnabled |true| to enable, |false| to disable. 1.228 + // @param aCallbacks object for callbacks: 1.229 + // .onEnabled 1.230 + // .onDisabled 1.231 + // .onSupplicantConnected 1.232 + manager.setEnabled = function(aEnabled, aCallbacks) { 1.233 + let event = { 1.234 + id: (aEnabled ? EVENT_P2P_CMD_ENABLE : EVENT_P2P_CMD_DISABLE), 1.235 + info: { 1.236 + onEnabled: aCallbacks.onEnabled, 1.237 + onDisabled: aCallbacks.onDisabled, 1.238 + onSupplicantConnected: aCallbacks.onSupplicantConnected 1.239 + } 1.240 + }; 1.241 + _stateMachine.sendEvent(event); 1.242 + }; 1.243 + 1.244 + // Enable/disable the wifi p2p scan. 1.245 + // 1.246 + // @param aEnabled |true| to enable scan, |false| to disable scan. 1.247 + // @param aCallback Callback |true| on success to enable/disable scan. 1.248 + // |false| on failed to enable/disable scan. 1.249 + manager.setScanEnabled = function(aEnabled, aCallback) { 1.250 + let event = { 1.251 + id: (aEnabled ? EVENT_P2P_CMD_ENABLE_SCAN : EVENT_P2P_CMD_DISABLE_SCAN), 1.252 + info: { callback: aCallback } 1.253 + }; 1.254 + _stateMachine.sendEvent(event); 1.255 + }; 1.256 + 1.257 + // Block wifi p2p scan. 1.258 + manager.blockScan = function() { 1.259 + _stateMachine.sendEvent({ id: EVENT_P2P_CMD_BLOCK_SCAN }); 1.260 + }; 1.261 + 1.262 + // Un-block and do the pending scan if any. 1.263 + manager.unblockScan = function() { 1.264 + _stateMachine.sendEvent({ id: EVENT_P2P_CMD_UNBLOCK_SCAN }); 1.265 + }; 1.266 + 1.267 + // Set the p2p device name. 1.268 + manager.setDeviceName = function(newDeivceName, callback) { 1.269 + aP2pCommand.setDeviceName(newDeivceName, callback); 1.270 + }; 1.271 + 1.272 + // Parse wps_supplicant event string. 1.273 + // 1.274 + // @param aEventString The raw event string from wpa_supplicant. 1.275 + // 1.276 + // @return Object: 1.277 + // .id: a number to represent an event. 1.278 + // .info: the additional information carried by this event string. 1.279 + function parseEventString(aEventString) { 1.280 + if (isIgnoredEvent(aEventString)) { 1.281 + return { id: EVENT_IGNORED }; 1.282 + } 1.283 + 1.284 + let match = RegExp("p2p_dev_addr=([0-9a-fA-F:]+) " + 1.285 + "pri_dev_type=([0-9a-zA-Z-]+) " + 1.286 + "name='(.*)' " + 1.287 + "config_methods=0x([0-9a-fA-F]+) " + 1.288 + "dev_capab=0x([0-9a-fA-F]+) " + 1.289 + "group_capab=0x([0-9a-fA-F]+) ").exec(aEventString + ' '); 1.290 + 1.291 + let tokens = aEventString.split(" "); 1.292 + 1.293 + let id = EVENT_UNKNOWN; 1.294 + 1.295 + // general info. 1.296 + let info = {}; 1.297 + 1.298 + if (match) { 1.299 + info = { 1.300 + address: match[1] ? match[1] : null, 1.301 + type: match[2] ? match[2] : null, 1.302 + name: match[3] ? match[3] : null, 1.303 + wpsFlag: match[4] ? parseInt(match[4], 16) : null, 1.304 + devFlag: match[5] ? parseInt(match[5], 16) : null, 1.305 + groupFlag: match[6] ? parseInt(match[6], 16) : null 1.306 + }; 1.307 + } 1.308 + 1.309 + if (0 === aEventString.indexOf("P2P-DEVICE-FOUND")) { 1.310 + id = EVENT_P2P_DEVICE_FOUND; 1.311 + info.wpsCapabilities = wpsFlagToCapabilities(info.wpsFlag); 1.312 + info.isGroupOwner = isPeerGroupOwner(info.groupFlag); 1.313 + } else if (0 === aEventString.indexOf("P2P-DEVICE-LOST")) { 1.314 + // e.g. "P2P-DEVICE-LOST p2p_dev_addr=5e:0a:5b:15:1f:80". 1.315 + id = EVENT_P2P_DEVICE_LOST; 1.316 + info.address = /p2p_dev_addr=([0-9a-f:]+)/.exec(aEventString)[1]; 1.317 + } else if (0 === aEventString.indexOf("P2P-GROUP-STARTED")) { 1.318 + // e.g. "P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F Testing 1.319 + // passphrase="12345678" go_dev_addr=02:40:61:c2:f3:b7 [PERSISTENT]". 1.320 + 1.321 + id = EVENT_P2P_GROUP_STARTED; 1.322 + let groupMatch = RegExp('ssid="(.*)" ' + 1.323 + 'freq=([0-9]*) ' + 1.324 + '(passphrase|psk)=([^ ]+) ' + 1.325 + 'go_dev_addr=([0-9a-f:]+)').exec(aEventString); 1.326 + info.ssid = groupMatch[1]; 1.327 + info.freq = groupMatch[2]; 1.328 + if ('passphrase' === groupMatch[3]) { 1.329 + let s = groupMatch[4]; // e.g. "G7jHkkz9". 1.330 + info.passphrase = s.substring(1, s.length-1); // Trim the double quote. 1.331 + } else { // psk 1.332 + info.psk = groupMatch[4]; 1.333 + } 1.334 + info.goAddress = groupMatch[5]; 1.335 + info.ifname = tokens[1]; 1.336 + info.role = tokens[2]; 1.337 + } else if (0 === aEventString.indexOf("P2P-GROUP-REMOVED")) { 1.338 + id = EVENT_P2P_GROUP_REMOVED; 1.339 + // e.g. "P2P-GROUP-REMOVED wlan0-p2p-0 GO". 1.340 + info.ifname = tokens[1]; 1.341 + info.role = tokens[2]; 1.342 + } else if (0 === aEventString.indexOf("P2P-PROV-DISC-PBC-REQ")) { 1.343 + id = EVENT_P2P_PROV_DISC_PBC_REQ; 1.344 + info.wpsMethod = WPS_METHOD_PBC; 1.345 + } else if (0 === aEventString.indexOf("P2P-PROV-DISC-PBC-RESP")) { 1.346 + id = EVENT_P2P_PROV_DISC_PBC_RESP; 1.347 + // The address is different from the general pattern. 1.348 + info.address = aEventString.split(" ")[1]; 1.349 + info.wpsMethod = WPS_METHOD_PBC; 1.350 + } else if (0 === aEventString.indexOf("P2P-PROV-DISC-SHOW-PIN")) { 1.351 + id = EVENT_P2P_PROV_DISC_SHOW_PIN; 1.352 + // Obtain peer address and pin from tokens. 1.353 + info.address = tokens[1]; 1.354 + info.pin = tokens[2]; 1.355 + info.wpsMethod = WPS_METHOD_DISPLAY; 1.356 + } else if (0 === aEventString.indexOf("P2P-PROV-DISC-ENTER-PIN")) { 1.357 + id = EVENT_P2P_PROV_DISC_ENTER_PIN; 1.358 + // Obtain peer address from tokens. 1.359 + info.address = tokens[1]; 1.360 + info.wpsMethod = WPS_METHOD_KEYPAD; 1.361 + } else if (0 === aEventString.indexOf("P2P-GO-NEG-REQUEST")) { 1.362 + id = EVENT_P2P_GO_NEG_REQUEST; 1.363 + info.address = tokens[1]; 1.364 + switch (parseInt(tokens[2].split("=")[1], 10)) { 1.365 + case DEV_PW_REGISTRAR_SPECIFIED: // (5) Peer is display. 1.366 + info.wpsMethod = WPS_METHOD_KEYPAD; 1.367 + break; 1.368 + case DEV_PW_USER_SPECIFIED: // (1) Peer is keypad. 1.369 + info.wpsMethod = WPS_METHOD_DISPLAY; 1.370 + break; 1.371 + case DEV_PW_PUSHBUTTON: // (4) Peer is pbc. 1.372 + info.wpsMethod = WPS_METHOD_PBC; 1.373 + break; 1.374 + default: 1.375 + debug('Unknown wps method from event P2P-GO-NEG-REQUEST'); 1.376 + break; 1.377 + } 1.378 + } else if (0 === aEventString.indexOf("P2P-GO-NEG-SUCCESS")) { 1.379 + id = EVENT_P2P_GO_NEG_SUCCESS; 1.380 + } else if (0 === aEventString.indexOf("P2P-GO-NEG-FAILURE")) { 1.381 + id = EVENT_P2P_GO_NEG_FAILURE; 1.382 + } else if (0 === aEventString.indexOf("P2P-GROUP-FORMATION-FAILURE")) { 1.383 + id = EVENT_P2P_GROUP_FORMATION_FAILURE; 1.384 + } else if (0 === aEventString.indexOf("P2P-GROUP-FORMATION-SUCCESS")) { 1.385 + id = EVENT_P2P_GROUP_FORMATION_SUCCESS; 1.386 + } else if (0 === aEventString.indexOf("P2P-FIND-STOPPED")) { 1.387 + id = EVENT_P2P_FIND_STOPPED; 1.388 + } else if (0 === aEventString.indexOf("P2P-INVITATION-RESULT")) { 1.389 + id = EVENT_P2P_INVITATION_RESULT; 1.390 + info.status = /status=([0-9]+)/.exec(aEventString)[1]; 1.391 + } else if (0 === aEventString.indexOf("P2P-INVITATION-RECEIVED")) { 1.392 + // e.g. "P2P-INVITATION-RECEIVED sa=32:85:a9:da:e6:1f persistent=7". 1.393 + id = EVENT_P2P_INVITATION_RECEIVED; 1.394 + info.address = /sa=([0-9a-f:]+)/.exec(aEventString)[1]; 1.395 + info.netId = /persistent=([0-9]+)/.exec(aEventString)[1]; 1.396 + } else if (0 === aEventString.indexOf("P2P-PROV-DISC-FAILURE")) { 1.397 + id = EVENT_P2P_PROV_DISC_FAILURE; 1.398 + } else { 1.399 + // Not P2P event but we do receive it. Try to recognize it. 1.400 + if (0 === aEventString.indexOf("AP-STA-DISCONNECTED")) { 1.401 + id = EVENT_AP_STA_DISCONNECTED; 1.402 + info.address = tokens[1]; 1.403 + } else if (0 === aEventString.indexOf("AP-STA-CONNECTED")) { 1.404 + id = EVENT_AP_STA_CONNECTED; 1.405 + info.address = tokens[1]; 1.406 + } else { 1.407 + // Neither P2P event nor recognized supplicant event. 1.408 + debug('Unknwon event string: ' + aEventString); 1.409 + } 1.410 + } 1.411 + 1.412 + let event = {id: id, info: info}; 1.413 + debug('Event parsing result: ' + aEventString + ": " + JSON.stringify(event)); 1.414 + 1.415 + return event; 1.416 + } 1.417 + 1.418 + function isIgnoredEvent(aEventString) { 1.419 + const IGNORED_EVENTS = [ 1.420 + "CTRL-EVENT-BSS-ADDED", 1.421 + "CTRL-EVENT-BSS-REMOVED", 1.422 + "CTRL-EVENT-SCAN-RESULTS", 1.423 + "CTRL-EVENT-STATE-CHANGE", 1.424 + "WPS-AP-AVAILABLE", 1.425 + "WPS-ENROLLEE-SEEN" 1.426 + ]; 1.427 + for(let i = 0; i < IGNORED_EVENTS.length; i++) { 1.428 + if (0 === aEventString.indexOf(IGNORED_EVENTS[i])) { 1.429 + return true; 1.430 + } 1.431 + } 1.432 + return false; 1.433 + } 1.434 + 1.435 + function isPeerGroupOwner(aGroupFlag) { 1.436 + return (aGroupFlag & GROUP_CAPAB_GROUP_OWNER) !== 0; 1.437 + } 1.438 + 1.439 + // Convert flag to a wps capability array. 1.440 + // 1.441 + // @param aWpsFlag Number that represents the wps capabilities. 1.442 + // @return Array of WPS flag. 1.443 + function wpsFlagToCapabilities(aWpsFlag) { 1.444 + let wpsCapabilities = []; 1.445 + if (aWpsFlag & 0x8) { 1.446 + wpsCapabilities.push(WPS_METHOD_DISPLAY); 1.447 + } 1.448 + if (aWpsFlag & 0x80) { 1.449 + wpsCapabilities.push(WPS_METHOD_PBC); 1.450 + } 1.451 + if (aWpsFlag & 0x100) { 1.452 + wpsCapabilities.push(WPS_METHOD_KEYPAD); 1.453 + } 1.454 + return wpsCapabilities; 1.455 + } 1.456 + 1.457 + _stateMachine.start(); 1.458 + return manager; 1.459 +}; 1.460 + 1.461 +function P2pStateMachine(aP2pCommand, aNetUtil) { 1.462 + function debug(aMsg) { 1.463 + if (gDebug) { 1.464 + dump('-------------- WifiP2pStateMachine: ' + aMsg); 1.465 + } 1.466 + } 1.467 + 1.468 + let p2pSm = {}; // The state machine to return. 1.469 + 1.470 + let _sm = StateMachine('WIFIP2P'); // The general purpose state machine. 1.471 + 1.472 + // Information we need to keep track across states. 1.473 + let _observer; 1.474 + 1.475 + let _onEnabled; 1.476 + let _onDisabled; 1.477 + let _onSupplicantConnected; 1.478 + let _savedConfig = {}; // Configuration used to do P2P_CONNECT. 1.479 + let _groupInfo = {}; // The information of the group we have formed. 1.480 + let _removedGroupInfo = {}; // Used to store the group info we are going to remove. 1.481 + 1.482 + let _scanBlocked = false; 1.483 + let _scanPostponded = false; 1.484 + 1.485 + let _localDevice = { 1.486 + address: "", 1.487 + deviceName: DEFAULT_P2P_DEVICE_NAME + "_" + libcutils.property_get("ro.build.product"), 1.488 + wpsCapabilities: [WPS_METHOD_PBC, WPS_METHOD_KEYPAD, WPS_METHOD_DISPLAY] 1.489 + }; 1.490 + 1.491 + let _p2pNetworkInterface = { 1.492 + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]), 1.493 + 1.494 + state: Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED, 1.495 + type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI_P2P, 1.496 + name: P2P_INTERFACE_NAME, 1.497 + ips: [], 1.498 + prefixLengths: [], 1.499 + dnses: [], 1.500 + gateways: [], 1.501 + httpProxyHost: null, 1.502 + httpProxyPort: null, 1.503 + 1.504 + // help 1.505 + registered: false, 1.506 + 1.507 + getAddresses: function (ips, prefixLengths) { 1.508 + ips.value = this.ips.slice(); 1.509 + prefixLengths.value = this.prefixLengths.slice(); 1.510 + 1.511 + return this.ips.length; 1.512 + }, 1.513 + 1.514 + getGateways: function (count) { 1.515 + if (count) { 1.516 + count.value = this.gateways.length; 1.517 + } 1.518 + return this.gateways.slice(); 1.519 + }, 1.520 + 1.521 + getDnses: function (count) { 1.522 + if (count) { 1.523 + count.value = this.dnses.length; 1.524 + } 1.525 + return this.dnses.slice(); 1.526 + } 1.527 + }; 1.528 + 1.529 + //--------------------------------------------------------- 1.530 + // State machine APIs. 1.531 + //--------------------------------------------------------- 1.532 + 1.533 + // Register the observer which is implemented in WifiP2pWorkerObserver.jsm. 1.534 + // 1.535 + // @param aObserver: 1.536 + // .onEnabled 1.537 + // .onDisbaled 1.538 + // .onPeerFound 1.539 + // .onPeerLost 1.540 + // .onConnecting 1.541 + // .onConnected 1.542 + // .onDisconnected 1.543 + // .onLocalDeviceChanged 1.544 + p2pSm.setObserver = function(aObserver) { 1.545 + _observer = aObserver; 1.546 + }; 1.547 + 1.548 + p2pSm.start = function() { 1.549 + _sm.start(stateDisabled); 1.550 + }; 1.551 + 1.552 + p2pSm.sendEvent = function(aEvent) { 1.553 + let willBeHandled = isInP2pManagedState(_sm.getCurrentState()); 1.554 + _sm.sendEvent(aEvent); 1.555 + return willBeHandled; 1.556 + }; 1.557 + 1.558 + // Initialize internal state machine _sm. 1.559 + _sm.setDefaultEventHandler(handleEventCommon); 1.560 + 1.561 + //---------------------------------------------------------- 1.562 + // State definition. 1.563 + //---------------------------------------------------------- 1.564 + 1.565 + // The initial state. 1.566 + var stateDisabled = _sm.makeState("DISABLED", { 1.567 + enter: function() { 1.568 + _onEnabled = null; 1.569 + _onSupplicantConnected = null; 1.570 + _savedConfig = null; 1.571 + _groupInfo = null; 1.572 + _removedGroupInfo = null; 1.573 + _scanBlocked = false; 1.574 + _scanPostponded = false; 1.575 + 1.576 + unregisterP2pNetworkInteface(); 1.577 + }, 1.578 + 1.579 + handleEvent: function(aEvent) { 1.580 + switch (aEvent.id) { 1.581 + case EVENT_P2P_CMD_ENABLE: 1.582 + _onEnabled = aEvent.info.onEnabled; 1.583 + _onSupplicantConnected = aEvent.info.onSupplicantConnected; 1.584 + _sm.gotoState(stateEnabling); 1.585 + break; 1.586 + 1.587 + default: 1.588 + return false; 1.589 + } // End of switch. 1.590 + return true; 1.591 + } 1.592 + }); 1.593 + 1.594 + // The state where we are trying to enable wifi p2p. 1.595 + var stateEnabling = _sm.makeState("ENABLING", { 1.596 + enter: function() { 1.597 + 1.598 + function onFailure() 1.599 + { 1.600 + _onEnabled(false); 1.601 + _sm.gotoState(stateDisabled); 1.602 + } 1.603 + 1.604 + function onSuccess() 1.605 + { 1.606 + _onEnabled(true); 1.607 + _sm.gotoState(stateInactive); 1.608 + } 1.609 + 1.610 + _sm.pause(); 1.611 + 1.612 + // Step 1: Connect to p2p0. 1.613 + aP2pCommand.connectToSupplicant(function (status) { 1.614 + let detail; 1.615 + 1.616 + if (0 !== status) { 1.617 + debug('Failed to connect to p2p0'); 1.618 + onFailure(); 1.619 + return; 1.620 + } 1.621 + 1.622 + debug('wpa_supplicant p2p0 connected!'); 1.623 + _onSupplicantConnected(); 1.624 + 1.625 + // Step 2: Get MAC address. 1.626 + if (!_localDevice.address) { 1.627 + aP2pCommand.getMacAddress(function (address) { 1.628 + if (!address) { 1.629 + debug('Failed to get MAC address....'); 1.630 + onFailure(); 1.631 + return; 1.632 + } 1.633 + debug('Got mac address: ' + address); 1.634 + _localDevice.address = address; 1.635 + _observer.onLocalDeviceChanged(_localDevice); 1.636 + }); 1.637 + } 1.638 + 1.639 + // Step 3: Enable p2p with the device name and wps methods. 1.640 + detail = { deviceName: _localDevice.deviceName, 1.641 + deviceType: libcutils.property_get("ro.moz.wifi.p2p_device_type") || DEFAULT_P2P_DEVICE_TYPE, 1.642 + wpsMethods: libcutils.property_get("ro.moz.wifi.p2p_wps_methods") || DEFAULT_P2P_WPS_METHODS }; 1.643 + 1.644 + aP2pCommand.p2pEnable(detail, function (success) { 1.645 + if (!success) { 1.646 + debug('Failed to enable p2p'); 1.647 + onFailure(); 1.648 + return; 1.649 + } 1.650 + 1.651 + debug('P2P is enabled! Enabling net interface...'); 1.652 + 1.653 + // Step 4: Enable p2p0 net interface. wpa_supplicant may have 1.654 + // already done it for us. 1.655 + aNetUtil.enableInterface(P2P_INTERFACE_NAME, function (success) { 1.656 + onSuccess(); 1.657 + }); 1.658 + }); 1.659 + }); 1.660 + }, 1.661 + 1.662 + handleEvent: function(aEvent) { 1.663 + // We won't receive any event since all of them will be blocked. 1.664 + return true; 1.665 + } 1.666 + }); 1.667 + 1.668 + // The state just after enabling wifi direct. 1.669 + var stateInactive = _sm.makeState("INACTIVE", { 1.670 + enter: function() { 1.671 + registerP2pNetworkInteface(); 1.672 + 1.673 + if (_sm.getPreviousState() !== stateEnabling) { 1.674 + _observer.onDisconnected(_savedConfig); 1.675 + } 1.676 + 1.677 + _savedConfig = null; // Used to connect p2p peer. 1.678 + _groupInfo = null; // The information of the formed group. 1.679 + }, 1.680 + 1.681 + handleEvent: function(aEvent) { 1.682 + switch (aEvent.id) { 1.683 + // Receiving the following 3 states implies someone is trying to 1.684 + // connect to me. 1.685 + case EVENT_P2P_PROV_DISC_PBC_REQ: 1.686 + case EVENT_P2P_PROV_DISC_SHOW_PIN: 1.687 + case EVENT_P2P_PROV_DISC_ENTER_PIN: 1.688 + debug('Someone is trying to connect to me: ' + JSON.stringify(aEvent.info)); 1.689 + 1.690 + _savedConfig = { 1.691 + name: aEvent.info.name, 1.692 + address: aEvent.info.address, 1.693 + wpsMethod: aEvent.info.wpsMethod, 1.694 + goIntent: DEFAULT_GO_INTENT, 1.695 + pin: aEvent.info.pin // EVENT_P2P_PROV_DISC_SHOW_PIN only. 1.696 + }; 1.697 + 1.698 + _sm.gotoState(stateWaitingForConfirmation); 1.699 + break; 1.700 + 1.701 + // Connect to a peer. 1.702 + case EVENT_P2P_CMD_CONNECT: 1.703 + debug('Trying to connect to peer: ' + JSON.stringify(aEvent.info)); 1.704 + 1.705 + _savedConfig = { 1.706 + address: aEvent.info.address, 1.707 + wpsMethod: aEvent.info.wpsMethod, 1.708 + goIntent: aEvent.info.goIntent 1.709 + }; 1.710 + 1.711 + _sm.gotoState(stateProvisionDiscovery); 1.712 + aEvent.info.onDoConnect(true); 1.713 + break; 1.714 + 1.715 + case EVENT_P2P_INVITATION_RECEIVED: 1.716 + _savedConfig = { 1.717 + address: aEvent.info.address, 1.718 + wpsMethod: WPS_METHOD_PBC, 1.719 + goIntent: DEFAULT_GO_INTENT, 1.720 + netId: aEvent.info.netId 1.721 + }; 1.722 + _sm.gotoState(stateWaitingForInvitationConfirmation); 1.723 + break; 1.724 + 1.725 + case EVENT_P2P_GROUP_STARTED: 1.726 + // Most likely the peer just reinvoked a peristen group and succeeeded. 1.727 + 1.728 + _savedConfig = { address: aEvent.info.goAddress }; 1.729 + 1.730 + _sm.pause(); 1.731 + handleGroupStarted(aEvent.info, function (success) { 1.732 + _sm.resume(); 1.733 + }); 1.734 + break; 1.735 + 1.736 + case EVENT_AP_STA_DISCONNECTED: 1.737 + // We will hit this case when we used to be a group owner and 1.738 + // requested to remove the group we owned. 1.739 + break; 1.740 + 1.741 + default: 1.742 + return false; 1.743 + } // End of switch. 1.744 + return true; 1.745 + }, 1.746 + }); 1.747 + 1.748 + // Waiting for user's confirmation. 1.749 + var stateWaitingForConfirmation = _sm.makeState("WAITING_FOR_CONFIRMATION", { 1.750 + timeoutTimer: null, 1.751 + 1.752 + enter: function() { 1.753 + gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); 1.754 + this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); 1.755 + }, 1.756 + 1.757 + handleEvent: function(aEvent) { 1.758 + switch (aEvent.id) { 1.759 + case EVENT_P2P_SET_PAIRING_CONFIRMATION: 1.760 + if (!aEvent.info.accepted) { 1.761 + debug('User rejected this request'); 1.762 + _sm.gotoState(stateInactive); // Reset to inactive state. 1.763 + break; 1.764 + } 1.765 + 1.766 + debug('User accepted this request'); 1.767 + 1.768 + // The only information we may have to grab from user. 1.769 + _savedConfig.pin = aEvent.info.pin; 1.770 + 1.771 + // The case that user requested to form a group ealier on. 1.772 + // Just go to connecting state and do p2p_connect. 1.773 + if (_sm.getPreviousState() === stateProvisionDiscovery) { 1.774 + _sm.gotoState(stateConnecting); 1.775 + break; 1.776 + } 1.777 + 1.778 + // Otherwise, wait for EVENT_P2P_GO_NEG_REQUEST. 1.779 + _sm.gotoState(stateWaitingForNegReq); 1.780 + break; 1.781 + 1.782 + case EVENT_TIMEOUT_PAIRING_CONFIRMATION: 1.783 + debug('Confirmation timeout!'); 1.784 + _sm.gotoState(stateInactive); 1.785 + break; 1.786 + 1.787 + case EVENT_P2P_GO_NEG_REQUEST: 1.788 + _sm.deferEvent(aEvent); 1.789 + break; 1.790 + 1.791 + default: 1.792 + return false; 1.793 + } // End of switch. 1.794 + 1.795 + return true; 1.796 + }, 1.797 + 1.798 + exit: function() { 1.799 + this.timeoutTimer.cancel(); 1.800 + this.timeoutTimer = null; 1.801 + } 1.802 + }); 1.803 + 1.804 + var stateWaitingForNegReq = _sm.makeState("WAITING_FOR_NEG_REQ", { 1.805 + timeoutTimer: null, 1.806 + 1.807 + enter: function() { 1.808 + debug('Wait for EVENT_P2P_GO_NEG_REQUEST'); 1.809 + this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_NEG_REQ); 1.810 + }, 1.811 + 1.812 + handleEvent: function(aEvent) { 1.813 + switch (aEvent.id) { 1.814 + case EVENT_P2P_GO_NEG_REQUEST: 1.815 + if (aEvent.info.wpsMethod !== _savedConfig.wpsMethod) { 1.816 + debug('Unmatched wps method: ' + aEvent.info.wpsMethod + ", " + _savedConfig.wpsMetho); 1.817 + } 1.818 + _sm.gotoState(stateConnecting); 1.819 + break; 1.820 + 1.821 + case EVENT_TIMEOUT_NEG_REQ: 1.822 + debug("Waiting for NEG-REQ timeout"); 1.823 + _sm.gotoState(stateInactive); 1.824 + break; 1.825 + 1.826 + default: 1.827 + return false; 1.828 + } // End of switch. 1.829 + return true; 1.830 + }, 1.831 + 1.832 + exit: function() { 1.833 + this.timeoutTimer.cancel(); 1.834 + this.timeoutTimer = null; 1.835 + } 1.836 + }); 1.837 + 1.838 + // Waiting for user's confirmation for invitation. 1.839 + var stateWaitingForInvitationConfirmation = _sm.makeState("WAITING_FOR_INV_CONFIRMATION", { 1.840 + timeoutTimer: null, 1.841 + 1.842 + enter: function() { 1.843 + gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); 1.844 + this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); 1.845 + }, 1.846 + 1.847 + handleEvent: function(aEvent) { 1.848 + switch (aEvent.id) { 1.849 + case EVENT_P2P_SET_PAIRING_CONFIRMATION: 1.850 + if (!aEvent.info.accepted) { 1.851 + debug('User rejected this request'); 1.852 + _sm.gotoState(stateInactive); // Reset to inactive state. 1.853 + break; 1.854 + } 1.855 + 1.856 + debug('User accepted this request'); 1.857 + _sm.pause(); 1.858 + aP2pCommand.p2pGetGroupCapab(_savedConfig.address, function (gc) { 1.859 + let isPeeGroupOwner = gc & GROUP_CAPAB_GROUP_OWNER; 1.860 + _sm.gotoState(isPeeGroupOwner ? stateGroupAdding : stateReinvoking); 1.861 + }); 1.862 + 1.863 + break; 1.864 + 1.865 + case EVENT_TIMEOUT_PAIRING_CONFIRMATION: 1.866 + debug('Confirmation timeout!'); 1.867 + _sm.gotoState(stateInactive); 1.868 + break; 1.869 + 1.870 + default: 1.871 + return false; 1.872 + } // End of switch. 1.873 + 1.874 + return true; 1.875 + }, 1.876 + 1.877 + exit: function() { 1.878 + this.timeoutTimer.cancel(); 1.879 + this.timeoutTimer = null; 1.880 + } 1.881 + }); 1.882 + 1.883 + var stateGroupAdding = _sm.makeState("GROUP_ADDING", { 1.884 + timeoutTimer: null, 1.885 + 1.886 + enter: function() { 1.887 + let self = this; 1.888 + 1.889 + _observer.onConnecting(_savedConfig); 1.890 + 1.891 + _sm.pause(); 1.892 + aP2pCommand.p2pGroupAdd(_savedConfig.netId, function (success) { 1.893 + if (!success) { 1.894 + _sm.gotoState(stateInactive); 1.895 + return; 1.896 + } 1.897 + // Waiting for EVENT_P2P_GROUP_STARTED. 1.898 + self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); 1.899 + _sm.resume(); 1.900 + }); 1.901 + }, 1.902 + 1.903 + handleEvent: function(aEvent) { 1.904 + switch (aEvent.id) { 1.905 + case EVENT_P2P_GROUP_STARTED: 1.906 + _sm.pause(); 1.907 + handleGroupStarted(aEvent.info, function (success) { 1.908 + _sm.resume(); 1.909 + }); 1.910 + break; 1.911 + 1.912 + case EVENT_P2P_GO_NEG_FAILURE: 1.913 + debug('Negotiation failure. Go back to inactive state'); 1.914 + _sm.gotoState(stateInactive); 1.915 + break; 1.916 + 1.917 + case EVENT_TIMEOUT_CONNECTING: 1.918 + debug('Connecting timeout! Go back to inactive state'); 1.919 + _sm.gotoState(stateInactive); 1.920 + break; 1.921 + 1.922 + case EVENT_P2P_GROUP_FORMATION_SUCCESS: 1.923 + case EVENT_P2P_GO_NEG_SUCCESS: 1.924 + break; 1.925 + 1.926 + case EVENT_P2P_GROUP_FORMATION_FAILURE: 1.927 + debug('Group formation failure'); 1.928 + _sm.gotoState(stateInactive); 1.929 + break; 1.930 + 1.931 + case EVENT_P2P_GROUP_REMOVED: 1.932 + debug('Received P2P-GROUP-REMOVED due to previous failed handleGroupdStarted()'); 1.933 + _removedGroupInfo = { 1.934 + role: aEvent.info.role, 1.935 + ifname: aEvent.info.ifname 1.936 + }; 1.937 + _sm.gotoState(stateDisconnecting); 1.938 + break; 1.939 + 1.940 + default: 1.941 + return false; 1.942 + } // End of switch. 1.943 + 1.944 + return true; 1.945 + }, 1.946 + 1.947 + exit: function() { 1.948 + this.timeoutTimer.cancel(); 1.949 + this.timeoutTimer = null; 1.950 + } 1.951 + }); 1.952 + 1.953 + var stateReinvoking = _sm.makeState("REINVOKING", { 1.954 + timeoutTimer: null, 1.955 + 1.956 + enter: function() { 1.957 + let self = this; 1.958 + 1.959 + _observer.onConnecting(_savedConfig); 1.960 + _sm.pause(); 1.961 + aP2pCommand.p2pReinvoke(_savedConfig.netId, _savedConfig.address, function(success) { 1.962 + if (!success) { 1.963 + _sm.gotoState(stateInactive); 1.964 + return; 1.965 + } 1.966 + // Waiting for EVENT_P2P_GROUP_STARTED. 1.967 + self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); 1.968 + _sm.resume(); 1.969 + }); 1.970 + }, 1.971 + 1.972 + handleEvent: function(aEvent) { 1.973 + switch (aEvent.id) { 1.974 + case EVENT_P2P_GROUP_STARTED: 1.975 + _sm.pause(); 1.976 + handleGroupStarted(aEvent.info, function(success) { 1.977 + _sm.resume(); 1.978 + }); 1.979 + break; 1.980 + 1.981 + case EVENT_P2P_GO_NEG_FAILURE: 1.982 + debug('Negotiation failure. Go back to inactive state'); 1.983 + _sm.gotoState(stateInactive); 1.984 + break; 1.985 + 1.986 + case EVENT_TIMEOUT_CONNECTING: 1.987 + debug('Connecting timeout! Go back to inactive state'); 1.988 + _sm.gotoState(stateInactive); 1.989 + break; 1.990 + 1.991 + case EVENT_P2P_GROUP_FORMATION_SUCCESS: 1.992 + case EVENT_P2P_GO_NEG_SUCCESS: 1.993 + break; 1.994 + 1.995 + case EVENT_P2P_GROUP_FORMATION_FAILURE: 1.996 + debug('Group formation failure'); 1.997 + _sm.gotoState(stateInactive); 1.998 + break; 1.999 + 1.1000 + case EVENT_P2P_GROUP_REMOVED: 1.1001 + debug('Received P2P-GROUP-REMOVED due to previous failed handleGroupdStarted()'); 1.1002 + _removedGroupInfo = { 1.1003 + role: aEvent.info.role, 1.1004 + ifname: aEvent.info.ifname 1.1005 + }; 1.1006 + _sm.gotoState(stateDisconnecting); 1.1007 + break; 1.1008 + 1.1009 + default: 1.1010 + return false; 1.1011 + } // End of switch. 1.1012 + 1.1013 + return true; 1.1014 + }, 1.1015 + 1.1016 + exit: function() { 1.1017 + this.timeoutTimer.cancel(); 1.1018 + } 1.1019 + }); 1.1020 + 1.1021 + var stateProvisionDiscovery = _sm.makeState("PROVISION_DISCOVERY", { 1.1022 + enter: function() { 1.1023 + function onDiscoveryCommandSent(success) { 1.1024 + if (!success) { 1.1025 + _sm.gotoState(stateInactive); 1.1026 + debug('Failed to send p2p_prov_disc. Go back to inactive state.'); 1.1027 + return; 1.1028 + } 1.1029 + 1.1030 + debug('p2p_prov_disc has been sent.'); 1.1031 + 1.1032 + _sm.resume(); 1.1033 + // Waiting for EVENT_P2P_PROV_DISC_PBC_RESP or 1.1034 + // EVENT_P2P_PROV_DISC_SHOW_PIN or 1.1035 + // EVENT_P2P_PROV_DISC_ENTER_PIN. 1.1036 + } 1.1037 + 1.1038 + _sm.pause(); 1.1039 + aP2pCommand.p2pProvDiscovery(_savedConfig.address, 1.1040 + toPeerWpsMethod(_savedConfig.wpsMethod), 1.1041 + onDiscoveryCommandSent); 1.1042 + }, 1.1043 + 1.1044 + handleEvent: function(aEvent) { 1.1045 + switch (aEvent.id) { 1.1046 + case EVENT_P2P_PROV_DISC_PBC_RESP: 1.1047 + _sm.gotoState(stateConnecting); // No need for local user grant. 1.1048 + break; 1.1049 + case EVENT_P2P_PROV_DISC_SHOW_PIN: 1.1050 + case EVENT_P2P_PROV_DISC_ENTER_PIN: 1.1051 + if (aEvent.info.wpsMethod !== _savedConfig.wpsMethod) { 1.1052 + debug('Unmatched wps method: ' + aEvent.info.wpsMethod + ":" + _savedConfig.wpsMethod); 1.1053 + } 1.1054 + if (EVENT_P2P_PROV_DISC_SHOW_PIN === aEvent.id) { 1.1055 + _savedConfig.pin = aEvent.info.pin; 1.1056 + } 1.1057 + _sm.gotoState(stateWaitingForConfirmation); 1.1058 + break; 1.1059 + 1.1060 + case EVENT_P2P_PROV_DISC_FAILURE: 1.1061 + _sm.gotoState(stateInactive); 1.1062 + break; 1.1063 + 1.1064 + default: 1.1065 + return false; 1.1066 + } // End of switch. 1.1067 + return true; 1.1068 + } 1.1069 + }); 1.1070 + 1.1071 + // We are going to connect to the peer. 1.1072 + // |_savedConfig| is supposed to have been filled properly. 1.1073 + var stateConnecting = _sm.makeState("CONNECTING", { 1.1074 + timeoutTimer: null, 1.1075 + 1.1076 + enter: function() { 1.1077 + let self = this; 1.1078 + 1.1079 + if (null === _savedConfig.goIntent) { 1.1080 + _savedConfig.goIntent = DEFAULT_GO_INTENT; 1.1081 + } 1.1082 + 1.1083 + _observer.onConnecting(_savedConfig); 1.1084 + 1.1085 + let wpsMethodWithPin; 1.1086 + if (WPS_METHOD_KEYPAD === _savedConfig.wpsMethod || 1.1087 + WPS_METHOD_DISPLAY === _savedConfig.wpsMethod) { 1.1088 + // e.g. '12345678 display or '12345678 keypad'. 1.1089 + wpsMethodWithPin = (_savedConfig.pin + ' ' + _savedConfig.wpsMethod); 1.1090 + } else { 1.1091 + // e.g. 'pbc'. 1.1092 + wpsMethodWithPin = _savedConfig.wpsMethod; 1.1093 + } 1.1094 + 1.1095 + _sm.pause(); 1.1096 + 1.1097 + aP2pCommand.p2pGetGroupCapab(_savedConfig.address, function(gc) { 1.1098 + debug('group capabilities of ' + _savedConfig.address + ': ' + gc); 1.1099 + 1.1100 + let isPeerGroupOwner = gc & GROUP_CAPAB_GROUP_OWNER; 1.1101 + let config = { address: _savedConfig.address, 1.1102 + wpsMethodWithPin: wpsMethodWithPin, 1.1103 + goIntent: _savedConfig.goIntent, 1.1104 + joinExistingGroup: isPeerGroupOwner }; 1.1105 + 1.1106 + aP2pCommand.p2pConnect(config, function (success) { 1.1107 + if (!success) { 1.1108 + debug('Failed to send p2p_connect'); 1.1109 + _sm.gotoState(stateInactive); 1.1110 + return; 1.1111 + } 1.1112 + debug('Waiting for EVENT_P2P_GROUP_STARTED.'); 1.1113 + self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); 1.1114 + _sm.resume(); 1.1115 + }); 1.1116 + }); 1.1117 + }, 1.1118 + 1.1119 + handleEvent: function(aEvent) { 1.1120 + switch (aEvent.id) { 1.1121 + case EVENT_P2P_GROUP_STARTED: 1.1122 + _sm.pause(); 1.1123 + handleGroupStarted(aEvent.info, function (success) { 1.1124 + _sm.resume(); 1.1125 + }); 1.1126 + break; 1.1127 + 1.1128 + case EVENT_P2P_GO_NEG_FAILURE: 1.1129 + debug('Negotiation failure. Go back to inactive state'); 1.1130 + _sm.gotoState(stateInactive); 1.1131 + break; 1.1132 + 1.1133 + case EVENT_TIMEOUT_CONNECTING: 1.1134 + debug('Connecting timeout! Go back to inactive state'); 1.1135 + _sm.gotoState(stateInactive); 1.1136 + break; 1.1137 + 1.1138 + case EVENT_P2P_GROUP_FORMATION_SUCCESS: 1.1139 + case EVENT_P2P_GO_NEG_SUCCESS: 1.1140 + break; 1.1141 + 1.1142 + case EVENT_P2P_GROUP_FORMATION_FAILURE: 1.1143 + debug('Group formation failure'); 1.1144 + _sm.gotoState(stateInactive); 1.1145 + break; 1.1146 + 1.1147 + case EVENT_P2P_GROUP_REMOVED: 1.1148 + debug('Received P2P-GROUP-REMOVED due to previous failed ' + 1.1149 + 'handleGroupdStarted()'); 1.1150 + _removedGroupInfo = { 1.1151 + role: aEvent.info.role, 1.1152 + ifname: aEvent.info.ifname 1.1153 + }; 1.1154 + _sm.gotoState(stateDisconnecting); 1.1155 + break; 1.1156 + 1.1157 + default: 1.1158 + return false; 1.1159 + } // End of switch. 1.1160 + 1.1161 + return true; 1.1162 + }, 1.1163 + 1.1164 + exit: function() { 1.1165 + this.timeoutTimer.cancel(); 1.1166 + } 1.1167 + }); 1.1168 + 1.1169 + var stateConnected = _sm.makeState("CONNECTED", { 1.1170 + groupOwner: null, 1.1171 + 1.1172 + enter: function() { 1.1173 + this.groupOwner = { 1.1174 + macAddress: _groupInfo.goAddress, 1.1175 + ipAddress: _groupInfo.networkInterface.gateway, 1.1176 + passphrase: _groupInfo.passphrase, 1.1177 + ssid: _groupInfo.ssid, 1.1178 + freq: _groupInfo.freq, 1.1179 + isLocal: _groupInfo.isGroupOwner 1.1180 + }; 1.1181 + 1.1182 + if (!_groupInfo.isGroupOwner) { 1.1183 + _observer.onConnected(this.groupOwner, _savedConfig); 1.1184 + } else { 1.1185 + // If I am a group owner, notify onConnected until EVENT_AP_STA_CONNECTED 1.1186 + // is received. 1.1187 + } 1.1188 + 1.1189 + _removedGroupInfo = null; 1.1190 + }, 1.1191 + 1.1192 + handleEvent: function(aEvent) { 1.1193 + switch (aEvent.id) { 1.1194 + case EVENT_AP_STA_CONNECTED: 1.1195 + if (_groupInfo.isGroupOwner) { 1.1196 + _observer.onConnected(this.groupOwner, _savedConfig); 1.1197 + } 1.1198 + break; 1.1199 + 1.1200 + case EVENT_P2P_GROUP_REMOVED: 1.1201 + _removedGroupInfo = { 1.1202 + role: aEvent.info.role, 1.1203 + ifname: aEvent.info.ifname 1.1204 + }; 1.1205 + _sm.gotoState(stateDisconnecting); 1.1206 + break; 1.1207 + 1.1208 + case EVENT_AP_STA_DISCONNECTED: 1.1209 + debug('Client disconnected: ' + aEvent.info.address); 1.1210 + 1.1211 + // Now we suppose it's the only client. Remove my group. 1.1212 + _sm.pause(); 1.1213 + aP2pCommand.p2pGroupRemove(_groupInfo.ifname, function (success) { 1.1214 + debug('Requested to remove p2p group. Wait for EVENT_P2P_GROUP_REMOVED.'); 1.1215 + _sm.resume(); 1.1216 + }); 1.1217 + break; 1.1218 + 1.1219 + case EVENT_P2P_CMD_DISCONNECT: 1.1220 + // Since we only support single connection, we can ignore 1.1221 + // the given peer address. 1.1222 + _sm.pause(); 1.1223 + aP2pCommand.p2pGroupRemove(_groupInfo.ifname, function(success) { 1.1224 + aEvent.info.onDoDisconnect(true); 1.1225 + _sm.resume(); 1.1226 + }); 1.1227 + 1.1228 + debug('Sent disconnect command. Wait for EVENT_P2P_GROUP_REMOVED.'); 1.1229 + break; 1.1230 + 1.1231 + case EVENT_P2P_PROV_DISC_PBC_REQ: 1.1232 + case EVENT_P2P_PROV_DISC_SHOW_PIN: 1.1233 + case EVENT_P2P_PROV_DISC_ENTER_PIN: 1.1234 + debug('Someone is trying to connect to me: ' + JSON.stringify(aEvent.info)); 1.1235 + 1.1236 + _savedConfig = { 1.1237 + name: aEvent.info.name, 1.1238 + address: aEvent.info.address, 1.1239 + wpsMethod: aEvent.info.wpsMethod, 1.1240 + pin: aEvent.info.pin 1.1241 + }; 1.1242 + 1.1243 + _sm.gotoState(stateWaitingForJoiningConfirmation); 1.1244 + break; 1.1245 + 1.1246 + default: 1.1247 + return false; 1.1248 + } // end of switch 1.1249 + return true; 1.1250 + } 1.1251 + }); 1.1252 + 1.1253 + var stateWaitingForJoiningConfirmation = _sm.makeState("WAITING_FOR_JOINING_CONFIRMATION", { 1.1254 + timeoutTimer: null, 1.1255 + 1.1256 + enter: function() { 1.1257 + gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); 1.1258 + this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); 1.1259 + }, 1.1260 + 1.1261 + handleEvent: function (aEvent) { 1.1262 + switch (aEvent.id) { 1.1263 + case EVENT_P2P_SET_PAIRING_CONFIRMATION: 1.1264 + if (!aEvent.info.accepted) { 1.1265 + debug('User rejected invitation!'); 1.1266 + _sm.gotoState(stateConnected); 1.1267 + break; 1.1268 + } 1.1269 + 1.1270 + let onWpsCommandSent = function(success) { 1.1271 + _observer.onConnecting(_savedConfig); 1.1272 + _sm.gotoState(stateConnected); 1.1273 + }; 1.1274 + 1.1275 + _sm.pause(); 1.1276 + if (WPS_METHOD_PBC === _savedConfig.wpsMethod) { 1.1277 + aP2pCommand.wpsPbc(_groupInfo.ifname, onWpsCommandSent); 1.1278 + } else { 1.1279 + let detail = { pin: _savedConfig.pin, iface: _groupInfo.ifname }; 1.1280 + aP2pCommand.wpsPin(detail, onWpsCommandSent); 1.1281 + } 1.1282 + break; 1.1283 + 1.1284 + case EVENT_TIMEOUT_PAIRING_CONFIRMATION: 1.1285 + debug('WAITING_FOR_JOINING_CONFIRMATION timeout!'); 1.1286 + _sm.gotoState(stateConnected); 1.1287 + break; 1.1288 + 1.1289 + default: 1.1290 + return false; 1.1291 + } // End of switch. 1.1292 + return true; 1.1293 + }, 1.1294 + 1.1295 + exit: function() { 1.1296 + this.timeoutTimer.cancel(); 1.1297 + this.timeoutTimer = null; 1.1298 + } 1.1299 + }); 1.1300 + 1.1301 + var stateDisconnecting = _sm.makeState("DISCONNECTING", { 1.1302 + enter: function() { 1.1303 + _sm.pause(); 1.1304 + handleGroupRemoved(_removedGroupInfo, function (success) { 1.1305 + if (!success) { 1.1306 + debug('Failed to handle group removed event. What can I do?'); 1.1307 + } 1.1308 + _sm.gotoState(stateInactive); 1.1309 + }); 1.1310 + }, 1.1311 + 1.1312 + handleEvent: function(aEvent) { 1.1313 + return false; // We will not receive any event in this state. 1.1314 + } 1.1315 + }); 1.1316 + 1.1317 + var stateDisabling = _sm.makeState("DISABLING", { 1.1318 + enter: function() { 1.1319 + _sm.pause(); 1.1320 + aNetUtil.stopDhcpServer(function (success) { // Stopping DHCP server is harmless. 1.1321 + debug('Stop DHCP server result: ' + success); 1.1322 + aP2pCommand.p2pDisable(function(success) { 1.1323 + debug('P2P function disabled'); 1.1324 + aP2pCommand.closeSupplicantConnection(function (status) { 1.1325 + debug('Supplicant connection closed'); 1.1326 + aNetUtil.disableInterface(P2P_INTERFACE_NAME, function (success){ 1.1327 + debug('Disabled interface: ' + P2P_INTERFACE_NAME); 1.1328 + _onDisabled(true); 1.1329 + _sm.gotoState(stateDisabled); 1.1330 + }); 1.1331 + }); 1.1332 + }); 1.1333 + }); 1.1334 + }, 1.1335 + 1.1336 + handleEvent: function(aEvent) { 1.1337 + return false; // We will not receive any event in this state. 1.1338 + } 1.1339 + }); 1.1340 + 1.1341 + //---------------------------------------------------------- 1.1342 + // Helper functions. 1.1343 + //---------------------------------------------------------- 1.1344 + 1.1345 + // Handle 'P2P_GROUP_STARTED' event. Note that this function 1.1346 + // will also do the state transitioning and error handling. 1.1347 + // 1.1348 + // @param aInfo Information carried by "P2P_GROUP_STARTED" event: 1.1349 + // .role: P2P_ROLE_GO or P2P_ROLE_CLIENT 1.1350 + // .ssid: 1.1351 + // .freq: 1.1352 + // .passphrase: Used to connect to GO for legacy device. 1.1353 + // .goAddress: 1.1354 + // .ifname: e.g. p2p-p2p0 1.1355 + // 1.1356 + // @param aCallback Callback function. 1.1357 + function handleGroupStarted(aInfo, aCallback) { 1.1358 + debug('handleGroupStarted: ' + JSON.stringify(aInfo)); 1.1359 + 1.1360 + function onSuccess() 1.1361 + { 1.1362 + _sm.gotoState(stateConnected); 1.1363 + aCallback(true); 1.1364 + } 1.1365 + 1.1366 + function onFailure() 1.1367 + { 1.1368 + debug('Failed to handleGroupdStarted(). Remove the group...'); 1.1369 + aP2pCommand.p2pGroupRemove(aInfo.ifname, function (success) { 1.1370 + aCallback(false); 1.1371 + 1.1372 + if (success) { 1.1373 + return; // Stay in current state and wait for EVENT_P2P_GROUP_REMOVED. 1.1374 + } 1.1375 + 1.1376 + debug('p2pGroupRemove command error!'); 1.1377 + _sm.gotoState(stateInactive); 1.1378 + }); 1.1379 + } 1.1380 + 1.1381 + // Save this group information. 1.1382 + _groupInfo = aInfo; 1.1383 + _groupInfo.isGroupOwner = (P2P_ROLE_GO === aInfo.role); 1.1384 + 1.1385 + if (_groupInfo.isGroupOwner) { 1.1386 + debug('Group owner. Start DHCP server'); 1.1387 + let dhcpServerConfig = { ifname: aInfo.ifname, 1.1388 + startIp: GO_DHCP_SERVER_IP_RANGE.startIp, 1.1389 + endIp: GO_DHCP_SERVER_IP_RANGE.endIp, 1.1390 + serverIp: GO_NETWORK_INTERFACE.ip, 1.1391 + maskLength: GO_NETWORK_INTERFACE.maskLength }; 1.1392 + 1.1393 + aNetUtil.startDhcpServer(dhcpServerConfig, function (success) { 1.1394 + if (!success) { 1.1395 + debug('Failed to start DHCP server'); 1.1396 + onFailure(); 1.1397 + return; 1.1398 + } 1.1399 + 1.1400 + // Update p2p network interface. 1.1401 + _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED; 1.1402 + _p2pNetworkInterface.ips = [GO_NETWORK_INTERFACE.ip]; 1.1403 + _p2pNetworkInterface.prefixLengths = [GO_NETWORK_INTERFACE.maskLength]; 1.1404 + _p2pNetworkInterface.gateways = [GO_NETWORK_INTERFACE.ip]; 1.1405 + handleP2pNetworkInterfaceStateChanged(); 1.1406 + 1.1407 + _groupInfo.networkInterface = _p2pNetworkInterface; 1.1408 + 1.1409 + debug('Everything is done. Happy p2p GO~'); 1.1410 + onSuccess(); 1.1411 + }); 1.1412 + 1.1413 + return; 1.1414 + } 1.1415 + 1.1416 + // We are the client. 1.1417 + 1.1418 + debug("Client. Request IP from DHCP server on interface: " + _groupInfo.ifname); 1.1419 + 1.1420 + aNetUtil.runDhcp(aInfo.ifname, function(dhcpData) { 1.1421 + if(!dhcpData || !dhcpData.info) { 1.1422 + debug('Failed to run DHCP client'); 1.1423 + onFailure(); 1.1424 + return; 1.1425 + } 1.1426 + 1.1427 + // Save network interface. 1.1428 + debug("DHCP request success: " + JSON.stringify(dhcpData.info)); 1.1429 + 1.1430 + // Update p2p network interface. 1.1431 + let maskLength = 1.1432 + netHelpers.getMaskLength(netHelpers.stringToIP(dhcpData.info.mask_str)); 1.1433 + if (!maskLength) { 1.1434 + maskLength = 32; // max prefix for IPv4. 1.1435 + } 1.1436 + _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED; 1.1437 + _p2pNetworkInterface.ips = [dhcpData.info.ipaddr_str]; 1.1438 + _p2pNetworkInterface.prefixLengths = [maskLength]; 1.1439 + if (typeof dhcpData.info.dns1_str == "string" && 1.1440 + dhcpData.info.dns1_str.length) { 1.1441 + _p2pNetworkInterface.dnses.push(dhcpData.info.dns1_str); 1.1442 + } 1.1443 + if (typeof dhcpData.info.dns2_str == "string" && 1.1444 + dhcpData.info.dns2_str.length) { 1.1445 + _p2pNetworkInterface.dnses.push(dhcpData.info.dns2_str); 1.1446 + } 1.1447 + _p2pNetworkInterface.gateways = [dhcpData.info.gateway_str]; 1.1448 + handleP2pNetworkInterfaceStateChanged(); 1.1449 + 1.1450 + _groupInfo.networkInterface = _p2pNetworkInterface; 1.1451 + 1.1452 + debug('Happy p2p client~'); 1.1453 + onSuccess(); 1.1454 + }); 1.1455 + } 1.1456 + 1.1457 + function resetP2pNetworkInterface() { 1.1458 + _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED; 1.1459 + _p2pNetworkInterface.ips = []; 1.1460 + _p2pNetworkInterface.prefixLengths = []; 1.1461 + _p2pNetworkInterface.dnses = []; 1.1462 + _p2pNetworkInterface.gateways = []; 1.1463 + } 1.1464 + 1.1465 + function registerP2pNetworkInteface() { 1.1466 + if (!_p2pNetworkInterface.registered) { 1.1467 + resetP2pNetworkInterface(); 1.1468 + gNetworkManager.registerNetworkInterface(_p2pNetworkInterface); 1.1469 + _p2pNetworkInterface.registered = true; 1.1470 + } 1.1471 + } 1.1472 + 1.1473 + function unregisterP2pNetworkInteface() { 1.1474 + if (_p2pNetworkInterface.registered) { 1.1475 + resetP2pNetworkInterface(); 1.1476 + gNetworkManager.unregisterNetworkInterface(_p2pNetworkInterface); 1.1477 + _p2pNetworkInterface.registered = false; 1.1478 + } 1.1479 + } 1.1480 + 1.1481 + function handleP2pNetworkInterfaceStateChanged() { 1.1482 + Services.obs.notifyObservers(_p2pNetworkInterface, 1.1483 + kNetworkInterfaceStateChangedTopic, 1.1484 + null); 1.1485 + } 1.1486 + 1.1487 + // Handle 'P2P_GROUP_STARTED' event. 1.1488 + // 1.1489 + // @param aInfo information carried by "P2P_GROUP_REMOVED" event: 1.1490 + // .ifname 1.1491 + // .role: "GO" or "client". 1.1492 + // 1.1493 + // @param aCallback Callback function. 1.1494 + function handleGroupRemoved(aInfo, aCallback) { 1.1495 + if (!_groupInfo) { 1.1496 + debug('No group info. Why?'); 1.1497 + aCallback(true); 1.1498 + return; 1.1499 + } 1.1500 + if (_groupInfo.ifname !== aInfo.ifname || 1.1501 + _groupInfo.role !== aInfo.role) { 1.1502 + debug('Unmatched group info: ' + JSON.stringify(_groupInfo) + 1.1503 + ' v.s. ' + JSON.stringify(aInfo)); 1.1504 + } 1.1505 + 1.1506 + // Update p2p network interface. 1.1507 + _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED; 1.1508 + handleP2pNetworkInterfaceStateChanged(); 1.1509 + 1.1510 + if (P2P_ROLE_GO === aInfo.role) { 1.1511 + aNetUtil.stopDhcpServer(function(success) { 1.1512 + debug('Stop DHCP server result: ' + success); 1.1513 + aCallback(true); 1.1514 + }); 1.1515 + } else { 1.1516 + aNetUtil.stopDhcp(aInfo.ifname, function() { 1.1517 + aCallback(true); 1.1518 + }); 1.1519 + } 1.1520 + } 1.1521 + 1.1522 + // Non state-specific event handler. 1.1523 + function handleEventCommon(aEvent) { 1.1524 + switch (aEvent.id) { 1.1525 + case EVENT_P2P_DEVICE_FOUND: 1.1526 + _observer.onPeerFound(aEvent.info); 1.1527 + break; 1.1528 + 1.1529 + case EVENT_P2P_DEVICE_LOST: 1.1530 + _observer.onPeerLost(aEvent.info); 1.1531 + break; 1.1532 + 1.1533 + case EVENT_P2P_CMD_DISABLE: 1.1534 + _onDisabled = aEvent.info.onDisabled; 1.1535 + _sm.gotoState(stateDisabling); 1.1536 + break; 1.1537 + 1.1538 + case EVENT_P2P_CMD_ENABLE_SCAN: 1.1539 + if (_scanBlocked) { 1.1540 + _scanPostponded = true; 1.1541 + aEvent.info.callback(true); 1.1542 + break; 1.1543 + } 1.1544 + aP2pCommand.p2pEnableScan(P2P_SCAN_TIMEOUT_SEC, aEvent.info.callback); 1.1545 + break; 1.1546 + 1.1547 + case EVENT_P2P_CMD_DISABLE_SCAN: 1.1548 + aP2pCommand.p2pDisableScan(aEvent.info.callback); 1.1549 + break; 1.1550 + 1.1551 + case EVENT_P2P_FIND_STOPPED: 1.1552 + break; 1.1553 + 1.1554 + case EVENT_P2P_CMD_BLOCK_SCAN: 1.1555 + _scanBlocked = true; 1.1556 + aP2pCommand.p2pDisableScan(function(success) {}); 1.1557 + break; 1.1558 + 1.1559 + case EVENT_P2P_CMD_UNBLOCK_SCAN: 1.1560 + _scanBlocked = false; 1.1561 + if (_scanPostponded) { 1.1562 + aP2pCommand.p2pEnableScan(P2P_SCAN_TIMEOUT_SEC, function(success) {}); 1.1563 + } 1.1564 + break; 1.1565 + 1.1566 + case EVENT_P2P_CMD_CONNECT: 1.1567 + case EVENT_P2P_CMD_DISCONNECT: 1.1568 + debug("The current state couldn't handle connect/disconnect request. Ignore it."); 1.1569 + break; 1.1570 + 1.1571 + default: 1.1572 + return false; 1.1573 + } // End of switch. 1.1574 + return true; 1.1575 + } 1.1576 + 1.1577 + function isInP2pManagedState(aState) { 1.1578 + let p2pManagedStates = [stateWaitingForConfirmation, 1.1579 + stateWaitingForNegReq, 1.1580 + stateProvisionDiscovery, 1.1581 + stateWaitingForInvitationConfirmation, 1.1582 + stateGroupAdding, 1.1583 + stateReinvoking, 1.1584 + stateConnecting, 1.1585 + stateConnected, 1.1586 + stateDisconnecting]; 1.1587 + 1.1588 + for (let i = 0; i < p2pManagedStates.length; i++) { 1.1589 + if (aState === p2pManagedStates[i]) { 1.1590 + return true; 1.1591 + } 1.1592 + } 1.1593 + 1.1594 + return false; 1.1595 + } 1.1596 + 1.1597 + function initTimeoutTimer(aTimeoutMs, aTimeoutEvent) { 1.1598 + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.1599 + function onTimerFired() { 1.1600 + _sm.sendEvent({ id: aTimeoutEvent }); 1.1601 + timer = null; 1.1602 + } 1.1603 + timer.initWithCallback(onTimerFired.bind(this), aTimeoutMs, 1.1604 + Ci.nsITimer.TYPE_ONE_SHOT); 1.1605 + return timer; 1.1606 + } 1.1607 + 1.1608 + // Converts local WPS method to peer WPS method. 1.1609 + function toPeerWpsMethod(aLocalWpsMethod) { 1.1610 + switch (aLocalWpsMethod) { 1.1611 + case WPS_METHOD_DISPLAY: 1.1612 + return WPS_METHOD_KEYPAD; 1.1613 + case WPS_METHOD_KEYPAD: 1.1614 + return WPS_METHOD_DISPLAY; 1.1615 + case WPS_METHOD_PBC: 1.1616 + return WPS_METHOD_PBC; 1.1617 + default: 1.1618 + return WPS_METHOD_PBC; // Use "push button" as the default method. 1.1619 + } 1.1620 + } 1.1621 + 1.1622 + return p2pSm; 1.1623 +} 1.1624 + 1.1625 +this.WifiP2pManager.INTERFACE_NAME = P2P_INTERFACE_NAME;