blob: 0b27312fef720155659beb5c70f37550646b746b [file] [log] [blame]
/*
* 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 static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.net.MacAddress;
import android.net.TrafficStats;
import android.net.apf.ApfCapabilities;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiAnnotations;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
import android.net.wifi.nl80211.DeviceWiphyCapabilities;
import android.net.wifi.nl80211.NativeScanResult;
import android.net.wifi.nl80211.RadioChainInfo;
import android.net.wifi.nl80211.WifiNl80211Manager;
import android.os.Handler;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.HexDump;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.util.FrameParser;
import com.android.server.wifi.util.InformationElementUtil;
import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.NetdWrapper;
import com.android.server.wifi.util.NetdWrapper.NetdEventObserver;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;
/**
* Native calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
*
* {@hide}
*/
public class WifiNative {
private static final String TAG = "WifiNative";
private final SupplicantStaIfaceHal mSupplicantStaIfaceHal;
private final HostapdHal mHostapdHal;
private final WifiVendorHal mWifiVendorHal;
private final WifiNl80211Manager mWifiCondManager;
private final WifiMonitor mWifiMonitor;
private final PropertyService mPropertyService;
private final WifiMetrics mWifiMetrics;
private final Handler mHandler;
private final Random mRandom;
private final WifiInjector mWifiInjector;
private NetdWrapper mNetdWrapper;
private boolean mVerboseLoggingEnabled = false;
private boolean mIsEnhancedOpenSupported = false;
public WifiNative(WifiVendorHal vendorHal,
SupplicantStaIfaceHal staIfaceHal, HostapdHal hostapdHal,
WifiNl80211Manager condManager, WifiMonitor wifiMonitor,
PropertyService propertyService, WifiMetrics wifiMetrics,
Handler handler, Random random,
WifiInjector wifiInjector) {
mWifiVendorHal = vendorHal;
mSupplicantStaIfaceHal = staIfaceHal;
mHostapdHal = hostapdHal;
mWifiCondManager = condManager;
mWifiMonitor = wifiMonitor;
mPropertyService = propertyService;
mWifiMetrics = wifiMetrics;
mHandler = handler;
mRandom = random;
mWifiInjector = wifiInjector;
}
/**
* Enable verbose logging for all sub modules.
*/
public void enableVerboseLogging(int verbose) {
mVerboseLoggingEnabled = verbose > 0 ? true : false;
mWifiCondManager.enableVerboseLogging(mVerboseLoggingEnabled);
mSupplicantStaIfaceHal.enableVerboseLogging(mVerboseLoggingEnabled);
mHostapdHal.enableVerboseLogging(mVerboseLoggingEnabled);
mWifiVendorHal.enableVerboseLogging(mVerboseLoggingEnabled);
}
/**
* Callbacks for SoftAp interface.
*/
public interface SoftApListener extends WifiNl80211Manager.SoftApCallback {
// dummy for now - provide a shell so that clients don't use a
// WifiNl80211Manager-specific API.
}
/********************************************************
* Interface management related methods.
********************************************************/
/**
* Meta-info about every iface that is active.
*/
private static class Iface {
/** Type of ifaces possible */
public static final int IFACE_TYPE_AP = 0;
public static final int IFACE_TYPE_STA_FOR_CONNECTIVITY = 1;
public static final int IFACE_TYPE_STA_FOR_SCAN = 2;
@IntDef({IFACE_TYPE_AP, IFACE_TYPE_STA_FOR_CONNECTIVITY, IFACE_TYPE_STA_FOR_SCAN})
@Retention(RetentionPolicy.SOURCE)
public @interface IfaceType{}
/** Identifier allocated for the interface */
public final int id;
/** Type of the iface: STA (for Connectivity or Scan) or AP */
public @IfaceType int type;
/** Name of the interface */
public String name;
/** Is the interface up? This is used to mask up/down notifications to external clients. */
public boolean isUp;
/** External iface destroyed listener for the iface */
public InterfaceCallback externalListener;
/** Network observer registered for this interface */
public NetworkObserverInternal networkObserver;
/** Interface feature set / capabilities */
public long featureSet;
public DeviceWiphyCapabilities phyCapabilities;
Iface(int id, @Iface.IfaceType int type) {
this.id = id;
this.type = type;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
String typeString;
switch(type) {
case IFACE_TYPE_STA_FOR_CONNECTIVITY:
typeString = "STA_CONNECTIVITY";
break;
case IFACE_TYPE_STA_FOR_SCAN:
typeString = "STA_SCAN";
break;
case IFACE_TYPE_AP:
typeString = "AP";
break;
default:
typeString = "<UNKNOWN>";
break;
}
sb.append("Iface:")
.append("{")
.append("Name=").append(name)
.append(",")
.append("Id=").append(id)
.append(",")
.append("Type=").append(typeString)
.append("}");
return sb.toString();
}
}
/**
* Iface Management entity. This class maintains list of all the active ifaces.
*/
private static class IfaceManager {
/** Integer to allocate for the next iface being created */
private int mNextId;
/** Map of the id to the iface structure */
private HashMap<Integer, Iface> mIfaces = new HashMap<>();
/** Allocate a new iface for the given type */
private Iface allocateIface(@Iface.IfaceType int type) {
Iface iface = new Iface(mNextId, type);
mIfaces.put(mNextId, iface);
mNextId++;
return iface;
}
/** Remove the iface using the provided id */
private Iface removeIface(int id) {
return mIfaces.remove(id);
}
/** Lookup the iface using the provided id */
private Iface getIface(int id) {
return mIfaces.get(id);
}
/** Lookup the iface using the provided name */
private Iface getIface(@NonNull String ifaceName) {
for (Iface iface : mIfaces.values()) {
if (TextUtils.equals(iface.name, ifaceName)) {
return iface;
}
}
return null;
}
/** Iterator to use for deleting all the ifaces while performing teardown on each of them */
private Iterator<Integer> getIfaceIdIter() {
return mIfaces.keySet().iterator();
}
/** Checks if there are any iface active. */
private boolean hasAnyIface() {
return !mIfaces.isEmpty();
}
/** Checks if there are any iface of the given type active. */
private boolean hasAnyIfaceOfType(@Iface.IfaceType int type) {
for (Iface iface : mIfaces.values()) {
if (iface.type == type) {
return true;
}
}
return false;
}
/** Checks if there are any iface of the given type active. */
private Iface findAnyIfaceOfType(@Iface.IfaceType int type) {
for (Iface iface : mIfaces.values()) {
if (iface.type == type) {
return iface;
}
}
return null;
}
/** Checks if there are any STA (for connectivity) iface active. */
private boolean hasAnyStaIfaceForConnectivity() {
return hasAnyIfaceOfType(Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY);
}
/** Checks if there are any STA (for scan) iface active. */
private boolean hasAnyStaIfaceForScan() {
return hasAnyIfaceOfType(Iface.IFACE_TYPE_STA_FOR_SCAN);
}
/** Checks if there are any AP iface active. */
private boolean hasAnyApIface() {
return hasAnyIfaceOfType(Iface.IFACE_TYPE_AP);
}
/** Finds the name of any STA iface active. */
private String findAnyStaIfaceName() {
Iface iface = findAnyIfaceOfType(Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY);
if (iface == null) {
iface = findAnyIfaceOfType(Iface.IFACE_TYPE_STA_FOR_SCAN);
}
if (iface == null) {
return null;
}
return iface.name;
}
/** Finds the name of any AP iface active. */
private String findAnyApIfaceName() {
Iface iface = findAnyIfaceOfType(Iface.IFACE_TYPE_AP);
if (iface == null) {
return null;
}
return iface.name;
}
private @NonNull Set<String> findAllStaIfaceNames() {
Set<String> ifaceNames = new ArraySet<>();
for (Iface iface : mIfaces.values()) {
if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY
|| iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) {
ifaceNames.add(iface.name);
}
}
return ifaceNames;
}
/** Removes the existing iface that does not match the provided id. */
public Iface removeExistingIface(int newIfaceId) {
Iface removedIface = null;
// The number of ifaces in the database could be 1 existing & 1 new at the max.
if (mIfaces.size() > 2) {
Log.wtf(TAG, "More than 1 existing interface found");
}
Iterator<Map.Entry<Integer, Iface>> iter = mIfaces.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Integer, Iface> entry = iter.next();
if (entry.getKey() != newIfaceId) {
removedIface = entry.getValue();
iter.remove();
}
}
return removedIface;
}
}
private class NormalScanEventCallback implements WifiNl80211Manager.ScanEventCallback {
private String mIfaceName;
NormalScanEventCallback(String ifaceName) {
mIfaceName = ifaceName;
}
@Override
public void onScanResultReady() {
Log.d(TAG, "Scan result ready event");
mWifiMonitor.broadcastScanResultEvent(mIfaceName);
}
@Override
public void onScanFailed() {
Log.d(TAG, "Scan failed event");
mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
}
}
private class PnoScanEventCallback implements WifiNl80211Manager.ScanEventCallback {
private String mIfaceName;
PnoScanEventCallback(String ifaceName) {
mIfaceName = ifaceName;
}
@Override
public void onScanResultReady() {
Log.d(TAG, "Pno scan result event");
mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName);
mWifiMetrics.incrementPnoFoundNetworkEventCount();
}
@Override
public void onScanFailed() {
Log.d(TAG, "Pno Scan failed event");
mWifiMetrics.incrementPnoScanFailedCount();
}
}
private final Object mLock = new Object();
private final IfaceManager mIfaceMgr = new IfaceManager();
private HashSet<StatusListener> mStatusListeners = new HashSet<>();
/** Helper method invoked to start supplicant if there were no ifaces */
private boolean startHal() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyIface()) {
if (mWifiVendorHal.isVendorHalSupported()) {
if (!mWifiVendorHal.startVendorHal()) {
Log.e(TAG, "Failed to start vendor HAL");
return false;
}
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring start.");
}
}
return true;
}
}
/** Helper method invoked to stop HAL if there are no more ifaces */
private void stopHalAndWificondIfNecessary() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyIface()) {
if (!mWifiCondManager.tearDownInterfaces()) {
Log.e(TAG, "Failed to teardown ifaces from wificond");
}
if (mWifiVendorHal.isVendorHalSupported()) {
mWifiVendorHal.stopVendorHal();
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring stop.");
}
}
}
}
private static final int CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS = 100;
private static final int CONNECT_TO_SUPPLICANT_RETRY_TIMES = 50;
/**
* This method is called to wait for establishing connection to wpa_supplicant.
*
* @return true if connection is established, false otherwise.
*/
private boolean startAndWaitForSupplicantConnection() {
// Start initialization if not already started.
if (!mSupplicantStaIfaceHal.isInitializationStarted()
&& !mSupplicantStaIfaceHal.initialize()) {
return false;
}
if (!mSupplicantStaIfaceHal.startDaemon()) {
Log.e(TAG, "Failed to startup supplicant");
return false;
}
boolean connected = false;
int connectTries = 0;
while (!connected && connectTries++ < CONNECT_TO_SUPPLICANT_RETRY_TIMES) {
// Check if the initialization is complete.
connected = mSupplicantStaIfaceHal.isInitializationComplete();
if (connected) {
break;
}
try {
Thread.sleep(CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS);
} catch (InterruptedException ignore) {
}
}
return connected;
}
/** Helper method invoked to start supplicant if there were no STA ifaces */
private boolean startSupplicant() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyStaIfaceForConnectivity()) {
if (!startAndWaitForSupplicantConnection()) {
Log.e(TAG, "Failed to connect to supplicant");
return false;
}
if (!mSupplicantStaIfaceHal.registerDeathHandler(
new SupplicantDeathHandlerInternal())) {
Log.e(TAG, "Failed to register supplicant death handler");
return false;
}
}
return true;
}
}
/** Helper method invoked to stop supplicant if there are no more STA ifaces */
private void stopSupplicantIfNecessary() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyStaIfaceForConnectivity()) {
if (!mSupplicantStaIfaceHal.deregisterDeathHandler()) {
Log.e(TAG, "Failed to deregister supplicant death handler");
}
mSupplicantStaIfaceHal.terminate();
}
}
}
/** Helper method invoked to start hostapd if there were no AP ifaces */
private boolean startHostapd() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyApIface()) {
if (!startAndWaitForHostapdConnection()) {
Log.e(TAG, "Failed to connect to hostapd");
return false;
}
if (!mHostapdHal.registerDeathHandler(
new HostapdDeathHandlerInternal())) {
Log.e(TAG, "Failed to register hostapd death handler");
return false;
}
}
return true;
}
}
/** Helper method invoked to stop hostapd if there are no more AP ifaces */
private void stopHostapdIfNecessary() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyApIface()) {
if (!mHostapdHal.deregisterDeathHandler()) {
Log.e(TAG, "Failed to deregister hostapd death handler");
}
mHostapdHal.terminate();
}
}
}
/** Helper method to register a network observer and return it */
private boolean registerNetworkObserver(NetworkObserverInternal observer) {
if (observer == null) return false;
mNetdWrapper.registerObserver(observer);
return true;
}
/** Helper method to unregister a network observer */
private boolean unregisterNetworkObserver(NetworkObserverInternal observer) {
if (observer == null) return false;
mNetdWrapper.unregisterObserver(observer);
return true;
}
/**
* Helper method invoked to teardown client iface (for connectivity) and perform
* necessary cleanup
*/
private void onClientInterfaceForConnectivityDestroyed(@NonNull Iface iface) {
synchronized (mLock) {
mWifiMonitor.stopMonitoring(iface.name);
if (!unregisterNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to unregister network observer on " + iface);
}
if (!mSupplicantStaIfaceHal.teardownIface(iface.name)) {
Log.e(TAG, "Failed to teardown iface in supplicant on " + iface);
}
if (!mWifiCondManager.tearDownClientInterface(iface.name)) {
Log.e(TAG, "Failed to teardown iface in wificond on " + iface);
}
stopSupplicantIfNecessary();
stopHalAndWificondIfNecessary();
}
}
/** Helper method invoked to teardown client iface (for scan) and perform necessary cleanup */
private void onClientInterfaceForScanDestroyed(@NonNull Iface iface) {
synchronized (mLock) {
mWifiMonitor.stopMonitoring(iface.name);
if (!unregisterNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to unregister network observer on " + iface);
}
if (!mWifiCondManager.tearDownClientInterface(iface.name)) {
Log.e(TAG, "Failed to teardown iface in wificond on " + iface);
}
stopHalAndWificondIfNecessary();
}
}
/** Helper method invoked to teardown softAp iface and perform necessary cleanup */
private void onSoftApInterfaceDestroyed(@NonNull Iface iface) {
synchronized (mLock) {
if (!unregisterNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to unregister network observer on " + iface);
}
if (!mHostapdHal.removeAccessPoint(iface.name)) {
Log.e(TAG, "Failed to remove access point on " + iface);
}
if (!mWifiCondManager.tearDownSoftApInterface(iface.name)) {
Log.e(TAG, "Failed to teardown iface in wificond on " + iface);
}
stopHostapdIfNecessary();
stopHalAndWificondIfNecessary();
}
}
/** Helper method invoked to teardown iface and perform necessary cleanup */
private void onInterfaceDestroyed(@NonNull Iface iface) {
synchronized (mLock) {
if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY) {
onClientInterfaceForConnectivityDestroyed(iface);
} else if (iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) {
onClientInterfaceForScanDestroyed(iface);
} else if (iface.type == Iface.IFACE_TYPE_AP) {
onSoftApInterfaceDestroyed(iface);
}
// Invoke the external callback.
iface.externalListener.onDestroyed(iface.name);
}
}
/**
* Callback to be invoked by HalDeviceManager when an interface is destroyed.
*/
private class InterfaceDestoyedListenerInternal
implements HalDeviceManager.InterfaceDestroyedListener {
/** Identifier allocated for the interface */
private final int mInterfaceId;
InterfaceDestoyedListenerInternal(int ifaceId) {
mInterfaceId = ifaceId;
}
@Override
public void onDestroyed(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.removeIface(mInterfaceId);
if (iface == null) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received iface destroyed notification on an invalid iface="
+ ifaceName);
}
return;
}
onInterfaceDestroyed(iface);
Log.i(TAG, "Successfully torn down " + iface);
}
}
}
/**
* Helper method invoked to trigger the status changed callback after one of the native
* daemon's death.
*/
private void onNativeDaemonDeath() {
synchronized (mLock) {
for (StatusListener listener : mStatusListeners) {
listener.onStatusChanged(false);
}
for (StatusListener listener : mStatusListeners) {
listener.onStatusChanged(true);
}
}
}
/**
* Death handler for the Vendor HAL daemon.
*/
private class VendorHalDeathHandlerInternal implements VendorHalDeathEventHandler {
@Override
public void onDeath() {
synchronized (mLock) {
Log.i(TAG, "Vendor HAL died. Cleaning up internal state.");
onNativeDaemonDeath();
mWifiMetrics.incrementNumHalCrashes();
}
}
}
/**
* Death handler for the wificond daemon.
*/
private class WificondDeathHandlerInternal implements Runnable {
@Override
public void run() {
synchronized (mLock) {
Log.i(TAG, "wificond died. Cleaning up internal state.");
onNativeDaemonDeath();
mWifiMetrics.incrementNumWificondCrashes();
}
}
}
/**
* Death handler for the supplicant daemon.
*/
private class SupplicantDeathHandlerInternal implements SupplicantDeathEventHandler {
@Override
public void onDeath() {
synchronized (mLock) {
Log.i(TAG, "wpa_supplicant died. Cleaning up internal state.");
onNativeDaemonDeath();
mWifiMetrics.incrementNumSupplicantCrashes();
}
}
}
/**
* Death handler for the hostapd daemon.
*/
private class HostapdDeathHandlerInternal implements HostapdDeathEventHandler {
@Override
public void onDeath() {
synchronized (mLock) {
Log.i(TAG, "hostapd died. Cleaning up internal state.");
onNativeDaemonDeath();
mWifiMetrics.incrementNumHostapdCrashes();
}
}
}
/** Helper method invoked to handle interface change. */
private void onInterfaceStateChanged(Iface iface, boolean isUp) {
synchronized (mLock) {
// Mask multiple notifications with the same state.
if (isUp == iface.isUp) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Interface status unchanged on " + iface + " from " + isUp
+ ", Ignoring...");
}
return;
}
Log.i(TAG, "Interface state changed on " + iface + ", isUp=" + isUp);
if (isUp) {
iface.externalListener.onUp(iface.name);
} else {
iface.externalListener.onDown(iface.name);
if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY
|| iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) {
mWifiMetrics.incrementNumClientInterfaceDown();
} else if (iface.type == Iface.IFACE_TYPE_AP) {
mWifiMetrics.incrementNumSoftApInterfaceDown();
}
}
iface.isUp = isUp;
}
}
/**
* Network observer to use for all interface up/down notifications.
*/
private class NetworkObserverInternal implements NetdEventObserver {
/** Identifier allocated for the interface */
private final int mInterfaceId;
NetworkObserverInternal(int id) {
mInterfaceId = id;
}
/**
* Note: We should ideally listen to
* {@link NetdEventObserver#interfaceStatusChanged(String, boolean)} here. But, that
* callback is not working currently (broken in netd). So, instead listen to link state
* change callbacks as triggers to query the real interface state. We should get rid of
* this workaround if we get the |interfaceStatusChanged| callback to work in netd.
* Also, this workaround will not detect an interface up event, if the link state is
* still down.
*/
@Override
public void interfaceLinkStateChanged(String ifaceName, boolean unusedIsLinkUp) {
// This is invoked from the main system_server thread. Post to our handler.
mHandler.post(() -> {
synchronized (mLock) {
final Iface ifaceWithId = mIfaceMgr.getIface(mInterfaceId);
if (ifaceWithId == null) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received iface link up/down notification on an invalid"
+ " iface=" + mInterfaceId);
}
return;
}
final Iface ifaceWithName = mIfaceMgr.getIface(ifaceName);
if (ifaceWithName == null || ifaceWithName != ifaceWithId) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received iface link up/down notification on an invalid"
+ " iface=" + ifaceName);
}
return;
}
onInterfaceStateChanged(ifaceWithName, isInterfaceUp(ifaceName));
}
});
}
@Override
public void interfaceStatusChanged(String ifaceName, boolean unusedIsLinkUp) {
// unused currently. Look at note above.
}
}
/**
* Radio mode change handler for the Vendor HAL daemon.
*/
private class VendorHalRadioModeChangeHandlerInternal
implements VendorHalRadioModeChangeEventHandler {
@Override
public void onMcc(int band) {
synchronized (mLock) {
Log.i(TAG, "Device is in MCC mode now");
mWifiMetrics.incrementNumRadioModeChangeToMcc();
}
}
@Override
public void onScc(int band) {
synchronized (mLock) {
Log.i(TAG, "Device is in SCC mode now");
mWifiMetrics.incrementNumRadioModeChangeToScc();
}
}
@Override
public void onSbs(int band) {
synchronized (mLock) {
Log.i(TAG, "Device is in SBS mode now");
mWifiMetrics.incrementNumRadioModeChangeToSbs();
}
}
@Override
public void onDbs() {
synchronized (mLock) {
Log.i(TAG, "Device is in DBS mode now");
mWifiMetrics.incrementNumRadioModeChangeToDbs();
}
}
}
// For devices that don't support the vendor HAL, we will not support any concurrency.
// So simulate the HalDeviceManager behavior by triggering the destroy listener for
// any active interface.
private String handleIfaceCreationWhenVendorHalNotSupported(@NonNull Iface newIface) {
synchronized (mLock) {
Iface existingIface = mIfaceMgr.removeExistingIface(newIface.id);
if (existingIface != null) {
onInterfaceDestroyed(existingIface);
Log.i(TAG, "Successfully torn down " + existingIface);
}
// Return the interface name directly from the system property.
return mPropertyService.getString("wifi.interface", "wlan0");
}
}
/**
* Helper function to handle creation of STA iface.
* For devices which do not the support the HAL, this will bypass HalDeviceManager &
* teardown any existing iface.
*/
private String createStaIface(@NonNull Iface iface) {
synchronized (mLock) {
if (mWifiVendorHal.isVendorHalSupported()) {
return mWifiVendorHal.createStaIface(
new InterfaceDestoyedListenerInternal(iface.id));
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring createStaIface.");
return handleIfaceCreationWhenVendorHalNotSupported(iface);
}
}
}
/**
* Helper function to handle creation of AP iface.
* For devices which do not the support the HAL, this will bypass HalDeviceManager &
* teardown any existing iface.
*/
private String createApIface(@NonNull Iface iface) {
synchronized (mLock) {
if (mWifiVendorHal.isVendorHalSupported()) {
return mWifiVendorHal.createApIface(
new InterfaceDestoyedListenerInternal(iface.id));
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring createApIface.");
return handleIfaceCreationWhenVendorHalNotSupported(iface);
}
}
}
// For devices that don't support the vendor HAL, we will not support any concurrency.
// So simulate the HalDeviceManager behavior by triggering the destroy listener for
// the interface.
private boolean handleIfaceRemovalWhenVendorHalNotSupported(@NonNull Iface iface) {
synchronized (mLock) {
mIfaceMgr.removeIface(iface.id);
onInterfaceDestroyed(iface);
Log.i(TAG, "Successfully torn down " + iface);
return true;
}
}
/**
* Helper function to handle removal of STA iface.
* For devices which do not the support the HAL, this will bypass HalDeviceManager &
* teardown any existing iface.
*/
private boolean removeStaIface(@NonNull Iface iface) {
synchronized (mLock) {
if (mWifiVendorHal.isVendorHalSupported()) {
return mWifiVendorHal.removeStaIface(iface.name);
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring removeStaIface.");
return handleIfaceRemovalWhenVendorHalNotSupported(iface);
}
}
}
/**
* Helper function to handle removal of STA iface.
*/
private boolean removeApIface(@NonNull Iface iface) {
synchronized (mLock) {
if (mWifiVendorHal.isVendorHalSupported()) {
return mWifiVendorHal.removeApIface(iface.name);
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring removeApIface.");
return handleIfaceRemovalWhenVendorHalNotSupported(iface);
}
}
}
/**
* Initialize the native modules.
*
* @return true on success, false otherwise.
*/
public boolean initialize() {
synchronized (mLock) {
if (!mWifiVendorHal.initialize(new VendorHalDeathHandlerInternal())) {
Log.e(TAG, "Failed to initialize vendor HAL");
return false;
}
mWifiCondManager.setOnServiceDeadCallback(new WificondDeathHandlerInternal());
mWifiCondManager.tearDownInterfaces();
mWifiVendorHal.registerRadioModeChangeHandler(
new VendorHalRadioModeChangeHandlerInternal());
mNetdWrapper = mWifiInjector.makeNetdWrapper();
return true;
}
}
/**
* Callback to notify when the status of one of the native daemons
* (wificond, wpa_supplicant & vendor HAL) changes.
*/
public interface StatusListener {
/**
* @param allReady Indicates if all the native daemons are ready for operation or not.
*/
void onStatusChanged(boolean allReady);
}
/**
* Register a StatusListener to get notified about any status changes from the native daemons.
*
* It is safe to re-register the same callback object - duplicates are detected and only a
* single copy kept.
*
* @param listener StatusListener listener object.
*/
public void registerStatusListener(@NonNull StatusListener listener) {
mStatusListeners.add(listener);
}
/**
* Callback to notify when the availability of an interface has changed.
*/
public interface InterfaceAvailableForRequestListener {
/**
* @param isAvailable Whether it is possible to create an iface of the specified type or
* not.
*/
void onAvailabilityChanged(boolean isAvailable);
}
/**
* Register a callback to notify when the availability of Client interface has changed.
*
* It is safe to re-register the same callback object - duplicates are detected and only a
* single copy kept.
*
* @param listener Instance of {@link InterfaceAvailableForRequestListener}.
*/
public void registerClientInterfaceAvailabilityListener(
@NonNull InterfaceAvailableForRequestListener listener) {
mWifiVendorHal.registerStaIfaceAvailabilityListener(listener);
}
/**
* Register a callback to notify when the availability of SoftAp interface has changed.
*
* It is safe to re-register the same callback object - duplicates are detected and only a
* single copy kept.
*
* @param listener Instance of {@link InterfaceAvailableForRequestListener}.
*/
public void registerSoftApInterfaceAvailabilityListener(
@NonNull InterfaceAvailableForRequestListener listener) {
mWifiVendorHal.registerApIfaceAvailabilityListener(listener);
}
/**
* Callback to notify when the associated interface is destroyed, up or down.
*/
public interface InterfaceCallback {
/**
* Interface destroyed by HalDeviceManager.
*
* @param ifaceName Name of the iface.
*/
void onDestroyed(String ifaceName);
/**
* Interface is up.
*
* @param ifaceName Name of the iface.
*/
void onUp(String ifaceName);
/**
* Interface is down.
*
* @param ifaceName Name of the iface.
*/
void onDown(String ifaceName);
}
private void initializeNwParamsForClientInterface(@NonNull String ifaceName) {
try {
// A runtime crash or shutting down AP mode can leave
// IP addresses configured, and this affects
// connectivity when supplicant starts up.
// Ensure we have no IP addresses before a supplicant start.
mNetdWrapper.clearInterfaceAddresses(ifaceName);
// Set privacy extensions
mNetdWrapper.setInterfaceIpv6PrivacyExtensions(ifaceName, true);
// IPv6 is enabled only as long as access point is connected since:
// - IPv6 addresses and routes stick around after disconnection
// - kernel is unaware when connected and fails to start IPv6 negotiation
// - kernel can start autoconfiguration when 802.1x is not complete
mNetdWrapper.disableIpv6(ifaceName);
} catch (IllegalStateException e) {
Log.e(TAG, "Unable to change interface settings", e);
}
}
/**
* Setup an interface for client mode (for connectivity) operations.
*
* This method configures an interface in STA mode in all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
*
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForClientInConnectivityMode(
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (!startSupplicant()) {
Log.e(TAG, "Failed to start supplicant");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY);
if (iface == null) {
Log.e(TAG, "Failed to allocate new STA iface");
return null;
}
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface);
if (TextUtils.isEmpty(iface.name)) {
Log.e(TAG, "Failed to create STA iface in vendor HAL");
mIfaceMgr.removeIface(iface.id);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
new NormalScanEventCallback(iface.name),
new PnoScanEventCallback(iface.name))) {
Log.e(TAG, "Failed to setup iface in wificond on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to register network observer on " + iface);
teardownInterface(iface.name);
return null;
}
mWifiMonitor.startMonitoring(iface.name);
// Just to avoid any race conditions with interface state change callbacks,
// update the interface state before we exit.
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
initializeNwParamsForClientInterface(iface.name);
Log.i(TAG, "Successfully setup " + iface);
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
mIsEnhancedOpenSupported = (iface.featureSet & WIFI_FEATURE_OWE) != 0;
return iface.name;
}
}
/**
* Setup an interface for client mode (for scan) operations.
*
* This method configures an interface in STA mode in the native daemons
* (wificond, vendor HAL).
*
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForClientInScanMode(
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN);
if (iface == null) {
Log.e(TAG, "Failed to allocate new STA iface");
return null;
}
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface);
if (TextUtils.isEmpty(iface.name)) {
Log.e(TAG, "Failed to create iface in vendor HAL");
mIfaceMgr.removeIface(iface.id);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
new NormalScanEventCallback(iface.name),
new PnoScanEventCallback(iface.name))) {
Log.e(TAG, "Failed to setup iface in wificond=" + iface.name);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to register network observer for iface=" + iface.name);
teardownInterface(iface.name);
return null;
}
mWifiMonitor.startMonitoring(iface.name);
// Just to avoid any race conditions with interface state change callbacks,
// update the interface state before we exit.
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
Log.i(TAG, "Successfully setup " + iface);
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
return iface.name;
}
}
/**
* Setup an interface for Soft AP mode operations.
*
* This method configures an interface in AP mode in all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
*
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
return null;
}
if (!startHostapd()) {
Log.e(TAG, "Failed to start hostapd");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);
if (iface == null) {
Log.e(TAG, "Failed to allocate new AP iface");
return null;
}
iface.externalListener = interfaceCallback;
iface.name = createApIface(iface);
if (TextUtils.isEmpty(iface.name)) {
Log.e(TAG, "Failed to create AP iface in vendor HAL");
mIfaceMgr.removeIface(iface.id);
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
return null;
}
if (!mWifiCondManager.setupInterfaceForSoftApMode(iface.name)) {
Log.e(TAG, "Failed to setup iface in wificond on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToWificond();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to register network observer on " + iface);
teardownInterface(iface.name);
return null;
}
// Just to avoid any race conditions with interface state change callbacks,
// update the interface state before we exit.
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
Log.i(TAG, "Successfully setup " + iface);
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
return iface.name;
}
}
/**
* Switches an existing Client mode interface from connectivity
* {@link Iface#IFACE_TYPE_STA_FOR_CONNECTIVITY} to scan mode
* {@link Iface#IFACE_TYPE_STA_FOR_SCAN}.
*
* @param ifaceName Name of the interface.
* @return true if the operation succeeded, false if there is an error or the iface is already
* in scan mode.
*/
public boolean switchClientInterfaceToScanMode(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Trying to switch to scan mode on an invalid iface=" + ifaceName);
return false;
}
if (iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) {
Log.e(TAG, "Already in scan mode on iface=" + ifaceName);
return true;
}
if (!mSupplicantStaIfaceHal.teardownIface(iface.name)) {
Log.e(TAG, "Failed to teardown iface in supplicant on " + iface);
teardownInterface(iface.name);
return false;
}
iface.type = Iface.IFACE_TYPE_STA_FOR_SCAN;
stopSupplicantIfNecessary();
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
iface.phyCapabilities = null;
Log.i(TAG, "Successfully switched to scan mode on iface=" + iface);
return true;
}
}
/**
* Switches an existing Client mode interface from scan mode
* {@link Iface#IFACE_TYPE_STA_FOR_SCAN} to connectivity mode
* {@link Iface#IFACE_TYPE_STA_FOR_CONNECTIVITY}.
*
* @param ifaceName Name of the interface.
* @return true if the operation succeeded, false if there is an error or the iface is already
* in scan mode.
*/
public boolean switchClientInterfaceToConnectivityMode(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Trying to switch to connectivity mode on an invalid iface="
+ ifaceName);
return false;
}
if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY) {
Log.e(TAG, "Already in connectivity mode on iface=" + ifaceName);
return true;
}
if (!startSupplicant()) {
Log.e(TAG, "Failed to start supplicant");
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return false;
}
if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return false;
}
iface.type = Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY;
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
mIsEnhancedOpenSupported = (iface.featureSet & WIFI_FEATURE_OWE) != 0;
Log.i(TAG, "Successfully switched to connectivity mode on iface=" + iface);
return true;
}
}
/**
*
* Check if the interface is up or down.
*
* @param ifaceName Name of the interface.
* @return true if iface is up, false if it's down or on error.
*/
public boolean isInterfaceUp(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Trying to get iface state on invalid iface=" + ifaceName);
return false;
}
try {
return mNetdWrapper.isInterfaceUp(ifaceName);
} catch (IllegalStateException e) {
Log.e(TAG, "Unable to get interface config", e);
return false;
}
}
}
/**
* Teardown an interface in Client/AP mode.
*
* This method tears down the associated interface from all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
* Also, brings down the HAL, supplicant or hostapd as necessary.
*
* @param ifaceName Name of the interface.
*/
public void teardownInterface(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Trying to teardown an invalid iface=" + ifaceName);
return;
}
// Trigger the iface removal from HAL. The rest of the cleanup will be triggered
// from the interface destroyed callback.
if (iface.type == Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY
|| iface.type == Iface.IFACE_TYPE_STA_FOR_SCAN) {
if (!removeStaIface(iface)) {
Log.e(TAG, "Failed to remove iface in vendor HAL=" + ifaceName);
return;
}
} else if (iface.type == Iface.IFACE_TYPE_AP) {
if (!removeApIface(iface)) {
Log.e(TAG, "Failed to remove iface in vendor HAL=" + ifaceName);
return;
}
}
Log.i(TAG, "Successfully initiated teardown for iface=" + ifaceName);
}
}
/**
* Teardown all the active interfaces.
*
* This method tears down the associated interfaces from all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
* Also, brings down the HAL, supplicant or hostapd as necessary.
*/
public void teardownAllInterfaces() {
synchronized (mLock) {
Iterator<Integer> ifaceIdIter = mIfaceMgr.getIfaceIdIter();
while (ifaceIdIter.hasNext()) {
Iface iface = mIfaceMgr.getIface(ifaceIdIter.next());
ifaceIdIter.remove();
onInterfaceDestroyed(iface);
Log.i(TAG, "Successfully torn down " + iface);
}
Log.i(TAG, "Successfully torn down all ifaces");
}
}
/**
* Get name of the client interface.
*
* This is mainly used by external modules that needs to perform some
* client operations on the STA interface.
*
* TODO(b/70932231): This may need to be reworked once we start supporting STA + STA.
*
* @return Interface name of any active client interface, null if no active client interface
* exist.
* Return Values for the different scenarios are listed below:
* a) When there are no client interfaces, returns null.
* b) when there is 1 client interface, returns the name of that interface.
* c) When there are 2 or more client interface, returns the name of any client interface.
*/
public String getClientInterfaceName() {
synchronized (mLock) {
return mIfaceMgr.findAnyStaIfaceName();
}
}
/**
* Get names of all the client interfaces.
*
* @return List of interface name of all active client interfaces.
*/
public Set<String> getClientInterfaceNames() {
synchronized (mLock) {
return mIfaceMgr.findAllStaIfaceNames();
}
}
/**
* Get name of the softap interface.
*
* This is mainly used by external modules that needs to perform some
* operations on the AP interface.
*
* TODO(b/70932231): This may need to be reworked once we start supporting AP + AP.
*
* @return Interface name of any active softap interface, null if no active softap interface
* exist.
* Return Values for the different scenarios are listed below:
* a) When there are no softap interfaces, returns null.
* b) when there is 1 softap interface, returns the name of that interface.
* c) When there are 2 or more softap interface, returns the name of any softap interface.
*/
public String getSoftApInterfaceName() {
synchronized (mLock) {
return mIfaceMgr.findAnyApIfaceName();
}
}
/********************************************************
* Wificond operations
********************************************************/
/**
* Request signal polling to wificond.
*
* @param ifaceName Name of the interface.
* Returns an SignalPollResult object.
* Returns null on failure.
*/
public WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String ifaceName) {
return mWifiCondManager.signalPoll(ifaceName);
}
/**
* Query the list of valid frequencies for the provided band.
* The result depends on the on the country code that has been set.
*
* @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
* The following bands are supported {@link WifiAnnotations.WifiBandBasic}:
* WifiScanner.WIFI_BAND_24_GHZ
* WifiScanner.WIFI_BAND_5_GHZ
* WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
* WifiScanner.WIFI_BAND_6_GHZ
* @return frequencies vector of valid frequencies (MHz), or null for error.
* @throws IllegalArgumentException if band is not recognized.
*/
public int [] getChannelsForBand(@WifiAnnotations.WifiBandBasic int band) {
return mWifiCondManager.getChannelsMhzForBand(band);
}
/**
* Start a scan using wificond for the given parameters.
* @param ifaceName Name of the interface.
* @param scanType Type of scan to perform. One of {@link WifiScanner#SCAN_TYPE_LOW_LATENCY},
* {@link WifiScanner#SCAN_TYPE_LOW_POWER} or {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
* @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
* @return Returns true on success.
*/
public boolean scan(
@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, Set<Integer> freqs,
List<String> hiddenNetworkSSIDs) {
List<byte[]> hiddenNetworkSsidsArrays = new ArrayList<>();
for (String hiddenNetworkSsid : hiddenNetworkSSIDs) {
try {
hiddenNetworkSsidsArrays.add(
NativeUtil.byteArrayFromArrayList(
NativeUtil.decodeSsid(hiddenNetworkSsid)));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + hiddenNetworkSsid, e);
continue;
}
}
return mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays);
}
/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName) {
return convertNativeScanResults(mWifiCondManager.getScanResults(
ifaceName, WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN));
}
/**
* Fetch the latest scan result from kernel via wificond.
* @param ifaceName Name of the interface.
* @return Returns an ArrayList of ScanDetail.
* Returns an empty ArrayList on failure.
*/
public ArrayList<ScanDetail> getPnoScanResults(@NonNull String ifaceName) {
return convertNativeScanResults(mWifiCondManager.getScanResults(ifaceName,
WifiNl80211Manager.SCAN_TYPE_PNO_SCAN));
}
private ArrayList<ScanDetail> convertNativeScanResults(List<NativeScanResult> nativeResults) {
ArrayList<ScanDetail> results = new ArrayList<>();
for (NativeScanResult result : nativeResults) {
WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.getSsid());
MacAddress bssidMac = result.getBssid();
if (bssidMac == null) {
Log.e(TAG, "Invalid MAC (BSSID) for SSID " + wifiSsid);
continue;
}
String bssid = bssidMac.toString();
ScanResult.InformationElement[] ies =
InformationElementUtil.parseInformationElements(result.getInformationElements());
InformationElementUtil.Capabilities capabilities =
new InformationElementUtil.Capabilities();
capabilities.from(ies, result.getCapabilities(), mIsEnhancedOpenSupported);
String flags = capabilities.generateCapabilitiesString();
NetworkDetail networkDetail;
try {
networkDetail = new NetworkDetail(bssid, ies, null, result.getFrequencyMhz());
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e);
continue;
}
ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
result.getSignalMbm() / 100, result.getFrequencyMhz(), result.getTsf(), ies,
null, result.getInformationElements());
ScanResult scanResult = scanDetail.getScanResult();
scanResult.setWifiStandard(wifiModeToWifiStandard(networkDetail.getWifiMode()));
// Fill up the radio chain info.
scanResult.radioChainInfos =
new ScanResult.RadioChainInfo[result.getRadioChainInfos().size()];
int idx = 0;
for (RadioChainInfo nativeRadioChainInfo : result.getRadioChainInfos()) {
scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo();
scanResult.radioChainInfos[idx].id = nativeRadioChainInfo.getChainId();
scanResult.radioChainInfos[idx].level = nativeRadioChainInfo.getLevelDbm();
idx++;
}
results.add(scanDetail);
}
if (mVerboseLoggingEnabled) {
Log.d(TAG, "get " + results.size() + " scan results from wificond");
}
return results;
}
@WifiAnnotations.WifiStandard
private static int wifiModeToWifiStandard(int wifiMode) {
switch (wifiMode) {
case InformationElementUtil.WifiMode.MODE_11A:
case InformationElementUtil.WifiMode.MODE_11B:
case InformationElementUtil.WifiMode.MODE_11G:
return ScanResult.WIFI_STANDARD_LEGACY;
case InformationElementUtil.WifiMode.MODE_11N:
return ScanResult.WIFI_STANDARD_11N;
case InformationElementUtil.WifiMode.MODE_11AC:
return ScanResult.WIFI_STANDARD_11AC;
case InformationElementUtil.WifiMode.MODE_11AX:
return ScanResult.WIFI_STANDARD_11AX;
case InformationElementUtil.WifiMode.MODE_UNDEFINED:
default:
return ScanResult.WIFI_STANDARD_UNKNOWN;
}
}
/**
* Start PNO scan.
* @param ifaceName Name of the interface.
* @param pnoSettings Pno scan configuration.
* @return true on success.
*/
public boolean startPnoScan(@NonNull String ifaceName, PnoSettings pnoSettings) {
return mWifiCondManager.startPnoScan(ifaceName, pnoSettings.toNativePnoSettings(),
Runnable::run,
new WifiNl80211Manager.PnoScanRequestCallback() {
@Override
public void onPnoRequestSucceeded() {
mWifiMetrics.incrementPnoScanStartAttemptCount();
}
@Override
public void onPnoRequestFailed() {
mWifiMetrics.incrementPnoScanStartAttemptCount();
mWifiMetrics.incrementPnoScanFailedCount();
}
});
}
/**
* Stop PNO scan.
* @param ifaceName Name of the interface.
* @return true on success.
*/
public boolean stopPnoScan(@NonNull String ifaceName) {
return mWifiCondManager.stopPnoScan(ifaceName);
}
/**
* Sends an arbitrary 802.11 management frame on the current channel.
*
* @param ifaceName Name of the interface.
* @param frame Bytes of the 802.11 management frame to be sent, including the header, but not
* including the frame check sequence (FCS).
* @param callback A callback triggered when the transmitted frame is ACKed or the transmission
* fails.
* @param mcs The MCS index that the frame will be sent at. If mcs < 0, the driver will select
* the rate automatically. If the device does not support sending the frame at a
* specified MCS rate, the transmission will be aborted and
* {@link WifiNl80211Manager.SendMgmtFrameCallback#onFailure(int)} will be called
* with reason {@link WifiNl80211Manager#SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED}.
*/
public void sendMgmtFrame(@NonNull String ifaceName, @NonNull byte[] frame,
@NonNull WifiNl80211Manager.SendMgmtFrameCallback callback, int mcs) {
mWifiCondManager.sendMgmtFrame(ifaceName, frame, mcs, Runnable::run, callback);
}
/**
* Sends a probe request to the AP and waits for a response in order to determine whether
* there is connectivity between the device and AP.
*
* @param ifaceName Name of the interface.
* @param receiverMac the MAC address of the AP that the probe request will be sent to.
* @param callback callback triggered when the probe was ACKed by the AP, or when
* an error occurs after the link probe was started.
* @param mcs The MCS index that this probe will be sent at. If mcs < 0, the driver will select
* the rate automatically. If the device does not support sending the frame at a
* specified MCS rate, the transmission will be aborted and
* {@link WifiNl80211Manager.SendMgmtFrameCallback#onFailure(int)} will be called
* with reason {@link WifiNl80211Manager#SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED}.
*/
public void probeLink(@NonNull String ifaceName, @NonNull MacAddress receiverMac,
@NonNull WifiNl80211Manager.SendMgmtFrameCallback callback, int mcs) {
if (callback == null) {
Log.e(TAG, "callback cannot be null!");
return;
}
if (receiverMac == null) {
Log.e(TAG, "Receiver MAC address cannot be null!");
callback.onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
return;
}
String senderMacStr = getMacAddress(ifaceName);
if (senderMacStr == null) {
Log.e(TAG, "Failed to get this device's MAC Address");
callback.onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
return;
}
byte[] frame = buildProbeRequestFrame(
receiverMac.toByteArray(),
NativeUtil.macAddressToByteArray(senderMacStr));
sendMgmtFrame(ifaceName, frame, callback, mcs);
}
// header = 24 bytes, minimal body = 2 bytes, no FCS (will be added by driver)
private static final int BASIC_PROBE_REQUEST_FRAME_SIZE = 24 + 2;
private byte[] buildProbeRequestFrame(byte[] receiverMac, byte[] transmitterMac) {
ByteBuffer frame = ByteBuffer.allocate(BASIC_PROBE_REQUEST_FRAME_SIZE);
// ByteBuffer is big endian by default, switch to little endian
frame.order(ByteOrder.LITTLE_ENDIAN);
// Protocol version = 0, Type = management, Subtype = Probe Request
frame.put((byte) 0x40);
// no flags set
frame.put((byte) 0x00);
// duration = 60 microseconds. Note: this is little endian
// Note: driver should calculate the duration and replace it before sending, putting a
// reasonable default value here just in case.
frame.putShort((short) 0x3c);
// receiver/destination MAC address byte array
frame.put(receiverMac);
// sender MAC address byte array
frame.put(transmitterMac);
// BSSID (same as receiver address since we are sending to the AP)
frame.put(receiverMac);
// Generate random sequence number, fragment number = 0
// Note: driver should replace the sequence number with the correct number that is
// incremented from the last used sequence number. Putting a random sequence number as a
// default here just in case.
// bit 0 is least significant bit, bit 15 is most significant bit
// bits [0, 7] go in byte 0
// bits [8, 15] go in byte 1
// bits [0, 3] represent the fragment number (which is 0)
// bits [4, 15] represent the sequence number (which is random)
// clear bits [0, 3] to set fragment number = 0
short sequenceAndFragmentNumber = (short) (mRandom.nextInt() & 0xfff0);
frame.putShort(sequenceAndFragmentNumber);
// NL80211 rejects frames with an empty body, so we just need to put a placeholder
// information element.
// Tag for SSID
frame.put((byte) 0x00);
// Represents broadcast SSID. Not accurate, but works as placeholder.
frame.put((byte) 0x00);
return frame.array();
}
private static final int CONNECT_TO_HOSTAPD_RETRY_INTERVAL_MS = 100;
private static final int CONNECT_TO_HOSTAPD_RETRY_TIMES = 50;
/**
* This method is called to wait for establishing connection to hostapd.
*
* @return true if connection is established, false otherwise.
*/
private boolean startAndWaitForHostapdConnection() {
// Start initialization if not already started.
if (!mHostapdHal.isInitializationStarted()
&& !mHostapdHal.initialize()) {
return false;
}
if (!mHostapdHal.startDaemon()) {
Log.e(TAG, "Failed to startup hostapd");
return false;
}
boolean connected = false;
int connectTries = 0;
while (!connected && connectTries++ < CONNECT_TO_HOSTAPD_RETRY_TIMES) {
// Check if the initialization is complete.
connected = mHostapdHal.isInitializationComplete();
if (connected) {
break;
}
try {
Thread.sleep(CONNECT_TO_HOSTAPD_RETRY_INTERVAL_MS);
} catch (InterruptedException ignore) {
}
}
return connected;
}
/**
* Start Soft AP operation using the provided configuration.
*
* @param ifaceName Name of the interface.
* @param config Configuration to use for the soft ap created.
* @param listener Callback for AP events.
* @return true on success, false otherwise.
*/
public boolean startSoftAp(
@NonNull String ifaceName, SoftApConfiguration config, SoftApListener listener) {
if (!mWifiCondManager.registerApCallback(ifaceName, Runnable::run, listener)) {
Log.e(TAG, "Failed to register ap listener");
return false;
}
if (!mHostapdHal.addAccessPoint(ifaceName, config, listener::onFailure)) {
Log.e(TAG, "Failed to add acccess point");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
return false;
}
return true;
}
/**
* Force a softap client disconnect with specific reason code.
*
* @param ifaceName Name of the interface.
* @param client Mac address to force disconnect in clients of the SoftAp.
* @param reasonCode One of disconnect reason code which defined in {@link ApConfigUtil}.
* @return true on success, false otherwise.
*/
public boolean forceClientDisconnect(@NonNull String ifaceName,
@NonNull MacAddress client, int reasonCode) {
return mHostapdHal.forceClientDisconnect(ifaceName, client, reasonCode);
}
/**
* Set MAC address of the given interface
* @param interfaceName Name of the interface
* @param mac Mac address to change into
* @return true on success
*/
public boolean setMacAddress(String interfaceName, MacAddress mac) {
// TODO(b/72459123): Suppress interface down/up events from this call
// Trigger an explicit disconnect to avoid losing the disconnect event reason (if currently
// connected) from supplicant if the interface is brought down for MAC address change.
disconnect(interfaceName);
return mWifiVendorHal.setMacAddress(interfaceName, mac);
}
/**
* Returns true if Hal version supports setMacAddress, otherwise false.
*
* @param interfaceName Name of the interface
*/
public boolean isSetMacAddressSupported(@NonNull String interfaceName) {
return mWifiVendorHal.isSetMacAddressSupported(interfaceName);
}
/**
* Get the factory MAC address of the given interface
* @param interfaceName Name of the interface.
* @return factory MAC address, or null on a failed call or if feature is unavailable.
*/
public MacAddress getFactoryMacAddress(@NonNull String interfaceName) {
return mWifiVendorHal.getFactoryMacAddress(interfaceName);
}
/********************************************************
* Hostapd operations
********************************************************/
/**
* Callback to notify hostapd death.
*/
public interface HostapdDeathEventHandler {
/**
* Invoked when the supplicant dies.
*/
void onDeath();
}
/********************************************************
* Supplicant operations
********************************************************/
/**
* Callback to notify supplicant death.
*/
public interface SupplicantDeathEventHandler {
/**
* Invoked when the supplicant dies.
*/
void onDeath();
}
/**
* Set supplicant log level
*
* @param turnOnVerbose Whether to turn on verbose logging or not.
*/
public void setSupplicantLogLevel(boolean turnOnVerbose) {
mSupplicantStaIfaceHal.setLogLevel(turnOnVerbose);
}
/**
* Trigger a reconnection if the iface is disconnected.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean reconnect(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.reconnect(ifaceName);
}
/**
* Trigger a reassociation even if the iface is currently connected.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean reassociate(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.reassociate(ifaceName);
}
/**
* Trigger a disconnection from the currently connected network.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean disconnect(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.disconnect(ifaceName);
}
/**
* Makes a callback to HIDL to getMacAddress from supplicant
*
* @param ifaceName Name of the interface.
* @return string containing the MAC address, or null on a failed call
*/
public String getMacAddress(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.getMacAddress(ifaceName);
}
public static final int RX_FILTER_TYPE_V4_MULTICAST = 0;
public static final int RX_FILTER_TYPE_V6_MULTICAST = 1;
/**
* Start filtering out Multicast V4 packets
* @param ifaceName Name of the interface.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*
* Multicast filtering rules work as follows:
*
* The driver can filter multicast (v4 and/or v6) and broadcast packets when in
* a power optimized mode (typically when screen goes off).
*
* In order to prevent the driver from filtering the multicast/broadcast packets, we have to
* add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
*
* DRIVER RXFILTER-ADD Num
* where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
*
* and DRIVER RXFILTER-START
* In order to stop the usage of these rules, we do
*
* DRIVER RXFILTER-STOP
* DRIVER RXFILTER-REMOVE Num
* where Num is as described for RXFILTER-ADD
*
* The SETSUSPENDOPT driver command overrides the filtering rules
*/
public boolean startFilteringMulticastV4Packets(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.stopRxFilter(ifaceName)
&& mSupplicantStaIfaceHal.removeRxFilter(
ifaceName, RX_FILTER_TYPE_V4_MULTICAST)
&& mSupplicantStaIfaceHal.startRxFilter(ifaceName);
}
/**
* Stop filtering out Multicast V4 packets.
* @param ifaceName Name of the interface.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean stopFilteringMulticastV4Packets(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.stopRxFilter(ifaceName)
&& mSupplicantStaIfaceHal.addRxFilter(
ifaceName, RX_FILTER_TYPE_V4_MULTICAST)
&& mSupplicantStaIfaceHal.startRxFilter(ifaceName);
}
/**
* Start filtering out Multicast V6 packets
* @param ifaceName Name of the interface.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean startFilteringMulticastV6Packets(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.stopRxFilter(ifaceName)
&& mSupplicantStaIfaceHal.removeRxFilter(
ifaceName, RX_FILTER_TYPE_V6_MULTICAST)
&& mSupplicantStaIfaceHal.startRxFilter(ifaceName);
}
/**
* Stop filtering out Multicast V6 packets.
* @param ifaceName Name of the interface.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean stopFilteringMulticastV6Packets(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.stopRxFilter(ifaceName)
&& mSupplicantStaIfaceHal.addRxFilter(
ifaceName, RX_FILTER_TYPE_V6_MULTICAST)
&& mSupplicantStaIfaceHal.startRxFilter(ifaceName);
}
public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
/**
* Sets the bluetooth coexistence mode.
*
* @param ifaceName Name of the interface.
* @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
* {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
* {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
* @return Whether the mode was successfully set.
*/
public boolean setBluetoothCoexistenceMode(@NonNull String ifaceName, int mode) {
return mSupplicantStaIfaceHal.setBtCoexistenceMode(ifaceName, mode);
}
/**
* Enable or disable Bluetooth coexistence scan mode. When this mode is on,
* some of the low-level scan parameters used by the driver are changed to
* reduce interference with A2DP streaming.
*
* @param ifaceName Name of the interface.
* @param setCoexScanMode whether to enable or disable this mode
* @return {@code true} if the command succeeded, {@code false} otherwise.
*/
public boolean setBluetoothCoexistenceScanMode(
@NonNull String ifaceName, boolean setCoexScanMode) {
return mSupplicantStaIfaceHal.setBtCoexistenceScanModeEnabled(
ifaceName, setCoexScanMode);
}
/**
* Enable or disable suspend mode optimizations.
*
* @param ifaceName Name of the interface.
* @param enabled true to enable, false otherwise.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setSuspendOptimizations(@NonNull String ifaceName, boolean enabled) {
return mSupplicantStaIfaceHal.setSuspendModeEnabled(ifaceName, enabled);
}
/**
* Set country code.
*
* @param ifaceName Name of the interface.
* @param countryCode 2 byte ASCII string. For ex: US, CA.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setCountryCode(@NonNull String ifaceName, String countryCode) {
return mSupplicantStaIfaceHal.setCountryCode(ifaceName, countryCode);
}
/**
* Flush all previously configured HLPs.
*
* @return true if request is sent successfully, false otherwise.
*/
public boolean flushAllHlp(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.flushAllHlp(ifaceName);
}
/**
* Set FILS HLP packet.
*
* @param dst Destination MAC address.
* @param hlpPacket Hlp Packet data in hex.
* @return true if request is sent successfully, false otherwise.
*/
public boolean addHlpReq(@NonNull String ifaceName, MacAddress dst, byte [] hlpPacket) {
return mSupplicantStaIfaceHal.addHlpReq(ifaceName, dst.toByteArray(), hlpPacket);
}
/**
* Initiate TDLS discover and setup or teardown with the specified peer.
*
* @param ifaceName Name of the interface.
* @param macAddr MAC Address of the peer.
* @param enable true to start discovery and setup, false to teardown.
*/
public void startTdls(@NonNull String ifaceName, String macAddr, boolean enable) {
if (enable) {
mSupplicantStaIfaceHal.initiateTdlsDiscover(ifaceName, macAddr);
mSupplicantStaIfaceHal.initiateTdlsSetup(ifaceName, macAddr);
} else {
mSupplicantStaIfaceHal.initiateTdlsTeardown(ifaceName, macAddr);
}
}
/**
* Start WPS pin display operation with the specified peer.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the peer.
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsPbc(@NonNull String ifaceName, String bssid) {
return mSupplicantStaIfaceHal.startWpsPbc(ifaceName, bssid);
}
/**
* Start WPS pin keypad operation with the specified pin.
*
* @param ifaceName Name of the interface.
* @param pin Pin to be used.
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsPinKeypad(@NonNull String ifaceName, String pin) {
return mSupplicantStaIfaceHal.startWpsPinKeypad(ifaceName, pin);
}
/**
* Start WPS pin display operation with the specified peer.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the peer.
* @return new pin generated on success, null otherwise.
*/
public String startWpsPinDisplay(@NonNull String ifaceName, String bssid) {
return mSupplicantStaIfaceHal.startWpsPinDisplay(ifaceName, bssid);
}
/**
* Sets whether to use external sim for SIM/USIM processing.
*
* @param ifaceName Name of the interface.
* @param external true to enable, false otherwise.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setExternalSim(@NonNull String ifaceName, boolean external) {
return mSupplicantStaIfaceHal.setExternalSim(ifaceName, external);
}
/**
* Sim auth response types.
*/
public static final String SIM_AUTH_RESP_TYPE_GSM_AUTH = "GSM-AUTH";
public static final String SIM_AUTH_RESP_TYPE_UMTS_AUTH = "UMTS-AUTH";
public static final String SIM_AUTH_RESP_TYPE_UMTS_AUTS = "UMTS-AUTS";
/**
* EAP-SIM Error Codes
*/
public static final int EAP_SIM_NOT_SUBSCRIBED = 1031;
public static final int EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED = 16385;
/**
* Send the sim auth response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param type |GSM-AUTH|, |UMTS-AUTH| or |UMTS-AUTS|.
* @param response Response params.
* @return true if succeeds, false otherwise.
*/
public boolean simAuthResponse(
@NonNull String ifaceName, String type, String response) {
if (SIM_AUTH_RESP_TYPE_GSM_AUTH.equals(type)) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse(
ifaceName, response);
} else if (SIM_AUTH_RESP_TYPE_UMTS_AUTH.equals(type)) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(
ifaceName, response);
} else if (SIM_AUTH_RESP_TYPE_UMTS_AUTS.equals(type)) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAutsResponse(
ifaceName, response);
} else {
return false;
}
}
/**
* Send the eap sim gsm auth failure for the currently configured network.
*
* @param ifaceName Name of the interface.
* @return true if succeeds, false otherwise.
*/
public boolean simAuthFailedResponse(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure(ifaceName);
}
/**
* Send the eap sim umts auth failure for the currently configured network.
*
* @param ifaceName Name of the interface.
* @return true if succeeds, false otherwise.
*/
public boolean umtsAuthFailedResponse(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure(ifaceName);
}
/**
* Send the eap identity response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param unencryptedResponse String to send.
* @param encryptedResponse String to send.
* @return true if succeeds, false otherwise.
*/
public boolean simIdentityResponse(@NonNull String ifaceName, String unencryptedResponse,
String encryptedResponse) {
return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(ifaceName,
unencryptedResponse, encryptedResponse);
}
/**
* This get anonymous identity from supplicant and returns it as a string.
*
* @param ifaceName Name of the interface.
* @return anonymous identity string if succeeds, null otherwise.
*/
public String getEapAnonymousIdentity(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.getCurrentNetworkEapAnonymousIdentity(ifaceName);
}
/**
* Start WPS pin registrar operation with the specified peer and pin.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the peer.
* @param pin Pin to be used.
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsRegistrar(@NonNull String ifaceName, String bssid, String pin) {
return mSupplicantStaIfaceHal.startWpsRegistrar(ifaceName, bssid, pin);
}
/**
* Cancels any ongoing WPS requests.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean cancelWps(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.cancelWps(ifaceName);
}
/**
* Set WPS device name.
*
* @param ifaceName Name of the interface.
* @param name String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setDeviceName(@NonNull String ifaceName, String name) {
return mSupplicantStaIfaceHal.setWpsDeviceName(ifaceName, name);
}
/**
* Set WPS device type.
*
* @param ifaceName Name of the interface.
* @param type Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
* @return true if request is sent successfully, false otherwise.
*/
public boolean setDeviceType(@NonNull String ifaceName, String type) {
return mSupplicantStaIfaceHal.setWpsDeviceType(ifaceName, type);
}
/**
* Set WPS config methods
*
* @param cfg List of config methods.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setConfigMethods(@NonNull String ifaceName, String cfg) {
return mSupplicantStaIfaceHal.setWpsConfigMethods(ifaceName, cfg);
}
/**
* Set WPS manufacturer.
*
* @param ifaceName Name of the interface.
* @param value String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setManufacturer(@NonNull String ifaceName, String value) {
return mSupplicantStaIfaceHal.setWpsManufacturer(ifaceName, value);
}
/**
* Set WPS model name.
*
* @param ifaceName Name of the interface.
* @param value String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setModelName(@NonNull String ifaceName, String value) {
return mSupplicantStaIfaceHal.setWpsModelName(ifaceName, value);
}
/**
* Set WPS model number.
*
* @param ifaceName Name of the interface.
* @param value String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setModelNumber(@NonNull String ifaceName, String value) {
return mSupplicantStaIfaceHal.setWpsModelNumber(ifaceName, value);
}
/**
* Set WPS serial number.
*
* @param ifaceName Name of the interface.
* @param value String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setSerialNumber(@NonNull String ifaceName, String value) {
return mSupplicantStaIfaceHal.setWpsSerialNumber(ifaceName, value);
}
/**
* Enable or disable power save mode.
*
* @param ifaceName Name of the interface.
* @param enabled true to enable, false to disable.
*/
public void setPowerSave(@NonNull String ifaceName, boolean enabled) {
mSupplicantStaIfaceHal.setPowerSave(ifaceName, enabled);
}
/**
* Enable or disable low latency mode.
*
* @param enabled true to enable, false to disable.
* @return true on success, false on failure
*/
public boolean setLowLatencyMode(boolean enabled) {
return mWifiVendorHal.setLowLatencyMode(enabled);
}
/**
* Set concurrency priority between P2P & STA operations.
*
* @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
* false otherwise.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
return mSupplicantStaIfaceHal.setConcurrencyPriority(isStaHigherPriority);
}
/**
* Enable/Disable auto reconnect functionality in wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param enable true to enable auto reconnecting, false to disable.
* @return true if request is sent successfully, false otherwise.
*/
public boolean enableStaAutoReconnect(@NonNull String ifaceName, boolean enable) {
return mSupplicantStaIfaceHal.enableAutoReconnect(ifaceName, enable);
}
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. Abort any ongoing scan to unblock the connection request.
* 2. Remove any existing network in wpa_supplicant(This implicitly triggers disconnect).
* 3. Add a new network to wpa_supplicant.
* 4. Save the provided configuration to wpa_supplicant.
* 5. Select the new network in wpa_supplicant.
* 6. Triggers reconnect command to wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param configuration WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock connection request.
mWifiCondManager.abortScan(ifaceName);
return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
}
/**
* Initiates roaming to the already configured network in wpa_supplicant. If the network
* configuration provided does not match the already configured network, then this triggers
* a new connection attempt (instead of roam).
* 1. Abort any ongoing scan to unblock the roam request.
* 2. First check if we're attempting to connect to the same network as we currently have
* configured.
* 3. Set the new bssid for the network in wpa_supplicant.
* 4. Triggers reassociate command to wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param configuration WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean roamToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock roaming request.
mWifiCondManager.abortScan(ifaceName);
return mSupplicantStaIfaceHal.roamToNetwork(ifaceName, configuration);
}
/**
* Remove all the networks.
*
* @param ifaceName Name of the interface.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean removeAllNetworks(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.removeAllNetworks(ifaceName);
}
/**
* Set the BSSID for the currently configured network in wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @return true if successful, false otherwise.
*/
public boolean setConfiguredNetworkBSSID(@NonNull String ifaceName, String bssid) {
return mSupplicantStaIfaceHal.setCurrentNetworkBssid(ifaceName, bssid);
}
/**
* Initiate ANQP query.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the AP to be queried
* @param anqpIds Set of anqp IDs.
* @param hs20Subtypes Set of HS20 subtypes.
* @return true on success, false otherwise.
*/
public boolean requestAnqp(
@NonNull String ifaceName, String bssid, Set<Integer> anqpIds,
Set<Integer> hs20Subtypes) {
if (bssid == null || ((anqpIds == null || anqpIds.isEmpty())
&& (hs20Subtypes == null || hs20Subtypes.isEmpty()))) {
Log.e(TAG, "Invalid arguments for ANQP request.");
return false;
}
ArrayList<Short> anqpIdList = new ArrayList<>();
for (Integer anqpId : anqpIds) {
anqpIdList.add(anqpId.shortValue());
}
ArrayList<Integer> hs20SubtypeList = new ArrayList<>();
hs20SubtypeList.addAll(hs20Subtypes);
return mSupplicantStaIfaceHal.initiateAnqpQuery(
ifaceName, bssid, anqpIdList, hs20SubtypeList);
}
/**
* Request a passpoint icon file |filename| from the specified AP |bssid|.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the AP
* @param fileName name of the icon file
* @return true if request is sent successfully, false otherwise
*/
public boolean requestIcon(@NonNull String ifaceName, String bssid, String fileName) {
if (bssid == null || fileName == null) {
Log.e(TAG, "Invalid arguments for Icon request.");
return false;
}
return mSupplicantStaIfaceHal.initiateHs20IconQuery(ifaceName, bssid, fileName);
}
/**
* Get the currently configured network's WPS NFC token.
*
* @param ifaceName Name of the interface.
* @return Hex string corresponding to the WPS NFC token.
*/
public String getCurrentNetworkWpsNfcConfigurationToken(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken(ifaceName);
}
/**
* Clean HAL cached data for |networkId|.
*
* @param networkId network id of the network to be removed from supplicant.
*/
public void removeNetworkCachedData(int networkId) {
mSupplicantStaIfaceHal.removeNetworkCachedData(networkId);
}
/** Clear HAL cached data for |networkId| if MAC address is changed.
*
* @param networkId network id of the network to be checked.
* @param curMacAddress current MAC address
*/
public void removeNetworkCachedDataIfNeeded(int networkId, MacAddress curMacAddress) {
mSupplicantStaIfaceHal.removeNetworkCachedDataIfNeeded(networkId, curMacAddress);
}
/*
* DPP
*/
/**
* Adds a DPP peer URI to the URI list.
*
* @param ifaceName Interface name
* @param uri Bootstrap (URI) string (e.g. DPP:....)
* @return ID, or -1 for failure
*/
public int addDppPeerUri(@NonNull String ifaceName, @NonNull String uri) {
return mSupplicantStaIfaceHal.addDppPeerUri(ifaceName, uri);
}
/**
* Removes a DPP URI to the URI list given an ID.
*
* @param ifaceName Interface name
* @param bootstrapId Bootstrap (URI) ID
* @return true when operation is successful, or false for failure
*/
public boolean removeDppUri(@NonNull String ifaceName, int bootstrapId) {
return mSupplicantStaIfaceHal.removeDppUri(ifaceName, bootstrapId);
}
/**
* Stops/aborts DPP Initiator request
*
* @param ifaceName Interface name
* @return true when operation is successful, or false for failure
*/
public boolean stopDppInitiator(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.stopDppInitiator(ifaceName);
}
/**
* Starts DPP Configurator-Initiator request
*
* @param ifaceName Interface name
* @param peerBootstrapId Peer's bootstrap (URI) ID
* @param ownBootstrapId Own bootstrap (URI) ID - Optional, 0 for none
* @param ssid SSID of the selected network
* @param password Password of the selected network, or
* @param psk PSK of the selected network in hexadecimal representation
* @param netRole The network role of the enrollee (STA or AP)
* @param securityAkm Security AKM to use: PSK, SAE
* @return true when operation is successful, or false for failure
*/
public boolean startDppConfiguratorInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId, @NonNull String ssid, String password, String psk,
int netRole, int securityAkm) {
return mSupplicantStaIfaceHal.startDppConfiguratorInitiator(ifaceName, peerBootstrapId,
ownBootstrapId, ssid, password, psk, netRole, securityAkm);
}
/**
* Starts DPP Enrollee-Initiator request
*
* @param ifaceName Interface name
* @param peerBootstrapId Peer's bootstrap (URI) ID
* @param ownBootstrapId Own bootstrap (URI) ID - Optional, 0 for none
* @return true when operation is successful, or false for failure
*/
public boolean startDppEnrolleeInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId) {
return mSupplicantStaIfaceHal.startDppEnrolleeInitiator(ifaceName, peerBootstrapId,
ownBootstrapId);
}
/**
* Callback to notify about DPP success, failure and progress events.
*/
public interface DppEventCallback {
/**
* Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the
* peer DPP configurator.
*
* @param newWifiConfiguration New Wi-Fi configuration received from the configurator
*/
void onSuccessConfigReceived(WifiConfiguration newWifiConfiguration);
/**
* DPP Success event.
*
* @param dppStatusCode Status code of the success event.
*/
void onSuccess(int dppStatusCode);
/**
* DPP Progress event.
*
* @param dppStatusCode Status code of the progress event.
*/
void onProgress(int dppStatusCode);
/**
* DPP Failure event.
*
* @param dppStatusCode Status code of the failure event.
* @param ssid SSID of the network the Enrollee tried to connect to.
* @param channelList List of channels the Enrollee scanned for the network.
* @param bandList List of bands the Enrollee supports.
*/
void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList);
}
/**
* Registers DPP event callbacks.
*
* @param dppEventCallback Callback object.
*/
public void registerDppEventCallback(DppEventCallback dppEventCallback) {
mSupplicantStaIfaceHal.registerDppCallback(dppEventCallback);
}
/********************************************************
* Vendor HAL operations
********************************************************/
/**
* Callback to notify vendor HAL death.
*/
public interface VendorHalDeathEventHandler {
/**
* Invoked when the vendor HAL dies.
*/
void onDeath();
}
/**
* Callback to notify when vendor HAL detects that a change in radio mode.
*/
public interface VendorHalRadioModeChangeEventHandler {
/**
* Invoked when the vendor HAL detects a change to MCC mode.
* MCC (Multi channel concurrency) = Multiple interfaces are active on the same band,
* different channels, same radios.
*
* @param band Band on which MCC is detected (specified by one of the
* WifiScanner.WIFI_BAND_* constants)
*/
void onMcc(int band);
/**
* Invoked when the vendor HAL detects a change to SCC mode.
* SCC (Single channel concurrency) = Multiple interfaces are active on the same band, same
* channels, same radios.
*
* @param band Band on which SCC is detected (specified by one of the
* WifiScanner.WIFI_BAND_* constants)
*/
void onScc(int band);
/**
* Invoked when the vendor HAL detects a change to SBS mode.
* SBS (Single Band Simultaneous) = Multiple interfaces are active on the same band,
* different channels, different radios.
*
* @param band Band on which SBS is detected (specified by one of the
* WifiScanner.WIFI_BAND_* constants)
*/
void onSbs(int band);
/**
* Invoked when the vendor HAL detects a change to DBS mode.
* DBS (Dual Band Simultaneous) = Multiple interfaces are active on the different bands,
* different channels, different radios.
*/
void onDbs();
}
/**
* Tests whether the HAL is running or not
*/
public boolean isHalStarted() {
return mWifiVendorHal.isHalStarted();
}
// TODO: Change variable names to camel style.
public static class ScanCapabilities {
public int max_scan_cache_size;
public int max_scan_buckets;
public int max_ap_cache_per_scan;
public int max_rssi_sample_size;
public int max_scan_reporting_threshold;
}
/**
* Gets the scan capabilities
*
* @param ifaceName Name of the interface.
* @param capabilities object to be filled in
* @return true for success. false for failure
*/
public boolean getBgScanCapabilities(
@NonNull String ifaceName, ScanCapabilities capabilities) {
return mWifiVendorHal.getBgScanCapabilities(ifaceName, capabilities);
}
public static class ChannelSettings {
public int frequency;
public int dwell_time_ms;
public boolean passive;
}
public static class BucketSettings {
public int bucket;
public int band;
public int period_ms;
public int max_period_ms;
public int step_count;
public int report_events;
public int num_channels;
public ChannelSettings[] channels;
}
/**
* Network parameters for hidden networks to be scanned for.
*/
public static class HiddenNetwork {
public String ssid;
@Override
public boolean equals(Object otherObj) {
if (this == otherObj) {
return true;
} else if (otherObj == null || getClass() != otherObj.getClass()) {
return false;
}
HiddenNetwork other = (HiddenNetwork) otherObj;
return Objects.equals(ssid, other.ssid);
}
@Override
public int hashCode() {
return Objects.hash(ssid);
}
}
public static class ScanSettings {
/**
* Type of scan to perform. One of {@link WifiScanner#SCAN_TYPE_LOW_LATENCY},
* {@link WifiScanner#SCAN_TYPE_LOW_POWER} or {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}.
*/
@WifiAnnotations.ScanType
public int scanType;
public int base_period_ms;
public int max_ap_per_scan;
public int report_threshold_percent;
public int report_threshold_num_scans;
public int num_buckets;
/* Not used for bg scans. Only works for single scans. */
public HiddenNetwork[] hiddenNetworks;
public BucketSettings[] buckets;
}
/**
* Network parameters to start PNO scan.
*/
public static class PnoNetwork {
public String ssid;
public byte flags;
public byte auth_bit_field;
public int[] frequencies;
@Override
public boolean equals(Object otherObj) {
if (this == otherObj) {
return true;
} else if (otherObj == null || getClass() != otherObj.getClass()) {
return false;
}
PnoNetwork other = (PnoNetwork) otherObj;
return ((Objects.equals(ssid, other.ssid)) && (flags == other.flags)
&& (auth_bit_field == other.auth_bit_field))
&& Arrays.equals(frequencies, other.frequencies);
}
@Override
public int hashCode() {
return Objects.hash(ssid, flags, auth_bit_field, frequencies);
}
android.net.wifi.nl80211.PnoNetwork toNativePnoNetwork() {
android.net.wifi.nl80211.PnoNetwork nativePnoNetwork =
new android.net.wifi.nl80211.PnoNetwork();
nativePnoNetwork.setHidden(
(flags & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0);
try {
nativePnoNetwork.setSsid(
NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid)));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + ssid, e);
return null;
}
nativePnoNetwork.setFrequenciesMhz(frequencies);
return nativePnoNetwork;
}
}
/**
* Parameters to start PNO scan. This holds the list of networks which are going to used for
* PNO scan.
*/
public static class PnoSettings {
public int min5GHzRssi;
public int min24GHzRssi;
public int min6GHzRssi;
public int periodInMs;
public boolean isConnected;
public PnoNetwork[] networkList;
android.net.wifi.nl80211.PnoSettings toNativePnoSettings() {
android.net.wifi.nl80211.PnoSettings nativePnoSettings =
new android.net.wifi.nl80211.PnoSettings();
nativePnoSettings.setIntervalMillis(periodInMs);
nativePnoSettings.setMin2gRssiDbm(min24GHzRssi);
nativePnoSettings.setMin5gRssiDbm(min5GHzRssi);
nativePnoSettings.setMin6gRssiDbm(min6GHzRssi);
List<android.net.wifi.nl80211.PnoNetwork> pnoNetworks = new ArrayList<>();
if (networkList != null) {
for (PnoNetwork network : networkList) {
android.net.wifi.nl80211.PnoNetwork nativeNetwork =
network.toNativePnoNetwork();
if (nativeNetwork != null) {
pnoNetworks.add(nativeNetwork);
}
}
}
nativePnoSettings.setPnoNetworks(pnoNetworks);
return nativePnoSettings;
}
}
public static interface ScanEventHandler {
/**
* Called for each AP as it is found with the entire contents of the beacon/probe response.
* Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
*/
void onFullScanResult(ScanResult fullScanResult, int bucketsScanned);
/**
* Callback on an event during a gscan scan.
* See WifiNative.WIFI_SCAN_* for possible values.
*/
void onScanStatus(int event);
/**
* Called with the current cached scan results when gscan is paused.
*/
void onScanPaused(WifiScanner.ScanData[] data);
/**
* Called with the current cached scan results when gscan is resumed.
*/
void onScanRestarted();
}
/**
* Handler to notify the occurrence of various events during PNO scan.
*/
public interface PnoEventHandler {
/**
* Callback to notify when one of the shortlisted networks is found during PNO scan.
* @param results List of Scan results received.
*/
void onPnoNetworkFound(ScanResult[] results);
/**
* Callback to notify when the PNO scan schedule fails.
*/
void onPnoScanFailed();
}
public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
public static final int WIFI_SCAN_FAILED = 3;
/**
* Starts a background scan.
* Any ongoing scan will be stopped first
*
* @param ifaceName Name of the interface.
* @param settings to control the scan
* @param eventHandler to call with the results
* @return true for success
*/
public boolean startBgScan(
@NonNull String ifaceName, ScanSettings settings, ScanEventHandler eventHandler) {
return mWifiVendorHal.startBgScan(ifaceName, settings, eventHandler);
}
/**
* Stops any ongoing backgound scan
* @param ifaceName Name of the interface.
*/
public void stopBgScan(@NonNull String ifaceName) {
mWifiVendorHal.stopBgScan(ifaceName);
}
/**
* Pauses an ongoing backgound scan
* @param ifaceName Name of the interface.
*/
public void pauseBgScan(@NonNull String ifaceName) {
mWifiVendorHal.pauseBgScan(ifaceName);
}
/**
* Restarts a paused scan
* @param ifaceName Name of the interface.
*/
public void restartBgScan(@NonNull String ifaceName) {
mWifiVendorHal.restartBgScan(ifaceName);
}
/**
* Gets the latest scan results received.
* @param ifaceName Name of the interface.
*/
public WifiScanner.ScanData[] getBgScanResults(@NonNull String ifaceName) {
return mWifiVendorHal.getBgScanResults(ifaceName);
}
/**
* Gets the latest link layer stats
* @param ifaceName Name of the interface.
*/
public WifiLinkLayerStats getWifiLinkLayerStats(@NonNull String ifaceName) {
return mWifiVendorHal.getWifiLinkLayerStats(ifaceName);
}
/**
* Returns whether STA/AP concurrency is supported or not.
*/
public boolean isStaApConcurrencySupported() {
synchronized (mLock) {
return mWifiVendorHal.isStaApConcurrencySupported();
}
}
/**
* Get the supported features
*
* @param ifaceName Name of the interface.
* @return bitmask defined by WifiManager.WIFI_FEATURE_*
*/
public long getSupportedFeatureSet(@NonNull String ifaceName) {
synchronized (mLock) {
Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Could not get Iface object for interface " + ifaceName);
return 0;
}
return iface.featureSet;
}
}
/**
* Get the supported features
*
* @param ifaceName Name of the interface.
* @return bitmask defined by WifiManager.WIFI_FEATURE_*
*/
private long getSupportedFeatureSetInternal(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.getAdvancedKeyMgmtCapabilities(ifaceName)
| mWifiVendorHal.getSupportedFeatureSet(ifaceName)
| mSupplicantStaIfaceHal.getWpaDriverFeatureSet(ifaceName);
}
/**
* Class to retrieve connection capability parameters after association
*/
public static class ConnectionCapabilities {
public @WifiAnnotations.WifiStandard int wifiStandard;
public int channelBandwidth;
public int maxNumberTxSpatialStreams;
public int maxNumberRxSpatialStreams;
ConnectionCapabilities() {
wifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN;
channelBandwidth = ScanResult.CHANNEL_WIDTH_20MHZ;
maxNumberTxSpatialStreams = 1;
maxNumberRxSpatialStreams = 1;
}
}
/**
* Returns connection capabilities of the current network
*
* @param ifaceName Name of the interface.
* @return connection capabilities of the current network
*/
public ConnectionCapabilities getConnectionCapabilities(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.getConnectionCapabilities(ifaceName);
}
/**
* Get the APF (Android Packet Filter) capabilities of the device
* @param ifaceName Name of the interface.
*/
public ApfCapabilities getApfCapabilities(@NonNull String ifaceName) {
return mWifiVendorHal.getApfCapabilities(ifaceName);
}
/**
* Installs an APF program on this iface, replacing any existing program.
*
* @param ifaceName Name of the interface
* @param filter is the android packet filter program
* @return true for success
*/
public boolean installPacketFilter(@NonNull String ifaceName, byte[] filter) {
return mWifiVendorHal.installPacketFilter(ifaceName, filter);
}
/**
* Reads the APF program and data buffer for this iface.
*
* @param ifaceName Name of the interface
* @return the buffer returned by the driver, or null in case of an error
*/
public byte[] readPacketFilter(@NonNull String ifaceName) {
return mWifiVendorHal.readPacketFilter(ifaceName);
}
/**
* Set country code for this AP iface.
* @param ifaceName Name of the interface.
* @param countryCode - two-letter country code (as ISO 3166)
* @return true for success
*/
public boolean setCountryCodeHal(@NonNull String ifaceName, String countryCode) {
return mWifiVendorHal.setCountryCodeHal(ifaceName, countryCode);
}
//---------------------------------------------------------------------------------
/* Wifi Logger commands/events */
public static interface WifiLoggerEventHandler {
void onRingBufferData(RingBufferStatus status, byte[] buffer);
void onWifiAlert(int errorCode, byte[] buffer);
}
/**
* Registers the logger callback and enables alerts.
* Ring buffer data collection is only triggered when |startLoggingRingBuffer| is invoked.
*
* @param handler Callback to be invoked.
* @return true on success, false otherwise.
*/
public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
return mWifiVendorHal.setLoggingEventHandler(handler);
}
/**
* Control debug data collection
*
* @param verboseLevel 0 to 3, inclusive. 0 stops logging.
* @param flags Ignored.
* @param maxInterval Maximum interval between reports; ignore if 0.
* @param minDataSize Minimum data size in buffer for report; ignore if 0.
* @param ringName Name of the ring for which data collection is to start.
* @return true for success, false otherwise.
*/
public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
int minDataSize, String ringName){
return mWifiVendorHal.startLoggingRingBuffer(
verboseLevel, flags, maxInterval, minDataSize, ringName);
}
/**
* Logger features exposed.
* This is a no-op now, will always return -1.
*
* @return true on success, false otherwise.
*/
public int getSupportedLoggerFeatureSet() {
return mWifiVendorHal.getSupportedLoggerFeatureSet();
}
/**
* Stops all logging and resets the logger callback.
* This stops both the alerts and ring buffer data collection.
* @return true on success, false otherwise.
*/
public boolean resetLogHandler() {
return mWifiVendorHal.resetLogHandler();
}
/**
* Vendor-provided wifi driver version string
*
* @return String returned from the HAL.
*/
public String getDriverVersion() {
return mWifiVendorHal.getDriverVersion();
}
/**
* Vendor-provided wifi firmware version string
*
* @return String returned from the HAL.
*/
public String getFirmwareVersion() {
return mWifiVendorHal.getFirmwareVersion();
}
public static class RingBufferStatus{
String name;
int flag;
int ringBufferId;
int ringBufferByteSize;
int verboseLevel;
int writtenBytes;
int readBytes;
int writtenRecords;
// Bit masks for interpreting |flag|
public static final int HAS_BINARY_ENTRIES = (1 << 0);
public static final int HAS_ASCII_ENTRIES = (1 << 1);
public static final int HAS_PER_PACKET_ENTRIES = (1 << 2);
@Override
public String toString() {
return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
" ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
" writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
" writtenRecords: " + writtenRecords;
}
}
/**
* API to get the status of all ring buffers supported by driver
*/
public RingBufferStatus[] getRingBufferStatus() {
return mWifiVendorHal.getRingBufferStatus();
}
/**
* Indicates to driver that all the data has to be uploaded urgently
*
* @param ringName Name of the ring buffer requested.
* @return true on success, false otherwise.
*/
public boolean getRingBufferData(String ringName) {
return mWifiVendorHal.getRingBufferData(ringName);
}
/**
* Request hal to flush ring buffers to files
*
* @return true on success, false otherwise.
*/
public boolean flushRingBufferData() {
return mWifiVendorHal.flushRingBufferData();
}
/**
* Request vendor debug info from the firmware
*
* @return Raw data obtained from the HAL.
*/
public byte[] getFwMemoryDump() {
return mWifiVendorHal.getFwMemoryDump();
}
/**
* Request vendor debug info from the driver
*
* @return Raw data obtained from the HAL.
*/
public byte[] getDriverStateDump() {
return mWifiVendorHal.getDriverStateDump();
}
//---------------------------------------------------------------------------------
/* Packet fate API */
@Immutable
abstract static class FateReport {
final static int USEC_PER_MSEC = 1000;
// The driver timestamp is a 32-bit counter, in microseconds. This field holds the
// maximal value of a driver timestamp in milliseconds.
final static int MAX_DRIVER_TIMESTAMP_MSEC = (int) (0xffffffffL / 1000);
final static SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss.SSS");
final byte mFate;
final long mDriverTimestampUSec;
final byte mFrameType;
final byte[] mFrameBytes;
final long mEstimatedWallclockMSec;
FateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
mFate = fate;
mDriverTimestampUSec = driverTimestampUSec;
mEstimatedWallclockMSec =
convertDriverTimestampUSecToWallclockMSec(mDriverTimestampUSec);
mFrameType = frameType;
mFrameBytes = frameBytes;
}
public String toTableRowString() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
FrameParser parser = new FrameParser(mFrameType, mFrameBytes);
dateFormatter.setTimeZone(TimeZone.getDefault());
pw.format("%-15s %12s %-9s %-32s %-12s %-23s %s\n",
mDriverTimestampUSec,
dateFormatter.format(new Date(mEstimatedWallclockMSec)),
directionToString(), fateToString(), parser.mMostSpecificProtocolString,
parser.mTypeString, parser.mResultString);
return sw.toString();
}
public String toVerboseStringWithPiiAllowed() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
FrameParser parser = new FrameParser(mFrameType, mFrameBytes);
pw.format("Frame direction: %s\n", directionToString());
pw.format("Frame timestamp: %d\n", mDriverTimestampUSec);
pw.format("Frame fate: %s\n", fateToString());
pw.format("Frame type: %s\n", frameTypeToString(mFrameType));
pw.format("Frame protocol: %s\n", parser.mMostSpecificProtocolString);
pw.format("Frame protocol type: %s\n", parser.mTypeString);
pw.format("Frame length: %d\n", mFrameBytes.length);
pw.append("Frame bytes");
pw.append(HexDump.dumpHexString(mFrameBytes)); // potentially contains PII
pw.append("\n");
return sw.toString();
}
/* Returns a header to match the output of toTableRowString(). */
public static String getTableHeader() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.format("\n%-15s %-12s %-9s %-32s %-12s %-23s %s\n",
"Time usec", "Walltime", "Direction", "Fate", "Protocol", "Type", "Result");
pw.format("%-15s %-12s %-9s %-32s %-12s %-23s %s\n",
"---------", "--------", "---------", "----", "--------", "----", "------");
return sw.toString();
}
protected abstract String directionToString();
protected abstract String fateToString();
private static String frameTypeToString(byte frameType) {
switch (frameType) {
case WifiLoggerHal.FRAME_TYPE_UNKNOWN:
return "unknown";
case WifiLoggerHal.FRAME_TYPE_ETHERNET_II:
return "data";
case WifiLoggerHal.FRAME_TYPE_80211_MGMT:
return "802.11 management";
default:
return Byte.toString(frameType);
}
}
/**
* Converts a driver timestamp to a wallclock time, based on the current
* BOOTTIME to wallclock mapping. The driver timestamp is a 32-bit counter of
* microseconds, with the same base as BOOTTIME.
*/
private static long convertDriverTimestampUSecToWallclockMSec(long driverTimestampUSec) {
final long wallclockMillisNow = System.currentTimeMillis();
final long boottimeMillisNow = SystemClock.elapsedRealtime();
final long driverTimestampMillis = driverTimestampUSec / USEC_PER_MSEC;
long boottimeTimestampMillis = boottimeMillisNow % MAX_DRIVER_TIMESTAMP_MSEC;
if (boottimeTimestampMillis < driverTimestampMillis) {
// The 32-bit microsecond count has wrapped between the time that the driver
// recorded the packet, and the call to this function. Adjust the BOOTTIME
// timestamp, to compensate.
//
// Note that overflow is not a concern here, since the result is less than
// 2 * MAX_DRIVER_TIMESTAMP_MSEC. (Given the modulus operation above,
// boottimeTimestampMillis must be less than MAX_DRIVER_TIMESTAMP_MSEC.) And, since
// MAX_DRIVER_TIMESTAMP_MSEC is an int, 2 * MAX_DRIVER_TIMESTAMP_MSEC must fit
// within a long.
boottimeTimestampMillis += MAX_DRIVER_TIMESTAMP_MSEC;
}
final long millisSincePacketTimestamp = boottimeTimestampMillis - driverTimestampMillis;
return wallclockMillisNow - millisSincePacketTimestamp;
}
}
/**
* Represents the fate information for one outbound packet.
*/
@Immutable
public static final class TxFateReport extends FateReport {
TxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
super(fate, driverTimestampUSec, frameType, frameBytes);
}
@Override
protected String directionToString() {
return "TX";
}
@Override
protected String fateToString() {
switch (mFate) {
case WifiLoggerHal.TX_PKT_FATE_ACKED:
return "acked";
case WifiLoggerHal.TX_PKT_FATE_SENT:
return "sent";
case WifiLoggerHal.TX_PKT_FATE_FW_QUEUED:
return "firmware queued";
case WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID:
return "firmware dropped (invalid frame)";
case WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS:
return "firmware dropped (no bufs)";
case WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER:
return "firmware dropped (other)";
case WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED:
return "driver queued";
case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID:
return "driver dropped (invalid frame)";
case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS:
return "driver dropped (no bufs)";
case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER:
return "driver dropped (other)";
default:
return Byte.toString(mFate);
}
}
}
/**
* Represents the fate information for one inbound packet.
*/
@Immutable
public static final class RxFateReport extends FateReport {
RxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
super(fate, driverTimestampUSec, frameType, frameBytes);
}
@Override
protected String directionToString() {
return "RX";
}
@Override
protected String fateToString() {
switch (mFate) {
case WifiLoggerHal.RX_PKT_FATE_SUCCESS:
return "success";
case WifiLoggerHal.RX_PKT_FATE_FW_QUEUED:
return "firmware queued";
case WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER:
return "firmware dropped (filter)";
case WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID:
return "firmware dropped (invalid frame)";
case WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS:
return "firmware dropped (no bufs)";
case WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER:
return "firmware dropped (other)";
case WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED:
return "driver queued";
case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER:
return "driver dropped (filter)";
case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID:
return "driver dropped (invalid frame)";
case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS:
return "driver dropped (no bufs)";
case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER:
return "driver dropped (other)";
default:
return Byte.toString(mFate);
}
}
}
/**
* Ask the HAL to enable packet fate monitoring. Fails unless HAL is started.
*
* @param ifaceName Name of the interface.
* @return true for success, false otherwise.
*/
public boolean startPktFateMonitoring(@NonNull String ifaceName) {
return mWifiVendorHal.startPktFateMonitoring(ifaceName);
}
/**
* Fetch the most recent TX packet fates from the HAL. Fails unless HAL is started.
*
* @param ifaceName Name of the interface.
* @return true for success, false otherwise.
*/
public boolean getTxPktFates(@NonNull String ifaceName, TxFateReport[] reportBufs) {
return mWifiVendorHal.getTxPktFates(ifaceName, reportBufs);
}
/**
* Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started.
* @param ifaceName Name of the interface.
*/
public boolean getRxPktFates(@NonNull String ifaceName, RxFateReport[] reportBufs) {
return mWifiVendorHal.getRxPktFates(ifaceName, reportBufs);
}
/**
* Get the tx packet counts for the interface.
*
* @param ifaceName Name of the interface.
* @return tx packet counts
*/
public long getTxPackets(@NonNull String ifaceName) {
return TrafficStats.getTxPackets(ifaceName);
}
/**
* Get the rx packet counts for the interface.
*
* @param ifaceName Name of the interface
* @return rx packet counts
*/
public long getRxPackets(@NonNull String ifaceName) {
return TrafficStats.getRxPackets(ifaceName);
}
/**
* Start sending the specified keep alive packets periodically.
*
* @param ifaceName Name of the interface.
* @param slot Integer used to identify each request.
* @param dstMac Destination MAC Address
* @param packet Raw packet contents to send.
* @param protocol The ethernet protocol type
* @param period Period to use for sending these packets.
* @return 0 for success, -1 for error
*/
public int startSendingOffloadedPacket(@NonNull String ifaceName, int slot,
byte[] dstMac, byte[] packet, int protocol, int period) {
byte[] srcMac = NativeUtil.macAddressToByteArray(getMacAddress(ifaceName));
return mWifiVendorHal.startSendingOffloadedPacket(
ifaceName, slot, srcMac, dstMac, packet, protocol, period);
}
/**
* Stop sending the specified keep alive packets.
*
* @param ifaceName Name of the interface.
* @param slot id - same as startSendingOffloadedPacket call.
* @return 0 for success, -1 for error
*/
public int stopSendingOffloadedPacket(@NonNull String ifaceName, int slot) {
return mWifiVendorHal.stopSendingOffloadedPacket(ifaceName, slot);
}
public static interface WifiRssiEventHandler {
void onRssiThresholdBreached(byte curRssi);
}
/**
* Start RSSI monitoring on the currently connected access point.
*
* @param ifaceName Name of the interface.
* @param maxRssi Maximum RSSI threshold.
* @param minRssi Minimum RSSI threshold.
* @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
* @return 0 for success, -1 for failure
*/
public int startRssiMonitoring(
@NonNull String ifaceName, byte maxRssi, byte minRssi,
WifiRssiEventHandler rssiEventHandler) {
return mWifiVendorHal.startRssiMonitoring(
ifaceName, maxRssi, minRssi, rssiEventHandler);
}
/**
* Stop RSSI monitoring on the currently connected access point.
*
* @param ifaceName Name of the interface.
* @return 0 for success, -1 for failure
*/
public int stopRssiMonitoring(@NonNull String ifaceName) {
return mWifiVendorHal.stopRssiMonitoring(ifaceName);
}
/**
* Fetch the host wakeup reasons stats from wlan driver.
*
* @return the |WlanWakeReasonAndCounts| object retrieved from the wlan driver.
*/
public WlanWakeReasonAndCounts getWlanWakeReasonCount() {
return mWifiVendorHal.getWlanWakeReasonCount();
}
/**
* Enable/Disable Neighbour discovery offload functionality in the firmware.
*
* @param ifaceName Name of the interface.
* @param enabled true to enable, false to disable.
* @return true for success, false otherwise.
*/
public boolean configureNeighborDiscoveryOffload(@NonNull String ifaceName, boolean enabled) {
return mWifiVendorHal.configureNeighborDiscoveryOffload(ifaceName, enabled);
}
// Firmware roaming control.
/**
* Class to retrieve firmware roaming capability parameters.
*/
public static class RoamingCapabilities {
public int maxBlacklistSize;
public int maxWhitelistSize;
}
/**
* Query the firmware roaming capabilities.
* @param ifaceName Name of the interface.
* @return true for success, false otherwise.
*/
public boolean getRoamingCapabilities(
@NonNull String ifaceName, RoamingCapabilities capabilities) {
return mWifiVendorHal.getRoamingCapabilities(ifaceName, capabilities);
}
/**
* Macros for controlling firmware roaming.
*/
public static final int DISABLE_FIRMWARE_ROAMING = 0;
public static final int ENABLE_FIRMWARE_ROAMING = 1;
/**
* Indicates success for enableFirmwareRoaming
*/
public static final int SET_FIRMWARE_ROAMING_SUCCESS = 0;
/**
* Indicates failure for enableFirmwareRoaming
*/
public static final int SET_FIRMWARE_ROAMING_FAILURE = 1;
/**
* Indicates temporary failure for enableFirmwareRoaming - try again later
*/
public static final int SET_FIRMWARE_ROAMING_BUSY = 2;
/**
* Enable/disable firmware roaming.
*
* @param ifaceName Name of the interface.
* @return SET_FIRMWARE_ROAMING_SUCCESS, SET_FIRMWARE_ROAMING_FAILURE,
* or SET_FIRMWARE_ROAMING_BUSY
*/
public int enableFirmwareRoaming(@NonNull String ifaceName, int state) {
return mWifiVendorHal.enableFirmwareRoaming(ifaceName, state);
}
/**
* Class for specifying the roaming configurations.
*/
public static class RoamingConfig {
public ArrayList<String> blacklistBssids;
public ArrayList<String> whitelistSsids;
}
/**
* Set firmware roaming configurations.
* @param ifaceName Name of the interface.
*/
public boolean configureRoaming(@NonNull String ifaceName, RoamingConfig config) {
return mWifiVendorHal.configureRoaming(ifaceName, config);
}
/**
* Reset firmware roaming configuration.
* @param ifaceName Name of the interface.
*/
public boolean resetRoamingConfiguration(@NonNull String ifaceName) {
// Pass in an empty RoamingConfig object which translates to zero size
// blacklist and whitelist to reset the firmware roaming configuration.
return mWifiVendorHal.configureRoaming(ifaceName, new RoamingConfig());
}
/**
* Select one of the pre-configured transmit power level scenarios or reset it back to normal.
* Primarily used for meeting SAR requirements.
*
* @param sarInfo The collection of inputs used to select the SAR scenario.
* @return true for success; false for failure or if the HAL version does not support this API.
*/
public boolean selectTxPowerScenario(SarInfo sarInfo) {
return mWifiVendorHal.selectTxPowerScenario(sarInfo);
}
/**
* Set MBO cellular data status
*
* @param ifaceName Name of the interface.
* @param available cellular data status,
* true means cellular data available, false otherwise.
*/
public void setMboCellularDataStatus(@NonNull String ifaceName, boolean available) {
mSupplicantStaIfaceHal.setMboCellularDataStatus(ifaceName, available);
return;
}
/**
* Query of support of Wi-Fi standard
*
* @param ifaceName name of the interface to check support on
* @param standard the wifi standard to check on
* @return true if the wifi standard is supported on this interface, false otherwise.
*/
public boolean isWifiStandardSupported(@NonNull String ifaceName,
@WifiAnnotations.WifiStandard int standard) {
synchronized (mLock) {
Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null || iface.phyCapabilities == null) {
return false;
}
return iface.phyCapabilities.isWifiStandardSupported(standard);
}
}
/**
* Get the Wiphy capabilities of a device for a given interface
* If the interface is not associated with one,
* it will be read from the device through wificond
*
* @param ifaceName name of the interface
* @return the device capabilities for this interface
*/
public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) {
synchronized (mLock) {
Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Failed to get device capabilities, interface not found: " + ifaceName);
return null;
}
if (iface.phyCapabilities == null) {
iface.phyCapabilities = mWifiCondManager.getDeviceWiphyCapabilities(ifaceName);
}
return iface.phyCapabilities;
}
}
/**
* Set the Wiphy capabilities of a device for a given interface
*
* @param ifaceName name of the interface
* @param capabilities the wiphy capabilities to set for this interface
*/
public void setDeviceWiphyCapabilities(@NonNull String ifaceName,
DeviceWiphyCapabilities capabilities) {
synchronized (mLock) {
Iface iface = mIfaceMgr.getIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Failed to set device capabilities, interface not found: " + ifaceName);
return;
}
iface.phyCapabilities = capabilities;
}
}
}