blob: bd756f6439bd84342b1f595ee65b36278b96f98d [file] [log] [blame]
/*
* 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<String, IClientInterface> mClientInterfaces = new HashMap<>();
private HashMap<String, IApInterface> mApInterfaces = new HashMap<>();
private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>();
private HashMap<String, IScanEvent> mScanEventHandlers = new HashMap<>();
private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
private HashMap<String, IApInterfaceEventCallback> 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<String, IWifiScannerImpl> 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<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
ArrayList<ScanDetail> 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<Integer> freqs,
List<String> 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;
}
}