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 com.android.server.wifi;
18
19import android.net.wifi.SupplicantState;
20import android.net.wifi.WifiEnterpriseConfig;
21import android.net.wifi.WifiManager;
22import android.net.wifi.WifiSsid;
23import android.os.Handler;
24import android.os.Message;
25import android.util.ArraySet;
26import android.util.Log;
27import android.util.SparseArray;
28
29import com.android.internal.annotations.VisibleForTesting;
30import com.android.internal.util.Protocol;
31import com.android.internal.util.StateMachine;
32import com.android.server.wifi.hotspot2.AnqpEvent;
33import com.android.server.wifi.hotspot2.IconEvent;
34import com.android.server.wifi.hotspot2.WnmData;
35import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
36
37import java.util.HashMap;
38import java.util.Map;
39import java.util.Set;
40
41/**
42 * Listens for events from the wpa_supplicant server, and passes them on
43 * to the {@link StateMachine} for handling.
44 *
45 * @hide
46 */
47public class WifiMonitor {
48    private static final String TAG = "WifiMonitor";
49
50    /* Supplicant events reported to a state machine */
51    private static final int BASE = Protocol.BASE_WIFI_MONITOR;
52
53    /* Connection to supplicant established */
54    public static final int SUP_CONNECTION_EVENT                 = BASE + 1;
55    /* Connection to supplicant lost */
56    public static final int SUP_DISCONNECTION_EVENT              = BASE + 2;
57   /* Network connection completed */
58    public static final int NETWORK_CONNECTION_EVENT             = BASE + 3;
59    /* Network disconnection completed */
60    public static final int NETWORK_DISCONNECTION_EVENT          = BASE + 4;
61    /* Scan results are available */
62    public static final int SCAN_RESULTS_EVENT                   = BASE + 5;
63    /* Supplicate state changed */
64    public static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 6;
65    /* Password failure and EAP authentication failure */
66    public static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 7;
67    /* WPS success detected */
68    public static final int WPS_SUCCESS_EVENT                    = BASE + 8;
69    /* WPS failure detected */
70    public static final int WPS_FAIL_EVENT                       = BASE + 9;
71     /* WPS overlap detected */
72    public static final int WPS_OVERLAP_EVENT                    = BASE + 10;
73     /* WPS timeout detected */
74    public static final int WPS_TIMEOUT_EVENT                    = BASE + 11;
75
76    /* Request Identity */
77    public static final int SUP_REQUEST_IDENTITY                 = BASE + 15;
78
79    /* Request SIM Auth */
80    public static final int SUP_REQUEST_SIM_AUTH                 = BASE + 16;
81
82    public static final int SCAN_FAILED_EVENT                    = BASE + 17;
83    /* Pno scan results are available */
84    public static final int PNO_SCAN_RESULTS_EVENT               = BASE + 18;
85
86
87    /* Indicates assoc reject event */
88    public static final int ASSOCIATION_REJECTION_EVENT          = BASE + 43;
89    public static final int ANQP_DONE_EVENT                      = BASE + 44;
90
91    /* hotspot 2.0 ANQP events */
92    public static final int GAS_QUERY_START_EVENT                = BASE + 51;
93    public static final int GAS_QUERY_DONE_EVENT                 = BASE + 52;
94    public static final int RX_HS20_ANQP_ICON_EVENT              = BASE + 53;
95
96    /* hotspot 2.0 events */
97    public static final int HS20_REMEDIATION_EVENT               = BASE + 61;
98
99    /* WPS config errrors */
100    private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
101    private static final int CONFIG_AUTH_FAILURE = 18;
102
103    /* WPS error indications */
104    private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
105    private static final int REASON_WEP_PROHIBITED = 2;
106
107    private final WifiInjector mWifiInjector;
108    private boolean mVerboseLoggingEnabled = false;
109    private boolean mConnected = false;
110
111    public WifiMonitor(WifiInjector wifiInjector) {
112        mWifiInjector = wifiInjector;
113    }
114
115    void enableVerboseLogging(int verbose) {
116        if (verbose > 0) {
117            mVerboseLoggingEnabled = true;
118        } else {
119            mVerboseLoggingEnabled = false;
120        }
121    }
122
123    // TODO(b/27569474) remove support for multiple handlers for the same event
124    private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>();
125    public synchronized void registerHandler(String iface, int what, Handler handler) {
126        SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
127        if (ifaceHandlers == null) {
128            ifaceHandlers = new SparseArray<>();
129            mHandlerMap.put(iface, ifaceHandlers);
130        }
131        Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
132        if (ifaceWhatHandlers == null) {
133            ifaceWhatHandlers = new ArraySet<>();
134            ifaceHandlers.put(what, ifaceWhatHandlers);
135        }
136        ifaceWhatHandlers.add(handler);
137    }
138
139    private final Map<String, Boolean> mMonitoringMap = new HashMap<>();
140    private boolean isMonitoring(String iface) {
141        Boolean val = mMonitoringMap.get(iface);
142        if (val == null) {
143            return false;
144        } else {
145            return val.booleanValue();
146        }
147    }
148
149    /**
150     * Enable/Disable monitoring for the provided iface.
151     *
152     * @param iface Name of the iface.
153     * @param enabled true to enable, false to disable.
154     */
155    @VisibleForTesting
156    public void setMonitoring(String iface, boolean enabled) {
157        mMonitoringMap.put(iface, enabled);
158    }
159
160    private void setMonitoringNone() {
161        for (String iface : mMonitoringMap.keySet()) {
162            setMonitoring(iface, false);
163        }
164    }
165
166    /**
167     * Wait for wpa_supplicant's control interface to be ready.
168     *
169     * TODO: Add unit tests for these once we remove the legacy code.
170     */
171    private boolean ensureConnectedLocked() {
172        if (mConnected) {
173            return true;
174        }
175        if (mVerboseLoggingEnabled) Log.d(TAG, "connecting to supplicant");
176        int connectTries = 0;
177        while (true) {
178            mConnected = mWifiInjector.getWifiNative().connectToSupplicant();
179            if (mConnected) {
180                return true;
181            }
182            if (connectTries++ < 50) {
183                try {
184                    Thread.sleep(100);
185                } catch (InterruptedException ignore) {
186                }
187            } else {
188                return false;
189            }
190        }
191    }
192
193    /**
194     * Start Monitoring for wpa_supplicant events.
195     *
196     * @param iface Name of iface.
197     * TODO: Add unit tests for these once we remove the legacy code.
198     */
199    public synchronized void startMonitoring(String iface, boolean isStaIface) {
200        if (ensureConnectedLocked()) {
201            setMonitoring(iface, true);
202            broadcastSupplicantConnectionEvent(iface);
203        } else {
204            boolean originalMonitoring = isMonitoring(iface);
205            setMonitoring(iface, true);
206            broadcastSupplicantDisconnectionEvent(iface);
207            setMonitoring(iface, originalMonitoring);
208            Log.e(TAG, "startMonitoring(" + iface + ") failed!");
209        }
210    }
211
212    /**
213     * Stop Monitoring for wpa_supplicant events.
214     *
215     * @param iface Name of iface.
216     * TODO: Add unit tests for these once we remove the legacy code.
217     */
218    public synchronized void stopMonitoring(String iface) {
219        if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
220        setMonitoring(iface, true);
221        broadcastSupplicantDisconnectionEvent(iface);
222        setMonitoring(iface, false);
223    }
224
225    /**
226     * Stop Monitoring for wpa_supplicant events.
227     *
228     * TODO: Add unit tests for these once we remove the legacy code.
229     */
230    public synchronized void stopAllMonitoring() {
231        mConnected = false;
232        setMonitoringNone();
233    }
234
235
236    /**
237     * Similar functions to Handler#sendMessage that send the message to the registered handler
238     * for the given interface and message what.
239     * All of these should be called with the WifiMonitor class lock
240     */
241    private void sendMessage(String iface, int what) {
242        sendMessage(iface, Message.obtain(null, what));
243    }
244
245    private void sendMessage(String iface, int what, Object obj) {
246        sendMessage(iface, Message.obtain(null, what, obj));
247    }
248
249    private void sendMessage(String iface, int what, int arg1) {
250        sendMessage(iface, Message.obtain(null, what, arg1, 0));
251    }
252
253    private void sendMessage(String iface, int what, int arg1, int arg2) {
254        sendMessage(iface, Message.obtain(null, what, arg1, arg2));
255    }
256
257    private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {
258        sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));
259    }
260
261    private void sendMessage(String iface, Message message) {
262        SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
263        if (iface != null && ifaceHandlers != null) {
264            if (isMonitoring(iface)) {
265                Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
266                if (ifaceWhatHandlers != null) {
267                    for (Handler handler : ifaceWhatHandlers) {
268                        if (handler != null) {
269                            sendMessage(handler, Message.obtain(message));
270                        }
271                    }
272                }
273            } else {
274                if (mVerboseLoggingEnabled) {
275                    Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
276                }
277            }
278        } else {
279            if (mVerboseLoggingEnabled) {
280                Log.d(TAG, "Sending to all monitors because there's no matching iface");
281            }
282            for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
283                if (isMonitoring(entry.getKey())) {
284                    Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
285                    for (Handler handler : ifaceWhatHandlers) {
286                        if (handler != null) {
287                            sendMessage(handler, Message.obtain(message));
288                        }
289                    }
290                }
291            }
292        }
293
294        message.recycle();
295    }
296
297    private void sendMessage(Handler handler, Message message) {
298        message.setTarget(handler);
299        message.sendToTarget();
300    }
301
302    /**
303     * Broadcast the WPS fail event to all the handlers registered for this event.
304     *
305     * @param iface Name of iface on which this occurred.
306     * @param cfgError Configuration error code.
307     * @param vendorErrorCode Vendor specific error indication code.
308     */
309    public void broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode) {
310        int reason = 0;
311        switch(vendorErrorCode) {
312            case REASON_TKIP_ONLY_PROHIBITED:
313                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
314                return;
315            case REASON_WEP_PROHIBITED:
316                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
317                return;
318            default:
319                reason = vendorErrorCode;
320                break;
321        }
322        switch(cfgError) {
323            case CONFIG_AUTH_FAILURE:
324                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
325                return;
326            case CONFIG_MULTIPLE_PBC_DETECTED:
327                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
328                return;
329            default:
330                if (reason == 0) {
331                    reason = cfgError;
332                }
333                break;
334        }
335        //For all other errors, return a generic internal error
336        sendMessage(iface, WPS_FAIL_EVENT, WifiManager.ERROR, reason);
337    }
338
339   /**
340    * Broadcast the WPS succes event to all the handlers registered for this event.
341    *
342    * @param iface Name of iface on which this occurred.
343    */
344    public void broadcastWpsSuccessEvent(String iface) {
345        sendMessage(iface, WPS_SUCCESS_EVENT);
346    }
347
348    /**
349     * Broadcast the WPS overlap event to all the handlers registered for this event.
350     *
351     * @param iface Name of iface on which this occurred.
352     */
353    public void broadcastWpsOverlapEvent(String iface) {
354        sendMessage(iface, WPS_OVERLAP_EVENT);
355    }
356
357    /**
358     * Broadcast the WPS timeout event to all the handlers registered for this event.
359     *
360     * @param iface Name of iface on which this occurred.
361     */
362    public void broadcastWpsTimeoutEvent(String iface) {
363        sendMessage(iface, WPS_TIMEOUT_EVENT);
364    }
365
366    /**
367     * Broadcast the ANQP done event to all the handlers registered for this event.
368     *
369     * @param iface Name of iface on which this occurred.
370     * @param anqpEvent ANQP result retrieved.
371     */
372    public void broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent) {
373        sendMessage(iface, ANQP_DONE_EVENT, anqpEvent);
374    }
375
376    /**
377     * Broadcast the Icon done event to all the handlers registered for this event.
378     *
379     * @param iface Name of iface on which this occurred.
380     * @param iconEvent Instance of IconEvent containing the icon data retrieved.
381     */
382    public void broadcastIconDoneEvent(String iface, IconEvent iconEvent) {
383        sendMessage(iface, RX_HS20_ANQP_ICON_EVENT, iconEvent);
384    }
385
386    /**
387     * Broadcast the WNM event to all the handlers registered for this event.
388     *
389     * @param iface Name of iface on which this occurred.
390     * @param wnmData Instance of WnmData containing the event data.
391     */
392    public void broadcastWnmEvent(String iface, WnmData wnmData) {
393        sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
394    }
395
396    /**
397     * Broadcast the Network identity request event to all the handlers registered for this event.
398     *
399     * @param iface Name of iface on which this occurred.
400     * @param networkId ID of the network in wpa_supplicant.
401     * @param ssid SSID of the network.
402     */
403    public void broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid) {
404        sendMessage(iface, SUP_REQUEST_IDENTITY, 0, networkId, ssid);
405    }
406
407    /**
408     * Broadcast the Network Gsm Sim auth request event to all the handlers registered for this
409     * event.
410     *
411     * @param iface Name of iface on which this occurred.
412     * @param networkId ID of the network in wpa_supplicant.
413     * @param ssid SSID of the network.
414     * @param data Accompanying event data.
415     */
416    public void broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid,
417                                                    String[] data) {
418        sendMessage(iface, SUP_REQUEST_SIM_AUTH,
419                new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.SIM, ssid, data));
420    }
421
422    /**
423     * Broadcast the Network Umts Sim auth request event to all the handlers registered for this
424     * event.
425     *
426     * @param iface Name of iface on which this occurred.
427     * @param networkId ID of the network in wpa_supplicant.
428     * @param ssid SSID of the network.
429     * @param data Accompanying event data.
430     */
431    public void broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid,
432                                                     String[] data) {
433        sendMessage(iface, SUP_REQUEST_SIM_AUTH,
434                new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.AKA, ssid, data));
435    }
436
437    /**
438     * Broadcast scan result event to all the handlers registered for this event.
439     * @param iface Name of iface on which this occurred.
440     */
441    public void broadcastScanResultEvent(String iface) {
442        sendMessage(iface, SCAN_RESULTS_EVENT);
443    }
444
445    /**
446     * Broadcast pno scan result event to all the handlers registered for this event.
447     * @param iface Name of iface on which this occurred.
448     */
449    public void broadcastPnoScanResultEvent(String iface) {
450        sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
451    }
452
453    /**
454     * Broadcast scan failed event to all the handlers registered for this event.
455     * @param iface Name of iface on which this occurred.
456     */
457    public void broadcastScanFailedEvent(String iface) {
458        sendMessage(iface, SCAN_FAILED_EVENT);
459    }
460
461    /**
462     * Broadcast the authentication failure event to all the handlers registered for this event.
463     *
464     * @param iface Name of iface on which this occurred.
465     * @param reason Reason for authentication failure. This has to be one of the
466     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
467     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
468     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
469     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
470     */
471    public void broadcastAuthenticationFailureEvent(String iface, int reason) {
472        sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, 0, reason);
473    }
474
475    /**
476     * Broadcast the association rejection event to all the handlers registered for this event.
477     *
478     * @param iface Name of iface on which this occurred.
479     * @param status Status code for association rejection.
480     * @param timedOut Indicates if the association timed out.
481     * @param bssid BSSID of the access point from which we received the reject.
482     */
483    public void broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut,
484                                                   String bssid) {
485        sendMessage(iface, ASSOCIATION_REJECTION_EVENT, timedOut ? 1 : 0, status, bssid);
486    }
487
488    /**
489     * Broadcast the association success event to all the handlers registered for this event.
490     *
491     * @param iface Name of iface on which this occurred.
492     * @param bssid BSSID of the access point.
493     */
494    public void broadcastAssociatedBssidEvent(String iface, String bssid) {
495        sendMessage(iface, WifiStateMachine.CMD_ASSOCIATED_BSSID, 0, 0, bssid);
496    }
497
498    /**
499     * Broadcast the start of association event to all the handlers registered for this event.
500     *
501     * @param iface Name of iface on which this occurred.
502     * @param bssid BSSID of the access point.
503     */
504    public void broadcastTargetBssidEvent(String iface, String bssid) {
505        sendMessage(iface, WifiStateMachine.CMD_TARGET_BSSID, 0, 0, bssid);
506    }
507
508    /**
509     * Broadcast the network connection event to all the handlers registered for this event.
510     *
511     * @param iface Name of iface on which this occurred.
512     * @param networkId ID of the network in wpa_supplicant.
513     * @param bssid BSSID of the access point.
514     */
515    public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
516        sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
517    }
518
519    /**
520     * Broadcast the network disconnection event to all the handlers registered for this event.
521     *
522     * @param iface Name of iface on which this occurred.
523     * @param local Whether the disconnect was locally triggered.
524     * @param reason Disconnect reason code.
525     * @param bssid BSSID of the access point.
526     */
527    public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
528                                                   String bssid) {
529        sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
530    }
531
532    /**
533     * Broadcast the supplicant state change event to all the handlers registered for this event.
534     *
535     * @param iface Name of iface on which this occurred.
536     * @param networkId ID of the network in wpa_supplicant.
537     * @param bssid BSSID of the access point.
538     * @param newSupplicantState New supplicant state.
539     */
540    public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
541                                                    String bssid,
542                                                    SupplicantState newSupplicantState) {
543        sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
544                new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
545    }
546
547    /**
548     * Broadcast the connection to wpa_supplicant event to all the handlers registered for
549     * this event.
550     *
551     * @param iface Name of iface on which this occurred.
552     */
553    public void broadcastSupplicantConnectionEvent(String iface) {
554        sendMessage(iface, SUP_CONNECTION_EVENT);
555    }
556
557    /**
558     * Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
559     * this event.
560     *
561     * @param iface Name of iface on which this occurred.
562     */
563    public void broadcastSupplicantDisconnectionEvent(String iface) {
564        sendMessage(iface, SUP_DISCONNECTION_EVENT);
565    }
566}
567