dom/wifi/WifiP2pManager.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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;

mercurial