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.StateChangeResult;
     24 import android.os.Message;
     25 import android.util.Log;
     26 
     27 
     28 import com.android.internal.util.Protocol;
     29 import com.android.internal.util.StateMachine;
     30 
     31 import java.util.regex.Pattern;
     32 import java.util.regex.Matcher;
     33 
     34 /**
     35  * Listens for events from the wpa_supplicant server, and passes them on
     36  * to the {@link StateMachine} for handling. Runs in its own thread.
     37  *
     38  * @hide
     39  */
     40 public class WifiMonitor {
     41 
     42     private static final String TAG = "WifiMonitor";
     43 
     44     /** Events we receive from the supplicant daemon */
     45 
     46     private static final int CONNECTED    = 1;
     47     private static final int DISCONNECTED = 2;
     48     private static final int STATE_CHANGE = 3;
     49     private static final int SCAN_RESULTS = 4;
     50     private static final int LINK_SPEED   = 5;
     51     private static final int TERMINATING  = 6;
     52     private static final int DRIVER_STATE = 7;
     53     private static final int EAP_FAILURE  = 8;
     54     private static final int UNKNOWN      = 9;
     55 
     56     /** All events coming from the supplicant start with this prefix */
     57     private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
     58     private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length();
     59 
     60     /** All WPA events coming from the supplicant start with this prefix */
     61     private static final String WPA_EVENT_PREFIX_STR = "WPA:";
     62     private static final String PASSWORD_MAY_BE_INCORRECT_STR =
     63        "pre-shared key may be incorrect";
     64 
     65     /* WPS events */
     66     private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
     67 
     68     /**
     69      * Names of events from wpa_supplicant (minus the prefix). In the
     70      * format descriptions, * &quot;<code>x</code>&quot;
     71      * designates a dynamic value that needs to be parsed out from the event
     72      * string
     73      */
     74     /**
     75      * <pre>
     76      * CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed
     77      * </pre>
     78      * <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
     79      */
     80     private static final String CONNECTED_STR =    "CONNECTED";
     81     /**
     82      * <pre>
     83      * CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
     84      * </pre>
     85      */
     86     private static final String DISCONNECTED_STR = "DISCONNECTED";
     87     /**
     88      * <pre>
     89      * CTRL-EVENT-STATE-CHANGE x
     90      * </pre>
     91      * <code>x</code> is the numerical value of the new state.
     92      */
     93     private static final String STATE_CHANGE_STR =  "STATE-CHANGE";
     94     /**
     95      * <pre>
     96      * CTRL-EVENT-SCAN-RESULTS ready
     97      * </pre>
     98      */
     99     private static final String SCAN_RESULTS_STR =  "SCAN-RESULTS";
    100 
    101     /**
    102      * <pre>
    103      * CTRL-EVENT-LINK-SPEED x Mb/s
    104      * </pre>
    105      * {@code x} is the link speed in Mb/sec.
    106      */
    107     private static final String LINK_SPEED_STR = "LINK-SPEED";
    108     /**
    109      * <pre>
    110      * CTRL-EVENT-TERMINATING - signal x
    111      * </pre>
    112      * <code>x</code> is the signal that caused termination.
    113      */
    114     private static final String TERMINATING_STR =  "TERMINATING";
    115     /**
    116      * <pre>
    117      * CTRL-EVENT-DRIVER-STATE state
    118      * </pre>
    119      * <code>state</code> can be HANGED
    120      */
    121     private static final String DRIVER_STATE_STR = "DRIVER-STATE";
    122     /**
    123      * <pre>
    124      * CTRL-EVENT-EAP-FAILURE EAP authentication failed
    125      * </pre>
    126      */
    127     private static final String EAP_FAILURE_STR = "EAP-FAILURE";
    128 
    129     /**
    130      * This indicates an authentication failure on EAP FAILURE event
    131      */
    132     private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
    133 
    134     /**
    135      * Regex pattern for extracting an Ethernet-style MAC address from a string.
    136      * Matches a strings like the following:<pre>
    137      * CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]</pre>
    138      */
    139     private static Pattern mConnectedEventPattern =
    140         Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
    141 
    142     /** P2P events */
    143     private static final String P2P_EVENT_PREFIX_STR = "P2P";
    144 
    145     /* P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1
    146        name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0 */
    147     private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND";
    148 
    149     /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
    150     private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
    151 
    152     /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
    153     private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
    154 
    155     private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
    156 
    157     private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
    158 
    159     private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
    160             "P2P-GROUP-FORMATION-SUCCESS";
    161 
    162     private static final String P2P_GROUP_FORMATION_FAILURE_STR =
    163             "P2P-GROUP-FORMATION-FAILURE";
    164 
    165     /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
    166        [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
    167        go_dev_addr=fa:7b:7a:42:02:13 */
    168     private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
    169 
    170     /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
    171     private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED";
    172 
    173     /* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
    174         bssid=fa:7b:7a:42:82:13 unknown-network */
    175     private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED";
    176 
    177     /* P2P-INVITATION-RESULT status=1 */
    178     private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT";
    179 
    180     /* P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
    181        pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
    182        group_capab=0x0 */
    183     private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
    184     /* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
    185        pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
    186        group_capab=0x0 */
    187     private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN";
    188     /* P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
    189        pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
    190        group_capab=0x0 */
    191     private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
    192 
    193     private static final String HOST_AP_EVENT_PREFIX_STR = "AP";
    194     /* AP-STA-CONNECTED 42:fc:89:a8:96:09 */
    195     private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
    196     /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
    197     private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
    198 
    199     private final StateMachine mStateMachine;
    200 
    201     /* Supplicant events reported to a state machine */
    202     private static final int BASE = Protocol.BASE_WIFI_MONITOR;
    203 
    204     /* Connection to supplicant established */
    205     public static final int SUP_CONNECTION_EVENT                 = BASE + 1;
    206     /* Connection to supplicant lost */
    207     public static final int SUP_DISCONNECTION_EVENT              = BASE + 2;
    208    /* Network connection completed */
    209     public static final int NETWORK_CONNECTION_EVENT             = BASE + 3;
    210     /* Network disconnection completed */
    211     public static final int NETWORK_DISCONNECTION_EVENT          = BASE + 4;
    212     /* Scan results are available */
    213     public static final int SCAN_RESULTS_EVENT                   = BASE + 5;
    214     /* Supplicate state changed */
    215     public static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 6;
    216     /* Password failure and EAP authentication failure */
    217     public static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 7;
    218     /* WPS overlap detected */
    219     public static final int WPS_OVERLAP_EVENT                    = BASE + 8;
    220     /* Driver was hung */
    221     public static final int DRIVER_HUNG_EVENT                    = BASE + 9;
    222 
    223     /* P2P events */
    224     public static final int P2P_DEVICE_FOUND_EVENT               = BASE + 21;
    225     public static final int P2P_DEVICE_LOST_EVENT                = BASE + 22;
    226     public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT     = BASE + 23;
    227     public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT     = BASE + 25;
    228     public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT     = BASE + 26;
    229     public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT    = BASE + 27;
    230     public static final int P2P_GROUP_FORMATION_FAILURE_EVENT    = BASE + 28;
    231     public static final int P2P_GROUP_STARTED_EVENT              = BASE + 29;
    232     public static final int P2P_GROUP_REMOVED_EVENT              = BASE + 30;
    233     public static final int P2P_INVITATION_RECEIVED_EVENT        = BASE + 31;
    234     public static final int P2P_INVITATION_RESULT_EVENT          = BASE + 32;
    235     public static final int P2P_PROV_DISC_PBC_REQ_EVENT          = BASE + 33;
    236     public static final int P2P_PROV_DISC_ENTER_PIN_EVENT        = BASE + 34;
    237     public static final int P2P_PROV_DISC_SHOW_PIN_EVENT         = BASE + 35;
    238 
    239     /* hostap events */
    240     public static final int AP_STA_DISCONNECTED_EVENT            = BASE + 41;
    241     public static final int AP_STA_CONNECTED_EVENT               = BASE + 42;
    242 
    243     /**
    244      * This indicates the supplicant connection for the monitor is closed
    245      */
    246     private static final String MONITOR_SOCKET_CLOSED_STR = "connection closed";
    247 
    248     /**
    249      * This indicates a read error on the monitor socket conenction
    250      */
    251     private static final String WPA_RECV_ERROR_STR = "recv error";
    252 
    253     /**
    254      * Tracks consecutive receive errors
    255      */
    256     private int mRecvErrors = 0;
    257 
    258     /**
    259      * Max errors before we close supplicant connection
    260      */
    261     private static final int MAX_RECV_ERRORS    = 10;
    262 
    263     public WifiMonitor(StateMachine wifiStateMachine) {
    264         mStateMachine = wifiStateMachine;
    265     }
    266 
    267     public void startMonitoring() {
    268         new MonitorThread().start();
    269     }
    270 
    271     class MonitorThread extends Thread {
    272         public MonitorThread() {
    273             super("WifiMonitor");
    274         }
    275 
    276         public void run() {
    277 
    278             if (connectToSupplicant()) {
    279                 // Send a message indicating that it is now possible to send commands
    280                 // to the supplicant
    281                 mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
    282             } else {
    283                 mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
    284                 return;
    285             }
    286 
    287             //noinspection InfiniteLoopStatement
    288             for (;;) {
    289                 String eventStr = WifiNative.waitForEvent();
    290 
    291                 // Skip logging the common but mostly uninteresting scan-results event
    292                 if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
    293                     Log.d(TAG, "Event [" + eventStr + "]");
    294                 }
    295                 if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
    296                     if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
    297                             0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
    298                         mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
    299                     } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
    300                         mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
    301                     } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
    302                         handleP2pEvents(eventStr);
    303                     } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
    304                         handleHostApEvents(eventStr);
    305                     }
    306                     continue;
    307                 }
    308 
    309                 String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
    310                 int nameEnd = eventName.indexOf(' ');
    311                 if (nameEnd != -1)
    312                     eventName = eventName.substring(0, nameEnd);
    313                 if (eventName.length() == 0) {
    314                     if (false) Log.i(TAG, "Received wpa_supplicant event with empty event name");
    315                     continue;
    316                 }
    317                 /*
    318                  * Map event name into event enum
    319                  */
    320                 int event;
    321                 if (eventName.equals(CONNECTED_STR))
    322                     event = CONNECTED;
    323                 else if (eventName.equals(DISCONNECTED_STR))
    324                     event = DISCONNECTED;
    325                 else if (eventName.equals(STATE_CHANGE_STR))
    326                     event = STATE_CHANGE;
    327                 else if (eventName.equals(SCAN_RESULTS_STR))
    328                     event = SCAN_RESULTS;
    329                 else if (eventName.equals(LINK_SPEED_STR))
    330                     event = LINK_SPEED;
    331                 else if (eventName.equals(TERMINATING_STR))
    332                     event = TERMINATING;
    333                 else if (eventName.equals(DRIVER_STATE_STR))
    334                     event = DRIVER_STATE;
    335                 else if (eventName.equals(EAP_FAILURE_STR))
    336                     event = EAP_FAILURE;
    337                 else
    338                     event = UNKNOWN;
    339 
    340                 String eventData = eventStr;
    341                 if (event == DRIVER_STATE || event == LINK_SPEED)
    342                     eventData = eventData.split(" ")[1];
    343                 else if (event == STATE_CHANGE || event == EAP_FAILURE) {
    344                     int ind = eventStr.indexOf(" ");
    345                     if (ind != -1) {
    346                         eventData = eventStr.substring(ind + 1);
    347                     }
    348                 } else {
    349                     int ind = eventStr.indexOf(" - ");
    350                     if (ind != -1) {
    351                         eventData = eventStr.substring(ind + 3);
    352                     }
    353                 }
    354 
    355                 if (event == STATE_CHANGE) {
    356                     handleSupplicantStateChange(eventData);
    357                 } else if (event == DRIVER_STATE) {
    358                     handleDriverEvent(eventData);
    359                 } else if (event == TERMINATING) {
    360                     /**
    361                      * If monitor socket is closed, we have already
    362                      * stopped the supplicant, simply exit the monitor thread
    363                      */
    364                     if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
    365                         if (false) {
    366                             Log.d(TAG, "Monitor socket is closed, exiting thread");
    367                         }
    368                         break;
    369                     }
    370 
    371                     /**
    372                      * Close the supplicant connection if we see
    373                      * too many recv errors
    374                      */
    375                     if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
    376                         if (++mRecvErrors > MAX_RECV_ERRORS) {
    377                             if (false) {
    378                                 Log.d(TAG, "too many recv errors, closing connection");
    379                             }
    380                         } else {
    381                             continue;
    382                         }
    383                     }
    384 
    385                     // notify and exit
    386                     mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
    387                     break;
    388                 } else if (event == EAP_FAILURE) {
    389                     if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
    390                         mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
    391                     }
    392                 } else {
    393                     handleEvent(event, eventData);
    394                 }
    395                 mRecvErrors = 0;
    396             }
    397         }
    398 
    399         private boolean connectToSupplicant() {
    400             int connectTries = 0;
    401 
    402             while (true) {
    403                 if (WifiNative.connectToSupplicant()) {
    404                     return true;
    405                 }
    406                 if (connectTries++ < 5) {
    407                     nap(1);
    408                 } else {
    409                     break;
    410                 }
    411             }
    412             return false;
    413         }
    414 
    415         private void handleDriverEvent(String state) {
    416             if (state == null) {
    417                 return;
    418             }
    419             if (state.equals("HANGED")) {
    420                 mStateMachine.sendMessage(DRIVER_HUNG_EVENT);
    421             }
    422         }
    423 
    424         /**
    425          * Handle all supplicant events except STATE-CHANGE
    426          * @param event the event type
    427          * @param remainder the rest of the string following the
    428          * event name and &quot;&#8195;&#8212;&#8195;&quot;
    429          */
    430         void handleEvent(int event, String remainder) {
    431             switch (event) {
    432                 case DISCONNECTED:
    433                     handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
    434                     break;
    435 
    436                 case CONNECTED:
    437                     handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
    438                     break;
    439 
    440                 case SCAN_RESULTS:
    441                     mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
    442                     break;
    443 
    444                 case UNKNOWN:
    445                     break;
    446             }
    447         }
    448 
    449         /**
    450          * Handle p2p events
    451          */
    452         private void handleP2pEvents(String dataString) {
    453             if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
    454                 mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
    455             } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
    456                 mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
    457             } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
    458                 mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
    459                         new WifiP2pConfig(dataString));
    460             } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
    461                 mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
    462             } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
    463                 mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT);
    464             } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
    465                 mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
    466             } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
    467                 mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT);
    468             } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
    469                 mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
    470             } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
    471                 mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString));
    472             } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
    473                 mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
    474                         new WifiP2pGroup(dataString));
    475             } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
    476                 String[] tokens = dataString.split(" ");
    477                 if (tokens.length != 2) return;
    478                 String[] nameValue = tokens[1].split("=");
    479                 if (nameValue.length != 2) return;
    480                 mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
    481             } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
    482                 mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
    483                         new WifiP2pDevice(dataString));
    484             } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
    485                 mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT,
    486                         new WifiP2pDevice(dataString));
    487             }
    488         }
    489 
    490         /**
    491          * Handle hostap events
    492          */
    493         private void handleHostApEvents(String dataString) {
    494             String[] tokens = dataString.split(" ");
    495             if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
    496                 mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, tokens[1]);
    497             } else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
    498                 mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, tokens[1]);
    499             }
    500         }
    501 
    502         /**
    503          * Handle the supplicant STATE-CHANGE event
    504          * @param dataString New supplicant state string in the format:
    505          * id=network-id state=new-state
    506          */
    507         private void handleSupplicantStateChange(String dataString) {
    508             String[] dataTokens = dataString.split(" ");
    509 
    510             String BSSID = null;
    511             int networkId = -1;
    512             int newState  = -1;
    513             for (String token : dataTokens) {
    514                 String[] nameValue = token.split("=");
    515                 if (nameValue.length != 2) {
    516                     continue;
    517                 }
    518 
    519                 if (nameValue[0].equals("BSSID")) {
    520                     BSSID = nameValue[1];
    521                     continue;
    522                 }
    523 
    524                 int value;
    525                 try {
    526                     value = Integer.parseInt(nameValue[1]);
    527                 } catch (NumberFormatException e) {
    528                     Log.w(TAG, "STATE-CHANGE non-integer parameter: " + token);
    529                     continue;
    530                 }
    531 
    532                 if (nameValue[0].equals("id")) {
    533                     networkId = value;
    534                 } else if (nameValue[0].equals("state")) {
    535                     newState = value;
    536                 }
    537             }
    538 
    539             if (newState == -1) return;
    540 
    541             SupplicantState newSupplicantState = SupplicantState.INVALID;
    542             for (SupplicantState state : SupplicantState.values()) {
    543                 if (state.ordinal() == newState) {
    544                     newSupplicantState = state;
    545                     break;
    546                 }
    547             }
    548             if (newSupplicantState == SupplicantState.INVALID) {
    549                 Log.w(TAG, "Invalid supplicant state: " + newState);
    550             }
    551             notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
    552         }
    553     }
    554 
    555     private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
    556         String BSSID = null;
    557         int networkId = -1;
    558         if (newState == NetworkInfo.DetailedState.CONNECTED) {
    559             Matcher match = mConnectedEventPattern.matcher(data);
    560             if (!match.find()) {
    561                 if (false) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
    562             } else {
    563                 BSSID = match.group(1);
    564                 try {
    565                     networkId = Integer.parseInt(match.group(2));
    566                 } catch (NumberFormatException e) {
    567                     networkId = -1;
    568                 }
    569             }
    570         }
    571         notifyNetworkStateChange(newState, BSSID, networkId);
    572     }
    573 
    574     /**
    575      * Send the state machine a notification that the state of Wifi connectivity
    576      * has changed.
    577      * @param networkId the configured network on which the state change occurred
    578      * @param newState the new network state
    579      * @param BSSID when the new state is {@link DetailedState#CONNECTED
    580      * NetworkInfo.DetailedState.CONNECTED},
    581      * this is the MAC address of the access point. Otherwise, it
    582      * is {@code null}.
    583      */
    584     void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
    585         if (newState == NetworkInfo.DetailedState.CONNECTED) {
    586             Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
    587                     netId, 0, BSSID);
    588             mStateMachine.sendMessage(m);
    589         } else {
    590             Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
    591                     netId, 0, BSSID);
    592             mStateMachine.sendMessage(m);
    593         }
    594     }
    595 
    596     /**
    597      * Send the state machine a notification that the state of the supplicant
    598      * has changed.
    599      * @param networkId the configured network on which the state change occurred
    600      * @param newState the new {@code SupplicantState}
    601      */
    602     void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
    603         mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
    604                 new StateChangeResult(networkId, BSSID, newState)));
    605     }
    606 
    607     /**
    608      * Sleep for a period of time.
    609      * @param secs the number of seconds to sleep
    610      */
    611     private static void nap(int secs) {
    612         try {
    613             Thread.sleep(secs * 1000);
    614         } catch (InterruptedException ignore) {
    615         }
    616     }
    617 }
    618