| /* |
| * 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.annotation.Nullable; |
| import android.net.MacAddress; |
| import android.net.TrafficStats; |
| import android.net.apf.ApfCapabilities; |
| import android.net.wifi.CoexUnsafeChannel; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.SoftApConfiguration; |
| import android.net.wifi.WifiAnnotations; |
| import android.net.wifi.WifiAvailableChannel; |
| 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.NativeWifiClient; |
| import android.net.wifi.nl80211.RadioChainInfo; |
| import android.net.wifi.nl80211.WifiNl80211Manager; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.SystemClock; |
| import android.os.WorkSource; |
| 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.modules.utils.build.SdkLevel; |
| 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 BuildProperties mBuildProperties; |
| private final WifiInjector mWifiInjector; |
| private NetdWrapper mNetdWrapper; |
| private boolean mVerboseLoggingEnabled = false; |
| private boolean mIsEnhancedOpenSupported = false; |
| private final List<CoexUnsafeChannel> mCachedCoexUnsafeChannels = new ArrayList<>(); |
| private int mCachedCoexRestrictions; |
| private CountryCodeChangeListenerInternal mCountryCodeChangeListener; |
| private boolean mUseFakeScanDetails; |
| private final ArrayList<ScanDetail> mFakeScanDetails = new ArrayList<>(); |
| |
| public WifiNative(WifiVendorHal vendorHal, |
| SupplicantStaIfaceHal staIfaceHal, HostapdHal hostapdHal, |
| WifiNl80211Manager condManager, WifiMonitor wifiMonitor, |
| PropertyService propertyService, WifiMetrics wifiMetrics, |
| Handler handler, Random random, BuildProperties buildProperties, |
| WifiInjector wifiInjector) { |
| mWifiVendorHal = vendorHal; |
| mSupplicantStaIfaceHal = staIfaceHal; |
| mHostapdHal = hostapdHal; |
| mWifiCondManager = condManager; |
| mWifiMonitor = wifiMonitor; |
| mPropertyService = propertyService; |
| mWifiMetrics = wifiMetrics; |
| mHandler = handler; |
| mRandom = random; |
| mBuildProperties = buildProperties; |
| mWifiInjector = wifiInjector; |
| } |
| |
| /** |
| * Enable verbose logging for all sub modules. |
| */ |
| public void enableVerboseLogging(boolean verbose) { |
| mVerboseLoggingEnabled = verbose; |
| setSupplicantLogLevel(mVerboseLoggingEnabled); |
| mWifiCondManager.enableVerboseLogging(mVerboseLoggingEnabled); |
| mSupplicantStaIfaceHal.enableVerboseLogging(mVerboseLoggingEnabled); |
| mHostapdHal.enableVerboseLogging(mVerboseLoggingEnabled); |
| mWifiVendorHal.enableVerboseLogging(mVerboseLoggingEnabled); |
| } |
| |
| /** |
| * Callbacks for SoftAp interface. |
| */ |
| public class SoftApListenerFromWificond implements WifiNl80211Manager.SoftApCallback { |
| // placeholder for now - provide a shell so that clients don't use a |
| // WifiNl80211Manager-specific API. |
| private String mIfaceName; |
| private SoftApListener mSoftApListener; |
| |
| SoftApListenerFromWificond(String ifaceName, |
| SoftApListener softApListener) { |
| mIfaceName = ifaceName; |
| mSoftApListener = softApListener; |
| } |
| |
| @Override |
| public void onFailure() { |
| mSoftApListener.onFailure(); |
| } |
| |
| @Override |
| public void onSoftApChannelSwitched(int frequency, int bandwidth) { |
| mSoftApListener.onInfoChanged(mIfaceName, frequency, bandwidth, |
| ScanResult.WIFI_STANDARD_UNKNOWN, null); |
| } |
| |
| @Override |
| public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) { |
| mSoftApListener.onConnectedClientsChanged(mIfaceName, |
| client.getMacAddress(), isConnected); |
| } |
| } |
| |
| private static class CountryCodeChangeListenerInternal implements |
| WifiNl80211Manager.CountryCodeChangedListener { |
| private WifiCountryCode.ChangeListener mListener; |
| |
| public void setChangeListener(@NonNull WifiCountryCode.ChangeListener listener) { |
| mListener = listener; |
| } |
| |
| public void onSetCountryCodeSucceeded(String country) { |
| Log.d(TAG, "onSetCountryCodeSucceeded: " + country); |
| if (mListener != null) { |
| mListener.onSetCountryCodeSucceeded(country); |
| } |
| } |
| |
| @Override |
| public void onCountryCodeChanged(String country) { |
| Log.d(TAG, "onCountryCodeChanged: " + country); |
| if (mListener != null) { |
| mListener.onDriverCountryCodeChanged(country); |
| } |
| } |
| } |
| |
| /** |
| * Callbacks for SoftAp instance. |
| */ |
| public interface SoftApListener { |
| /** |
| * Invoked when there is a fatal failure and the SoftAp is shutdown. |
| */ |
| void onFailure(); |
| |
| /** |
| * Invoked when a channel switch event happens - i.e. the SoftAp is moved to a different |
| * channel. Also called on initial registration. |
| * |
| * @param apIfaceInstance The identity of the ap instance. |
| * @param frequency The new frequency of the SoftAp. A value of 0 is invalid and is an |
| * indication that the SoftAp is not enabled. |
| * @param bandwidth The new bandwidth of the SoftAp. |
| * @param generation The new generation of the SoftAp. |
| */ |
| void onInfoChanged(String apIfaceInstance, int frequency, int bandwidth, |
| int generation, MacAddress apIfaceInstanceMacAddress); |
| /** |
| * Invoked when there is a change in the associated station (STA). |
| * |
| * @param apIfaceInstance The identity of the ap instance. |
| * @param clientAddress Macaddress of the client. |
| * @param isConnected Indication as to whether the client is connected (true), or |
| * disconnected (false). |
| */ |
| void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress, |
| boolean isConnected); |
| } |
| |
| /******************************************************** |
| * 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 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); |
| } |
| |
| 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; |
| } |
| |
| private @NonNull Set<String> findAllApIfaceNames() { |
| Set<String> ifaceNames = new ArraySet<>(); |
| for (Iface iface : mIfaces.values()) { |
| if (iface.type == Iface.IFACE_TYPE_AP) { |
| 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; |
| } |
| if (SdkLevel.isAtLeastS()) { |
| mWifiVendorHal.setCoexUnsafeChannels( |
| mCachedCoexUnsafeChannels, mCachedCoexRestrictions); |
| } |
| } else { |
| Log.i(TAG, "Vendor Hal not supported, ignoring start."); |
| } |
| } |
| registerWificondListenerIfNecessary(); |
| 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."); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Helper method invoked to setup wificond related callback/listener. |
| */ |
| private void registerWificondListenerIfNecessary() { |
| if (mCountryCodeChangeListener == null && SdkLevel.isAtLeastS()) { |
| // The country code listener is a new API in S. |
| mCountryCodeChangeListener = new CountryCodeChangeListenerInternal(); |
| mWifiCondManager.registerCountryCodeChangedListener(Runnable::run, |
| mCountryCodeChangeListener); |
| } |
| } |
| |
| 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 only if the iface was not destroyed because of vendor |
| // HAL crash. In case of vendor HAL crash, let the crash recovery destroy the mode |
| // managers. |
| if (mWifiVendorHal.isVendorHalReady()) { |
| 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, @NonNull WorkSource requestorWs) { |
| synchronized (mLock) { |
| if (mWifiVendorHal.isVendorHalSupported()) { |
| return mWifiVendorHal.createStaIface( |
| new InterfaceDestoyedListenerInternal(iface.id), requestorWs); |
| } 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, @NonNull WorkSource requestorWs, |
| @SoftApConfiguration.BandType int band, boolean isBridged) { |
| synchronized (mLock) { |
| if (mWifiVendorHal.isVendorHalSupported()) { |
| return mWifiVendorHal.createApIface( |
| new InterfaceDestoyedListenerInternal(iface.id), requestorWs, |
| band, isBridged); |
| } else { |
| Log.i(TAG, "Vendor Hal not supported, ignoring createApIface."); |
| return handleIfaceCreationWhenVendorHalNotSupported(iface); |
| } |
| } |
| } |
| |
| /** |
| * Get list of instance name from this bridged AP iface. |
| * |
| * @param ifaceName Name of the bridged interface. |
| * @return list of instance name when succeed, otherwise null. |
| */ |
| @Nullable |
| private List<String> getBridgedApInstances(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| if (mWifiVendorHal.isVendorHalSupported()) { |
| return mWifiVendorHal.getBridgedApInstances(ifaceName); |
| } else { |
| Log.i(TAG, "Vendor Hal not supported, ignoring getBridgedApInstances."); |
| return null; |
| } |
| } |
| } |
| |
| // 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); |
| } |
| } |
| } |
| |
| /** |
| * Helper function to remove specific instance in bridged AP iface. |
| * |
| * @param ifaceName Name of the iface. |
| * @param apIfaceInstance The identity of the ap instance. |
| * @return true if the operation succeeded, false if there is an error in Hal. |
| */ |
| public boolean removeIfaceInstanceFromBridgedApIface(@NonNull String ifaceName, |
| @NonNull String apIfaceInstance) { |
| synchronized (mLock) { |
| if (mWifiVendorHal.isVendorHalSupported()) { |
| return mWifiVendorHal.removeIfaceInstanceFromBridgedApIface(ifaceName, |
| apIfaceInstance); |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| /** |
| * Register listener for subsystem restart event |
| * |
| * @param listener SubsystemRestartListener listener object. |
| */ |
| public void registerSubsystemRestartListener( |
| HalDeviceManager.SubsystemRestartListener listener) { |
| if (listener != null) { |
| mWifiVendorHal.registerSubsystemRestartListener(listener); |
| } |
| } |
| |
| /** |
| * 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 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. |
| * @param requestorWs Requestor worksource. |
| * @return Returns the name of the allocated interface, will be null on failure. |
| */ |
| public String setupInterfaceForClientInConnectivityMode( |
| @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs) { |
| 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, requestorWs); |
| 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. |
| * @param requestorWs Requestor worksource. |
| * @return Returns the name of the allocated interface, will be null on failure. |
| */ |
| public String setupInterfaceForClientInScanMode( |
| @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs) { |
| 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, requestorWs); |
| 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. |
| * @param requestorWs Requestor worksource. |
| * @param isBridged Whether or not AP interface is a bridge interface. |
| * @return Returns the name of the allocated interface, will be null on failure. |
| */ |
| public String setupInterfaceForSoftApMode( |
| @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs, |
| @SoftApConfiguration.BandType int band, boolean isBridged) { |
| 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, requestorWs, band, isBridged); |
| if (TextUtils.isEmpty(iface.name)) { |
| Log.e(TAG, "Failed to create AP iface in vendor HAL"); |
| mIfaceMgr.removeIface(iface.id); |
| mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal(); |
| return null; |
| } |
| String ifaceInstanceName = iface.name; |
| if (isBridged) { |
| List<String> instances = getBridgedApInstances(iface.name); |
| if (instances == null || instances.size() == 0) { |
| Log.e(TAG, "Failed to get bridged AP instances" + iface.name); |
| teardownInterface(iface.name); |
| mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal(); |
| return null; |
| } |
| // Always select first instance as wificond interface. |
| ifaceInstanceName = instances.get(0); |
| } |
| if (!mWifiCondManager.setupInterfaceForSoftApMode(ifaceInstanceName)) { |
| 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. |
| * @param requestorWs Requestor worksource. |
| * @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, |
| @NonNull WorkSource requestorWs) { |
| 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 (mWifiVendorHal.isVendorHalSupported() |
| && !mWifiVendorHal.replaceStaIfaceRequestorWs(iface.name, requestorWs)) { |
| Log.e(TAG, "Failed to replace requestor ws on " + iface); |
| teardownInterface(iface.name); |
| return false; |
| } |
| 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. |
| * @param requestorWs Requestor worksource. |
| * @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, |
| @NonNull WorkSource requestorWs) { |
| 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 (mWifiVendorHal.isVendorHalSupported() |
| && !mWifiVendorHal.replaceStaIfaceRequestorWs(iface.name, requestorWs)) { |
| Log.e(TAG, "Failed to replace requestor ws on " + iface); |
| teardownInterface(iface.name); |
| return false; |
| } |
| 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; |
| } |
| } |
| |
| /** |
| * Change the requestor WorkSource for a given STA iface. |
| * @return true if the operation succeeded, false otherwise. |
| */ |
| public boolean replaceStaIfaceRequestorWs(@NonNull String ifaceName, WorkSource newWorkSource) { |
| final Iface iface = mIfaceMgr.getIface(ifaceName); |
| if (iface == null) { |
| Log.e(TAG, "Called replaceStaIfaceRequestorWs() on an invalid iface=" + ifaceName); |
| return false; |
| } |
| if (!mWifiVendorHal.isVendorHalSupported()) { |
| // if vendor HAL isn't supported, return true since there's nothing to do. |
| return true; |
| } |
| if (!mWifiVendorHal.replaceStaIfaceRequestorWs(iface.name, newWorkSource)) { |
| Log.e(TAG, "Failed to replace requestor ws on " + iface); |
| teardownInterface(iface.name); |
| return false; |
| } |
| 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 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 names of all the client interfaces. |
| * |
| * @return List of interface name of all active client interfaces. |
| */ |
| public Set<String> getSoftApInterfaceNames() { |
| synchronized (mLock) { |
| return mIfaceMgr.findAllApIfaceNames(); |
| } |
| } |
| |
| /******************************************************** |
| * 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 |
| * WifiScanner.WIFI_BAND_60_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) { |
| if (!SdkLevel.isAtLeastS() && band == WifiScanner.WIFI_BAND_60_GHZ) { |
| // 60 GHz band is new in Android S, return empty array on older SDK versions |
| return new int[0]; |
| } |
| 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. |
| * @param enable6GhzRnr whether Reduced Neighbor Report should be enabled for 6Ghz scanning. |
| * @return Returns true on success. |
| */ |
| public boolean scan( |
| @NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, Set<Integer> freqs, |
| List<String> hiddenNetworkSSIDs, boolean enable6GhzRnr) { |
| 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; |
| } |
| } |
| // enable6GhzRnr is a new parameter first introduced in Android S. |
| if (SdkLevel.isAtLeastS()) { |
| Bundle extraScanningParams = new Bundle(); |
| extraScanningParams.putBoolean(WifiNl80211Manager.SCANNING_PARAM_ENABLE_6GHZ_RNR, |
| enable6GhzRnr); |
| return mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays, |
| extraScanningParams); |
| } else { |
| 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) { |
| if (mUseFakeScanDetails) { |
| synchronized (mFakeScanDetails) { |
| ArrayList<ScanDetail> copy = new ArrayList<>(); |
| for (ScanDetail sd: mFakeScanDetails) { |
| sd.getScanResult().ifaceName = ifaceName; |
| // otherwise the fake will be too old |
| sd.getScanResult().timestamp = SystemClock.elapsedRealtime() * 1000; |
| |
| // clone the ScanResult (which was updated above) so that each call gets a |
| // unique timestamp |
| copy.add(new ScanDetail(new ScanResult(sd.getScanResult()), |
| sd.getNetworkDetail())); |
| } |
| return copy; |
| } |
| } |
| return convertNativeScanResults(ifaceName, mWifiCondManager.getScanResults( |
| ifaceName, WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN)); |
| } |
| |
| /** |
| * Start faking scan results - using information provided via |
| * {@link #addFakeScanDetail(ScanDetail)}. Stop with {@link #stopFakingScanDetails()}. |
| */ |
| public void startFakingScanDetails() { |
| if (mBuildProperties.isUserBuild()) { |
| Log.wtf(TAG, "Can't fake scan results in a user build!"); |
| return; |
| } |
| Log.d(TAG, "Starting faking scan results - " + mFakeScanDetails); |
| mUseFakeScanDetails = true; |
| } |
| |
| /** |
| * Add fake scan result. Fakes are not used until activated via |
| * {@link #startFakingScanDetails()}. |
| * @param fakeScanDetail |
| */ |
| public void addFakeScanDetail(@NonNull ScanDetail fakeScanDetail) { |
| synchronized (mFakeScanDetails) { |
| mFakeScanDetails.add(fakeScanDetail); |
| } |
| } |
| |
| /** |
| * Reset the fake scan result list updated via {@link #addFakeScanDetail(ScanDetail)} .} |
| */ |
| public void resetFakeScanDetails() { |
| synchronized (mFakeScanDetails) { |
| mFakeScanDetails.clear(); |
| } |
| } |
| |
| /** |
| * Stop faking scan results. Started with {@link #startFakingScanDetails()}. |
| */ |
| public void stopFakingScanDetails() { |
| mUseFakeScanDetails = false; |
| } |
| |
| /** |
| * 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(ifaceName, mWifiCondManager.getScanResults(ifaceName, |
| WifiNl80211Manager.SCAN_TYPE_PNO_SCAN)); |
| } |
| |
| private ArrayList<ScanDetail> convertNativeScanResults(@NonNull String ifaceName, |
| 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, |
| result.getFrequencyMhz()); |
| 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())); |
| scanResult.ifaceName = ifaceName; |
| |
| // 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 isMetered Indicates the network is metered or not. |
| * @param listener Callback for AP events. |
| * @return true on success, false otherwise. |
| */ |
| public boolean startSoftAp( |
| @NonNull String ifaceName, SoftApConfiguration config, boolean isMetered, |
| SoftApListener listener) { |
| if (mHostapdHal.isApInfoCallbackSupported()) { |
| if (!mHostapdHal.registerApCallback(ifaceName, listener)) { |
| Log.e(TAG, "Failed to register ap listener"); |
| return false; |
| } |
| } else { |
| SoftApListenerFromWificond softApListenerFromWificond = |
| new SoftApListenerFromWificond(ifaceName, listener); |
| if (!mWifiCondManager.registerApCallback(ifaceName, |
| Runnable::run, softApListenerFromWificond)) { |
| Log.e(TAG, "Failed to register ap listener from wificond"); |
| return false; |
| } |
| } |
| |
| if (!mHostapdHal.addAccessPoint(ifaceName, config, isMetered, 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 setStaMacAddress(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.setStaMacAddress(interfaceName, mac); |
| } |
| |
| /** |
| * 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 setApMacAddress(String interfaceName, MacAddress mac) { |
| return mWifiVendorHal.setApMacAddress(interfaceName, mac); |
| } |
| |
| /** |
| * Returns true if Hal version supports setMacAddress, otherwise false. |
| * |
| * @param interfaceName Name of the interface |
| */ |
| public boolean isStaSetMacAddressSupported(@NonNull String interfaceName) { |
| return mWifiVendorHal.isStaSetMacAddressSupported(interfaceName); |
| } |
| |
| /** |
| * Returns true if Hal version supports setMacAddress, otherwise false. |
| * |
| * @param interfaceName Name of the interface |
| */ |
| public boolean isApSetMacAddressSupported(@NonNull String interfaceName) { |
| return mWifiVendorHal.isApSetMacAddressSupported(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 getStaFactoryMacAddress(@NonNull String interfaceName) { |
| return mWifiVendorHal.getStaFactoryMacAddress(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 getApFactoryMacAddress(@NonNull String interfaceName) { |
| return mWifiVendorHal.getApFactoryMacAddress(interfaceName); |
| } |
| |
| /** |
| * Reset MAC address to factory MAC address on the given interface |
| * |
| * @param interfaceName Name of the interface |
| * @return true for success |
| */ |
| public boolean resetApMacToFactoryMacAddress(@NonNull String interfaceName) { |
| return mWifiVendorHal.resetApMacToFactoryMacAddress(interfaceName); |
| } |
| |
| /** |
| * Set the unsafe channels and restrictions to avoid for coex. |
| * @param unsafeChannels List of {@link CoexUnsafeChannel} to avoid |
| * @param restrictions Bitmask of WifiManager.COEX_RESTRICTION_ flags |
| */ |
| public void setCoexUnsafeChannels( |
| @NonNull List<CoexUnsafeChannel> unsafeChannels, int restrictions) { |
| mCachedCoexUnsafeChannels.clear(); |
| mCachedCoexUnsafeChannels.addAll(unsafeChannels); |
| mCachedCoexRestrictions = restrictions; |
| mWifiVendorHal.setCoexUnsafeChannels(mCachedCoexUnsafeChannels, mCachedCoexRestrictions); |
| } |
| |
| /******************************************************** |
| * 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 for STA interface |
| * |
| * @param ifaceName Name of the STA interface. |
| * @param countryCode 2 byte ASCII string. For ex: US, CA. |
| * @return true if request is sent successfully, false otherwise. |
| */ |
| public boolean setStaCountryCode(@NonNull String ifaceName, String countryCode) { |
| if (mSupplicantStaIfaceHal.setCountryCode(ifaceName, countryCode)) { |
| if (mCountryCodeChangeListener != null) { |
| mCountryCodeChangeListener.onSetCountryCodeSucceeded(countryCode); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * 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) { |
| String anonymousIdentity = mSupplicantStaIfaceHal |
| .getCurrentNetworkEapAnonymousIdentity(ifaceName); |
| |
| if (TextUtils.isEmpty(anonymousIdentity)) { |
| return anonymousIdentity; |
| } |
| |
| int indexOfDecoration = anonymousIdentity.lastIndexOf('!'); |
| if (indexOfDecoration >= 0) { |
| if (anonymousIdentity.substring(indexOfDecoration).length() < 2) { |
| // Invalid identity, shouldn't happen |
| Log.e(TAG, "Unexpected anonymous identity: " + anonymousIdentity); |
| return null; |
| } |
| // Truncate RFC 7542 decorated prefix, if exists. Keep only the anonymous identity or |
| // pseudonym. |
| anonymousIdentity = anonymousIdentity.substring(indexOfDecoration + 1); |
| } |
| |
| return anonymousIdentity; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Disable the currently configured network in supplicant |
| * |
| * @param ifaceName Name of the interface. |
| */ |
| public boolean disableNetwork(@NonNull String ifaceName) { |
| return mSupplicantStaIfaceHal.disableCurrentNetwork(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 setNetworkBSSID(@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); |
| } |
| |
| /** |
| * Initiate Venue URL ANQP query. |
| * |
| * @param ifaceName Name of the interface. |
| * @param bssid BSSID of the AP to be queried |
| * @return true on success, false otherwise. |
| */ |
| public boolean requestVenueUrlAnqp( |
| @NonNull String ifaceName, String bssid) { |
| if (bssid == null) { |
| Log.e(TAG, "Invalid arguments for Venue URL ANQP request."); |
| return false; |
| } |
| return mSupplicantStaIfaceHal.initiateVenueUrlAnqpQuery(ifaceName, bssid); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Class to get generated bootstrap info for DPP responder operation. |
| */ |
| public static class DppBootstrapQrCodeInfo { |
| public int bootstrapId; |
| public int listenChannel; |
| public String uri = new String(); |
| DppBootstrapQrCodeInfo() { |
| bootstrapId = -1; |
| listenChannel = -1; |
| } |
| } |
| |
| /** |
| * Generate DPP bootstrap Information:Bootstrap ID, DPP URI and the listen channel. |
| * |
| * @param ifaceName Interface name |
| * @param deviceInfo Device specific info to attach in DPP URI. |
| * @param dppCurve Elliptic curve cryptography type used to generate DPP |
| * public/private key pair. |
| * @return ID, or -1 for failure |
| */ |
| public DppBootstrapQrCodeInfo generateDppBootstrapInfoForResponder(@NonNull String ifaceName, |
| String deviceInfo, int dppCurve) { |
| return mSupplicantStaIfaceHal.generateDppBootstrapInfoForResponder(ifaceName, |
| getMacAddress(ifaceName), deviceInfo, dppCurve); |
| } |
| |
| /** |
| * start DPP Enrollee responder mode. |
| * |
| * @param ifaceName Interface name |
| * @param listenChannel Listen channel to wait for DPP authentication request. |
| * @return ID, or -1 for failure |
| */ |
| public boolean startDppEnrolleeResponder(@NonNull String ifaceName, int listenChannel) { |
| return mSupplicantStaIfaceHal.startDppEnrolleeResponder(ifaceName, listenChannel); |
| } |
| |
| /** |
| * Stops/aborts DPP Responder request |
| * |
| * @param ifaceName Interface name |
| * @param ownBootstrapId Bootstrap (URI) ID |
| * @return true when operation is successful, or false for failure |
| */ |
| public boolean stopDppResponder(@NonNull String ifaceName, int ownBootstrapId) { |
| return mSupplicantStaIfaceHal.stopDppResponder(ifaceName, ownBootstrapId); |
| } |
| |
| |
| /** |
| * 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; |
| public boolean enable6GhzRnr; |
| /* 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); |
| } |
| |
| /** |
| * Gets the usable channels |
| * @param band one of the {@code WifiScanner#WIFI_BAND_*} constants. |
| * @param mode bitmask of {@code WifiAvailablechannel#OP_MODE_*} constants. |
| * @param filter bitmask of filters (regulatory, coex, concurrency). |
| * |
| * @return list of channels |
| */ |
| public List<WifiAvailableChannel> getUsableChannels( |
| @WifiScanner.WifiBand int band, |
| @WifiAvailableChannel.OpMode int mode, |
| @WifiAvailableChannel.Filter int filter) { |
| return mWifiVendorHal.getUsableChannels(band, mode, filter); |
| } |
| |
| /** |
| * Returns whether STA + AP concurrency is supported or not. |
| */ |
| public boolean isStaApConcurrencySupported() { |
| synchronized (mLock) { |
| return mWifiVendorHal.isStaApConcurrencySupported(); |
| } |
| } |
| |
| /** |
| * Returns whether STA + STA concurrency is supported or not. |
| */ |
| public boolean isStaStaConcurrencySupported() { |
| synchronized (mLock) { |
| return mWifiVendorHal.isStaStaConcurrencySupported(); |
| } |
| } |
| |
| /** |
| * Returns whether a new AP iface can be created or not. |
| */ |
| public boolean isItPossibleToCreateApIface(@NonNull WorkSource requestorWs) { |
| synchronized (mLock) { |
| return mWifiVendorHal.isItPossibleToCreateApIface(requestorWs); |
| } |
| } |
| |
| /** |
| * Returns whether a new STA iface can be created or not. |
| */ |
| public boolean isItPossibleToCreateStaIface(@NonNull WorkSource requestorWs) { |
| synchronized (mLock) { |
| return mWifiVendorHal.isItPossibleToCreateStaIface(requestorWs); |
| } |
| } |
| |
| /** |
| * Set primary connection when multiple STA ifaces are active. |
| * |
| * @param ifaceName Name of the interface. |
| * @return true for success |
| */ |
| public boolean setMultiStaPrimaryConnection(@NonNull String ifaceName) { |
| synchronized (mLock) { |
| return mWifiVendorHal.setMultiStaPrimaryConnection(ifaceName); |
| } |
| } |
| |
| /** |
| * Multi STA use case flags. |
| */ |
| public static final int DUAL_STA_TRANSIENT_PREFER_PRIMARY = 0; |
| public static final int DUAL_STA_NON_TRANSIENT_UNBIASED = 1; |
| |
| @IntDef({DUAL_STA_TRANSIENT_PREFER_PRIMARY, DUAL_STA_NON_TRANSIENT_UNBIASED}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface MultiStaUseCase{} |
| |
| /** |
| * Set use-case when multiple STA ifaces are active. |
| * |
| * @param useCase one of the use cases. |
| * @return true for success |
| */ |
| public boolean setMultiStaUseCase(@MultiStaUseCase int useCase) { |
| synchronized (mLock) { |
| return mWifiVendorHal.setMultiStaUseCase(useCase); |
| } |
| } |
| |
| /** |
| * 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.getAdvancedCapabilities(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; |
| public boolean is11bMode; |
| ConnectionCapabilities() { |
| wifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN; |
| channelBandwidth = ScanResult.CHANNEL_WIDTH_20MHZ; |
| maxNumberTxSpatialStreams = 1; |
| maxNumberRxSpatialStreams = 1; |
| is11bMode = false; |
| } |
| } |
| |
| /** |
| * 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 AP interface. |
| * @param countryCode - two-letter country code (as ISO 3166) |
| * @return true for success |
| */ |
| public boolean setApCountryCode(@NonNull String ifaceName, String countryCode) { |
| if (mWifiVendorHal.setApCountryCode(ifaceName, countryCode)) { |
| if (mCountryCodeChangeListener != null) { |
| mCountryCodeChangeListener.onSetCountryCodeSucceeded(countryCode); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Set country code for this chip |
| * @param countryCode - two-letter country code (as ISO 3166) |
| * @return true for success |
| */ |
| public boolean setChipCountryCode(String countryCode) { |
| if (mWifiVendorHal.setChipCountryCode(countryCode)) { |
| if (mCountryCodeChangeListener != null) { |
| mCountryCodeChangeListener.onSetCountryCodeSucceeded(countryCode); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| //--------------------------------------------------------------------------------- |
| /* 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 TxFateReport list on success, empty list on failure. Never returns null. |
| */ |
| @NonNull |
| public List<TxFateReport> getTxPktFates(@NonNull String ifaceName) { |
| return mWifiVendorHal.getTxPktFates(ifaceName); |
| } |
| |
| /** |
| * Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started. |
| * @param ifaceName Name of the interface. |
| * @return RxFateReport list on success, empty list on failure. Never returns null. |
| */ |
| @NonNull |
| public List<RxFateReport> getRxPktFates(@NonNull String ifaceName) { |
| return mWifiVendorHal.getRxPktFates(ifaceName); |
| } |
| |
| /** |
| * 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 maxBlocklistSize; |
| public int maxAllowlistSize; |
| } |
| |
| /** |
| * Query the firmware roaming capabilities. |
| * @param ifaceName Name of the interface. |
| * @return capabilities object on success, null otherwise. |
| */ |
| @Nullable |
| public RoamingCapabilities getRoamingCapabilities(@NonNull String ifaceName) { |
| return mWifiVendorHal.getRoamingCapabilities(ifaceName); |
| } |
| |
| /** |
| * Macros for controlling firmware roaming. |
| */ |
| public static final int DISABLE_FIRMWARE_ROAMING = 0; |
| public static final int ENABLE_FIRMWARE_ROAMING = 1; |
| |
| @IntDef({ENABLE_FIRMWARE_ROAMING, DISABLE_FIRMWARE_ROAMING}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface RoamingEnableState {} |
| |
| /** |
| * 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; |
| |
| @IntDef({SET_FIRMWARE_ROAMING_SUCCESS, SET_FIRMWARE_ROAMING_FAILURE, SET_FIRMWARE_ROAMING_BUSY}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface RoamingEnableStatus {} |
| |
| /** |
| * 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 @RoamingEnableStatus int enableFirmwareRoaming(@NonNull String ifaceName, |
| @RoamingEnableState int state) { |
| return mWifiVendorHal.enableFirmwareRoaming(ifaceName, state); |
| } |
| |
| /** |
| * Class for specifying the roaming configurations. |
| */ |
| public static class RoamingConfig { |
| public ArrayList<String> blocklistBssids; |
| public ArrayList<String> allowlistSsids; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * 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; |
| } |
| } |
| |
| /** |
| * Notify scan mode state to driver to save power in scan-only mode. |
| * |
| * @param ifaceName Name of the interface. |
| * @param enable whether is in scan-only mode |
| * @return true for success |
| */ |
| public boolean setScanMode(String ifaceName, boolean enable) { |
| return mWifiVendorHal.setScanMode(ifaceName, enable); |
| } |
| |
| /** updates linked networks of the |networkId| in supplicant if it's the current network, |
| * if the current configured network matches |networkId|. |
| * |
| * @param ifaceName Name of the interface. |
| * @param networkId network id of the network to be updated from supplicant. |
| * @param linkedNetworks Map of config profile key and config for linking. |
| */ |
| public boolean updateLinkedNetworks(@NonNull String ifaceName, int networkId, |
| Map<String, WifiConfiguration> linkedNetworks) { |
| return mSupplicantStaIfaceHal.updateLinkedNetworks(ifaceName, networkId, linkedNetworks); |
| } |
| |
| /** |
| * Start Subsystem Restart |
| * @return true on success |
| */ |
| public boolean startSubsystemRestart() { |
| return mWifiVendorHal.startSubsystemRestart(); |
| } |
| |
| /** |
| * Register the provided listener for country code event. |
| * |
| * @param listener listener for country code changed events. |
| */ |
| public void registerCountryCodeEventListener(WifiCountryCode.ChangeListener listener) { |
| registerWificondListenerIfNecessary(); |
| if (mCountryCodeChangeListener != null) { |
| mCountryCodeChangeListener.setChangeListener(listener); |
| } |
| } |
| } |