/* * Copyright (C) 2017 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.NonNull; import android.app.AlarmManager; import android.net.wifi.IApInterface; import android.net.wifi.IApInterfaceEventCallback; import android.net.wifi.IClientInterface; import android.net.wifi.IPnoScanEvent; import android.net.wifi.IScanEvent; import android.net.wifi.ISendMgmtFrameEvent; import android.net.wifi.IWifiScannerImpl; import android.net.wifi.IWificond; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; import android.net.wifi.WifiSsid; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import com.android.server.wifi.WifiNative.SendMgmtFrameCallback; import com.android.server.wifi.WifiNative.SoftApListener; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.ScanResultUtil; import com.android.server.wifi.wificond.ChannelSettings; import com.android.server.wifi.wificond.HiddenNetwork; import com.android.server.wifi.wificond.NativeScanResult; import com.android.server.wifi.wificond.PnoNetwork; import com.android.server.wifi.wificond.PnoSettings; import com.android.server.wifi.wificond.RadioChainInfo; import com.android.server.wifi.wificond.SingleScanSettings; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** * This class provides methods for WifiNative to send control commands to wificond. * NOTE: This class should only be used from WifiNative. */ public class WificondControl implements IBinder.DeathRecipient { private boolean mVerboseLoggingEnabled = false; /** * The {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()} * timeout, in milliseconds, after which * {@link SendMgmtFrameCallback#onFailure(int)} will be called with reason * {@link WifiNative#SEND_MGMT_FRAME_ERROR_TIMEOUT}. */ public static final int SEND_MGMT_FRAME_TIMEOUT_MS = 1000; private static final String TAG = "WificondControl"; private static final String TIMEOUT_ALARM_TAG = TAG + " Send Management Frame Timeout"; /* Get scan results for a single scan */ public static final int SCAN_TYPE_SINGLE_SCAN = 0; /* Get scan results for Pno Scan */ public static final int SCAN_TYPE_PNO_SCAN = 1; private WifiInjector mWifiInjector; private WifiMonitor mWifiMonitor; private final CarrierNetworkConfig mCarrierNetworkConfig; private AlarmManager mAlarmManager; private Handler mEventHandler; private Clock mClock; private WifiNative mWifiNative = null; // Cached wificond binder handlers. private IWificond mWificond; private HashMap mClientInterfaces = new HashMap<>(); private HashMap mApInterfaces = new HashMap<>(); private HashMap mWificondScanners = new HashMap<>(); private HashMap mScanEventHandlers = new HashMap<>(); private HashMap mPnoScanEventHandlers = new HashMap<>(); private HashMap mApInterfaceListeners = new HashMap<>(); private WifiNative.WificondDeathEventHandler mDeathEventHandler; /** * Ensures that no more than one sendMgmtFrame operation runs concurrently. */ private AtomicBoolean mSendMgmtFrameInProgress = new AtomicBoolean(false); private boolean mIsEnhancedOpenSupportedInitialized = false; private boolean mIsEnhancedOpenSupported; private class ScanEventHandler extends IScanEvent.Stub { private String mIfaceName; ScanEventHandler(@NonNull 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); } } WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor, CarrierNetworkConfig carrierNetworkConfig, AlarmManager alarmManager, Looper looper, Clock clock) { mWifiInjector = wifiInjector; mWifiMonitor = wifiMonitor; mCarrierNetworkConfig = carrierNetworkConfig; mAlarmManager = alarmManager; mEventHandler = new Handler(looper); mClock = clock; } private class PnoScanEventHandler extends IPnoScanEvent.Stub { private String mIfaceName; PnoScanEventHandler(@NonNull String ifaceName) { mIfaceName = ifaceName; } @Override public void OnPnoNetworkFound() { Log.d(TAG, "Pno scan result event"); mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName); mWifiInjector.getWifiMetrics().incrementPnoFoundNetworkEventCount(); } @Override public void OnPnoScanFailed() { Log.d(TAG, "Pno Scan failed event"); mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount(); } @Override public void OnPnoScanOverOffloadStarted() { Log.d(TAG, "Pno scan over offload started"); mWifiInjector.getWifiMetrics().incrementPnoScanStartedOverOffloadCount(); } @Override public void OnPnoScanOverOffloadFailed(int reason) { Log.d(TAG, "Pno scan over offload failed"); mWifiInjector.getWifiMetrics().incrementPnoScanFailedOverOffloadCount(); } } /** * Listener for AP Interface events. */ private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub { private SoftApListener mSoftApListener; ApInterfaceEventCallback(SoftApListener listener) { mSoftApListener = listener; } @Override public void onNumAssociatedStationsChanged(int numStations) { mSoftApListener.onNumAssociatedStationsChanged(numStations); } @Override public void onSoftApChannelSwitched(int frequency, int bandwidth) { mSoftApListener.onSoftApChannelSwitched(frequency, bandwidth); } } /** * Callback triggered by wificond. */ private class SendMgmtFrameEvent extends ISendMgmtFrameEvent.Stub { private SendMgmtFrameCallback mCallback; private AlarmManager.OnAlarmListener mTimeoutCallback; /** * ensures that mCallback is only called once */ private boolean mWasCalled; private void runIfFirstCall(Runnable r) { if (mWasCalled) return; mWasCalled = true; mSendMgmtFrameInProgress.set(false); r.run(); } SendMgmtFrameEvent(@NonNull SendMgmtFrameCallback callback) { mCallback = callback; // called in main thread mTimeoutCallback = () -> runIfFirstCall(() -> { if (mVerboseLoggingEnabled) { Log.e(TAG, "Timed out waiting for ACK"); } mCallback.onFailure(WifiNative.SEND_MGMT_FRAME_ERROR_TIMEOUT); }); mWasCalled = false; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mClock.getElapsedSinceBootMillis() + SEND_MGMT_FRAME_TIMEOUT_MS, TIMEOUT_ALARM_TAG, mTimeoutCallback, mEventHandler); } // called in binder thread @Override public void OnAck(int elapsedTimeMs) { // post to main thread mEventHandler.post(() -> runIfFirstCall(() -> { mAlarmManager.cancel(mTimeoutCallback); mCallback.onAck(elapsedTimeMs); })); } // called in binder thread @Override public void OnFailure(int reason) { // post to main thread mEventHandler.post(() -> runIfFirstCall(() -> { mAlarmManager.cancel(mTimeoutCallback); mCallback.onFailure(reason); })); } } /** * Called by the binder subsystem upon remote object death. * Invoke all the register death handlers and clear state. */ @Override public void binderDied() { mEventHandler.post(() -> { Log.e(TAG, "Wificond died!"); clearState(); // Invalidate the global wificond handle on death. Will be refreshed // on the next setup call. mWificond = null; if (mDeathEventHandler != null) { mDeathEventHandler.onDeath(); } }); } /** Enable or disable verbose logging of WificondControl. * @param enable True to enable verbose logging. False to disable verbose logging. */ public void enableVerboseLogging(boolean enable) { mVerboseLoggingEnabled = enable; } /** * Initializes wificond & registers a death notification for wificond. * This method clears any existing state in wificond daemon. * * @return Returns true on success. */ public boolean initialize(@NonNull WifiNative.WificondDeathEventHandler handler) { if (mDeathEventHandler != null) { Log.e(TAG, "Death handler already present"); } mDeathEventHandler = handler; tearDownInterfaces(); return true; } /** * Helper method to retrieve the global wificond handle and register for * death notifications. */ private boolean retrieveWificondAndRegisterForDeath() { if (mWificond != null) { if (mVerboseLoggingEnabled) { Log.d(TAG, "Wificond handle already retrieved"); } // We already have a wificond handle. return true; } mWificond = mWifiInjector.makeWificond(); if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); return false; } try { mWificond.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { Log.e(TAG, "Failed to register death notification for wificond"); // The remote has already died. return false; } return true; } /** * Setup interface for client mode via wificond. * @return An IClientInterface as wificond client interface binder handler. * Returns null on failure. */ public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) { Log.d(TAG, "Setting up interface for client mode"); if (!retrieveWificondAndRegisterForDeath()) { return null; } IClientInterface clientInterface = null; try { clientInterface = mWificond.createClientInterface(ifaceName); } catch (RemoteException e1) { Log.e(TAG, "Failed to get IClientInterface due to remote exception"); return null; } if (clientInterface == null) { Log.e(TAG, "Could not get IClientInterface instance from wificond"); return null; } Binder.allowBlocking(clientInterface.asBinder()); // Refresh Handlers mClientInterfaces.put(ifaceName, clientInterface); try { IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl(); if (wificondScanner == null) { Log.e(TAG, "Failed to get WificondScannerImpl"); return null; } mWificondScanners.put(ifaceName, wificondScanner); Binder.allowBlocking(wificondScanner.asBinder()); ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName); mScanEventHandlers.put(ifaceName, scanEventHandler); wificondScanner.subscribeScanEvents(scanEventHandler); PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName); mPnoScanEventHandlers.put(ifaceName, pnoScanEventHandler); wificondScanner.subscribePnoScanEvents(pnoScanEventHandler); } catch (RemoteException e) { Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); } return clientInterface; } /** * Teardown a specific STA interface configured in wificond. * * @return Returns true on success. */ public boolean tearDownClientInterface(@NonNull String ifaceName) { if (getClientInterface(ifaceName) == null) { Log.e(TAG, "No valid wificond client interface handler"); return false; } try { IWifiScannerImpl scannerImpl = mWificondScanners.get(ifaceName); if (scannerImpl != null) { scannerImpl.unsubscribeScanEvents(); scannerImpl.unsubscribePnoScanEvents(); } } catch (RemoteException e) { Log.e(TAG, "Failed to unsubscribe wificond scanner due to remote exception"); return false; } boolean success; try { success = mWificond.tearDownClientInterface(ifaceName); } catch (RemoteException e1) { Log.e(TAG, "Failed to teardown client interface due to remote exception"); return false; } if (!success) { Log.e(TAG, "Failed to teardown client interface"); return false; } mClientInterfaces.remove(ifaceName); mWificondScanners.remove(ifaceName); mScanEventHandlers.remove(ifaceName); mPnoScanEventHandlers.remove(ifaceName); return true; } /** * Setup interface for softAp mode via wificond. * @return An IApInterface as wificond Ap interface binder handler. * Returns null on failure. */ public IApInterface setupInterfaceForSoftApMode(@NonNull String ifaceName) { Log.d(TAG, "Setting up interface for soft ap mode"); if (!retrieveWificondAndRegisterForDeath()) { return null; } IApInterface apInterface = null; try { apInterface = mWificond.createApInterface(ifaceName); } catch (RemoteException e1) { Log.e(TAG, "Failed to get IApInterface due to remote exception"); return null; } if (apInterface == null) { Log.e(TAG, "Could not get IApInterface instance from wificond"); return null; } Binder.allowBlocking(apInterface.asBinder()); // Refresh Handlers mApInterfaces.put(ifaceName, apInterface); return apInterface; } /** * Teardown a specific AP interface configured in wificond. * * @return Returns true on success. */ public boolean tearDownSoftApInterface(@NonNull String ifaceName) { if (getApInterface(ifaceName) == null) { Log.e(TAG, "No valid wificond ap interface handler"); return false; } boolean success; try { success = mWificond.tearDownApInterface(ifaceName); } catch (RemoteException e1) { Log.e(TAG, "Failed to teardown AP interface due to remote exception"); return false; } if (!success) { Log.e(TAG, "Failed to teardown AP interface"); return false; } mApInterfaces.remove(ifaceName); mApInterfaceListeners.remove(ifaceName); return true; } /** * Teardown all interfaces configured in wificond. * @return Returns true on success. */ public boolean tearDownInterfaces() { Log.d(TAG, "tearing down interfaces in wificond"); // Explicitly refresh the wificodn handler because |tearDownInterfaces()| // could be used to cleanup before we setup any interfaces. if (!retrieveWificondAndRegisterForDeath()) { return false; } try { for (Map.Entry entry : mWificondScanners.entrySet()) { entry.getValue().unsubscribeScanEvents(); entry.getValue().unsubscribePnoScanEvents(); } mWificond.tearDownInterfaces(); clearState(); return true; } catch (RemoteException e) { Log.e(TAG, "Failed to tear down interfaces due to remote exception"); } return false; } /** Helper function to look up the interface handle using name */ private IClientInterface getClientInterface(@NonNull String ifaceName) { return mClientInterfaces.get(ifaceName); } /** * Request signal polling to wificond. * @param ifaceName Name of the interface. * Returns an SignalPollResult object. * Returns null on failure. */ public WifiNative.SignalPollResult signalPoll(@NonNull String ifaceName) { IClientInterface iface = getClientInterface(ifaceName); if (iface == null) { Log.e(TAG, "No valid wificond client interface handler"); return null; } int[] resultArray; try { resultArray = iface.signalPoll(); if (resultArray == null || resultArray.length != 4) { Log.e(TAG, "Invalid signal poll result from wificond"); return null; } } catch (RemoteException e) { Log.e(TAG, "Failed to do signal polling due to remote exception"); return null; } WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult(); pollResult.currentRssi = resultArray[0]; pollResult.txBitrate = resultArray[1]; pollResult.associationFrequency = resultArray[2]; pollResult.rxBitrate = resultArray[3]; return pollResult; } /** * Fetch TX packet counters on current connection from wificond. * @param ifaceName Name of the interface. * Returns an TxPacketCounters object. * Returns null on failure. */ public WifiNative.TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) { IClientInterface iface = getClientInterface(ifaceName); if (iface == null) { Log.e(TAG, "No valid wificond client interface handler"); return null; } int[] resultArray; try { resultArray = iface.getPacketCounters(); if (resultArray == null || resultArray.length != 2) { Log.e(TAG, "Invalid signal poll result from wificond"); return null; } } catch (RemoteException e) { Log.e(TAG, "Failed to do signal polling due to remote exception"); return null; } WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters(); counters.txSucceeded = resultArray[0]; counters.txFailed = resultArray[1]; return counters; } /** Helper function to look up the scanner impl handle using name */ private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) { return mWificondScanners.get(ifaceName); } /** * 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 getScanResults(@NonNull String ifaceName, int scanType) { ArrayList results = new ArrayList<>(); IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return results; } try { NativeScanResult[] nativeResults; if (scanType == SCAN_TYPE_SINGLE_SCAN) { nativeResults = scannerImpl.getScanResults(); } else { nativeResults = scannerImpl.getPnoScanResults(); } for (NativeScanResult result : nativeResults) { WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid); String bssid; try { bssid = NativeUtil.macAddressFromByteArray(result.bssid); } catch (IllegalArgumentException e) { Log.e(TAG, "Illegal argument " + result.bssid, e); continue; } if (bssid == null) { Log.e(TAG, "Illegal null bssid"); continue; } ScanResult.InformationElement[] ies = InformationElementUtil.parseInformationElements(result.infoElement); InformationElementUtil.Capabilities capabilities = new InformationElementUtil.Capabilities(); capabilities.from(ies, result.capability, isEnhancedOpenSupported()); String flags = capabilities.generateCapabilitiesString(); NetworkDetail networkDetail; try { networkDetail = new NetworkDetail(bssid, ies, null, result.frequency); } 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.signalMbm / 100, result.frequency, result.tsf, ies, null); ScanResult scanResult = scanDetail.getScanResult(); // Update carrier network info if this AP's SSID is associated with a carrier Wi-Fi // network and it uses EAP. if (ScanResultUtil.isScanResultForEapNetwork(scanDetail.getScanResult()) && mCarrierNetworkConfig.isCarrierNetwork(wifiSsid.toString())) { scanResult.isCarrierAp = true; scanResult.carrierApEapType = mCarrierNetworkConfig.getNetworkEapType(wifiSsid.toString()); scanResult.carrierName = mCarrierNetworkConfig.getCarrierName(wifiSsid.toString()); } // Fill up the radio chain info. if (result.radioChainInfos != null) { scanResult.radioChainInfos = new ScanResult.RadioChainInfo[result.radioChainInfos.size()]; int idx = 0; for (RadioChainInfo nativeRadioChainInfo : result.radioChainInfos) { scanResult.radioChainInfos[idx] = new ScanResult.RadioChainInfo(); scanResult.radioChainInfos[idx].id = nativeRadioChainInfo.chainId; scanResult.radioChainInfos[idx].level = nativeRadioChainInfo.level; idx++; } } results.add(scanDetail); } } catch (RemoteException e1) { Log.e(TAG, "Failed to create ScanDetail ArrayList"); } if (mVerboseLoggingEnabled) { Log.d(TAG, "get " + results.size() + " scan results from wificond"); } return results; } /** * Return scan type for the parcelable {@link SingleScanSettings} */ private static int getScanType(int scanType) { switch (scanType) { case WifiNative.SCAN_TYPE_LOW_LATENCY: return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN; case WifiNative.SCAN_TYPE_LOW_POWER: return IWifiScannerImpl.SCAN_TYPE_LOW_POWER; case WifiNative.SCAN_TYPE_HIGH_ACCURACY: return IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY; default: throw new IllegalArgumentException("Invalid scan type " + scanType); } } /** * Start a scan using wificond for the given parameters. * @param ifaceName Name of the interface. * @param scanType Type of scan to perform. * @param freqs list of frequencies to scan for, if null scan all supported channels. * @param hiddenNetworkSSIDs List of hidden networks to be scanned for. * @return Returns true on success. */ public boolean scan(@NonNull String ifaceName, int scanType, Set freqs, List hiddenNetworkSSIDs) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return false; } SingleScanSettings settings = new SingleScanSettings(); try { settings.scanType = getScanType(scanType); } catch (IllegalArgumentException e) { Log.e(TAG, "Invalid scan type ", e); return false; } settings.channelSettings = new ArrayList<>(); settings.hiddenNetworks = new ArrayList<>(); if (freqs != null) { for (Integer freq : freqs) { ChannelSettings channel = new ChannelSettings(); channel.frequency = freq; settings.channelSettings.add(channel); } } if (hiddenNetworkSSIDs != null) { for (String ssid : hiddenNetworkSSIDs) { HiddenNetwork network = new HiddenNetwork(); try { network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid)); } catch (IllegalArgumentException e) { Log.e(TAG, "Illegal argument " + ssid, e); continue; } // settings.hiddenNetworks is expected to be very small, so this shouldn't cause // any performance issues. if (!settings.hiddenNetworks.contains(network)) { settings.hiddenNetworks.add(network); } } } try { return scannerImpl.scan(settings); } catch (RemoteException e1) { Log.e(TAG, "Failed to request scan due to remote exception"); } return false; } /** * Start PNO scan. * @param ifaceName Name of the interface. * @param pnoSettings Pno scan configuration. * @return true on success. */ public boolean startPnoScan(@NonNull String ifaceName, WifiNative.PnoSettings pnoSettings) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return false; } PnoSettings settings = new PnoSettings(); settings.pnoNetworks = new ArrayList<>(); settings.intervalMs = pnoSettings.periodInMs; settings.min2gRssi = pnoSettings.min24GHzRssi; settings.min5gRssi = pnoSettings.min5GHzRssi; if (pnoSettings.networkList != null) { for (WifiNative.PnoNetwork network : pnoSettings.networkList) { PnoNetwork condNetwork = new PnoNetwork(); condNetwork.isHidden = (network.flags & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0; try { condNetwork.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid)); } catch (IllegalArgumentException e) { Log.e(TAG, "Illegal argument " + network.ssid, e); continue; } condNetwork.frequencies = network.frequencies; settings.pnoNetworks.add(condNetwork); } } try { boolean success = scannerImpl.startPnoScan(settings); mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount(); if (!success) { mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount(); } return success; } catch (RemoteException e1) { Log.e(TAG, "Failed to start pno scan due to remote exception"); } return false; } /** * Stop PNO scan. * @param ifaceName Name of the interface. * @return true on success. */ public boolean stopPnoScan(@NonNull String ifaceName) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return false; } try { return scannerImpl.stopPnoScan(); } catch (RemoteException e1) { Log.e(TAG, "Failed to stop pno scan due to remote exception"); } return false; } /** * Abort ongoing single scan. * @param ifaceName Name of the interface. */ public void abortScan(@NonNull String ifaceName) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return; } try { scannerImpl.abortScan(); } catch (RemoteException e1) { Log.e(TAG, "Failed to request abortScan due to remote exception"); } } /** * 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: * WifiScanner.WIFI_BAND_24_GHZ * WifiScanner.WIFI_BAND_5_GHZ * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY * @return frequencies vector of valid frequencies (MHz), or null for error. * @throws IllegalArgumentException if band is not recognized. */ public int [] getChannelsForBand(int band) { if (mWificond == null) { Log.e(TAG, "No valid wificond scanner interface handler"); return null; } try { switch (band) { case WifiScanner.WIFI_BAND_24_GHZ: return mWificond.getAvailable2gChannels(); case WifiScanner.WIFI_BAND_5_GHZ: return mWificond.getAvailable5gNonDFSChannels(); case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY: return mWificond.getAvailableDFSChannels(); default: throw new IllegalArgumentException("unsupported band " + band); } } catch (RemoteException e1) { Log.e(TAG, "Failed to request getChannelsForBand due to remote exception"); } return null; } /** Helper function to look up the interface handle using name */ private IApInterface getApInterface(@NonNull String ifaceName) { return mApInterfaces.get(ifaceName); } /** * Register the provided listener for SoftAp events. * * @param ifaceName Name of the interface. * @param listener Callback for AP events. * @return true on success, false otherwise. */ public boolean registerApListener(@NonNull String ifaceName, SoftApListener listener) { IApInterface iface = getApInterface(ifaceName); if (iface == null) { Log.e(TAG, "No valid ap interface handler"); return false; } try { IApInterfaceEventCallback callback = new ApInterfaceEventCallback(listener); mApInterfaceListeners.put(ifaceName, callback); boolean success = iface.registerCallback(callback); if (!success) { Log.e(TAG, "Failed to register ap callback."); return false; } } catch (RemoteException e) { Log.e(TAG, "Exception in registering AP callback: " + e); return false; } return true; } /** * See {@link WifiNative#sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int)} */ public void sendMgmtFrame(@NonNull String ifaceName, @NonNull byte[] frame, @NonNull SendMgmtFrameCallback callback, int mcs) { if (callback == null) { Log.e(TAG, "callback cannot be null!"); return; } if (frame == null) { Log.e(TAG, "frame cannot be null!"); callback.onFailure(WifiNative.SEND_MGMT_FRAME_ERROR_UNKNOWN); return; } // TODO (b/112029045) validate mcs IClientInterface clientInterface = getClientInterface(ifaceName); if (clientInterface == null) { Log.e(TAG, "No valid wificond client interface handler"); callback.onFailure(WifiNative.SEND_MGMT_FRAME_ERROR_UNKNOWN); return; } if (!mSendMgmtFrameInProgress.compareAndSet(false, true)) { Log.e(TAG, "An existing management frame transmission is in progress!"); callback.onFailure(WifiNative.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); return; } SendMgmtFrameEvent sendMgmtFrameEvent = new SendMgmtFrameEvent(callback); try { clientInterface.SendMgmtFrame(frame, sendMgmtFrameEvent, mcs); } catch (RemoteException e) { Log.e(TAG, "Exception while starting link probe: " + e); // Call sendMgmtFrameEvent.OnFailure() instead of callback.onFailure() so that // sendMgmtFrameEvent can clean up internal state, such as cancelling the timer. sendMgmtFrameEvent.OnFailure(WifiNative.SEND_MGMT_FRAME_ERROR_UNKNOWN); } } /** * Clear all internal handles. */ private void clearState() { // Refresh handlers mClientInterfaces.clear(); mWificondScanners.clear(); mPnoScanEventHandlers.clear(); mScanEventHandlers.clear(); mApInterfaces.clear(); mApInterfaceListeners.clear(); mSendMgmtFrameInProgress.set(false); } /** * Check if OWE (Enhanced Open) is supported on the device * * @return true if OWE is supported */ private boolean isEnhancedOpenSupported() { if (mIsEnhancedOpenSupportedInitialized) { return mIsEnhancedOpenSupported; } // WifiNative handle might be null, check this here if (mWifiNative == null) { mWifiNative = mWifiInjector.getWifiNative(); if (mWifiNative == null) { return false; } } String iface = mWifiNative.getClientInterfaceName(); if (iface == null) { // Client interface might not be initialized during boot or Wi-Fi off return false; } mIsEnhancedOpenSupportedInitialized = true; mIsEnhancedOpenSupported = (mWifiNative.getSupportedFeatureSet(iface) & WIFI_FEATURE_OWE) != 0; return mIsEnhancedOpenSupported; } }