/*
|
* 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;
|
}
|
}
|