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