1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi;
18
19import android.net.NetworkInfo;
20import android.net.wifi.p2p.WifiP2pConfig;
21import android.net.wifi.p2p.WifiP2pDevice;
22import android.net.wifi.p2p.WifiP2pGroup;
23import android.net.wifi.p2p.WifiP2pService;
24import android.net.wifi.p2p.WifiP2pService.P2pStatus;
25import android.net.wifi.p2p.WifiP2pProvDiscEvent;
26import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
27import android.net.wifi.StateChangeResult;
28import android.os.Message;
29import android.util.Log;
30
31
32import com.android.internal.util.Protocol;
33import com.android.internal.util.StateMachine;
34
35import java.util.List;
36import java.util.regex.Pattern;
37import java.util.regex.Matcher;
38
39/**
40 * Listens for events from the wpa_supplicant server, and passes them on
41 * to the {@link StateMachine} for handling. Runs in its own thread.
42 *
43 * @hide
44 */
45public class WifiMonitor {
46
47    private static final String TAG = "WifiMonitor";
48
49    /** Events we receive from the supplicant daemon */
50
51    private static final int CONNECTED    = 1;
52    private static final int DISCONNECTED = 2;
53    private static final int STATE_CHANGE = 3;
54    private static final int SCAN_RESULTS = 4;
55    private static final int LINK_SPEED   = 5;
56    private static final int TERMINATING  = 6;
57    private static final int DRIVER_STATE = 7;
58    private static final int EAP_FAILURE  = 8;
59    private static final int UNKNOWN      = 9;
60
61    /** All events coming from the supplicant start with this prefix */
62    private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
63    private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length();
64
65    /** All WPA events coming from the supplicant start with this prefix */
66    private static final String WPA_EVENT_PREFIX_STR = "WPA:";
67    private static final String PASSWORD_MAY_BE_INCORRECT_STR =
68       "pre-shared key may be incorrect";
69
70    /* WPS events */
71    private static final String WPS_SUCCESS_STR = "WPS-SUCCESS";
72
73    /* Format: WPS-FAIL msg=%d [config_error=%d] [reason=%d (%s)] */
74    private static final String WPS_FAIL_STR    = "WPS-FAIL";
75    private static final String WPS_FAIL_PATTERN =
76            "WPS-FAIL msg=\\d+(?: config_error=(\\d+))?(?: reason=(\\d+))?";
77
78    /* config error code values for config_error=%d */
79    private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
80    private static final int CONFIG_AUTH_FAILURE = 18;
81
82    /* reason code values for reason=%d */
83    private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
84    private static final int REASON_WEP_PROHIBITED = 2;
85
86    private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
87    private static final String WPS_TIMEOUT_STR = "WPS-TIMEOUT";
88
89    /**
90     * Names of events from wpa_supplicant (minus the prefix). In the
91     * format descriptions, * &quot;<code>x</code>&quot;
92     * designates a dynamic value that needs to be parsed out from the event
93     * string
94     */
95    /**
96     * <pre>
97     * CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed
98     * </pre>
99     * <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
100     */
101    private static final String CONNECTED_STR =    "CONNECTED";
102    /**
103     * <pre>
104     * CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
105     * </pre>
106     */
107    private static final String DISCONNECTED_STR = "DISCONNECTED";
108    /**
109     * <pre>
110     * CTRL-EVENT-STATE-CHANGE x
111     * </pre>
112     * <code>x</code> is the numerical value of the new state.
113     */
114    private static final String STATE_CHANGE_STR =  "STATE-CHANGE";
115    /**
116     * <pre>
117     * CTRL-EVENT-SCAN-RESULTS ready
118     * </pre>
119     */
120    private static final String SCAN_RESULTS_STR =  "SCAN-RESULTS";
121
122    /**
123     * <pre>
124     * CTRL-EVENT-LINK-SPEED x Mb/s
125     * </pre>
126     * {@code x} is the link speed in Mb/sec.
127     */
128    private static final String LINK_SPEED_STR = "LINK-SPEED";
129    /**
130     * <pre>
131     * CTRL-EVENT-TERMINATING - signal x
132     * </pre>
133     * <code>x</code> is the signal that caused termination.
134     */
135    private static final String TERMINATING_STR =  "TERMINATING";
136    /**
137     * <pre>
138     * CTRL-EVENT-DRIVER-STATE state
139     * </pre>
140     * <code>state</code> can be HANGED
141     */
142    private static final String DRIVER_STATE_STR = "DRIVER-STATE";
143    /**
144     * <pre>
145     * CTRL-EVENT-EAP-FAILURE EAP authentication failed
146     * </pre>
147     */
148    private static final String EAP_FAILURE_STR = "EAP-FAILURE";
149
150    /**
151     * This indicates an authentication failure on EAP FAILURE event
152     */
153    private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
154
155    /**
156     * Regex pattern for extracting an Ethernet-style MAC address from a string.
157     * Matches a strings like the following:<pre>
158     * CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]</pre>
159     */
160    private static Pattern mConnectedEventPattern =
161        Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
162
163    /** P2P events */
164    private static final String P2P_EVENT_PREFIX_STR = "P2P";
165
166    /* P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1
167       name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0 */
168    private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND";
169
170    /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
171    private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
172
173    /* P2P-FIND-STOPPED */
174    private static final String P2P_FIND_STOPPED_STR = "P2P-FIND-STOPPED";
175
176    /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
177    private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
178
179    private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
180
181    /* P2P-GO-NEG-FAILURE status=x */
182    private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
183
184    private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
185            "P2P-GROUP-FORMATION-SUCCESS";
186
187    private static final String P2P_GROUP_FORMATION_FAILURE_STR =
188            "P2P-GROUP-FORMATION-FAILURE";
189
190    /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
191       [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
192       go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] */
193    private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
194
195    /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
196    private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED";
197
198    /* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
199        bssid=fa:7b:7a:42:82:13 unknown-network */
200    private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED";
201
202    /* P2P-INVITATION-RESULT status=1 */
203    private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT";
204
205    /* P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
206       pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
207       group_capab=0x0 */
208    private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
209
210    /* P2P-PROV-DISC-PBC-RESP 02:12:47:f2:5a:36 */
211    private static final String P2P_PROV_DISC_PBC_RSP_STR = "P2P-PROV-DISC-PBC-RESP";
212
213    /* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
214       pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
215       group_capab=0x0 */
216    private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN";
217    /* P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
218       pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
219       group_capab=0x0 */
220    private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
221    /* P2P-PROV-DISC-FAILURE p2p_dev_addr=42:fc:89:e1:e2:27 */
222    private static final String P2P_PROV_DISC_FAILURE_STR = "P2P-PROV-DISC-FAILURE";
223
224    /*
225     * Protocol format is as follows.<br>
226     * See the Table.62 in the WiFi Direct specification for the detail.
227     * ______________________________________________________________
228     * |           Length(2byte)     | Type(1byte) | TransId(1byte)}|
229     * ______________________________________________________________
230     * | status(1byte)  |            vendor specific(variable)      |
231     *
232     * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300000101
233     * length=3, service type=0(ALL Service), transaction id=1,
234     * status=1(service protocol type not available)<br>
235     *
236     * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300020201
237     * length=3, service type=2(UPnP), transaction id=2,
238     * status=1(service protocol type not available)
239     *
240     * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 990002030010757569643a3131323
241     * 2646534652d383537342d353961622d393332322d3333333435363738393034343a3
242     * a75726e3a736368656d61732d75706e702d6f72673a736572766963653a436f6e746
243     * 56e744469726563746f72793a322c757569643a36383539646564652d383537342d3
244     * 53961622d393333322d3132333435363738393031323a3a75706e703a726f6f74646
245     * 576696365
246     * length=153,type=2(UPnP),transaction id=3,status=0
247     *
248     * UPnP Protocol format is as follows.
249     * ______________________________________________________
250     * |  Version (1)  |          USN (Variable)            |
251     *
252     * version=0x10(UPnP1.0) data=usn:uuid:1122de4e-8574-59ab-9322-33345678
253     * 9044::urn:schemas-upnp-org:service:ContentDirectory:2,usn:uuid:6859d
254     * ede-8574-59ab-9332-123456789012::upnp:rootdevice
255     *
256     * P2P-SERV-DISC-RESP 58:17:0c:bc:dd:ca 21 1900010200045f6970
257     * 70c00c000c01094d795072696e746572c027
258     * length=25, type=1(Bonjour),transaction id=2,status=0
259     *
260     * Bonjour Protocol format is as follows.
261     * __________________________________________________________
262     * |DNS Name(Variable)|DNS Type(1)|Version(1)|RDATA(Variable)|
263     *
264     * DNS Name=_ipp._tcp.local.,DNS type=12(PTR), Version=1,
265     * RDATA=MyPrinter._ipp._tcp.local.
266     *
267     */
268    private static final String P2P_SERV_DISC_RESP_STR = "P2P-SERV-DISC-RESP";
269
270    private static final String HOST_AP_EVENT_PREFIX_STR = "AP";
271    /* AP-STA-CONNECTED 42:fc:89:a8:96:09 dev_addr=02:90:4c:a0:92:54 */
272    private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
273    /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
274    private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
275
276    private final StateMachine mStateMachine;
277    private final WifiNative mWifiNative;
278
279    /* Supplicant events reported to a state machine */
280    private static final int BASE = Protocol.BASE_WIFI_MONITOR;
281
282    /* Connection to supplicant established */
283    public static final int SUP_CONNECTION_EVENT                 = BASE + 1;
284    /* Connection to supplicant lost */
285    public static final int SUP_DISCONNECTION_EVENT              = BASE + 2;
286   /* Network connection completed */
287    public static final int NETWORK_CONNECTION_EVENT             = BASE + 3;
288    /* Network disconnection completed */
289    public static final int NETWORK_DISCONNECTION_EVENT          = BASE + 4;
290    /* Scan results are available */
291    public static final int SCAN_RESULTS_EVENT                   = BASE + 5;
292    /* Supplicate state changed */
293    public static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 6;
294    /* Password failure and EAP authentication failure */
295    public static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 7;
296    /* WPS success detected */
297    public static final int WPS_SUCCESS_EVENT                    = BASE + 8;
298    /* WPS failure detected */
299    public static final int WPS_FAIL_EVENT                       = BASE + 9;
300     /* WPS overlap detected */
301    public static final int WPS_OVERLAP_EVENT                    = BASE + 10;
302     /* WPS timeout detected */
303    public static final int WPS_TIMEOUT_EVENT                    = BASE + 11;
304    /* Driver was hung */
305    public static final int DRIVER_HUNG_EVENT                    = BASE + 12;
306
307    /* P2P events */
308    public static final int P2P_DEVICE_FOUND_EVENT               = BASE + 21;
309    public static final int P2P_DEVICE_LOST_EVENT                = BASE + 22;
310    public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT     = BASE + 23;
311    public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT     = BASE + 25;
312    public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT     = BASE + 26;
313    public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT    = BASE + 27;
314    public static final int P2P_GROUP_FORMATION_FAILURE_EVENT    = BASE + 28;
315    public static final int P2P_GROUP_STARTED_EVENT              = BASE + 29;
316    public static final int P2P_GROUP_REMOVED_EVENT              = BASE + 30;
317    public static final int P2P_INVITATION_RECEIVED_EVENT        = BASE + 31;
318    public static final int P2P_INVITATION_RESULT_EVENT          = BASE + 32;
319    public static final int P2P_PROV_DISC_PBC_REQ_EVENT          = BASE + 33;
320    public static final int P2P_PROV_DISC_PBC_RSP_EVENT          = BASE + 34;
321    public static final int P2P_PROV_DISC_ENTER_PIN_EVENT        = BASE + 35;
322    public static final int P2P_PROV_DISC_SHOW_PIN_EVENT         = BASE + 36;
323    public static final int P2P_FIND_STOPPED_EVENT               = BASE + 37;
324    public static final int P2P_SERV_DISC_RESP_EVENT             = BASE + 38;
325    public static final int P2P_PROV_DISC_FAILURE_EVENT          = BASE + 39;
326
327    /* hostap events */
328    public static final int AP_STA_DISCONNECTED_EVENT            = BASE + 41;
329    public static final int AP_STA_CONNECTED_EVENT               = BASE + 42;
330
331    /**
332     * This indicates the supplicant connection for the monitor is closed
333     */
334    private static final String MONITOR_SOCKET_CLOSED_STR = "connection closed";
335
336    /**
337     * This indicates a read error on the monitor socket conenction
338     */
339    private static final String WPA_RECV_ERROR_STR = "recv error";
340
341    /**
342     * Tracks consecutive receive errors
343     */
344    private int mRecvErrors = 0;
345
346    /**
347     * Max errors before we close supplicant connection
348     */
349    private static final int MAX_RECV_ERRORS    = 10;
350
351    public WifiMonitor(StateMachine wifiStateMachine, WifiNative wifiNative) {
352        mStateMachine = wifiStateMachine;
353        mWifiNative = wifiNative;
354    }
355
356    public void startMonitoring() {
357        new MonitorThread().start();
358    }
359
360    class MonitorThread extends Thread {
361        public MonitorThread() {
362            super("WifiMonitor");
363        }
364
365        public void run() {
366
367            if (connectToSupplicant()) {
368                // Send a message indicating that it is now possible to send commands
369                // to the supplicant
370                mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
371            } else {
372                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
373                return;
374            }
375
376            //noinspection InfiniteLoopStatement
377            for (;;) {
378                String eventStr = mWifiNative.waitForEvent();
379
380                // Skip logging the common but mostly uninteresting scan-results event
381                if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
382                    Log.d(TAG, "Event [" + eventStr + "]");
383                }
384                if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
385                    if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
386                            0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
387                        mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
388                    } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
389                        mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
390                    } else if (eventStr.startsWith(WPS_FAIL_STR)) {
391                        handleWpsFailEvent(eventStr);
392                    } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
393                        mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
394                    } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
395                        mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
396                    } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
397                        handleP2pEvents(eventStr);
398                    } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
399                        handleHostApEvents(eventStr);
400                    }
401                    continue;
402                }
403
404                String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
405                int nameEnd = eventName.indexOf(' ');
406                if (nameEnd != -1)
407                    eventName = eventName.substring(0, nameEnd);
408                if (eventName.length() == 0) {
409                    if (false) Log.i(TAG, "Received wpa_supplicant event with empty event name");
410                    continue;
411                }
412                /*
413                 * Map event name into event enum
414                 */
415                int event;
416                if (eventName.equals(CONNECTED_STR))
417                    event = CONNECTED;
418                else if (eventName.equals(DISCONNECTED_STR))
419                    event = DISCONNECTED;
420                else if (eventName.equals(STATE_CHANGE_STR))
421                    event = STATE_CHANGE;
422                else if (eventName.equals(SCAN_RESULTS_STR))
423                    event = SCAN_RESULTS;
424                else if (eventName.equals(LINK_SPEED_STR))
425                    event = LINK_SPEED;
426                else if (eventName.equals(TERMINATING_STR))
427                    event = TERMINATING;
428                else if (eventName.equals(DRIVER_STATE_STR))
429                    event = DRIVER_STATE;
430                else if (eventName.equals(EAP_FAILURE_STR))
431                    event = EAP_FAILURE;
432                else
433                    event = UNKNOWN;
434
435                String eventData = eventStr;
436                if (event == DRIVER_STATE || event == LINK_SPEED)
437                    eventData = eventData.split(" ")[1];
438                else if (event == STATE_CHANGE || event == EAP_FAILURE) {
439                    int ind = eventStr.indexOf(" ");
440                    if (ind != -1) {
441                        eventData = eventStr.substring(ind + 1);
442                    }
443                } else {
444                    int ind = eventStr.indexOf(" - ");
445                    if (ind != -1) {
446                        eventData = eventStr.substring(ind + 3);
447                    }
448                }
449
450                if (event == STATE_CHANGE) {
451                    handleSupplicantStateChange(eventData);
452                } else if (event == DRIVER_STATE) {
453                    handleDriverEvent(eventData);
454                } else if (event == TERMINATING) {
455                    /**
456                     * Close the supplicant connection if we see
457                     * too many recv errors
458                     */
459                    if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
460                        if (++mRecvErrors > MAX_RECV_ERRORS) {
461                            if (false) {
462                                Log.d(TAG, "too many recv errors, closing connection");
463                            }
464                        } else {
465                            continue;
466                        }
467                    }
468
469                    // notify and exit
470                    mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
471                    break;
472                } else if (event == EAP_FAILURE) {
473                    if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
474                        mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
475                    }
476                } else {
477                    handleEvent(event, eventData);
478                }
479                mRecvErrors = 0;
480            }
481        }
482
483        private boolean connectToSupplicant() {
484            int connectTries = 0;
485
486            while (true) {
487                if (mWifiNative.connectToSupplicant()) {
488                    return true;
489                }
490                if (connectTries++ < 5) {
491                    nap(1);
492                } else {
493                    break;
494                }
495            }
496            return false;
497        }
498
499        private void handleDriverEvent(String state) {
500            if (state == null) {
501                return;
502            }
503            if (state.equals("HANGED")) {
504                mStateMachine.sendMessage(DRIVER_HUNG_EVENT);
505            }
506        }
507
508        /**
509         * Handle all supplicant events except STATE-CHANGE
510         * @param event the event type
511         * @param remainder the rest of the string following the
512         * event name and &quot;&#8195;&#8212;&#8195;&quot;
513         */
514        void handleEvent(int event, String remainder) {
515            switch (event) {
516                case DISCONNECTED:
517                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
518                    break;
519
520                case CONNECTED:
521                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
522                    break;
523
524                case SCAN_RESULTS:
525                    mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
526                    break;
527
528                case UNKNOWN:
529                    break;
530            }
531        }
532
533        private void handleWpsFailEvent(String dataString) {
534            final Pattern p = Pattern.compile(WPS_FAIL_PATTERN);
535            Matcher match = p.matcher(dataString);
536            if (match.find()) {
537                String cfgErr = match.group(1);
538                String reason = match.group(2);
539
540                if (reason != null) {
541                    switch(Integer.parseInt(reason)) {
542                        case REASON_TKIP_ONLY_PROHIBITED:
543                            mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
544                                    WifiManager.WPS_TKIP_ONLY_PROHIBITED, 0));
545                            return;
546                        case REASON_WEP_PROHIBITED:
547                            mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
548                                    WifiManager.WPS_WEP_PROHIBITED, 0));
549                            return;
550                    }
551                }
552                if (cfgErr != null) {
553                    switch(Integer.parseInt(cfgErr)) {
554                        case CONFIG_AUTH_FAILURE:
555                            mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
556                                    WifiManager.WPS_AUTH_FAILURE, 0));
557                            return;
558                        case CONFIG_MULTIPLE_PBC_DETECTED:
559                            mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
560                                    WifiManager.WPS_OVERLAP_ERROR, 0));
561                            return;
562                    }
563                }
564            }
565            //For all other errors, return a generic internal error
566            mStateMachine.sendMessage(mStateMachine.obtainMessage(WPS_FAIL_EVENT,
567                    WifiManager.ERROR, 0));
568        }
569
570        /* <event> status=<err> and the special case of <event> reason=FREQ_CONFLICT */
571        private P2pStatus p2pError(String dataString) {
572            P2pStatus err = P2pStatus.UNKNOWN;
573            String[] tokens = dataString.split(" ");
574            if (tokens.length < 2) return err;
575            String[] nameValue = tokens[1].split("=");
576            if (nameValue.length != 2) return err;
577
578            /* Handle the special case of reason=FREQ+CONFLICT */
579            if (nameValue[1].equals("FREQ_CONFLICT")) {
580                return P2pStatus.NO_COMMON_CHANNEL;
581            }
582            try {
583                err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
584            } catch (NumberFormatException e) {
585                e.printStackTrace();
586            }
587            return err;
588        }
589
590        /**
591         * Handle p2p events
592         */
593        private void handleP2pEvents(String dataString) {
594            if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
595                mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
596            } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
597                mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
598            } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
599                mStateMachine.sendMessage(P2P_FIND_STOPPED_EVENT);
600            } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
601                mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
602                        new WifiP2pConfig(dataString));
603            } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
604                mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
605            } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
606                mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString));
607            } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
608                mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
609            } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
610                mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
611            } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
612                mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
613            } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
614                mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString));
615            } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
616                mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
617                        new WifiP2pGroup(dataString));
618            } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
619                mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, p2pError(dataString));
620            } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
621                mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
622                        new WifiP2pProvDiscEvent(dataString));
623            } else if (dataString.startsWith(P2P_PROV_DISC_PBC_RSP_STR)) {
624                mStateMachine.sendMessage(P2P_PROV_DISC_PBC_RSP_EVENT,
625                        new WifiP2pProvDiscEvent(dataString));
626            } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
627                mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT,
628                        new WifiP2pProvDiscEvent(dataString));
629            } else if (dataString.startsWith(P2P_PROV_DISC_SHOW_PIN_STR)) {
630                mStateMachine.sendMessage(P2P_PROV_DISC_SHOW_PIN_EVENT,
631                        new WifiP2pProvDiscEvent(dataString));
632            } else if (dataString.startsWith(P2P_PROV_DISC_FAILURE_STR)) {
633                mStateMachine.sendMessage(P2P_PROV_DISC_FAILURE_EVENT);
634            } else if (dataString.startsWith(P2P_SERV_DISC_RESP_STR)) {
635                List<WifiP2pServiceResponse> list = WifiP2pServiceResponse.newInstance(dataString);
636                if (list != null) {
637                    mStateMachine.sendMessage(P2P_SERV_DISC_RESP_EVENT, list);
638                } else {
639                    Log.e(TAG, "Null service resp " + dataString);
640                }
641            }
642        }
643
644        /**
645         * Handle hostap events
646         */
647        private void handleHostApEvents(String dataString) {
648            String[] tokens = dataString.split(" ");
649            /* AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
650            if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
651                mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, new WifiP2pDevice(dataString));
652            /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
653            } else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
654                mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, new WifiP2pDevice(dataString));
655            }
656        }
657
658        /**
659         * Handle the supplicant STATE-CHANGE event
660         * @param dataString New supplicant state string in the format:
661         * id=network-id state=new-state
662         */
663        private void handleSupplicantStateChange(String dataString) {
664            WifiSsid wifiSsid = null;
665            int index = dataString.lastIndexOf("SSID=");
666            if (index != -1) {
667                wifiSsid = WifiSsid.createFromAsciiEncoded(
668                        dataString.substring(index + 5));
669            }
670            String[] dataTokens = dataString.split(" ");
671
672            String BSSID = null;
673            int networkId = -1;
674            int newState  = -1;
675            for (String token : dataTokens) {
676                String[] nameValue = token.split("=");
677                if (nameValue.length != 2) {
678                    continue;
679                }
680
681                if (nameValue[0].equals("BSSID")) {
682                    BSSID = nameValue[1];
683                    continue;
684                }
685
686                int value;
687                try {
688                    value = Integer.parseInt(nameValue[1]);
689                } catch (NumberFormatException e) {
690                    continue;
691                }
692
693                if (nameValue[0].equals("id")) {
694                    networkId = value;
695                } else if (nameValue[0].equals("state")) {
696                    newState = value;
697                }
698            }
699
700            if (newState == -1) return;
701
702            SupplicantState newSupplicantState = SupplicantState.INVALID;
703            for (SupplicantState state : SupplicantState.values()) {
704                if (state.ordinal() == newState) {
705                    newSupplicantState = state;
706                    break;
707                }
708            }
709            if (newSupplicantState == SupplicantState.INVALID) {
710                Log.w(TAG, "Invalid supplicant state: " + newState);
711            }
712            notifySupplicantStateChange(networkId, wifiSsid, BSSID, newSupplicantState);
713        }
714    }
715
716    private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
717        String BSSID = null;
718        int networkId = -1;
719        if (newState == NetworkInfo.DetailedState.CONNECTED) {
720            Matcher match = mConnectedEventPattern.matcher(data);
721            if (!match.find()) {
722                if (false) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
723            } else {
724                BSSID = match.group(1);
725                try {
726                    networkId = Integer.parseInt(match.group(2));
727                } catch (NumberFormatException e) {
728                    networkId = -1;
729                }
730            }
731        }
732        notifyNetworkStateChange(newState, BSSID, networkId);
733    }
734
735    /**
736     * Send the state machine a notification that the state of Wifi connectivity
737     * has changed.
738     * @param networkId the configured network on which the state change occurred
739     * @param newState the new network state
740     * @param BSSID when the new state is {@link DetailedState#CONNECTED
741     * NetworkInfo.DetailedState.CONNECTED},
742     * this is the MAC address of the access point. Otherwise, it
743     * is {@code null}.
744     */
745    void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
746        if (newState == NetworkInfo.DetailedState.CONNECTED) {
747            Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
748                    netId, 0, BSSID);
749            mStateMachine.sendMessage(m);
750        } else {
751            Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
752                    netId, 0, BSSID);
753            mStateMachine.sendMessage(m);
754        }
755    }
756
757    /**
758     * Send the state machine a notification that the state of the supplicant
759     * has changed.
760     * @param networkId the configured network on which the state change occurred
761     * @param wifiSsid network name
762     * @param BSSID network address
763     * @param newState the new {@code SupplicantState}
764     */
765    void notifySupplicantStateChange(int networkId, WifiSsid wifiSsid, String BSSID,
766            SupplicantState newState) {
767        mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
768                new StateChangeResult(networkId, wifiSsid, BSSID, newState)));
769    }
770
771    /**
772     * Sleep for a period of time.
773     * @param secs the number of seconds to sleep
774     */
775    private static void nap(int secs) {
776        try {
777            Thread.sleep(secs * 1000);
778        } catch (InterruptedException ignore) {
779        }
780    }
781}
782