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    /**
140     * Deregister the given |handler|
141     * @param iface
142     * @param what
143     * @param handler
144     */
145    public synchronized void deregisterHandler(String iface, int what, Handler handler) {
146        SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
147        if (ifaceHandlers == null) {
148            return;
149        }
150        Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
151        if (ifaceWhatHandlers == null) {
152            return;
153        }
154        ifaceWhatHandlers.remove(handler);
155    }
156
157    private final Map<String, Boolean> mMonitoringMap = new HashMap<>();
158    private boolean isMonitoring(String iface) {
159        Boolean val = mMonitoringMap.get(iface);
160        if (val == null) {
161            return false;
162        } else {
163            return val.booleanValue();
164        }
165    }
166
167    /**
168     * Enable/Disable monitoring for the provided iface.
169     *
170     * @param iface Name of the iface.
171     * @param enabled true to enable, false to disable.
172     */
173    @VisibleForTesting
174    public void setMonitoring(String iface, boolean enabled) {
175        mMonitoringMap.put(iface, enabled);
176    }
177
178    private void setMonitoringNone() {
179        for (String iface : mMonitoringMap.keySet()) {
180            setMonitoring(iface, false);
181        }
182    }
183
184    /**
185     * Start Monitoring for wpa_supplicant events.
186     *
187     * @param iface Name of iface.
188     */
189    public synchronized void startMonitoring(String iface) {
190        if (mVerboseLoggingEnabled) Log.d(TAG, "startMonitoring(" + iface + ")");
191        setMonitoring(iface, true);
192        broadcastSupplicantConnectionEvent(iface);
193    }
194
195    /**
196     * Stop Monitoring for wpa_supplicant events.
197     *
198     * @param iface Name of iface.
199     */
200    public synchronized void stopMonitoring(String iface) {
201        if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
202        setMonitoring(iface, true);
203        broadcastSupplicantDisconnectionEvent(iface);
204        setMonitoring(iface, false);
205    }
206
207    /**
208     * Stop Monitoring for wpa_supplicant events.
209     *
210     * TODO: Add unit tests for these once we remove the legacy code.
211     */
212    public synchronized void stopAllMonitoring() {
213        mConnected = false;
214        setMonitoringNone();
215    }
216
217
218    /**
219     * Similar functions to Handler#sendMessage that send the message to the registered handler
220     * for the given interface and message what.
221     * All of these should be called with the WifiMonitor class lock
222     */
223    private void sendMessage(String iface, int what) {
224        sendMessage(iface, Message.obtain(null, what));
225    }
226
227    private void sendMessage(String iface, int what, Object obj) {
228        sendMessage(iface, Message.obtain(null, what, obj));
229    }
230
231    private void sendMessage(String iface, int what, int arg1) {
232        sendMessage(iface, Message.obtain(null, what, arg1, 0));
233    }
234
235    private void sendMessage(String iface, int what, int arg1, int arg2) {
236        sendMessage(iface, Message.obtain(null, what, arg1, arg2));
237    }
238
239    private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {
240        sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));
241    }
242
243    private void sendMessage(String iface, Message message) {
244        SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
245        if (iface != null && ifaceHandlers != null) {
246            if (isMonitoring(iface)) {
247                Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
248                if (ifaceWhatHandlers != null) {
249                    for (Handler handler : ifaceWhatHandlers) {
250                        if (handler != null) {
251                            sendMessage(handler, Message.obtain(message));
252                        }
253                    }
254                }
255            } else {
256                if (mVerboseLoggingEnabled) {
257                    Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
258                }
259            }
260        } else {
261            if (mVerboseLoggingEnabled) {
262                Log.d(TAG, "Sending to all monitors because there's no matching iface");
263            }
264            for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
265                if (isMonitoring(entry.getKey())) {
266                    Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
267                    for (Handler handler : ifaceWhatHandlers) {
268                        if (handler != null) {
269                            sendMessage(handler, Message.obtain(message));
270                        }
271                    }
272                }
273            }
274        }
275
276        message.recycle();
277    }
278
279    private void sendMessage(Handler handler, Message message) {
280        message.setTarget(handler);
281        message.sendToTarget();
282    }
283
284    /**
285     * Broadcast the WPS fail event to all the handlers registered for this event.
286     *
287     * @param iface Name of iface on which this occurred.
288     * @param cfgError Configuration error code.
289     * @param vendorErrorCode Vendor specific error indication code.
290     */
291    public void broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode) {
292        int reason = 0;
293        switch(vendorErrorCode) {
294            case REASON_TKIP_ONLY_PROHIBITED:
295                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
296                return;
297            case REASON_WEP_PROHIBITED:
298                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
299                return;
300            default:
301                reason = vendorErrorCode;
302                break;
303        }
304        switch(cfgError) {
305            case CONFIG_AUTH_FAILURE:
306                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
307                return;
308            case CONFIG_MULTIPLE_PBC_DETECTED:
309                sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
310                return;
311            default:
312                if (reason == 0) {
313                    reason = cfgError;
314                }
315                break;
316        }
317        //For all other errors, return a generic internal error
318        sendMessage(iface, WPS_FAIL_EVENT, WifiManager.ERROR, reason);
319    }
320
321   /**
322    * Broadcast the WPS succes event to all the handlers registered for this event.
323    *
324    * @param iface Name of iface on which this occurred.
325    */
326    public void broadcastWpsSuccessEvent(String iface) {
327        sendMessage(iface, WPS_SUCCESS_EVENT);
328    }
329
330    /**
331     * Broadcast the WPS overlap event to all the handlers registered for this event.
332     *
333     * @param iface Name of iface on which this occurred.
334     */
335    public void broadcastWpsOverlapEvent(String iface) {
336        sendMessage(iface, WPS_OVERLAP_EVENT);
337    }
338
339    /**
340     * Broadcast the WPS timeout event to all the handlers registered for this event.
341     *
342     * @param iface Name of iface on which this occurred.
343     */
344    public void broadcastWpsTimeoutEvent(String iface) {
345        sendMessage(iface, WPS_TIMEOUT_EVENT);
346    }
347
348    /**
349     * Broadcast the ANQP done event to all the handlers registered for this event.
350     *
351     * @param iface Name of iface on which this occurred.
352     * @param anqpEvent ANQP result retrieved.
353     */
354    public void broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent) {
355        sendMessage(iface, ANQP_DONE_EVENT, anqpEvent);
356    }
357
358    /**
359     * Broadcast the Icon done event to all the handlers registered for this event.
360     *
361     * @param iface Name of iface on which this occurred.
362     * @param iconEvent Instance of IconEvent containing the icon data retrieved.
363     */
364    public void broadcastIconDoneEvent(String iface, IconEvent iconEvent) {
365        sendMessage(iface, RX_HS20_ANQP_ICON_EVENT, iconEvent);
366    }
367
368    /**
369     * Broadcast the WNM event to all the handlers registered for this event.
370     *
371     * @param iface Name of iface on which this occurred.
372     * @param wnmData Instance of WnmData containing the event data.
373     */
374    public void broadcastWnmEvent(String iface, WnmData wnmData) {
375        sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
376    }
377
378    /**
379     * Broadcast the Network identity request event to all the handlers registered for this event.
380     *
381     * @param iface Name of iface on which this occurred.
382     * @param networkId ID of the network in wpa_supplicant.
383     * @param ssid SSID of the network.
384     */
385    public void broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid) {
386        sendMessage(iface, SUP_REQUEST_IDENTITY, 0, networkId, ssid);
387    }
388
389    /**
390     * Broadcast the Network Gsm Sim auth request event to all the handlers registered for this
391     * event.
392     *
393     * @param iface Name of iface on which this occurred.
394     * @param networkId ID of the network in wpa_supplicant.
395     * @param ssid SSID of the network.
396     * @param data Accompanying event data.
397     */
398    public void broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid,
399                                                    String[] data) {
400        sendMessage(iface, SUP_REQUEST_SIM_AUTH,
401                new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.SIM, ssid, data));
402    }
403
404    /**
405     * Broadcast the Network Umts Sim auth request event to all the handlers registered for this
406     * event.
407     *
408     * @param iface Name of iface on which this occurred.
409     * @param networkId ID of the network in wpa_supplicant.
410     * @param ssid SSID of the network.
411     * @param data Accompanying event data.
412     */
413    public void broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid,
414                                                     String[] data) {
415        sendMessage(iface, SUP_REQUEST_SIM_AUTH,
416                new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.AKA, ssid, data));
417    }
418
419    /**
420     * Broadcast scan result event to all the handlers registered for this event.
421     * @param iface Name of iface on which this occurred.
422     */
423    public void broadcastScanResultEvent(String iface) {
424        sendMessage(iface, SCAN_RESULTS_EVENT);
425    }
426
427    /**
428     * Broadcast pno scan result event to all the handlers registered for this event.
429     * @param iface Name of iface on which this occurred.
430     */
431    public void broadcastPnoScanResultEvent(String iface) {
432        sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
433    }
434
435    /**
436     * Broadcast scan failed event to all the handlers registered for this event.
437     * @param iface Name of iface on which this occurred.
438     */
439    public void broadcastScanFailedEvent(String iface) {
440        sendMessage(iface, SCAN_FAILED_EVENT);
441    }
442
443    /**
444     * Broadcast the authentication failure event to all the handlers registered for this event.
445     *
446     * @param iface Name of iface on which this occurred.
447     * @param reason Reason for authentication failure. This has to be one of the
448     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
449     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
450     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
451     *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
452     * @param errorCode Error code associated with the authentication failure event.
453     *               A value of -1 is used when no error code is reported.
454     */
455    public void broadcastAuthenticationFailureEvent(String iface, int reason, int errorCode) {
456        sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, reason, errorCode);
457    }
458
459    /**
460     * Broadcast the association rejection event to all the handlers registered for this event.
461     *
462     * @param iface Name of iface on which this occurred.
463     * @param status Status code for association rejection.
464     * @param timedOut Indicates if the association timed out.
465     * @param bssid BSSID of the access point from which we received the reject.
466     */
467    public void broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut,
468                                                   String bssid) {
469        sendMessage(iface, ASSOCIATION_REJECTION_EVENT, timedOut ? 1 : 0, status, bssid);
470    }
471
472    /**
473     * Broadcast the association success event to all the handlers registered for this event.
474     *
475     * @param iface Name of iface on which this occurred.
476     * @param bssid BSSID of the access point.
477     */
478    public void broadcastAssociatedBssidEvent(String iface, String bssid) {
479        sendMessage(iface, WifiStateMachine.CMD_ASSOCIATED_BSSID, 0, 0, bssid);
480    }
481
482    /**
483     * Broadcast the start of association event to all the handlers registered for this event.
484     *
485     * @param iface Name of iface on which this occurred.
486     * @param bssid BSSID of the access point.
487     */
488    public void broadcastTargetBssidEvent(String iface, String bssid) {
489        sendMessage(iface, WifiStateMachine.CMD_TARGET_BSSID, 0, 0, bssid);
490    }
491
492    /**
493     * Broadcast the network connection event to all the handlers registered for this event.
494     *
495     * @param iface Name of iface on which this occurred.
496     * @param networkId ID of the network in wpa_supplicant.
497     * @param bssid BSSID of the access point.
498     */
499    public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
500        sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
501    }
502
503    /**
504     * Broadcast the network disconnection event to all the handlers registered for this event.
505     *
506     * @param iface Name of iface on which this occurred.
507     * @param local Whether the disconnect was locally triggered.
508     * @param reason Disconnect reason code.
509     * @param bssid BSSID of the access point.
510     */
511    public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
512                                                   String bssid) {
513        sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
514    }
515
516    /**
517     * Broadcast the supplicant state change event to all the handlers registered for this event.
518     *
519     * @param iface Name of iface on which this occurred.
520     * @param networkId ID of the network in wpa_supplicant.
521     * @param bssid BSSID of the access point.
522     * @param newSupplicantState New supplicant state.
523     */
524    public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
525                                                    String bssid,
526                                                    SupplicantState newSupplicantState) {
527        sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
528                new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
529    }
530
531    /**
532     * Broadcast the connection to wpa_supplicant event to all the handlers registered for
533     * this event.
534     *
535     * @param iface Name of iface on which this occurred.
536     */
537    public void broadcastSupplicantConnectionEvent(String iface) {
538        sendMessage(iface, SUP_CONNECTION_EVENT);
539    }
540
541    /**
542     * Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
543     * this event.
544     *
545     * @param iface Name of iface on which this occurred.
546     */
547    public void broadcastSupplicantDisconnectionEvent(String iface) {
548        sendMessage(iface, SUP_DISCONNECTION_EVENT);
549    }
550}
551