/*
|
* Copyright (C) 2008 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wifi;
|
|
import android.net.wifi.SupplicantState;
|
import android.net.wifi.WifiEnterpriseConfig;
|
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiSsid;
|
import android.os.Handler;
|
import android.os.Message;
|
import android.util.ArraySet;
|
import android.util.Log;
|
import android.util.SparseArray;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.util.Protocol;
|
import com.android.server.wifi.hotspot2.AnqpEvent;
|
import com.android.server.wifi.hotspot2.IconEvent;
|
import com.android.server.wifi.hotspot2.WnmData;
|
import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
|
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.Set;
|
|
/**
|
* Listen for events from the wpa_supplicant & wificond and broadcast them on
|
* to the various {@link ClientModeImpl} modules interested in handling these events.
|
* @hide
|
*/
|
public class WifiMonitor {
|
private static final String TAG = "WifiMonitor";
|
|
/* Supplicant events reported to a state machine */
|
private static final int BASE = Protocol.BASE_WIFI_MONITOR;
|
|
/* Connection to supplicant established */
|
public static final int SUP_CONNECTION_EVENT = BASE + 1;
|
/* Connection to supplicant lost */
|
public static final int SUP_DISCONNECTION_EVENT = BASE + 2;
|
/* Network connection completed */
|
public static final int NETWORK_CONNECTION_EVENT = BASE + 3;
|
/* Network disconnection completed */
|
public static final int NETWORK_DISCONNECTION_EVENT = BASE + 4;
|
/* Scan results are available */
|
public static final int SCAN_RESULTS_EVENT = BASE + 5;
|
/* Supplicate state changed */
|
public static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 6;
|
/* Password failure and EAP authentication failure */
|
public static final int AUTHENTICATION_FAILURE_EVENT = BASE + 7;
|
/* WPS success detected */
|
public static final int WPS_SUCCESS_EVENT = BASE + 8;
|
/* WPS failure detected */
|
public static final int WPS_FAIL_EVENT = BASE + 9;
|
/* WPS overlap detected */
|
public static final int WPS_OVERLAP_EVENT = BASE + 10;
|
/* WPS timeout detected */
|
public static final int WPS_TIMEOUT_EVENT = BASE + 11;
|
|
/* Request Identity */
|
public static final int SUP_REQUEST_IDENTITY = BASE + 15;
|
|
/* Request SIM Auth */
|
public static final int SUP_REQUEST_SIM_AUTH = BASE + 16;
|
|
public static final int SCAN_FAILED_EVENT = BASE + 17;
|
/* Pno scan results are available */
|
public static final int PNO_SCAN_RESULTS_EVENT = BASE + 18;
|
|
|
/* Indicates assoc reject event */
|
public static final int ASSOCIATION_REJECTION_EVENT = BASE + 43;
|
public static final int ANQP_DONE_EVENT = BASE + 44;
|
|
/* hotspot 2.0 ANQP events */
|
public static final int GAS_QUERY_START_EVENT = BASE + 51;
|
public static final int GAS_QUERY_DONE_EVENT = BASE + 52;
|
public static final int RX_HS20_ANQP_ICON_EVENT = BASE + 53;
|
|
/* hotspot 2.0 events */
|
public static final int HS20_REMEDIATION_EVENT = BASE + 61;
|
|
/* WPS config errrors */
|
private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
|
private static final int CONFIG_AUTH_FAILURE = 18;
|
|
/* WPS error indications */
|
private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
|
private static final int REASON_WEP_PROHIBITED = 2;
|
|
private final WifiInjector mWifiInjector;
|
private boolean mVerboseLoggingEnabled = false;
|
private boolean mConnected = false;
|
|
public WifiMonitor(WifiInjector wifiInjector) {
|
mWifiInjector = wifiInjector;
|
}
|
|
void enableVerboseLogging(int verbose) {
|
if (verbose > 0) {
|
mVerboseLoggingEnabled = true;
|
} else {
|
mVerboseLoggingEnabled = false;
|
}
|
}
|
|
private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>();
|
public synchronized void registerHandler(String iface, int what, Handler handler) {
|
SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
|
if (ifaceHandlers == null) {
|
ifaceHandlers = new SparseArray<>();
|
mHandlerMap.put(iface, ifaceHandlers);
|
}
|
Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
|
if (ifaceWhatHandlers == null) {
|
ifaceWhatHandlers = new ArraySet<>();
|
ifaceHandlers.put(what, ifaceWhatHandlers);
|
}
|
ifaceWhatHandlers.add(handler);
|
}
|
|
/**
|
* Deregister the given |handler|
|
* @param iface
|
* @param what
|
* @param handler
|
*/
|
public synchronized void deregisterHandler(String iface, int what, Handler handler) {
|
SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
|
if (ifaceHandlers == null) {
|
return;
|
}
|
Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
|
if (ifaceWhatHandlers == null) {
|
return;
|
}
|
ifaceWhatHandlers.remove(handler);
|
}
|
|
private final Map<String, Boolean> mMonitoringMap = new HashMap<>();
|
private boolean isMonitoring(String iface) {
|
Boolean val = mMonitoringMap.get(iface);
|
if (val == null) {
|
return false;
|
} else {
|
return val.booleanValue();
|
}
|
}
|
|
/**
|
* Enable/Disable monitoring for the provided iface.
|
*
|
* @param iface Name of the iface.
|
* @param enabled true to enable, false to disable.
|
*/
|
@VisibleForTesting
|
public void setMonitoring(String iface, boolean enabled) {
|
mMonitoringMap.put(iface, enabled);
|
}
|
|
private void setMonitoringNone() {
|
for (String iface : mMonitoringMap.keySet()) {
|
setMonitoring(iface, false);
|
}
|
}
|
|
/**
|
* Start Monitoring for wpa_supplicant events.
|
*
|
* @param iface Name of iface.
|
*/
|
public synchronized void startMonitoring(String iface) {
|
if (mVerboseLoggingEnabled) Log.d(TAG, "startMonitoring(" + iface + ")");
|
setMonitoring(iface, true);
|
broadcastSupplicantConnectionEvent(iface);
|
}
|
|
/**
|
* Stop Monitoring for wpa_supplicant events.
|
*
|
* @param iface Name of iface.
|
*/
|
public synchronized void stopMonitoring(String iface) {
|
if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
|
setMonitoring(iface, true);
|
broadcastSupplicantDisconnectionEvent(iface);
|
setMonitoring(iface, false);
|
}
|
|
/**
|
* Stop Monitoring for wpa_supplicant events.
|
*
|
* TODO: Add unit tests for these once we remove the legacy code.
|
*/
|
public synchronized void stopAllMonitoring() {
|
mConnected = false;
|
setMonitoringNone();
|
}
|
|
|
/**
|
* Similar functions to Handler#sendMessage that send the message to the registered handler
|
* for the given interface and message what.
|
* All of these should be called with the WifiMonitor class lock
|
*/
|
private void sendMessage(String iface, int what) {
|
sendMessage(iface, Message.obtain(null, what));
|
}
|
|
private void sendMessage(String iface, int what, Object obj) {
|
sendMessage(iface, Message.obtain(null, what, obj));
|
}
|
|
private void sendMessage(String iface, int what, int arg1) {
|
sendMessage(iface, Message.obtain(null, what, arg1, 0));
|
}
|
|
private void sendMessage(String iface, int what, int arg1, int arg2) {
|
sendMessage(iface, Message.obtain(null, what, arg1, arg2));
|
}
|
|
private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {
|
sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));
|
}
|
|
private void sendMessage(String iface, Message message) {
|
SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
|
if (iface != null && ifaceHandlers != null) {
|
if (isMonitoring(iface)) {
|
Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
|
if (ifaceWhatHandlers != null) {
|
for (Handler handler : ifaceWhatHandlers) {
|
if (handler != null) {
|
sendMessage(handler, Message.obtain(message));
|
}
|
}
|
}
|
} else {
|
if (mVerboseLoggingEnabled) {
|
Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
|
}
|
}
|
} else {
|
if (mVerboseLoggingEnabled) {
|
Log.d(TAG, "Sending to all monitors because there's no matching iface");
|
}
|
for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
|
if (isMonitoring(entry.getKey())) {
|
Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
|
for (Handler handler : ifaceWhatHandlers) {
|
if (handler != null) {
|
sendMessage(handler, Message.obtain(message));
|
}
|
}
|
}
|
}
|
}
|
|
message.recycle();
|
}
|
|
private void sendMessage(Handler handler, Message message) {
|
message.setTarget(handler);
|
message.sendToTarget();
|
}
|
|
/**
|
* Broadcast the WPS fail event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param cfgError Configuration error code.
|
* @param vendorErrorCode Vendor specific error indication code.
|
*/
|
public void broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode) {
|
int reason = 0;
|
switch(vendorErrorCode) {
|
case REASON_TKIP_ONLY_PROHIBITED:
|
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
|
return;
|
case REASON_WEP_PROHIBITED:
|
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
|
return;
|
default:
|
reason = vendorErrorCode;
|
break;
|
}
|
switch(cfgError) {
|
case CONFIG_AUTH_FAILURE:
|
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
|
return;
|
case CONFIG_MULTIPLE_PBC_DETECTED:
|
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
|
return;
|
default:
|
if (reason == 0) {
|
reason = cfgError;
|
}
|
break;
|
}
|
//For all other errors, return a generic internal error
|
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.ERROR, reason);
|
}
|
|
/**
|
* Broadcast the WPS succes event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastWpsSuccessEvent(String iface) {
|
sendMessage(iface, WPS_SUCCESS_EVENT);
|
}
|
|
/**
|
* Broadcast the WPS overlap event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastWpsOverlapEvent(String iface) {
|
sendMessage(iface, WPS_OVERLAP_EVENT);
|
}
|
|
/**
|
* Broadcast the WPS timeout event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastWpsTimeoutEvent(String iface) {
|
sendMessage(iface, WPS_TIMEOUT_EVENT);
|
}
|
|
/**
|
* Broadcast the ANQP done event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param anqpEvent ANQP result retrieved.
|
*/
|
public void broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent) {
|
sendMessage(iface, ANQP_DONE_EVENT, anqpEvent);
|
}
|
|
/**
|
* Broadcast the Icon done event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param iconEvent Instance of IconEvent containing the icon data retrieved.
|
*/
|
public void broadcastIconDoneEvent(String iface, IconEvent iconEvent) {
|
sendMessage(iface, RX_HS20_ANQP_ICON_EVENT, iconEvent);
|
}
|
|
/**
|
* Broadcast the WNM event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param wnmData Instance of WnmData containing the event data.
|
*/
|
public void broadcastWnmEvent(String iface, WnmData wnmData) {
|
sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
|
}
|
|
/**
|
* Broadcast the Network identity request event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param networkId ID of the network in wpa_supplicant.
|
* @param ssid SSID of the network.
|
*/
|
public void broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid) {
|
sendMessage(iface, SUP_REQUEST_IDENTITY, 0, networkId, ssid);
|
}
|
|
/**
|
* Broadcast the Network Gsm Sim auth request event to all the handlers registered for this
|
* event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param networkId ID of the network in wpa_supplicant.
|
* @param ssid SSID of the network.
|
* @param data Accompanying event data.
|
*/
|
public void broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid,
|
String[] data) {
|
sendMessage(iface, SUP_REQUEST_SIM_AUTH,
|
new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.SIM, ssid, data));
|
}
|
|
/**
|
* Broadcast the Network Umts Sim auth request event to all the handlers registered for this
|
* event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param networkId ID of the network in wpa_supplicant.
|
* @param ssid SSID of the network.
|
* @param data Accompanying event data.
|
*/
|
public void broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid,
|
String[] data) {
|
sendMessage(iface, SUP_REQUEST_SIM_AUTH,
|
new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.AKA, ssid, data));
|
}
|
|
/**
|
* Broadcast scan result event to all the handlers registered for this event.
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastScanResultEvent(String iface) {
|
sendMessage(iface, SCAN_RESULTS_EVENT);
|
}
|
|
/**
|
* Broadcast pno scan result event to all the handlers registered for this event.
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastPnoScanResultEvent(String iface) {
|
sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
|
}
|
|
/**
|
* Broadcast scan failed event to all the handlers registered for this event.
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastScanFailedEvent(String iface) {
|
sendMessage(iface, SCAN_FAILED_EVENT);
|
}
|
|
/**
|
* Broadcast the authentication failure event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param reason Reason for authentication failure. This has to be one of the
|
* {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
|
* {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
|
* {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
|
* {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
|
* @param errorCode Error code associated with the authentication failure event.
|
* A value of -1 is used when no error code is reported.
|
*/
|
public void broadcastAuthenticationFailureEvent(String iface, int reason, int errorCode) {
|
sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, reason, errorCode);
|
}
|
|
/**
|
* Broadcast the association rejection event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param status Status code for association rejection.
|
* @param timedOut Indicates if the association timed out.
|
* @param bssid BSSID of the access point from which we received the reject.
|
*/
|
public void broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut,
|
String bssid) {
|
sendMessage(iface, ASSOCIATION_REJECTION_EVENT, timedOut ? 1 : 0, status, bssid);
|
}
|
|
/**
|
* Broadcast the association success event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param bssid BSSID of the access point.
|
*/
|
public void broadcastAssociatedBssidEvent(String iface, String bssid) {
|
sendMessage(iface, ClientModeImpl.CMD_ASSOCIATED_BSSID, 0, 0, bssid);
|
}
|
|
/**
|
* Broadcast the start of association event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param bssid BSSID of the access point.
|
*/
|
public void broadcastTargetBssidEvent(String iface, String bssid) {
|
sendMessage(iface, ClientModeImpl.CMD_TARGET_BSSID, 0, 0, bssid);
|
}
|
|
/**
|
* Broadcast the network connection event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param networkId ID of the network in wpa_supplicant.
|
* @param bssid BSSID of the access point.
|
*/
|
public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
|
sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
|
}
|
|
/**
|
* Broadcast the network disconnection event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param local Whether the disconnect was locally triggered.
|
* @param reason Disconnect reason code.
|
* @param bssid BSSID of the access point.
|
*/
|
public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
|
String bssid) {
|
sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
|
}
|
|
/**
|
* Broadcast the supplicant state change event to all the handlers registered for this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
* @param networkId ID of the network in wpa_supplicant.
|
* @param bssid BSSID of the access point.
|
* @param newSupplicantState New supplicant state.
|
*/
|
public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
|
String bssid,
|
SupplicantState newSupplicantState) {
|
sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
|
new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
|
}
|
|
/**
|
* Broadcast the connection to wpa_supplicant event to all the handlers registered for
|
* this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastSupplicantConnectionEvent(String iface) {
|
sendMessage(iface, SUP_CONNECTION_EVENT);
|
}
|
|
/**
|
* Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
|
* this event.
|
*
|
* @param iface Name of iface on which this occurred.
|
*/
|
public void broadcastSupplicantDisconnectionEvent(String iface) {
|
sendMessage(iface, SUP_DISCONNECTION_EVENT);
|
}
|
}
|