|
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; |