dom/wifi/WifiP2pManager.jsm

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

mercurial