Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | "use strict"; |
michael@0 | 8 | |
michael@0 | 9 | const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
michael@0 | 10 | |
michael@0 | 11 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 12 | Cu.import("resource://gre/modules/StateMachine.jsm"); |
michael@0 | 13 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 14 | Cu.import("resource://gre/modules/systemlibs.js"); |
michael@0 | 15 | |
michael@0 | 16 | XPCOMUtils.defineLazyServiceGetter(this, "gSysMsgr", |
michael@0 | 17 | "@mozilla.org/system-message-internal;1", |
michael@0 | 18 | "nsISystemMessagesInternal"); |
michael@0 | 19 | |
michael@0 | 20 | XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager", |
michael@0 | 21 | "@mozilla.org/network/manager;1", |
michael@0 | 22 | "nsINetworkManager"); |
michael@0 | 23 | |
michael@0 | 24 | const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed"; |
michael@0 | 25 | |
michael@0 | 26 | this.EXPORTED_SYMBOLS = ["WifiP2pManager"]; |
michael@0 | 27 | |
michael@0 | 28 | const EVENT_IGNORED = -1; |
michael@0 | 29 | const EVENT_UNKNOWN = -2; |
michael@0 | 30 | |
michael@0 | 31 | // Events from supplicant for p2p. |
michael@0 | 32 | const EVENT_P2P_DEVICE_FOUND = 0; |
michael@0 | 33 | const EVENT_P2P_DEVICE_LOST = 1; |
michael@0 | 34 | const EVENT_P2P_GROUP_STARTED = 2; |
michael@0 | 35 | const EVENT_P2P_GROUP_REMOVED = 3; |
michael@0 | 36 | const EVENT_P2P_PROV_DISC_PBC_REQ = 4; |
michael@0 | 37 | const EVENT_P2P_PROV_DISC_PBC_RESP = 5; |
michael@0 | 38 | const EVENT_P2P_PROV_DISC_SHOW_PIN = 6; |
michael@0 | 39 | const EVENT_P2P_PROV_DISC_ENTER_PIN = 7; |
michael@0 | 40 | const EVENT_P2P_GO_NEG_REQUEST = 8; |
michael@0 | 41 | const EVENT_P2P_GO_NEG_SUCCESS = 9; |
michael@0 | 42 | const EVENT_P2P_GO_NEG_FAILURE = 10; |
michael@0 | 43 | const EVENT_P2P_GROUP_FORMATION_SUCCESS = 11; |
michael@0 | 44 | const EVENT_P2P_GROUP_FORMATION_FAILURE = 12; |
michael@0 | 45 | const EVENT_P2P_FIND_STOPPED = 13; |
michael@0 | 46 | const EVENT_P2P_INVITATION_RESULT = 14; |
michael@0 | 47 | const EVENT_P2P_INVITATION_RECEIVED = 15; |
michael@0 | 48 | const EVENT_P2P_PROV_DISC_FAILURE = 16; |
michael@0 | 49 | |
michael@0 | 50 | // Events from supplicant but not p2p specific. |
michael@0 | 51 | const EVENT_AP_STA_DISCONNECTED = 100; |
michael@0 | 52 | const EVENT_AP_STA_CONNECTED = 101; |
michael@0 | 53 | |
michael@0 | 54 | // Events from DOM. |
michael@0 | 55 | const EVENT_P2P_SET_PAIRING_CONFIRMATION = 1000; |
michael@0 | 56 | const EVENT_P2P_CMD_CONNECT = 1001; |
michael@0 | 57 | const EVENT_P2P_CMD_DISCONNECT = 1002; |
michael@0 | 58 | const EVENT_P2P_CMD_ENABLE = 1003; |
michael@0 | 59 | const EVENT_P2P_CMD_DISABLE = 1004; |
michael@0 | 60 | const EVENT_P2P_CMD_ENABLE_SCAN = 1005; |
michael@0 | 61 | const EVENT_P2P_CMD_DISABLE_SCAN = 1006; |
michael@0 | 62 | const EVENT_P2P_CMD_BLOCK_SCAN = 1007; |
michael@0 | 63 | const EVENT_P2P_CMD_UNBLOCK_SCAN = 1008; |
michael@0 | 64 | |
michael@0 | 65 | // Internal events. |
michael@0 | 66 | const EVENT_TIMEOUT_PAIRING_CONFIRMATION = 10000; |
michael@0 | 67 | const EVENT_TIMEOUT_NEG_REQ = 10001; |
michael@0 | 68 | const EVENT_TIMEOUT_CONNECTING = 10002; |
michael@0 | 69 | const EVENT_P2P_ENABLE_SUCCESS = 10003; |
michael@0 | 70 | const EVENT_P2P_ENABLE_FAILED = 10004; |
michael@0 | 71 | const EVENT_P2P_DISABLE_SUCCESS = 10005; |
michael@0 | 72 | |
michael@0 | 73 | // WPS method string. |
michael@0 | 74 | const WPS_METHOD_PBC = "pbc"; |
michael@0 | 75 | const WPS_METHOD_DISPLAY = "display"; |
michael@0 | 76 | const WPS_METHOD_KEYPAD = "keypad"; |
michael@0 | 77 | |
michael@0 | 78 | // Role string. |
michael@0 | 79 | const P2P_ROLE_GO = "GO"; |
michael@0 | 80 | const P2P_ROLE_CLIENT = "client"; |
michael@0 | 81 | |
michael@0 | 82 | // System message for pairing request. |
michael@0 | 83 | const PAIRING_REQUEST_SYS_MSG = "wifip2p-pairing-request"; |
michael@0 | 84 | |
michael@0 | 85 | // Configuration. |
michael@0 | 86 | const P2P_INTERFACE_NAME = "p2p0"; |
michael@0 | 87 | const DEFAULT_GO_INTENT = 15; |
michael@0 | 88 | const DEFAULT_P2P_DEVICE_NAME = "FirefoxPhone"; |
michael@0 | 89 | const P2P_SCAN_TIMEOUT_SEC = 120; |
michael@0 | 90 | const DEFAULT_P2P_WPS_METHODS = "virtual_push_button physical_display keypad"; // For wpa_supplicant. |
michael@0 | 91 | const DEFAULT_P2P_DEVICE_TYPE = "10-0050F204-5"; // For wpa_supplicant. |
michael@0 | 92 | |
michael@0 | 93 | const GO_NETWORK_INTERFACE = { |
michael@0 | 94 | ip: "192.168.2.1", |
michael@0 | 95 | maskLength: 24, |
michael@0 | 96 | gateway: "192.168.2.1", |
michael@0 | 97 | dns1: "0.0.0.0", |
michael@0 | 98 | dns2: "0.0.0.0", |
michael@0 | 99 | dhcpServer: "192.168.2.1" |
michael@0 | 100 | }; |
michael@0 | 101 | |
michael@0 | 102 | const GO_DHCP_SERVER_IP_RANGE = { |
michael@0 | 103 | startIp: "192.168.2.10", |
michael@0 | 104 | endIp: "192.168.2.30" |
michael@0 | 105 | }; |
michael@0 | 106 | |
michael@0 | 107 | let gDebug = false; |
michael@0 | 108 | |
michael@0 | 109 | // Device Capability bitmap |
michael@0 | 110 | const DEVICE_CAPAB_SERVICE_DISCOVERY = 1; |
michael@0 | 111 | const DEVICE_CAPAB_CLIENT_DISCOVERABILITY = 1<<1; |
michael@0 | 112 | const DEVICE_CAPAB_CONCURRENT_OPER = 1<<2; |
michael@0 | 113 | const DEVICE_CAPAB_INFRA_MANAGED = 1<<3; |
michael@0 | 114 | const DEVICE_CAPAB_DEVICE_LIMIT = 1<<4; |
michael@0 | 115 | const DEVICE_CAPAB_INVITATION_PROCEDURE = 1<<5; |
michael@0 | 116 | |
michael@0 | 117 | // Group Capability bitmap |
michael@0 | 118 | const GROUP_CAPAB_GROUP_OWNER = 1; |
michael@0 | 119 | const GROUP_CAPAB_PERSISTENT_GROUP = 1<<1; |
michael@0 | 120 | const GROUP_CAPAB_GROUP_LIMIT = 1<<2; |
michael@0 | 121 | const GROUP_CAPAB_INTRA_BSS_DIST = 1<<3; |
michael@0 | 122 | const GROUP_CAPAB_CROSS_CONN = 1<<4; |
michael@0 | 123 | const GROUP_CAPAB_PERSISTENT_RECONN = 1<<5; |
michael@0 | 124 | const GROUP_CAPAB_GROUP_FORMATION = 1<<6; |
michael@0 | 125 | |
michael@0 | 126 | // Constants defined in wpa_supplicants. |
michael@0 | 127 | const DEV_PW_REGISTRAR_SPECIFIED = 5; |
michael@0 | 128 | const DEV_PW_USER_SPECIFIED = 1; |
michael@0 | 129 | const DEV_PW_PUSHBUTTON = 4; |
michael@0 | 130 | |
michael@0 | 131 | this.WifiP2pManager = function (aP2pCommand, aNetUtil) { |
michael@0 | 132 | function debug(aMsg) { |
michael@0 | 133 | if (gDebug) { |
michael@0 | 134 | dump('-------------- WifiP2pManager: ' + aMsg); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | let manager = {}; |
michael@0 | 139 | |
michael@0 | 140 | let _stateMachine = P2pStateMachine(aP2pCommand, aNetUtil); |
michael@0 | 141 | |
michael@0 | 142 | // Set debug flag to true or false. |
michael@0 | 143 | // |
michael@0 | 144 | // @param aDebug Boolean to indicate enabling or disabling the debug flag. |
michael@0 | 145 | manager.setDebug = function(aDebug) { |
michael@0 | 146 | gDebug = aDebug; |
michael@0 | 147 | }; |
michael@0 | 148 | |
michael@0 | 149 | // Set observer of observing internal state machine events. |
michael@0 | 150 | // |
michael@0 | 151 | // @param aObserver Used to notify WifiWorker what's happening |
michael@0 | 152 | // in the internal p2p state machine. |
michael@0 | 153 | manager.setObserver = function(aObserver) { |
michael@0 | 154 | _stateMachine.setObserver(aObserver); |
michael@0 | 155 | }; |
michael@0 | 156 | |
michael@0 | 157 | // Handle wpa_supplicant events. |
michael@0 | 158 | // |
michael@0 | 159 | // @param aEventString string from wpa_supplicant. |
michael@0 | 160 | manager.handleEvent = function(aEventString) { |
michael@0 | 161 | let event = parseEventString(aEventString); |
michael@0 | 162 | if (EVENT_UNKNOWN === event.id || EVENT_IGNORED === event.id) { |
michael@0 | 163 | debug('Unknow or ignored event: ' + aEventString); |
michael@0 | 164 | return false; |
michael@0 | 165 | } |
michael@0 | 166 | return _stateMachine.sendEvent(event); |
michael@0 | 167 | }; |
michael@0 | 168 | |
michael@0 | 169 | // Set the confirmation of pairing request. |
michael@0 | 170 | // |
michael@0 | 171 | // @param aResult Object of confirmation result which contains: |
michael@0 | 172 | // .accepted: user granted. |
michael@0 | 173 | // .pin: pin code which is displaying or input by user. |
michael@0 | 174 | // .wpsMethod: string of "pbc" or "display" or "keypad". |
michael@0 | 175 | manager.setPairingConfirmation = function(aResult) { |
michael@0 | 176 | let event = { |
michael@0 | 177 | id: EVENT_P2P_SET_PAIRING_CONFIRMATION, |
michael@0 | 178 | info: { |
michael@0 | 179 | accepted: aResult.accepted, |
michael@0 | 180 | pin: aResult.pin |
michael@0 | 181 | } |
michael@0 | 182 | }; |
michael@0 | 183 | _stateMachine.sendEvent(event); |
michael@0 | 184 | }; |
michael@0 | 185 | |
michael@0 | 186 | // Connect to a known peer. |
michael@0 | 187 | // |
michael@0 | 188 | // @param aAddress MAC address of the peer to connect. |
michael@0 | 189 | // @param aWpsMethod String of "pbc" or "display" or "keypad". |
michael@0 | 190 | // @param aGoIntent Number from 0 to 15. |
michael@0 | 191 | // @param aCallback Callback |true| on attempting to connect. |
michael@0 | 192 | // |false| on failed to connect. |
michael@0 | 193 | manager.connect = function(aAddress, aWpsMethod, aGoIntent, aCallback) { |
michael@0 | 194 | let event = { |
michael@0 | 195 | id: EVENT_P2P_CMD_CONNECT, |
michael@0 | 196 | info: { |
michael@0 | 197 | wpsMethod: aWpsMethod, |
michael@0 | 198 | address: aAddress, |
michael@0 | 199 | goIntent: aGoIntent, |
michael@0 | 200 | onDoConnect: aCallback |
michael@0 | 201 | } |
michael@0 | 202 | }; |
michael@0 | 203 | _stateMachine.sendEvent(event); |
michael@0 | 204 | }; |
michael@0 | 205 | |
michael@0 | 206 | // Disconnect with a known peer. |
michael@0 | 207 | // |
michael@0 | 208 | // @param aAddress The address the user desires to disconect. |
michael@0 | 209 | // @param aCallback Callback |true| on "attempting" to disconnect. |
michael@0 | 210 | // |false| on failed to disconnect. |
michael@0 | 211 | manager.disconnect = function(aAddress, aCallback) { |
michael@0 | 212 | let event = { |
michael@0 | 213 | id: EVENT_P2P_CMD_DISCONNECT, |
michael@0 | 214 | info: { |
michael@0 | 215 | address: aAddress, |
michael@0 | 216 | onDoDisconnect: aCallback |
michael@0 | 217 | } |
michael@0 | 218 | }; |
michael@0 | 219 | _stateMachine.sendEvent(event); |
michael@0 | 220 | }; |
michael@0 | 221 | |
michael@0 | 222 | // Enable/disable wifi p2p. |
michael@0 | 223 | // |
michael@0 | 224 | // @param aEnabled |true| to enable, |false| to disable. |
michael@0 | 225 | // @param aCallbacks object for callbacks: |
michael@0 | 226 | // .onEnabled |
michael@0 | 227 | // .onDisabled |
michael@0 | 228 | // .onSupplicantConnected |
michael@0 | 229 | manager.setEnabled = function(aEnabled, aCallbacks) { |
michael@0 | 230 | let event = { |
michael@0 | 231 | id: (aEnabled ? EVENT_P2P_CMD_ENABLE : EVENT_P2P_CMD_DISABLE), |
michael@0 | 232 | info: { |
michael@0 | 233 | onEnabled: aCallbacks.onEnabled, |
michael@0 | 234 | onDisabled: aCallbacks.onDisabled, |
michael@0 | 235 | onSupplicantConnected: aCallbacks.onSupplicantConnected |
michael@0 | 236 | } |
michael@0 | 237 | }; |
michael@0 | 238 | _stateMachine.sendEvent(event); |
michael@0 | 239 | }; |
michael@0 | 240 | |
michael@0 | 241 | // Enable/disable the wifi p2p scan. |
michael@0 | 242 | // |
michael@0 | 243 | // @param aEnabled |true| to enable scan, |false| to disable scan. |
michael@0 | 244 | // @param aCallback Callback |true| on success to enable/disable scan. |
michael@0 | 245 | // |false| on failed to enable/disable scan. |
michael@0 | 246 | manager.setScanEnabled = function(aEnabled, aCallback) { |
michael@0 | 247 | let event = { |
michael@0 | 248 | id: (aEnabled ? EVENT_P2P_CMD_ENABLE_SCAN : EVENT_P2P_CMD_DISABLE_SCAN), |
michael@0 | 249 | info: { callback: aCallback } |
michael@0 | 250 | }; |
michael@0 | 251 | _stateMachine.sendEvent(event); |
michael@0 | 252 | }; |
michael@0 | 253 | |
michael@0 | 254 | // Block wifi p2p scan. |
michael@0 | 255 | manager.blockScan = function() { |
michael@0 | 256 | _stateMachine.sendEvent({ id: EVENT_P2P_CMD_BLOCK_SCAN }); |
michael@0 | 257 | }; |
michael@0 | 258 | |
michael@0 | 259 | // Un-block and do the pending scan if any. |
michael@0 | 260 | manager.unblockScan = function() { |
michael@0 | 261 | _stateMachine.sendEvent({ id: EVENT_P2P_CMD_UNBLOCK_SCAN }); |
michael@0 | 262 | }; |
michael@0 | 263 | |
michael@0 | 264 | // Set the p2p device name. |
michael@0 | 265 | manager.setDeviceName = function(newDeivceName, callback) { |
michael@0 | 266 | aP2pCommand.setDeviceName(newDeivceName, callback); |
michael@0 | 267 | }; |
michael@0 | 268 | |
michael@0 | 269 | // Parse wps_supplicant event string. |
michael@0 | 270 | // |
michael@0 | 271 | // @param aEventString The raw event string from wpa_supplicant. |
michael@0 | 272 | // |
michael@0 | 273 | // @return Object: |
michael@0 | 274 | // .id: a number to represent an event. |
michael@0 | 275 | // .info: the additional information carried by this event string. |
michael@0 | 276 | function parseEventString(aEventString) { |
michael@0 | 277 | if (isIgnoredEvent(aEventString)) { |
michael@0 | 278 | return { id: EVENT_IGNORED }; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | let match = RegExp("p2p_dev_addr=([0-9a-fA-F:]+) " + |
michael@0 | 282 | "pri_dev_type=([0-9a-zA-Z-]+) " + |
michael@0 | 283 | "name='(.*)' " + |
michael@0 | 284 | "config_methods=0x([0-9a-fA-F]+) " + |
michael@0 | 285 | "dev_capab=0x([0-9a-fA-F]+) " + |
michael@0 | 286 | "group_capab=0x([0-9a-fA-F]+) ").exec(aEventString + ' '); |
michael@0 | 287 | |
michael@0 | 288 | let tokens = aEventString.split(" "); |
michael@0 | 289 | |
michael@0 | 290 | let id = EVENT_UNKNOWN; |
michael@0 | 291 | |
michael@0 | 292 | // general info. |
michael@0 | 293 | let info = {}; |
michael@0 | 294 | |
michael@0 | 295 | if (match) { |
michael@0 | 296 | info = { |
michael@0 | 297 | address: match[1] ? match[1] : null, |
michael@0 | 298 | type: match[2] ? match[2] : null, |
michael@0 | 299 | name: match[3] ? match[3] : null, |
michael@0 | 300 | wpsFlag: match[4] ? parseInt(match[4], 16) : null, |
michael@0 | 301 | devFlag: match[5] ? parseInt(match[5], 16) : null, |
michael@0 | 302 | groupFlag: match[6] ? parseInt(match[6], 16) : null |
michael@0 | 303 | }; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | if (0 === aEventString.indexOf("P2P-DEVICE-FOUND")) { |
michael@0 | 307 | id = EVENT_P2P_DEVICE_FOUND; |
michael@0 | 308 | info.wpsCapabilities = wpsFlagToCapabilities(info.wpsFlag); |
michael@0 | 309 | info.isGroupOwner = isPeerGroupOwner(info.groupFlag); |
michael@0 | 310 | } else if (0 === aEventString.indexOf("P2P-DEVICE-LOST")) { |
michael@0 | 311 | // e.g. "P2P-DEVICE-LOST p2p_dev_addr=5e:0a:5b:15:1f:80". |
michael@0 | 312 | id = EVENT_P2P_DEVICE_LOST; |
michael@0 | 313 | info.address = /p2p_dev_addr=([0-9a-f:]+)/.exec(aEventString)[1]; |
michael@0 | 314 | } else if (0 === aEventString.indexOf("P2P-GROUP-STARTED")) { |
michael@0 | 315 | // e.g. "P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F Testing |
michael@0 | 316 | // passphrase="12345678" go_dev_addr=02:40:61:c2:f3:b7 [PERSISTENT]". |
michael@0 | 317 | |
michael@0 | 318 | id = EVENT_P2P_GROUP_STARTED; |
michael@0 | 319 | let groupMatch = RegExp('ssid="(.*)" ' + |
michael@0 | 320 | 'freq=([0-9]*) ' + |
michael@0 | 321 | '(passphrase|psk)=([^ ]+) ' + |
michael@0 | 322 | 'go_dev_addr=([0-9a-f:]+)').exec(aEventString); |
michael@0 | 323 | info.ssid = groupMatch[1]; |
michael@0 | 324 | info.freq = groupMatch[2]; |
michael@0 | 325 | if ('passphrase' === groupMatch[3]) { |
michael@0 | 326 | let s = groupMatch[4]; // e.g. "G7jHkkz9". |
michael@0 | 327 | info.passphrase = s.substring(1, s.length-1); // Trim the double quote. |
michael@0 | 328 | } else { // psk |
michael@0 | 329 | info.psk = groupMatch[4]; |
michael@0 | 330 | } |
michael@0 | 331 | info.goAddress = groupMatch[5]; |
michael@0 | 332 | info.ifname = tokens[1]; |
michael@0 | 333 | info.role = tokens[2]; |
michael@0 | 334 | } else if (0 === aEventString.indexOf("P2P-GROUP-REMOVED")) { |
michael@0 | 335 | id = EVENT_P2P_GROUP_REMOVED; |
michael@0 | 336 | // e.g. "P2P-GROUP-REMOVED wlan0-p2p-0 GO". |
michael@0 | 337 | info.ifname = tokens[1]; |
michael@0 | 338 | info.role = tokens[2]; |
michael@0 | 339 | } else if (0 === aEventString.indexOf("P2P-PROV-DISC-PBC-REQ")) { |
michael@0 | 340 | id = EVENT_P2P_PROV_DISC_PBC_REQ; |
michael@0 | 341 | info.wpsMethod = WPS_METHOD_PBC; |
michael@0 | 342 | } else if (0 === aEventString.indexOf("P2P-PROV-DISC-PBC-RESP")) { |
michael@0 | 343 | id = EVENT_P2P_PROV_DISC_PBC_RESP; |
michael@0 | 344 | // The address is different from the general pattern. |
michael@0 | 345 | info.address = aEventString.split(" ")[1]; |
michael@0 | 346 | info.wpsMethod = WPS_METHOD_PBC; |
michael@0 | 347 | } else if (0 === aEventString.indexOf("P2P-PROV-DISC-SHOW-PIN")) { |
michael@0 | 348 | id = EVENT_P2P_PROV_DISC_SHOW_PIN; |
michael@0 | 349 | // Obtain peer address and pin from tokens. |
michael@0 | 350 | info.address = tokens[1]; |
michael@0 | 351 | info.pin = tokens[2]; |
michael@0 | 352 | info.wpsMethod = WPS_METHOD_DISPLAY; |
michael@0 | 353 | } else if (0 === aEventString.indexOf("P2P-PROV-DISC-ENTER-PIN")) { |
michael@0 | 354 | id = EVENT_P2P_PROV_DISC_ENTER_PIN; |
michael@0 | 355 | // Obtain peer address from tokens. |
michael@0 | 356 | info.address = tokens[1]; |
michael@0 | 357 | info.wpsMethod = WPS_METHOD_KEYPAD; |
michael@0 | 358 | } else if (0 === aEventString.indexOf("P2P-GO-NEG-REQUEST")) { |
michael@0 | 359 | id = EVENT_P2P_GO_NEG_REQUEST; |
michael@0 | 360 | info.address = tokens[1]; |
michael@0 | 361 | switch (parseInt(tokens[2].split("=")[1], 10)) { |
michael@0 | 362 | case DEV_PW_REGISTRAR_SPECIFIED: // (5) Peer is display. |
michael@0 | 363 | info.wpsMethod = WPS_METHOD_KEYPAD; |
michael@0 | 364 | break; |
michael@0 | 365 | case DEV_PW_USER_SPECIFIED: // (1) Peer is keypad. |
michael@0 | 366 | info.wpsMethod = WPS_METHOD_DISPLAY; |
michael@0 | 367 | break; |
michael@0 | 368 | case DEV_PW_PUSHBUTTON: // (4) Peer is pbc. |
michael@0 | 369 | info.wpsMethod = WPS_METHOD_PBC; |
michael@0 | 370 | break; |
michael@0 | 371 | default: |
michael@0 | 372 | debug('Unknown wps method from event P2P-GO-NEG-REQUEST'); |
michael@0 | 373 | break; |
michael@0 | 374 | } |
michael@0 | 375 | } else if (0 === aEventString.indexOf("P2P-GO-NEG-SUCCESS")) { |
michael@0 | 376 | id = EVENT_P2P_GO_NEG_SUCCESS; |
michael@0 | 377 | } else if (0 === aEventString.indexOf("P2P-GO-NEG-FAILURE")) { |
michael@0 | 378 | id = EVENT_P2P_GO_NEG_FAILURE; |
michael@0 | 379 | } else if (0 === aEventString.indexOf("P2P-GROUP-FORMATION-FAILURE")) { |
michael@0 | 380 | id = EVENT_P2P_GROUP_FORMATION_FAILURE; |
michael@0 | 381 | } else if (0 === aEventString.indexOf("P2P-GROUP-FORMATION-SUCCESS")) { |
michael@0 | 382 | id = EVENT_P2P_GROUP_FORMATION_SUCCESS; |
michael@0 | 383 | } else if (0 === aEventString.indexOf("P2P-FIND-STOPPED")) { |
michael@0 | 384 | id = EVENT_P2P_FIND_STOPPED; |
michael@0 | 385 | } else if (0 === aEventString.indexOf("P2P-INVITATION-RESULT")) { |
michael@0 | 386 | id = EVENT_P2P_INVITATION_RESULT; |
michael@0 | 387 | info.status = /status=([0-9]+)/.exec(aEventString)[1]; |
michael@0 | 388 | } else if (0 === aEventString.indexOf("P2P-INVITATION-RECEIVED")) { |
michael@0 | 389 | // e.g. "P2P-INVITATION-RECEIVED sa=32:85:a9:da:e6:1f persistent=7". |
michael@0 | 390 | id = EVENT_P2P_INVITATION_RECEIVED; |
michael@0 | 391 | info.address = /sa=([0-9a-f:]+)/.exec(aEventString)[1]; |
michael@0 | 392 | info.netId = /persistent=([0-9]+)/.exec(aEventString)[1]; |
michael@0 | 393 | } else if (0 === aEventString.indexOf("P2P-PROV-DISC-FAILURE")) { |
michael@0 | 394 | id = EVENT_P2P_PROV_DISC_FAILURE; |
michael@0 | 395 | } else { |
michael@0 | 396 | // Not P2P event but we do receive it. Try to recognize it. |
michael@0 | 397 | if (0 === aEventString.indexOf("AP-STA-DISCONNECTED")) { |
michael@0 | 398 | id = EVENT_AP_STA_DISCONNECTED; |
michael@0 | 399 | info.address = tokens[1]; |
michael@0 | 400 | } else if (0 === aEventString.indexOf("AP-STA-CONNECTED")) { |
michael@0 | 401 | id = EVENT_AP_STA_CONNECTED; |
michael@0 | 402 | info.address = tokens[1]; |
michael@0 | 403 | } else { |
michael@0 | 404 | // Neither P2P event nor recognized supplicant event. |
michael@0 | 405 | debug('Unknwon event string: ' + aEventString); |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | let event = {id: id, info: info}; |
michael@0 | 410 | debug('Event parsing result: ' + aEventString + ": " + JSON.stringify(event)); |
michael@0 | 411 | |
michael@0 | 412 | return event; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | function isIgnoredEvent(aEventString) { |
michael@0 | 416 | const IGNORED_EVENTS = [ |
michael@0 | 417 | "CTRL-EVENT-BSS-ADDED", |
michael@0 | 418 | "CTRL-EVENT-BSS-REMOVED", |
michael@0 | 419 | "CTRL-EVENT-SCAN-RESULTS", |
michael@0 | 420 | "CTRL-EVENT-STATE-CHANGE", |
michael@0 | 421 | "WPS-AP-AVAILABLE", |
michael@0 | 422 | "WPS-ENROLLEE-SEEN" |
michael@0 | 423 | ]; |
michael@0 | 424 | for(let i = 0; i < IGNORED_EVENTS.length; i++) { |
michael@0 | 425 | if (0 === aEventString.indexOf(IGNORED_EVENTS[i])) { |
michael@0 | 426 | return true; |
michael@0 | 427 | } |
michael@0 | 428 | } |
michael@0 | 429 | return false; |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | function isPeerGroupOwner(aGroupFlag) { |
michael@0 | 433 | return (aGroupFlag & GROUP_CAPAB_GROUP_OWNER) !== 0; |
michael@0 | 434 | } |
michael@0 | 435 | |
michael@0 | 436 | // Convert flag to a wps capability array. |
michael@0 | 437 | // |
michael@0 | 438 | // @param aWpsFlag Number that represents the wps capabilities. |
michael@0 | 439 | // @return Array of WPS flag. |
michael@0 | 440 | function wpsFlagToCapabilities(aWpsFlag) { |
michael@0 | 441 | let wpsCapabilities = []; |
michael@0 | 442 | if (aWpsFlag & 0x8) { |
michael@0 | 443 | wpsCapabilities.push(WPS_METHOD_DISPLAY); |
michael@0 | 444 | } |
michael@0 | 445 | if (aWpsFlag & 0x80) { |
michael@0 | 446 | wpsCapabilities.push(WPS_METHOD_PBC); |
michael@0 | 447 | } |
michael@0 | 448 | if (aWpsFlag & 0x100) { |
michael@0 | 449 | wpsCapabilities.push(WPS_METHOD_KEYPAD); |
michael@0 | 450 | } |
michael@0 | 451 | return wpsCapabilities; |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | _stateMachine.start(); |
michael@0 | 455 | return manager; |
michael@0 | 456 | }; |
michael@0 | 457 | |
michael@0 | 458 | function P2pStateMachine(aP2pCommand, aNetUtil) { |
michael@0 | 459 | function debug(aMsg) { |
michael@0 | 460 | if (gDebug) { |
michael@0 | 461 | dump('-------------- WifiP2pStateMachine: ' + aMsg); |
michael@0 | 462 | } |
michael@0 | 463 | } |
michael@0 | 464 | |
michael@0 | 465 | let p2pSm = {}; // The state machine to return. |
michael@0 | 466 | |
michael@0 | 467 | let _sm = StateMachine('WIFIP2P'); // The general purpose state machine. |
michael@0 | 468 | |
michael@0 | 469 | // Information we need to keep track across states. |
michael@0 | 470 | let _observer; |
michael@0 | 471 | |
michael@0 | 472 | let _onEnabled; |
michael@0 | 473 | let _onDisabled; |
michael@0 | 474 | let _onSupplicantConnected; |
michael@0 | 475 | let _savedConfig = {}; // Configuration used to do P2P_CONNECT. |
michael@0 | 476 | let _groupInfo = {}; // The information of the group we have formed. |
michael@0 | 477 | let _removedGroupInfo = {}; // Used to store the group info we are going to remove. |
michael@0 | 478 | |
michael@0 | 479 | let _scanBlocked = false; |
michael@0 | 480 | let _scanPostponded = false; |
michael@0 | 481 | |
michael@0 | 482 | let _localDevice = { |
michael@0 | 483 | address: "", |
michael@0 | 484 | deviceName: DEFAULT_P2P_DEVICE_NAME + "_" + libcutils.property_get("ro.build.product"), |
michael@0 | 485 | wpsCapabilities: [WPS_METHOD_PBC, WPS_METHOD_KEYPAD, WPS_METHOD_DISPLAY] |
michael@0 | 486 | }; |
michael@0 | 487 | |
michael@0 | 488 | let _p2pNetworkInterface = { |
michael@0 | 489 | QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]), |
michael@0 | 490 | |
michael@0 | 491 | state: Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED, |
michael@0 | 492 | type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI_P2P, |
michael@0 | 493 | name: P2P_INTERFACE_NAME, |
michael@0 | 494 | ips: [], |
michael@0 | 495 | prefixLengths: [], |
michael@0 | 496 | dnses: [], |
michael@0 | 497 | gateways: [], |
michael@0 | 498 | httpProxyHost: null, |
michael@0 | 499 | httpProxyPort: null, |
michael@0 | 500 | |
michael@0 | 501 | // help |
michael@0 | 502 | registered: false, |
michael@0 | 503 | |
michael@0 | 504 | getAddresses: function (ips, prefixLengths) { |
michael@0 | 505 | ips.value = this.ips.slice(); |
michael@0 | 506 | prefixLengths.value = this.prefixLengths.slice(); |
michael@0 | 507 | |
michael@0 | 508 | return this.ips.length; |
michael@0 | 509 | }, |
michael@0 | 510 | |
michael@0 | 511 | getGateways: function (count) { |
michael@0 | 512 | if (count) { |
michael@0 | 513 | count.value = this.gateways.length; |
michael@0 | 514 | } |
michael@0 | 515 | return this.gateways.slice(); |
michael@0 | 516 | }, |
michael@0 | 517 | |
michael@0 | 518 | getDnses: function (count) { |
michael@0 | 519 | if (count) { |
michael@0 | 520 | count.value = this.dnses.length; |
michael@0 | 521 | } |
michael@0 | 522 | return this.dnses.slice(); |
michael@0 | 523 | } |
michael@0 | 524 | }; |
michael@0 | 525 | |
michael@0 | 526 | //--------------------------------------------------------- |
michael@0 | 527 | // State machine APIs. |
michael@0 | 528 | //--------------------------------------------------------- |
michael@0 | 529 | |
michael@0 | 530 | // Register the observer which is implemented in WifiP2pWorkerObserver.jsm. |
michael@0 | 531 | // |
michael@0 | 532 | // @param aObserver: |
michael@0 | 533 | // .onEnabled |
michael@0 | 534 | // .onDisbaled |
michael@0 | 535 | // .onPeerFound |
michael@0 | 536 | // .onPeerLost |
michael@0 | 537 | // .onConnecting |
michael@0 | 538 | // .onConnected |
michael@0 | 539 | // .onDisconnected |
michael@0 | 540 | // .onLocalDeviceChanged |
michael@0 | 541 | p2pSm.setObserver = function(aObserver) { |
michael@0 | 542 | _observer = aObserver; |
michael@0 | 543 | }; |
michael@0 | 544 | |
michael@0 | 545 | p2pSm.start = function() { |
michael@0 | 546 | _sm.start(stateDisabled); |
michael@0 | 547 | }; |
michael@0 | 548 | |
michael@0 | 549 | p2pSm.sendEvent = function(aEvent) { |
michael@0 | 550 | let willBeHandled = isInP2pManagedState(_sm.getCurrentState()); |
michael@0 | 551 | _sm.sendEvent(aEvent); |
michael@0 | 552 | return willBeHandled; |
michael@0 | 553 | }; |
michael@0 | 554 | |
michael@0 | 555 | // Initialize internal state machine _sm. |
michael@0 | 556 | _sm.setDefaultEventHandler(handleEventCommon); |
michael@0 | 557 | |
michael@0 | 558 | //---------------------------------------------------------- |
michael@0 | 559 | // State definition. |
michael@0 | 560 | //---------------------------------------------------------- |
michael@0 | 561 | |
michael@0 | 562 | // The initial state. |
michael@0 | 563 | var stateDisabled = _sm.makeState("DISABLED", { |
michael@0 | 564 | enter: function() { |
michael@0 | 565 | _onEnabled = null; |
michael@0 | 566 | _onSupplicantConnected = null; |
michael@0 | 567 | _savedConfig = null; |
michael@0 | 568 | _groupInfo = null; |
michael@0 | 569 | _removedGroupInfo = null; |
michael@0 | 570 | _scanBlocked = false; |
michael@0 | 571 | _scanPostponded = false; |
michael@0 | 572 | |
michael@0 | 573 | unregisterP2pNetworkInteface(); |
michael@0 | 574 | }, |
michael@0 | 575 | |
michael@0 | 576 | handleEvent: function(aEvent) { |
michael@0 | 577 | switch (aEvent.id) { |
michael@0 | 578 | case EVENT_P2P_CMD_ENABLE: |
michael@0 | 579 | _onEnabled = aEvent.info.onEnabled; |
michael@0 | 580 | _onSupplicantConnected = aEvent.info.onSupplicantConnected; |
michael@0 | 581 | _sm.gotoState(stateEnabling); |
michael@0 | 582 | break; |
michael@0 | 583 | |
michael@0 | 584 | default: |
michael@0 | 585 | return false; |
michael@0 | 586 | } // End of switch. |
michael@0 | 587 | return true; |
michael@0 | 588 | } |
michael@0 | 589 | }); |
michael@0 | 590 | |
michael@0 | 591 | // The state where we are trying to enable wifi p2p. |
michael@0 | 592 | var stateEnabling = _sm.makeState("ENABLING", { |
michael@0 | 593 | enter: function() { |
michael@0 | 594 | |
michael@0 | 595 | function onFailure() |
michael@0 | 596 | { |
michael@0 | 597 | _onEnabled(false); |
michael@0 | 598 | _sm.gotoState(stateDisabled); |
michael@0 | 599 | } |
michael@0 | 600 | |
michael@0 | 601 | function onSuccess() |
michael@0 | 602 | { |
michael@0 | 603 | _onEnabled(true); |
michael@0 | 604 | _sm.gotoState(stateInactive); |
michael@0 | 605 | } |
michael@0 | 606 | |
michael@0 | 607 | _sm.pause(); |
michael@0 | 608 | |
michael@0 | 609 | // Step 1: Connect to p2p0. |
michael@0 | 610 | aP2pCommand.connectToSupplicant(function (status) { |
michael@0 | 611 | let detail; |
michael@0 | 612 | |
michael@0 | 613 | if (0 !== status) { |
michael@0 | 614 | debug('Failed to connect to p2p0'); |
michael@0 | 615 | onFailure(); |
michael@0 | 616 | return; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | debug('wpa_supplicant p2p0 connected!'); |
michael@0 | 620 | _onSupplicantConnected(); |
michael@0 | 621 | |
michael@0 | 622 | // Step 2: Get MAC address. |
michael@0 | 623 | if (!_localDevice.address) { |
michael@0 | 624 | aP2pCommand.getMacAddress(function (address) { |
michael@0 | 625 | if (!address) { |
michael@0 | 626 | debug('Failed to get MAC address....'); |
michael@0 | 627 | onFailure(); |
michael@0 | 628 | return; |
michael@0 | 629 | } |
michael@0 | 630 | debug('Got mac address: ' + address); |
michael@0 | 631 | _localDevice.address = address; |
michael@0 | 632 | _observer.onLocalDeviceChanged(_localDevice); |
michael@0 | 633 | }); |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | // Step 3: Enable p2p with the device name and wps methods. |
michael@0 | 637 | detail = { deviceName: _localDevice.deviceName, |
michael@0 | 638 | deviceType: libcutils.property_get("ro.moz.wifi.p2p_device_type") || DEFAULT_P2P_DEVICE_TYPE, |
michael@0 | 639 | wpsMethods: libcutils.property_get("ro.moz.wifi.p2p_wps_methods") || DEFAULT_P2P_WPS_METHODS }; |
michael@0 | 640 | |
michael@0 | 641 | aP2pCommand.p2pEnable(detail, function (success) { |
michael@0 | 642 | if (!success) { |
michael@0 | 643 | debug('Failed to enable p2p'); |
michael@0 | 644 | onFailure(); |
michael@0 | 645 | return; |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | debug('P2P is enabled! Enabling net interface...'); |
michael@0 | 649 | |
michael@0 | 650 | // Step 4: Enable p2p0 net interface. wpa_supplicant may have |
michael@0 | 651 | // already done it for us. |
michael@0 | 652 | aNetUtil.enableInterface(P2P_INTERFACE_NAME, function (success) { |
michael@0 | 653 | onSuccess(); |
michael@0 | 654 | }); |
michael@0 | 655 | }); |
michael@0 | 656 | }); |
michael@0 | 657 | }, |
michael@0 | 658 | |
michael@0 | 659 | handleEvent: function(aEvent) { |
michael@0 | 660 | // We won't receive any event since all of them will be blocked. |
michael@0 | 661 | return true; |
michael@0 | 662 | } |
michael@0 | 663 | }); |
michael@0 | 664 | |
michael@0 | 665 | // The state just after enabling wifi direct. |
michael@0 | 666 | var stateInactive = _sm.makeState("INACTIVE", { |
michael@0 | 667 | enter: function() { |
michael@0 | 668 | registerP2pNetworkInteface(); |
michael@0 | 669 | |
michael@0 | 670 | if (_sm.getPreviousState() !== stateEnabling) { |
michael@0 | 671 | _observer.onDisconnected(_savedConfig); |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | _savedConfig = null; // Used to connect p2p peer. |
michael@0 | 675 | _groupInfo = null; // The information of the formed group. |
michael@0 | 676 | }, |
michael@0 | 677 | |
michael@0 | 678 | handleEvent: function(aEvent) { |
michael@0 | 679 | switch (aEvent.id) { |
michael@0 | 680 | // Receiving the following 3 states implies someone is trying to |
michael@0 | 681 | // connect to me. |
michael@0 | 682 | case EVENT_P2P_PROV_DISC_PBC_REQ: |
michael@0 | 683 | case EVENT_P2P_PROV_DISC_SHOW_PIN: |
michael@0 | 684 | case EVENT_P2P_PROV_DISC_ENTER_PIN: |
michael@0 | 685 | debug('Someone is trying to connect to me: ' + JSON.stringify(aEvent.info)); |
michael@0 | 686 | |
michael@0 | 687 | _savedConfig = { |
michael@0 | 688 | name: aEvent.info.name, |
michael@0 | 689 | address: aEvent.info.address, |
michael@0 | 690 | wpsMethod: aEvent.info.wpsMethod, |
michael@0 | 691 | goIntent: DEFAULT_GO_INTENT, |
michael@0 | 692 | pin: aEvent.info.pin // EVENT_P2P_PROV_DISC_SHOW_PIN only. |
michael@0 | 693 | }; |
michael@0 | 694 | |
michael@0 | 695 | _sm.gotoState(stateWaitingForConfirmation); |
michael@0 | 696 | break; |
michael@0 | 697 | |
michael@0 | 698 | // Connect to a peer. |
michael@0 | 699 | case EVENT_P2P_CMD_CONNECT: |
michael@0 | 700 | debug('Trying to connect to peer: ' + JSON.stringify(aEvent.info)); |
michael@0 | 701 | |
michael@0 | 702 | _savedConfig = { |
michael@0 | 703 | address: aEvent.info.address, |
michael@0 | 704 | wpsMethod: aEvent.info.wpsMethod, |
michael@0 | 705 | goIntent: aEvent.info.goIntent |
michael@0 | 706 | }; |
michael@0 | 707 | |
michael@0 | 708 | _sm.gotoState(stateProvisionDiscovery); |
michael@0 | 709 | aEvent.info.onDoConnect(true); |
michael@0 | 710 | break; |
michael@0 | 711 | |
michael@0 | 712 | case EVENT_P2P_INVITATION_RECEIVED: |
michael@0 | 713 | _savedConfig = { |
michael@0 | 714 | address: aEvent.info.address, |
michael@0 | 715 | wpsMethod: WPS_METHOD_PBC, |
michael@0 | 716 | goIntent: DEFAULT_GO_INTENT, |
michael@0 | 717 | netId: aEvent.info.netId |
michael@0 | 718 | }; |
michael@0 | 719 | _sm.gotoState(stateWaitingForInvitationConfirmation); |
michael@0 | 720 | break; |
michael@0 | 721 | |
michael@0 | 722 | case EVENT_P2P_GROUP_STARTED: |
michael@0 | 723 | // Most likely the peer just reinvoked a peristen group and succeeeded. |
michael@0 | 724 | |
michael@0 | 725 | _savedConfig = { address: aEvent.info.goAddress }; |
michael@0 | 726 | |
michael@0 | 727 | _sm.pause(); |
michael@0 | 728 | handleGroupStarted(aEvent.info, function (success) { |
michael@0 | 729 | _sm.resume(); |
michael@0 | 730 | }); |
michael@0 | 731 | break; |
michael@0 | 732 | |
michael@0 | 733 | case EVENT_AP_STA_DISCONNECTED: |
michael@0 | 734 | // We will hit this case when we used to be a group owner and |
michael@0 | 735 | // requested to remove the group we owned. |
michael@0 | 736 | break; |
michael@0 | 737 | |
michael@0 | 738 | default: |
michael@0 | 739 | return false; |
michael@0 | 740 | } // End of switch. |
michael@0 | 741 | return true; |
michael@0 | 742 | }, |
michael@0 | 743 | }); |
michael@0 | 744 | |
michael@0 | 745 | // Waiting for user's confirmation. |
michael@0 | 746 | var stateWaitingForConfirmation = _sm.makeState("WAITING_FOR_CONFIRMATION", { |
michael@0 | 747 | timeoutTimer: null, |
michael@0 | 748 | |
michael@0 | 749 | enter: function() { |
michael@0 | 750 | gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); |
michael@0 | 751 | this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); |
michael@0 | 752 | }, |
michael@0 | 753 | |
michael@0 | 754 | handleEvent: function(aEvent) { |
michael@0 | 755 | switch (aEvent.id) { |
michael@0 | 756 | case EVENT_P2P_SET_PAIRING_CONFIRMATION: |
michael@0 | 757 | if (!aEvent.info.accepted) { |
michael@0 | 758 | debug('User rejected this request'); |
michael@0 | 759 | _sm.gotoState(stateInactive); // Reset to inactive state. |
michael@0 | 760 | break; |
michael@0 | 761 | } |
michael@0 | 762 | |
michael@0 | 763 | debug('User accepted this request'); |
michael@0 | 764 | |
michael@0 | 765 | // The only information we may have to grab from user. |
michael@0 | 766 | _savedConfig.pin = aEvent.info.pin; |
michael@0 | 767 | |
michael@0 | 768 | // The case that user requested to form a group ealier on. |
michael@0 | 769 | // Just go to connecting state and do p2p_connect. |
michael@0 | 770 | if (_sm.getPreviousState() === stateProvisionDiscovery) { |
michael@0 | 771 | _sm.gotoState(stateConnecting); |
michael@0 | 772 | break; |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | // Otherwise, wait for EVENT_P2P_GO_NEG_REQUEST. |
michael@0 | 776 | _sm.gotoState(stateWaitingForNegReq); |
michael@0 | 777 | break; |
michael@0 | 778 | |
michael@0 | 779 | case EVENT_TIMEOUT_PAIRING_CONFIRMATION: |
michael@0 | 780 | debug('Confirmation timeout!'); |
michael@0 | 781 | _sm.gotoState(stateInactive); |
michael@0 | 782 | break; |
michael@0 | 783 | |
michael@0 | 784 | case EVENT_P2P_GO_NEG_REQUEST: |
michael@0 | 785 | _sm.deferEvent(aEvent); |
michael@0 | 786 | break; |
michael@0 | 787 | |
michael@0 | 788 | default: |
michael@0 | 789 | return false; |
michael@0 | 790 | } // End of switch. |
michael@0 | 791 | |
michael@0 | 792 | return true; |
michael@0 | 793 | }, |
michael@0 | 794 | |
michael@0 | 795 | exit: function() { |
michael@0 | 796 | this.timeoutTimer.cancel(); |
michael@0 | 797 | this.timeoutTimer = null; |
michael@0 | 798 | } |
michael@0 | 799 | }); |
michael@0 | 800 | |
michael@0 | 801 | var stateWaitingForNegReq = _sm.makeState("WAITING_FOR_NEG_REQ", { |
michael@0 | 802 | timeoutTimer: null, |
michael@0 | 803 | |
michael@0 | 804 | enter: function() { |
michael@0 | 805 | debug('Wait for EVENT_P2P_GO_NEG_REQUEST'); |
michael@0 | 806 | this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_NEG_REQ); |
michael@0 | 807 | }, |
michael@0 | 808 | |
michael@0 | 809 | handleEvent: function(aEvent) { |
michael@0 | 810 | switch (aEvent.id) { |
michael@0 | 811 | case EVENT_P2P_GO_NEG_REQUEST: |
michael@0 | 812 | if (aEvent.info.wpsMethod !== _savedConfig.wpsMethod) { |
michael@0 | 813 | debug('Unmatched wps method: ' + aEvent.info.wpsMethod + ", " + _savedConfig.wpsMetho); |
michael@0 | 814 | } |
michael@0 | 815 | _sm.gotoState(stateConnecting); |
michael@0 | 816 | break; |
michael@0 | 817 | |
michael@0 | 818 | case EVENT_TIMEOUT_NEG_REQ: |
michael@0 | 819 | debug("Waiting for NEG-REQ timeout"); |
michael@0 | 820 | _sm.gotoState(stateInactive); |
michael@0 | 821 | break; |
michael@0 | 822 | |
michael@0 | 823 | default: |
michael@0 | 824 | return false; |
michael@0 | 825 | } // End of switch. |
michael@0 | 826 | return true; |
michael@0 | 827 | }, |
michael@0 | 828 | |
michael@0 | 829 | exit: function() { |
michael@0 | 830 | this.timeoutTimer.cancel(); |
michael@0 | 831 | this.timeoutTimer = null; |
michael@0 | 832 | } |
michael@0 | 833 | }); |
michael@0 | 834 | |
michael@0 | 835 | // Waiting for user's confirmation for invitation. |
michael@0 | 836 | var stateWaitingForInvitationConfirmation = _sm.makeState("WAITING_FOR_INV_CONFIRMATION", { |
michael@0 | 837 | timeoutTimer: null, |
michael@0 | 838 | |
michael@0 | 839 | enter: function() { |
michael@0 | 840 | gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); |
michael@0 | 841 | this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); |
michael@0 | 842 | }, |
michael@0 | 843 | |
michael@0 | 844 | handleEvent: function(aEvent) { |
michael@0 | 845 | switch (aEvent.id) { |
michael@0 | 846 | case EVENT_P2P_SET_PAIRING_CONFIRMATION: |
michael@0 | 847 | if (!aEvent.info.accepted) { |
michael@0 | 848 | debug('User rejected this request'); |
michael@0 | 849 | _sm.gotoState(stateInactive); // Reset to inactive state. |
michael@0 | 850 | break; |
michael@0 | 851 | } |
michael@0 | 852 | |
michael@0 | 853 | debug('User accepted this request'); |
michael@0 | 854 | _sm.pause(); |
michael@0 | 855 | aP2pCommand.p2pGetGroupCapab(_savedConfig.address, function (gc) { |
michael@0 | 856 | let isPeeGroupOwner = gc & GROUP_CAPAB_GROUP_OWNER; |
michael@0 | 857 | _sm.gotoState(isPeeGroupOwner ? stateGroupAdding : stateReinvoking); |
michael@0 | 858 | }); |
michael@0 | 859 | |
michael@0 | 860 | break; |
michael@0 | 861 | |
michael@0 | 862 | case EVENT_TIMEOUT_PAIRING_CONFIRMATION: |
michael@0 | 863 | debug('Confirmation timeout!'); |
michael@0 | 864 | _sm.gotoState(stateInactive); |
michael@0 | 865 | break; |
michael@0 | 866 | |
michael@0 | 867 | default: |
michael@0 | 868 | return false; |
michael@0 | 869 | } // End of switch. |
michael@0 | 870 | |
michael@0 | 871 | return true; |
michael@0 | 872 | }, |
michael@0 | 873 | |
michael@0 | 874 | exit: function() { |
michael@0 | 875 | this.timeoutTimer.cancel(); |
michael@0 | 876 | this.timeoutTimer = null; |
michael@0 | 877 | } |
michael@0 | 878 | }); |
michael@0 | 879 | |
michael@0 | 880 | var stateGroupAdding = _sm.makeState("GROUP_ADDING", { |
michael@0 | 881 | timeoutTimer: null, |
michael@0 | 882 | |
michael@0 | 883 | enter: function() { |
michael@0 | 884 | let self = this; |
michael@0 | 885 | |
michael@0 | 886 | _observer.onConnecting(_savedConfig); |
michael@0 | 887 | |
michael@0 | 888 | _sm.pause(); |
michael@0 | 889 | aP2pCommand.p2pGroupAdd(_savedConfig.netId, function (success) { |
michael@0 | 890 | if (!success) { |
michael@0 | 891 | _sm.gotoState(stateInactive); |
michael@0 | 892 | return; |
michael@0 | 893 | } |
michael@0 | 894 | // Waiting for EVENT_P2P_GROUP_STARTED. |
michael@0 | 895 | self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); |
michael@0 | 896 | _sm.resume(); |
michael@0 | 897 | }); |
michael@0 | 898 | }, |
michael@0 | 899 | |
michael@0 | 900 | handleEvent: function(aEvent) { |
michael@0 | 901 | switch (aEvent.id) { |
michael@0 | 902 | case EVENT_P2P_GROUP_STARTED: |
michael@0 | 903 | _sm.pause(); |
michael@0 | 904 | handleGroupStarted(aEvent.info, function (success) { |
michael@0 | 905 | _sm.resume(); |
michael@0 | 906 | }); |
michael@0 | 907 | break; |
michael@0 | 908 | |
michael@0 | 909 | case EVENT_P2P_GO_NEG_FAILURE: |
michael@0 | 910 | debug('Negotiation failure. Go back to inactive state'); |
michael@0 | 911 | _sm.gotoState(stateInactive); |
michael@0 | 912 | break; |
michael@0 | 913 | |
michael@0 | 914 | case EVENT_TIMEOUT_CONNECTING: |
michael@0 | 915 | debug('Connecting timeout! Go back to inactive state'); |
michael@0 | 916 | _sm.gotoState(stateInactive); |
michael@0 | 917 | break; |
michael@0 | 918 | |
michael@0 | 919 | case EVENT_P2P_GROUP_FORMATION_SUCCESS: |
michael@0 | 920 | case EVENT_P2P_GO_NEG_SUCCESS: |
michael@0 | 921 | break; |
michael@0 | 922 | |
michael@0 | 923 | case EVENT_P2P_GROUP_FORMATION_FAILURE: |
michael@0 | 924 | debug('Group formation failure'); |
michael@0 | 925 | _sm.gotoState(stateInactive); |
michael@0 | 926 | break; |
michael@0 | 927 | |
michael@0 | 928 | case EVENT_P2P_GROUP_REMOVED: |
michael@0 | 929 | debug('Received P2P-GROUP-REMOVED due to previous failed handleGroupdStarted()'); |
michael@0 | 930 | _removedGroupInfo = { |
michael@0 | 931 | role: aEvent.info.role, |
michael@0 | 932 | ifname: aEvent.info.ifname |
michael@0 | 933 | }; |
michael@0 | 934 | _sm.gotoState(stateDisconnecting); |
michael@0 | 935 | break; |
michael@0 | 936 | |
michael@0 | 937 | default: |
michael@0 | 938 | return false; |
michael@0 | 939 | } // End of switch. |
michael@0 | 940 | |
michael@0 | 941 | return true; |
michael@0 | 942 | }, |
michael@0 | 943 | |
michael@0 | 944 | exit: function() { |
michael@0 | 945 | this.timeoutTimer.cancel(); |
michael@0 | 946 | this.timeoutTimer = null; |
michael@0 | 947 | } |
michael@0 | 948 | }); |
michael@0 | 949 | |
michael@0 | 950 | var stateReinvoking = _sm.makeState("REINVOKING", { |
michael@0 | 951 | timeoutTimer: null, |
michael@0 | 952 | |
michael@0 | 953 | enter: function() { |
michael@0 | 954 | let self = this; |
michael@0 | 955 | |
michael@0 | 956 | _observer.onConnecting(_savedConfig); |
michael@0 | 957 | _sm.pause(); |
michael@0 | 958 | aP2pCommand.p2pReinvoke(_savedConfig.netId, _savedConfig.address, function(success) { |
michael@0 | 959 | if (!success) { |
michael@0 | 960 | _sm.gotoState(stateInactive); |
michael@0 | 961 | return; |
michael@0 | 962 | } |
michael@0 | 963 | // Waiting for EVENT_P2P_GROUP_STARTED. |
michael@0 | 964 | self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); |
michael@0 | 965 | _sm.resume(); |
michael@0 | 966 | }); |
michael@0 | 967 | }, |
michael@0 | 968 | |
michael@0 | 969 | handleEvent: function(aEvent) { |
michael@0 | 970 | switch (aEvent.id) { |
michael@0 | 971 | case EVENT_P2P_GROUP_STARTED: |
michael@0 | 972 | _sm.pause(); |
michael@0 | 973 | handleGroupStarted(aEvent.info, function(success) { |
michael@0 | 974 | _sm.resume(); |
michael@0 | 975 | }); |
michael@0 | 976 | break; |
michael@0 | 977 | |
michael@0 | 978 | case EVENT_P2P_GO_NEG_FAILURE: |
michael@0 | 979 | debug('Negotiation failure. Go back to inactive state'); |
michael@0 | 980 | _sm.gotoState(stateInactive); |
michael@0 | 981 | break; |
michael@0 | 982 | |
michael@0 | 983 | case EVENT_TIMEOUT_CONNECTING: |
michael@0 | 984 | debug('Connecting timeout! Go back to inactive state'); |
michael@0 | 985 | _sm.gotoState(stateInactive); |
michael@0 | 986 | break; |
michael@0 | 987 | |
michael@0 | 988 | case EVENT_P2P_GROUP_FORMATION_SUCCESS: |
michael@0 | 989 | case EVENT_P2P_GO_NEG_SUCCESS: |
michael@0 | 990 | break; |
michael@0 | 991 | |
michael@0 | 992 | case EVENT_P2P_GROUP_FORMATION_FAILURE: |
michael@0 | 993 | debug('Group formation failure'); |
michael@0 | 994 | _sm.gotoState(stateInactive); |
michael@0 | 995 | break; |
michael@0 | 996 | |
michael@0 | 997 | case EVENT_P2P_GROUP_REMOVED: |
michael@0 | 998 | debug('Received P2P-GROUP-REMOVED due to previous failed handleGroupdStarted()'); |
michael@0 | 999 | _removedGroupInfo = { |
michael@0 | 1000 | role: aEvent.info.role, |
michael@0 | 1001 | ifname: aEvent.info.ifname |
michael@0 | 1002 | }; |
michael@0 | 1003 | _sm.gotoState(stateDisconnecting); |
michael@0 | 1004 | break; |
michael@0 | 1005 | |
michael@0 | 1006 | default: |
michael@0 | 1007 | return false; |
michael@0 | 1008 | } // End of switch. |
michael@0 | 1009 | |
michael@0 | 1010 | return true; |
michael@0 | 1011 | }, |
michael@0 | 1012 | |
michael@0 | 1013 | exit: function() { |
michael@0 | 1014 | this.timeoutTimer.cancel(); |
michael@0 | 1015 | } |
michael@0 | 1016 | }); |
michael@0 | 1017 | |
michael@0 | 1018 | var stateProvisionDiscovery = _sm.makeState("PROVISION_DISCOVERY", { |
michael@0 | 1019 | enter: function() { |
michael@0 | 1020 | function onDiscoveryCommandSent(success) { |
michael@0 | 1021 | if (!success) { |
michael@0 | 1022 | _sm.gotoState(stateInactive); |
michael@0 | 1023 | debug('Failed to send p2p_prov_disc. Go back to inactive state.'); |
michael@0 | 1024 | return; |
michael@0 | 1025 | } |
michael@0 | 1026 | |
michael@0 | 1027 | debug('p2p_prov_disc has been sent.'); |
michael@0 | 1028 | |
michael@0 | 1029 | _sm.resume(); |
michael@0 | 1030 | // Waiting for EVENT_P2P_PROV_DISC_PBC_RESP or |
michael@0 | 1031 | // EVENT_P2P_PROV_DISC_SHOW_PIN or |
michael@0 | 1032 | // EVENT_P2P_PROV_DISC_ENTER_PIN. |
michael@0 | 1033 | } |
michael@0 | 1034 | |
michael@0 | 1035 | _sm.pause(); |
michael@0 | 1036 | aP2pCommand.p2pProvDiscovery(_savedConfig.address, |
michael@0 | 1037 | toPeerWpsMethod(_savedConfig.wpsMethod), |
michael@0 | 1038 | onDiscoveryCommandSent); |
michael@0 | 1039 | }, |
michael@0 | 1040 | |
michael@0 | 1041 | handleEvent: function(aEvent) { |
michael@0 | 1042 | switch (aEvent.id) { |
michael@0 | 1043 | case EVENT_P2P_PROV_DISC_PBC_RESP: |
michael@0 | 1044 | _sm.gotoState(stateConnecting); // No need for local user grant. |
michael@0 | 1045 | break; |
michael@0 | 1046 | case EVENT_P2P_PROV_DISC_SHOW_PIN: |
michael@0 | 1047 | case EVENT_P2P_PROV_DISC_ENTER_PIN: |
michael@0 | 1048 | if (aEvent.info.wpsMethod !== _savedConfig.wpsMethod) { |
michael@0 | 1049 | debug('Unmatched wps method: ' + aEvent.info.wpsMethod + ":" + _savedConfig.wpsMethod); |
michael@0 | 1050 | } |
michael@0 | 1051 | if (EVENT_P2P_PROV_DISC_SHOW_PIN === aEvent.id) { |
michael@0 | 1052 | _savedConfig.pin = aEvent.info.pin; |
michael@0 | 1053 | } |
michael@0 | 1054 | _sm.gotoState(stateWaitingForConfirmation); |
michael@0 | 1055 | break; |
michael@0 | 1056 | |
michael@0 | 1057 | case EVENT_P2P_PROV_DISC_FAILURE: |
michael@0 | 1058 | _sm.gotoState(stateInactive); |
michael@0 | 1059 | break; |
michael@0 | 1060 | |
michael@0 | 1061 | default: |
michael@0 | 1062 | return false; |
michael@0 | 1063 | } // End of switch. |
michael@0 | 1064 | return true; |
michael@0 | 1065 | } |
michael@0 | 1066 | }); |
michael@0 | 1067 | |
michael@0 | 1068 | // We are going to connect to the peer. |
michael@0 | 1069 | // |_savedConfig| is supposed to have been filled properly. |
michael@0 | 1070 | var stateConnecting = _sm.makeState("CONNECTING", { |
michael@0 | 1071 | timeoutTimer: null, |
michael@0 | 1072 | |
michael@0 | 1073 | enter: function() { |
michael@0 | 1074 | let self = this; |
michael@0 | 1075 | |
michael@0 | 1076 | if (null === _savedConfig.goIntent) { |
michael@0 | 1077 | _savedConfig.goIntent = DEFAULT_GO_INTENT; |
michael@0 | 1078 | } |
michael@0 | 1079 | |
michael@0 | 1080 | _observer.onConnecting(_savedConfig); |
michael@0 | 1081 | |
michael@0 | 1082 | let wpsMethodWithPin; |
michael@0 | 1083 | if (WPS_METHOD_KEYPAD === _savedConfig.wpsMethod || |
michael@0 | 1084 | WPS_METHOD_DISPLAY === _savedConfig.wpsMethod) { |
michael@0 | 1085 | // e.g. '12345678 display or '12345678 keypad'. |
michael@0 | 1086 | wpsMethodWithPin = (_savedConfig.pin + ' ' + _savedConfig.wpsMethod); |
michael@0 | 1087 | } else { |
michael@0 | 1088 | // e.g. 'pbc'. |
michael@0 | 1089 | wpsMethodWithPin = _savedConfig.wpsMethod; |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | _sm.pause(); |
michael@0 | 1093 | |
michael@0 | 1094 | aP2pCommand.p2pGetGroupCapab(_savedConfig.address, function(gc) { |
michael@0 | 1095 | debug('group capabilities of ' + _savedConfig.address + ': ' + gc); |
michael@0 | 1096 | |
michael@0 | 1097 | let isPeerGroupOwner = gc & GROUP_CAPAB_GROUP_OWNER; |
michael@0 | 1098 | let config = { address: _savedConfig.address, |
michael@0 | 1099 | wpsMethodWithPin: wpsMethodWithPin, |
michael@0 | 1100 | goIntent: _savedConfig.goIntent, |
michael@0 | 1101 | joinExistingGroup: isPeerGroupOwner }; |
michael@0 | 1102 | |
michael@0 | 1103 | aP2pCommand.p2pConnect(config, function (success) { |
michael@0 | 1104 | if (!success) { |
michael@0 | 1105 | debug('Failed to send p2p_connect'); |
michael@0 | 1106 | _sm.gotoState(stateInactive); |
michael@0 | 1107 | return; |
michael@0 | 1108 | } |
michael@0 | 1109 | debug('Waiting for EVENT_P2P_GROUP_STARTED.'); |
michael@0 | 1110 | self.timeoutTimer = initTimeoutTimer(60000, EVENT_TIMEOUT_CONNECTING); |
michael@0 | 1111 | _sm.resume(); |
michael@0 | 1112 | }); |
michael@0 | 1113 | }); |
michael@0 | 1114 | }, |
michael@0 | 1115 | |
michael@0 | 1116 | handleEvent: function(aEvent) { |
michael@0 | 1117 | switch (aEvent.id) { |
michael@0 | 1118 | case EVENT_P2P_GROUP_STARTED: |
michael@0 | 1119 | _sm.pause(); |
michael@0 | 1120 | handleGroupStarted(aEvent.info, function (success) { |
michael@0 | 1121 | _sm.resume(); |
michael@0 | 1122 | }); |
michael@0 | 1123 | break; |
michael@0 | 1124 | |
michael@0 | 1125 | case EVENT_P2P_GO_NEG_FAILURE: |
michael@0 | 1126 | debug('Negotiation failure. Go back to inactive state'); |
michael@0 | 1127 | _sm.gotoState(stateInactive); |
michael@0 | 1128 | break; |
michael@0 | 1129 | |
michael@0 | 1130 | case EVENT_TIMEOUT_CONNECTING: |
michael@0 | 1131 | debug('Connecting timeout! Go back to inactive state'); |
michael@0 | 1132 | _sm.gotoState(stateInactive); |
michael@0 | 1133 | break; |
michael@0 | 1134 | |
michael@0 | 1135 | case EVENT_P2P_GROUP_FORMATION_SUCCESS: |
michael@0 | 1136 | case EVENT_P2P_GO_NEG_SUCCESS: |
michael@0 | 1137 | break; |
michael@0 | 1138 | |
michael@0 | 1139 | case EVENT_P2P_GROUP_FORMATION_FAILURE: |
michael@0 | 1140 | debug('Group formation failure'); |
michael@0 | 1141 | _sm.gotoState(stateInactive); |
michael@0 | 1142 | break; |
michael@0 | 1143 | |
michael@0 | 1144 | case EVENT_P2P_GROUP_REMOVED: |
michael@0 | 1145 | debug('Received P2P-GROUP-REMOVED due to previous failed ' + |
michael@0 | 1146 | 'handleGroupdStarted()'); |
michael@0 | 1147 | _removedGroupInfo = { |
michael@0 | 1148 | role: aEvent.info.role, |
michael@0 | 1149 | ifname: aEvent.info.ifname |
michael@0 | 1150 | }; |
michael@0 | 1151 | _sm.gotoState(stateDisconnecting); |
michael@0 | 1152 | break; |
michael@0 | 1153 | |
michael@0 | 1154 | default: |
michael@0 | 1155 | return false; |
michael@0 | 1156 | } // End of switch. |
michael@0 | 1157 | |
michael@0 | 1158 | return true; |
michael@0 | 1159 | }, |
michael@0 | 1160 | |
michael@0 | 1161 | exit: function() { |
michael@0 | 1162 | this.timeoutTimer.cancel(); |
michael@0 | 1163 | } |
michael@0 | 1164 | }); |
michael@0 | 1165 | |
michael@0 | 1166 | var stateConnected = _sm.makeState("CONNECTED", { |
michael@0 | 1167 | groupOwner: null, |
michael@0 | 1168 | |
michael@0 | 1169 | enter: function() { |
michael@0 | 1170 | this.groupOwner = { |
michael@0 | 1171 | macAddress: _groupInfo.goAddress, |
michael@0 | 1172 | ipAddress: _groupInfo.networkInterface.gateway, |
michael@0 | 1173 | passphrase: _groupInfo.passphrase, |
michael@0 | 1174 | ssid: _groupInfo.ssid, |
michael@0 | 1175 | freq: _groupInfo.freq, |
michael@0 | 1176 | isLocal: _groupInfo.isGroupOwner |
michael@0 | 1177 | }; |
michael@0 | 1178 | |
michael@0 | 1179 | if (!_groupInfo.isGroupOwner) { |
michael@0 | 1180 | _observer.onConnected(this.groupOwner, _savedConfig); |
michael@0 | 1181 | } else { |
michael@0 | 1182 | // If I am a group owner, notify onConnected until EVENT_AP_STA_CONNECTED |
michael@0 | 1183 | // is received. |
michael@0 | 1184 | } |
michael@0 | 1185 | |
michael@0 | 1186 | _removedGroupInfo = null; |
michael@0 | 1187 | }, |
michael@0 | 1188 | |
michael@0 | 1189 | handleEvent: function(aEvent) { |
michael@0 | 1190 | switch (aEvent.id) { |
michael@0 | 1191 | case EVENT_AP_STA_CONNECTED: |
michael@0 | 1192 | if (_groupInfo.isGroupOwner) { |
michael@0 | 1193 | _observer.onConnected(this.groupOwner, _savedConfig); |
michael@0 | 1194 | } |
michael@0 | 1195 | break; |
michael@0 | 1196 | |
michael@0 | 1197 | case EVENT_P2P_GROUP_REMOVED: |
michael@0 | 1198 | _removedGroupInfo = { |
michael@0 | 1199 | role: aEvent.info.role, |
michael@0 | 1200 | ifname: aEvent.info.ifname |
michael@0 | 1201 | }; |
michael@0 | 1202 | _sm.gotoState(stateDisconnecting); |
michael@0 | 1203 | break; |
michael@0 | 1204 | |
michael@0 | 1205 | case EVENT_AP_STA_DISCONNECTED: |
michael@0 | 1206 | debug('Client disconnected: ' + aEvent.info.address); |
michael@0 | 1207 | |
michael@0 | 1208 | // Now we suppose it's the only client. Remove my group. |
michael@0 | 1209 | _sm.pause(); |
michael@0 | 1210 | aP2pCommand.p2pGroupRemove(_groupInfo.ifname, function (success) { |
michael@0 | 1211 | debug('Requested to remove p2p group. Wait for EVENT_P2P_GROUP_REMOVED.'); |
michael@0 | 1212 | _sm.resume(); |
michael@0 | 1213 | }); |
michael@0 | 1214 | break; |
michael@0 | 1215 | |
michael@0 | 1216 | case EVENT_P2P_CMD_DISCONNECT: |
michael@0 | 1217 | // Since we only support single connection, we can ignore |
michael@0 | 1218 | // the given peer address. |
michael@0 | 1219 | _sm.pause(); |
michael@0 | 1220 | aP2pCommand.p2pGroupRemove(_groupInfo.ifname, function(success) { |
michael@0 | 1221 | aEvent.info.onDoDisconnect(true); |
michael@0 | 1222 | _sm.resume(); |
michael@0 | 1223 | }); |
michael@0 | 1224 | |
michael@0 | 1225 | debug('Sent disconnect command. Wait for EVENT_P2P_GROUP_REMOVED.'); |
michael@0 | 1226 | break; |
michael@0 | 1227 | |
michael@0 | 1228 | case EVENT_P2P_PROV_DISC_PBC_REQ: |
michael@0 | 1229 | case EVENT_P2P_PROV_DISC_SHOW_PIN: |
michael@0 | 1230 | case EVENT_P2P_PROV_DISC_ENTER_PIN: |
michael@0 | 1231 | debug('Someone is trying to connect to me: ' + JSON.stringify(aEvent.info)); |
michael@0 | 1232 | |
michael@0 | 1233 | _savedConfig = { |
michael@0 | 1234 | name: aEvent.info.name, |
michael@0 | 1235 | address: aEvent.info.address, |
michael@0 | 1236 | wpsMethod: aEvent.info.wpsMethod, |
michael@0 | 1237 | pin: aEvent.info.pin |
michael@0 | 1238 | }; |
michael@0 | 1239 | |
michael@0 | 1240 | _sm.gotoState(stateWaitingForJoiningConfirmation); |
michael@0 | 1241 | break; |
michael@0 | 1242 | |
michael@0 | 1243 | default: |
michael@0 | 1244 | return false; |
michael@0 | 1245 | } // end of switch |
michael@0 | 1246 | return true; |
michael@0 | 1247 | } |
michael@0 | 1248 | }); |
michael@0 | 1249 | |
michael@0 | 1250 | var stateWaitingForJoiningConfirmation = _sm.makeState("WAITING_FOR_JOINING_CONFIRMATION", { |
michael@0 | 1251 | timeoutTimer: null, |
michael@0 | 1252 | |
michael@0 | 1253 | enter: function() { |
michael@0 | 1254 | gSysMsgr.broadcastMessage(PAIRING_REQUEST_SYS_MSG, _savedConfig); |
michael@0 | 1255 | this.timeoutTimer = initTimeoutTimer(30000, EVENT_TIMEOUT_PAIRING_CONFIRMATION); |
michael@0 | 1256 | }, |
michael@0 | 1257 | |
michael@0 | 1258 | handleEvent: function (aEvent) { |
michael@0 | 1259 | switch (aEvent.id) { |
michael@0 | 1260 | case EVENT_P2P_SET_PAIRING_CONFIRMATION: |
michael@0 | 1261 | if (!aEvent.info.accepted) { |
michael@0 | 1262 | debug('User rejected invitation!'); |
michael@0 | 1263 | _sm.gotoState(stateConnected); |
michael@0 | 1264 | break; |
michael@0 | 1265 | } |
michael@0 | 1266 | |
michael@0 | 1267 | let onWpsCommandSent = function(success) { |
michael@0 | 1268 | _observer.onConnecting(_savedConfig); |
michael@0 | 1269 | _sm.gotoState(stateConnected); |
michael@0 | 1270 | }; |
michael@0 | 1271 | |
michael@0 | 1272 | _sm.pause(); |
michael@0 | 1273 | if (WPS_METHOD_PBC === _savedConfig.wpsMethod) { |
michael@0 | 1274 | aP2pCommand.wpsPbc(_groupInfo.ifname, onWpsCommandSent); |
michael@0 | 1275 | } else { |
michael@0 | 1276 | let detail = { pin: _savedConfig.pin, iface: _groupInfo.ifname }; |
michael@0 | 1277 | aP2pCommand.wpsPin(detail, onWpsCommandSent); |
michael@0 | 1278 | } |
michael@0 | 1279 | break; |
michael@0 | 1280 | |
michael@0 | 1281 | case EVENT_TIMEOUT_PAIRING_CONFIRMATION: |
michael@0 | 1282 | debug('WAITING_FOR_JOINING_CONFIRMATION timeout!'); |
michael@0 | 1283 | _sm.gotoState(stateConnected); |
michael@0 | 1284 | break; |
michael@0 | 1285 | |
michael@0 | 1286 | default: |
michael@0 | 1287 | return false; |
michael@0 | 1288 | } // End of switch. |
michael@0 | 1289 | return true; |
michael@0 | 1290 | }, |
michael@0 | 1291 | |
michael@0 | 1292 | exit: function() { |
michael@0 | 1293 | this.timeoutTimer.cancel(); |
michael@0 | 1294 | this.timeoutTimer = null; |
michael@0 | 1295 | } |
michael@0 | 1296 | }); |
michael@0 | 1297 | |
michael@0 | 1298 | var stateDisconnecting = _sm.makeState("DISCONNECTING", { |
michael@0 | 1299 | enter: function() { |
michael@0 | 1300 | _sm.pause(); |
michael@0 | 1301 | handleGroupRemoved(_removedGroupInfo, function (success) { |
michael@0 | 1302 | if (!success) { |
michael@0 | 1303 | debug('Failed to handle group removed event. What can I do?'); |
michael@0 | 1304 | } |
michael@0 | 1305 | _sm.gotoState(stateInactive); |
michael@0 | 1306 | }); |
michael@0 | 1307 | }, |
michael@0 | 1308 | |
michael@0 | 1309 | handleEvent: function(aEvent) { |
michael@0 | 1310 | return false; // We will not receive any event in this state. |
michael@0 | 1311 | } |
michael@0 | 1312 | }); |
michael@0 | 1313 | |
michael@0 | 1314 | var stateDisabling = _sm.makeState("DISABLING", { |
michael@0 | 1315 | enter: function() { |
michael@0 | 1316 | _sm.pause(); |
michael@0 | 1317 | aNetUtil.stopDhcpServer(function (success) { // Stopping DHCP server is harmless. |
michael@0 | 1318 | debug('Stop DHCP server result: ' + success); |
michael@0 | 1319 | aP2pCommand.p2pDisable(function(success) { |
michael@0 | 1320 | debug('P2P function disabled'); |
michael@0 | 1321 | aP2pCommand.closeSupplicantConnection(function (status) { |
michael@0 | 1322 | debug('Supplicant connection closed'); |
michael@0 | 1323 | aNetUtil.disableInterface(P2P_INTERFACE_NAME, function (success){ |
michael@0 | 1324 | debug('Disabled interface: ' + P2P_INTERFACE_NAME); |
michael@0 | 1325 | _onDisabled(true); |
michael@0 | 1326 | _sm.gotoState(stateDisabled); |
michael@0 | 1327 | }); |
michael@0 | 1328 | }); |
michael@0 | 1329 | }); |
michael@0 | 1330 | }); |
michael@0 | 1331 | }, |
michael@0 | 1332 | |
michael@0 | 1333 | handleEvent: function(aEvent) { |
michael@0 | 1334 | return false; // We will not receive any event in this state. |
michael@0 | 1335 | } |
michael@0 | 1336 | }); |
michael@0 | 1337 | |
michael@0 | 1338 | //---------------------------------------------------------- |
michael@0 | 1339 | // Helper functions. |
michael@0 | 1340 | //---------------------------------------------------------- |
michael@0 | 1341 | |
michael@0 | 1342 | // Handle 'P2P_GROUP_STARTED' event. Note that this function |
michael@0 | 1343 | // will also do the state transitioning and error handling. |
michael@0 | 1344 | // |
michael@0 | 1345 | // @param aInfo Information carried by "P2P_GROUP_STARTED" event: |
michael@0 | 1346 | // .role: P2P_ROLE_GO or P2P_ROLE_CLIENT |
michael@0 | 1347 | // .ssid: |
michael@0 | 1348 | // .freq: |
michael@0 | 1349 | // .passphrase: Used to connect to GO for legacy device. |
michael@0 | 1350 | // .goAddress: |
michael@0 | 1351 | // .ifname: e.g. p2p-p2p0 |
michael@0 | 1352 | // |
michael@0 | 1353 | // @param aCallback Callback function. |
michael@0 | 1354 | function handleGroupStarted(aInfo, aCallback) { |
michael@0 | 1355 | debug('handleGroupStarted: ' + JSON.stringify(aInfo)); |
michael@0 | 1356 | |
michael@0 | 1357 | function onSuccess() |
michael@0 | 1358 | { |
michael@0 | 1359 | _sm.gotoState(stateConnected); |
michael@0 | 1360 | aCallback(true); |
michael@0 | 1361 | } |
michael@0 | 1362 | |
michael@0 | 1363 | function onFailure() |
michael@0 | 1364 | { |
michael@0 | 1365 | debug('Failed to handleGroupdStarted(). Remove the group...'); |
michael@0 | 1366 | aP2pCommand.p2pGroupRemove(aInfo.ifname, function (success) { |
michael@0 | 1367 | aCallback(false); |
michael@0 | 1368 | |
michael@0 | 1369 | if (success) { |
michael@0 | 1370 | return; // Stay in current state and wait for EVENT_P2P_GROUP_REMOVED. |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | debug('p2pGroupRemove command error!'); |
michael@0 | 1374 | _sm.gotoState(stateInactive); |
michael@0 | 1375 | }); |
michael@0 | 1376 | } |
michael@0 | 1377 | |
michael@0 | 1378 | // Save this group information. |
michael@0 | 1379 | _groupInfo = aInfo; |
michael@0 | 1380 | _groupInfo.isGroupOwner = (P2P_ROLE_GO === aInfo.role); |
michael@0 | 1381 | |
michael@0 | 1382 | if (_groupInfo.isGroupOwner) { |
michael@0 | 1383 | debug('Group owner. Start DHCP server'); |
michael@0 | 1384 | let dhcpServerConfig = { ifname: aInfo.ifname, |
michael@0 | 1385 | startIp: GO_DHCP_SERVER_IP_RANGE.startIp, |
michael@0 | 1386 | endIp: GO_DHCP_SERVER_IP_RANGE.endIp, |
michael@0 | 1387 | serverIp: GO_NETWORK_INTERFACE.ip, |
michael@0 | 1388 | maskLength: GO_NETWORK_INTERFACE.maskLength }; |
michael@0 | 1389 | |
michael@0 | 1390 | aNetUtil.startDhcpServer(dhcpServerConfig, function (success) { |
michael@0 | 1391 | if (!success) { |
michael@0 | 1392 | debug('Failed to start DHCP server'); |
michael@0 | 1393 | onFailure(); |
michael@0 | 1394 | return; |
michael@0 | 1395 | } |
michael@0 | 1396 | |
michael@0 | 1397 | // Update p2p network interface. |
michael@0 | 1398 | _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED; |
michael@0 | 1399 | _p2pNetworkInterface.ips = [GO_NETWORK_INTERFACE.ip]; |
michael@0 | 1400 | _p2pNetworkInterface.prefixLengths = [GO_NETWORK_INTERFACE.maskLength]; |
michael@0 | 1401 | _p2pNetworkInterface.gateways = [GO_NETWORK_INTERFACE.ip]; |
michael@0 | 1402 | handleP2pNetworkInterfaceStateChanged(); |
michael@0 | 1403 | |
michael@0 | 1404 | _groupInfo.networkInterface = _p2pNetworkInterface; |
michael@0 | 1405 | |
michael@0 | 1406 | debug('Everything is done. Happy p2p GO~'); |
michael@0 | 1407 | onSuccess(); |
michael@0 | 1408 | }); |
michael@0 | 1409 | |
michael@0 | 1410 | return; |
michael@0 | 1411 | } |
michael@0 | 1412 | |
michael@0 | 1413 | // We are the client. |
michael@0 | 1414 | |
michael@0 | 1415 | debug("Client. Request IP from DHCP server on interface: " + _groupInfo.ifname); |
michael@0 | 1416 | |
michael@0 | 1417 | aNetUtil.runDhcp(aInfo.ifname, function(dhcpData) { |
michael@0 | 1418 | if(!dhcpData || !dhcpData.info) { |
michael@0 | 1419 | debug('Failed to run DHCP client'); |
michael@0 | 1420 | onFailure(); |
michael@0 | 1421 | return; |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | // Save network interface. |
michael@0 | 1425 | debug("DHCP request success: " + JSON.stringify(dhcpData.info)); |
michael@0 | 1426 | |
michael@0 | 1427 | // Update p2p network interface. |
michael@0 | 1428 | let maskLength = |
michael@0 | 1429 | netHelpers.getMaskLength(netHelpers.stringToIP(dhcpData.info.mask_str)); |
michael@0 | 1430 | if (!maskLength) { |
michael@0 | 1431 | maskLength = 32; // max prefix for IPv4. |
michael@0 | 1432 | } |
michael@0 | 1433 | _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED; |
michael@0 | 1434 | _p2pNetworkInterface.ips = [dhcpData.info.ipaddr_str]; |
michael@0 | 1435 | _p2pNetworkInterface.prefixLengths = [maskLength]; |
michael@0 | 1436 | if (typeof dhcpData.info.dns1_str == "string" && |
michael@0 | 1437 | dhcpData.info.dns1_str.length) { |
michael@0 | 1438 | _p2pNetworkInterface.dnses.push(dhcpData.info.dns1_str); |
michael@0 | 1439 | } |
michael@0 | 1440 | if (typeof dhcpData.info.dns2_str == "string" && |
michael@0 | 1441 | dhcpData.info.dns2_str.length) { |
michael@0 | 1442 | _p2pNetworkInterface.dnses.push(dhcpData.info.dns2_str); |
michael@0 | 1443 | } |
michael@0 | 1444 | _p2pNetworkInterface.gateways = [dhcpData.info.gateway_str]; |
michael@0 | 1445 | handleP2pNetworkInterfaceStateChanged(); |
michael@0 | 1446 | |
michael@0 | 1447 | _groupInfo.networkInterface = _p2pNetworkInterface; |
michael@0 | 1448 | |
michael@0 | 1449 | debug('Happy p2p client~'); |
michael@0 | 1450 | onSuccess(); |
michael@0 | 1451 | }); |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | function resetP2pNetworkInterface() { |
michael@0 | 1455 | _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED; |
michael@0 | 1456 | _p2pNetworkInterface.ips = []; |
michael@0 | 1457 | _p2pNetworkInterface.prefixLengths = []; |
michael@0 | 1458 | _p2pNetworkInterface.dnses = []; |
michael@0 | 1459 | _p2pNetworkInterface.gateways = []; |
michael@0 | 1460 | } |
michael@0 | 1461 | |
michael@0 | 1462 | function registerP2pNetworkInteface() { |
michael@0 | 1463 | if (!_p2pNetworkInterface.registered) { |
michael@0 | 1464 | resetP2pNetworkInterface(); |
michael@0 | 1465 | gNetworkManager.registerNetworkInterface(_p2pNetworkInterface); |
michael@0 | 1466 | _p2pNetworkInterface.registered = true; |
michael@0 | 1467 | } |
michael@0 | 1468 | } |
michael@0 | 1469 | |
michael@0 | 1470 | function unregisterP2pNetworkInteface() { |
michael@0 | 1471 | if (_p2pNetworkInterface.registered) { |
michael@0 | 1472 | resetP2pNetworkInterface(); |
michael@0 | 1473 | gNetworkManager.unregisterNetworkInterface(_p2pNetworkInterface); |
michael@0 | 1474 | _p2pNetworkInterface.registered = false; |
michael@0 | 1475 | } |
michael@0 | 1476 | } |
michael@0 | 1477 | |
michael@0 | 1478 | function handleP2pNetworkInterfaceStateChanged() { |
michael@0 | 1479 | Services.obs.notifyObservers(_p2pNetworkInterface, |
michael@0 | 1480 | kNetworkInterfaceStateChangedTopic, |
michael@0 | 1481 | null); |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | // Handle 'P2P_GROUP_STARTED' event. |
michael@0 | 1485 | // |
michael@0 | 1486 | // @param aInfo information carried by "P2P_GROUP_REMOVED" event: |
michael@0 | 1487 | // .ifname |
michael@0 | 1488 | // .role: "GO" or "client". |
michael@0 | 1489 | // |
michael@0 | 1490 | // @param aCallback Callback function. |
michael@0 | 1491 | function handleGroupRemoved(aInfo, aCallback) { |
michael@0 | 1492 | if (!_groupInfo) { |
michael@0 | 1493 | debug('No group info. Why?'); |
michael@0 | 1494 | aCallback(true); |
michael@0 | 1495 | return; |
michael@0 | 1496 | } |
michael@0 | 1497 | if (_groupInfo.ifname !== aInfo.ifname || |
michael@0 | 1498 | _groupInfo.role !== aInfo.role) { |
michael@0 | 1499 | debug('Unmatched group info: ' + JSON.stringify(_groupInfo) + |
michael@0 | 1500 | ' v.s. ' + JSON.stringify(aInfo)); |
michael@0 | 1501 | } |
michael@0 | 1502 | |
michael@0 | 1503 | // Update p2p network interface. |
michael@0 | 1504 | _p2pNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED; |
michael@0 | 1505 | handleP2pNetworkInterfaceStateChanged(); |
michael@0 | 1506 | |
michael@0 | 1507 | if (P2P_ROLE_GO === aInfo.role) { |
michael@0 | 1508 | aNetUtil.stopDhcpServer(function(success) { |
michael@0 | 1509 | debug('Stop DHCP server result: ' + success); |
michael@0 | 1510 | aCallback(true); |
michael@0 | 1511 | }); |
michael@0 | 1512 | } else { |
michael@0 | 1513 | aNetUtil.stopDhcp(aInfo.ifname, function() { |
michael@0 | 1514 | aCallback(true); |
michael@0 | 1515 | }); |
michael@0 | 1516 | } |
michael@0 | 1517 | } |
michael@0 | 1518 | |
michael@0 | 1519 | // Non state-specific event handler. |
michael@0 | 1520 | function handleEventCommon(aEvent) { |
michael@0 | 1521 | switch (aEvent.id) { |
michael@0 | 1522 | case EVENT_P2P_DEVICE_FOUND: |
michael@0 | 1523 | _observer.onPeerFound(aEvent.info); |
michael@0 | 1524 | break; |
michael@0 | 1525 | |
michael@0 | 1526 | case EVENT_P2P_DEVICE_LOST: |
michael@0 | 1527 | _observer.onPeerLost(aEvent.info); |
michael@0 | 1528 | break; |
michael@0 | 1529 | |
michael@0 | 1530 | case EVENT_P2P_CMD_DISABLE: |
michael@0 | 1531 | _onDisabled = aEvent.info.onDisabled; |
michael@0 | 1532 | _sm.gotoState(stateDisabling); |
michael@0 | 1533 | break; |
michael@0 | 1534 | |
michael@0 | 1535 | case EVENT_P2P_CMD_ENABLE_SCAN: |
michael@0 | 1536 | if (_scanBlocked) { |
michael@0 | 1537 | _scanPostponded = true; |
michael@0 | 1538 | aEvent.info.callback(true); |
michael@0 | 1539 | break; |
michael@0 | 1540 | } |
michael@0 | 1541 | aP2pCommand.p2pEnableScan(P2P_SCAN_TIMEOUT_SEC, aEvent.info.callback); |
michael@0 | 1542 | break; |
michael@0 | 1543 | |
michael@0 | 1544 | case EVENT_P2P_CMD_DISABLE_SCAN: |
michael@0 | 1545 | aP2pCommand.p2pDisableScan(aEvent.info.callback); |
michael@0 | 1546 | break; |
michael@0 | 1547 | |
michael@0 | 1548 | case EVENT_P2P_FIND_STOPPED: |
michael@0 | 1549 | break; |
michael@0 | 1550 | |
michael@0 | 1551 | case EVENT_P2P_CMD_BLOCK_SCAN: |
michael@0 | 1552 | _scanBlocked = true; |
michael@0 | 1553 | aP2pCommand.p2pDisableScan(function(success) {}); |
michael@0 | 1554 | break; |
michael@0 | 1555 | |
michael@0 | 1556 | case EVENT_P2P_CMD_UNBLOCK_SCAN: |
michael@0 | 1557 | _scanBlocked = false; |
michael@0 | 1558 | if (_scanPostponded) { |
michael@0 | 1559 | aP2pCommand.p2pEnableScan(P2P_SCAN_TIMEOUT_SEC, function(success) {}); |
michael@0 | 1560 | } |
michael@0 | 1561 | break; |
michael@0 | 1562 | |
michael@0 | 1563 | case EVENT_P2P_CMD_CONNECT: |
michael@0 | 1564 | case EVENT_P2P_CMD_DISCONNECT: |
michael@0 | 1565 | debug("The current state couldn't handle connect/disconnect request. Ignore it."); |
michael@0 | 1566 | break; |
michael@0 | 1567 | |
michael@0 | 1568 | default: |
michael@0 | 1569 | return false; |
michael@0 | 1570 | } // End of switch. |
michael@0 | 1571 | return true; |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | function isInP2pManagedState(aState) { |
michael@0 | 1575 | let p2pManagedStates = [stateWaitingForConfirmation, |
michael@0 | 1576 | stateWaitingForNegReq, |
michael@0 | 1577 | stateProvisionDiscovery, |
michael@0 | 1578 | stateWaitingForInvitationConfirmation, |
michael@0 | 1579 | stateGroupAdding, |
michael@0 | 1580 | stateReinvoking, |
michael@0 | 1581 | stateConnecting, |
michael@0 | 1582 | stateConnected, |
michael@0 | 1583 | stateDisconnecting]; |
michael@0 | 1584 | |
michael@0 | 1585 | for (let i = 0; i < p2pManagedStates.length; i++) { |
michael@0 | 1586 | if (aState === p2pManagedStates[i]) { |
michael@0 | 1587 | return true; |
michael@0 | 1588 | } |
michael@0 | 1589 | } |
michael@0 | 1590 | |
michael@0 | 1591 | return false; |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | function initTimeoutTimer(aTimeoutMs, aTimeoutEvent) { |
michael@0 | 1595 | let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); |
michael@0 | 1596 | function onTimerFired() { |
michael@0 | 1597 | _sm.sendEvent({ id: aTimeoutEvent }); |
michael@0 | 1598 | timer = null; |
michael@0 | 1599 | } |
michael@0 | 1600 | timer.initWithCallback(onTimerFired.bind(this), aTimeoutMs, |
michael@0 | 1601 | Ci.nsITimer.TYPE_ONE_SHOT); |
michael@0 | 1602 | return timer; |
michael@0 | 1603 | } |
michael@0 | 1604 | |
michael@0 | 1605 | // Converts local WPS method to peer WPS method. |
michael@0 | 1606 | function toPeerWpsMethod(aLocalWpsMethod) { |
michael@0 | 1607 | switch (aLocalWpsMethod) { |
michael@0 | 1608 | case WPS_METHOD_DISPLAY: |
michael@0 | 1609 | return WPS_METHOD_KEYPAD; |
michael@0 | 1610 | case WPS_METHOD_KEYPAD: |
michael@0 | 1611 | return WPS_METHOD_DISPLAY; |
michael@0 | 1612 | case WPS_METHOD_PBC: |
michael@0 | 1613 | return WPS_METHOD_PBC; |
michael@0 | 1614 | default: |
michael@0 | 1615 | return WPS_METHOD_PBC; // Use "push button" as the default method. |
michael@0 | 1616 | } |
michael@0 | 1617 | } |
michael@0 | 1618 | |
michael@0 | 1619 | return p2pSm; |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | this.WifiP2pManager.INTERFACE_NAME = P2P_INTERFACE_NAME; |