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