blob: 0c358da6649f542db4887c08f19faf5bad47775a [file] [log] [blame]
/*
* Copyright (C) 2021 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_DECORATED_IDENTITY;
import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP;
import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP_ENROLLEE_RESPONDER;
import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256;
import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384;
import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO;
import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE;
import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS;
import static android.net.wifi.WifiManager.WIFI_FEATURE_SAE_PK;
import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE;
import static android.net.wifi.WifiManager.WIFI_FEATURE_WAPI;
import static android.net.wifi.WifiManager.WIFI_FEATURE_WFD_R2;
import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.wifi.V1_6.WifiChannelWidthInMhz;
import android.hardware.wifi.supplicant.BtCoexistenceMode;
import android.hardware.wifi.supplicant.ConnectionCapabilities;
import android.hardware.wifi.supplicant.DebugLevel;
import android.hardware.wifi.supplicant.DppAkm;
import android.hardware.wifi.supplicant.DppCurve;
import android.hardware.wifi.supplicant.DppNetRole;
import android.hardware.wifi.supplicant.DppResponderBootstrapInfo;
import android.hardware.wifi.supplicant.ISupplicant;
import android.hardware.wifi.supplicant.ISupplicantStaIface;
import android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback;
import android.hardware.wifi.supplicant.ISupplicantStaNetwork;
import android.hardware.wifi.supplicant.IfaceInfo;
import android.hardware.wifi.supplicant.IfaceType;
import android.hardware.wifi.supplicant.KeyMgmtMask;
import android.hardware.wifi.supplicant.LegacyMode;
import android.hardware.wifi.supplicant.MloLinksInfo;
import android.hardware.wifi.supplicant.QosPolicyClassifierParams;
import android.hardware.wifi.supplicant.QosPolicyClassifierParamsMask;
import android.hardware.wifi.supplicant.QosPolicyData;
import android.hardware.wifi.supplicant.QosPolicyRequestType;
import android.hardware.wifi.supplicant.QosPolicyStatus;
import android.hardware.wifi.supplicant.QosPolicyStatusCode;
import android.hardware.wifi.supplicant.RxFilterType;
import android.hardware.wifi.supplicant.WifiTechnology;
import android.hardware.wifi.supplicant.WpaDriverCapabilitiesMask;
import android.hardware.wifi.supplicant.WpsConfigMethods;
import android.net.DscpPolicy;
import android.net.MacAddress;
import android.net.NetworkAgent;
import android.net.wifi.ScanResult;
import android.net.wifi.SecurityParams;
import android.net.wifi.WifiAnnotations;
import android.net.wifi.WifiConfiguration;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.wifi.util.NativeUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* HAL calls to set up/tear down the supplicant daemon and make requests
* related to station mode. Uses the AIDL supplicant interface.
* To maintain thread-safety, the locking protocol is that every non-static method (regardless of
* access level) acquires mLock.
*/
public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal {
private static final String TAG = "SupplicantStaIfaceHalAidlImpl";
@VisibleForTesting
private static final String HAL_INSTANCE_NAME = ISupplicant.DESCRIPTOR + "/default";
/**
* Regex pattern for extracting the wps device type bytes.
* Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
*/
private static final Pattern WPS_DEVICE_TYPE_PATTERN =
Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
private static final int MIN_PORT_NUM = 0;
private static final int MAX_PORT_NUM = 65535;
private final Object mLock = new Object();
private boolean mVerboseLoggingEnabled = false;
private boolean mVerboseHalLoggingEnabled = false;
private boolean mServiceDeclared = false;
// Supplicant HAL interface objects
private ISupplicant mISupplicant = null;
private Map<String, ISupplicantStaIface> mISupplicantStaIfaces = new HashMap<>();
private Map<String, ISupplicantStaIfaceCallback>
mISupplicantStaIfaceCallbacks = new HashMap<>();
private Map<String, SupplicantStaNetworkHalAidlImpl>
mCurrentNetworkRemoteHandles = new HashMap<>();
private Map<String, WifiConfiguration> mCurrentNetworkLocalConfigs = new HashMap<>();
private Map<String, List<Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration>>>
mLinkedNetworkLocalAndRemoteConfigs = new HashMap<>();
@VisibleForTesting
PmkCacheManager mPmkCacheManager;
private WifiNative.SupplicantDeathEventHandler mDeathEventHandler;
private SupplicantDeathRecipient mSupplicantDeathRecipient;
private final Context mContext;
private final WifiMonitor mWifiMonitor;
private final Handler mEventHandler;
private WifiNative.DppEventCallback mDppCallback = null;
private final Clock mClock;
private final WifiMetrics mWifiMetrics;
private final WifiGlobals mWifiGlobals;
private class SupplicantDeathRecipient implements DeathRecipient {
@Override
public void binderDied() {
mEventHandler.post(() -> {
synchronized (mLock) {
Log.w(TAG, "ISupplicant binder died.");
supplicantServiceDiedHandler();
}
});
}
}
/**
* Linked to supplicant service death on call to terminate()
*/
private class TerminateDeathRecipient implements DeathRecipient {
@Override
public void binderDied() {
mEventHandler.post(() -> {
synchronized (mLock) {
Log.w(TAG, "ISupplicant was killed by terminate()");
// nothing more to be done here
}
});
}
}
public SupplicantStaIfaceHalAidlImpl(Context context, WifiMonitor monitor, Handler handler,
Clock clock, WifiMetrics wifiMetrics, WifiGlobals wifiGlobals) {
mContext = context;
mWifiMonitor = monitor;
mEventHandler = handler;
mClock = clock;
mWifiMetrics = wifiMetrics;
mWifiGlobals = wifiGlobals;
mSupplicantDeathRecipient = new SupplicantDeathRecipient();
mPmkCacheManager = new PmkCacheManager(mClock, mEventHandler);
}
/**
* Enable/Disable verbose logging.
*
* @param verboseEnabled Verbose flag set in overlay XML.
* @param halVerboseEnabled Verbose flag set by the user.
*/
public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
synchronized (mLock) {
mVerboseLoggingEnabled = verboseEnabled;
mVerboseHalLoggingEnabled = halVerboseEnabled;
setLogLevel(mVerboseHalLoggingEnabled);
}
}
protected boolean isVerboseLoggingEnabled() {
synchronized (mLock) {
return mVerboseLoggingEnabled;
}
}
/**
* Checks whether the ISupplicant service is declared, and therefore should be available.
*
* @return true if the ISupplicant service is declared
*/
public boolean initialize() {
synchronized (mLock) {
if (mISupplicant != null) {
Log.i(TAG, "Service is already initialized, skipping initialize method");
return true;
}
if (mVerboseLoggingEnabled) {
Log.i(TAG, "Checking for ISupplicant service.");
}
mISupplicantStaIfaces.clear();
mServiceDeclared = serviceDeclared();
return mServiceDeclared;
}
}
protected int getCurrentNetworkId(@NonNull String ifaceName) {
synchronized (mLock) {
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (currentConfig == null) {
return WifiConfiguration.INVALID_NETWORK_ID;
}
return currentConfig.networkId;
}
}
/**
* Setup a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
public boolean setupIface(@NonNull String ifaceName) {
synchronized (mLock) {
if (getStaIface(ifaceName) != null) {
Log.e(TAG, "Iface " + ifaceName + " already exists.");
return false;
}
ISupplicantStaIface iface = addIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Unable to add iface " + ifaceName);
return false;
}
ISupplicantStaIfaceCallback callback = new SupplicantStaIfaceCallbackAidlImpl(
SupplicantStaIfaceHalAidlImpl.this, ifaceName,
new Object(), mContext, mWifiMonitor);
if (registerCallback(iface, callback)) {
mISupplicantStaIfaces.put(ifaceName, iface);
// Keep callback in a store to avoid recycling by garbage collector
mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
return true;
} else {
Log.e(TAG, "Unable to register callback for iface " + ifaceName);
return false;
}
}
}
/**
* Create a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return ISupplicantStaIface object on success, null otherwise.
*/
private ISupplicantStaIface addIface(@NonNull String ifaceName) {
synchronized (mLock) {
String methodStr = "addIface";
if (!checkSupplicantAndLogFailure(methodStr)) {
return null;
}
try {
return mISupplicant.addStaInterface(ifaceName);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
} catch (NoSuchElementException | IllegalArgumentException e) {
Log.e(TAG, "Encountered exception at addIface: ", e);
}
return null;
}
}
/**
* Teardown a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
public boolean teardownIface(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "teardownIface";
if (checkStaIfaceAndLogFailure(ifaceName, methodStr) == null) {
return false;
}
if (!checkSupplicantAndLogFailure(methodStr)) {
return false;
}
try {
IfaceInfo ifaceInfo = new IfaceInfo();
ifaceInfo.name = ifaceName;
ifaceInfo.type = IfaceType.STA;
mISupplicant.removeInterface(ifaceInfo);
mISupplicantStaIfaces.remove(ifaceName);
mISupplicantStaIfaceCallbacks.remove(ifaceName);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
} catch (NoSuchElementException e) {
Log.e(TAG, "Encountered exception at teardownIface: ", e);
}
return false;
}
}
/**
* Registers a death notification for supplicant.
* @return Returns true on success.
*/
public boolean registerDeathHandler(@NonNull WifiNative.SupplicantDeathEventHandler handler) {
synchronized (mLock) {
if (mDeathEventHandler != null) {
Log.e(TAG, "Death handler already present");
}
mDeathEventHandler = handler;
return true;
}
}
/**
* Deregisters a death notification for supplicant.
* @return Returns true on success.
*/
public boolean deregisterDeathHandler() {
synchronized (mLock) {
if (mDeathEventHandler == null) {
Log.e(TAG, "No Death handler present");
}
mDeathEventHandler = null;
return true;
}
}
/**
* Signals whether initialization started successfully.
*/
public boolean isInitializationStarted() {
synchronized (mLock) {
return mServiceDeclared;
}
}
/**
* Signals whether initialization completed successfully.
*/
public boolean isInitializationComplete() {
synchronized (mLock) {
return mISupplicant != null;
}
}
/**
* Indicates whether the AIDL service is declared
*/
public static boolean serviceDeclared() {
// Service Manager API ServiceManager#isDeclared supported after T.
if (!SdkLevel.isAtLeastT()) {
return false;
}
return ServiceManager.isDeclared(HAL_INSTANCE_NAME);
}
private void clearState() {
synchronized (mLock) {
mISupplicant = null;
mISupplicantStaIfaces.clear();
mCurrentNetworkLocalConfigs.clear();
mCurrentNetworkRemoteHandles.clear();
mLinkedNetworkLocalAndRemoteConfigs.clear();
}
}
private void supplicantServiceDiedHandler() {
synchronized (mLock) {
clearState();
if (mDeathEventHandler != null) {
mDeathEventHandler.onDeath();
}
}
}
/**
* Start the supplicant daemon.
*
* @return true on success, false otherwise.
*/
public boolean startDaemon() {
final String methodStr = "startDaemon";
if (mISupplicant != null) {
Log.i(TAG, "Service is already initialized, skipping " + methodStr);
return true;
}
mISupplicant = getSupplicantMockable();
if (mISupplicant == null) {
Log.e(TAG, "Unable to obtain ISupplicant binder.");
return false;
}
Log.i(TAG, "Obtained ISupplicant binder.");
try {
IBinder serviceBinder = getServiceBinderMockable();
if (serviceBinder == null) {
return false;
}
serviceBinder.linkToDeath(mSupplicantDeathRecipient, /* flags= */ 0);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
/**
* Terminate the supplicant daemon & wait for its death.
*/
public void terminate() {
synchronized (mLock) {
final String methodStr = "terminate";
if (!checkSupplicantAndLogFailure(methodStr)) {
return;
}
try {
// Register a new death listener to confirm that terminate() killed supplicant
IBinder serviceBinder = getServiceBinderMockable();
if (serviceBinder == null) {
return;
}
serviceBinder.linkToDeath(new TerminateDeathRecipient(), /* flags= */ 0);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register death recipient.");
handleRemoteException(e, methodStr);
return;
}
try {
mISupplicant.terminate();
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
}
}
/**
* Wrapper functions to access HAL objects, created to be mockable in unit tests
*/
@VisibleForTesting
protected ISupplicant getSupplicantMockable() {
synchronized (mLock) {
try {
return ISupplicant.Stub.asInterface(
ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME));
} catch (Exception e) {
Log.e(TAG, "Unable to get ISupplicant service, " + e);
return null;
}
}
}
@VisibleForTesting
protected IBinder getServiceBinderMockable() {
synchronized (mLock) {
if (mISupplicant == null) {
return null;
}
return mISupplicant.asBinder();
}
}
/**
* Helper method to look up the specified iface.
*/
private ISupplicantStaIface getStaIface(@NonNull String ifaceName) {
synchronized (mLock) {
return mISupplicantStaIfaces.get(ifaceName);
}
}
/**
* Helper method to look up the network object for the specified iface.
*/
private SupplicantStaNetworkHalAidlImpl getCurrentNetworkRemoteHandle(
@NonNull String ifaceName) {
synchronized (mLock) {
return mCurrentNetworkRemoteHandles.get(ifaceName);
}
}
/**
* Helper method to look up the network config for the specified iface.
*/
protected WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) {
synchronized (mLock) {
return mCurrentNetworkLocalConfigs.get(ifaceName);
}
}
/**
* Add a network configuration to wpa_supplicant.
*
* @param config Config corresponding to the network.
* @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
* for the current network.
*/
private Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration>
addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
synchronized (mLock) {
if (config == null) {
Log.e(TAG, "Cannot add null network.");
return null;
}
SupplicantStaNetworkHalAidlImpl network = addNetwork(ifaceName);
if (network == null) {
Log.e(TAG, "Failed to add network.");
return null;
}
boolean saveSuccess = false;
try {
saveSuccess = network.saveWifiConfiguration(config);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Exception while saving config params: " + config, e);
}
if (!saveSuccess) {
Log.e(TAG, "Failed to save variables for: " + config.getProfileKey());
if (!removeAllNetworks(ifaceName)) {
Log.e(TAG, "Failed to remove all networks on failure.");
}
return null;
}
return new Pair(network, new WifiConfiguration(config));
}
}
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. If |config| is different to the current supplicant network, removes all supplicant
* networks and saves |config|.
* 2. Select the new network in wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param config WifiConfiguration parameters for the provided network.
* @return true if it succeeds, false otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
Log.d(TAG, "connectToNetwork " + config.getProfileKey());
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
String networkSelectionBSSIDCurrent = currentConfig.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
Log.d(TAG, "Network is already saved, will not trigger remove and add.");
} else {
Log.d(TAG, "Network is already saved, but need to update BSSID.");
if (!setCurrentNetworkBssid(
ifaceName,
config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
Log.e(TAG, "Failed to set current network BSSID.");
return false;
}
mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
}
} else {
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
mLinkedNetworkLocalAndRemoteConfigs.remove(ifaceName);
if (!removeAllNetworks(ifaceName)) {
Log.e(TAG, "Failed to remove existing networks");
return false;
}
Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, config);
if (pair == null) {
Log.e(TAG, "Failed to add/save network configuration: " + config
.getProfileKey());
return false;
}
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
}
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
if (networkHandle == null) {
Log.e(TAG, "No valid remote network handle for network configuration: "
+ config.getProfileKey());
return false;
}
SecurityParams params = config.getNetworkSelectionStatus()
.getCandidateSecurityParams();
if (params != null && !(params.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)
|| params.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP))) {
List<ArrayList<Byte>> pmkDataList = mPmkCacheManager.get(config.networkId);
if (pmkDataList != null) {
Log.i(TAG, "Set PMK cache for config id " + config.networkId);
pmkDataList.forEach(pmkData -> {
if (networkHandle.setPmkCache(NativeUtil.byteArrayFromArrayList(pmkData))) {
mWifiMetrics.setConnectionPmkCache(ifaceName, true);
}
});
}
}
if (!networkHandle.select()) {
Log.e(TAG, "Failed to select network configuration: " + config.getProfileKey());
return false;
}
return true;
}
}
/**
* 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. First check if we're attempting to connect to a linked network, and select the existing
* supplicant network if there is one.
* 2. Set the new bssid for the network in wpa_supplicant.
* 3. Trigger reassociate command to wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param config WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean roamToNetwork(@NonNull String ifaceName, WifiConfiguration config) {
synchronized (mLock) {
if (updateOnLinkedNetworkRoaming(ifaceName, config.networkId, true)) {
SupplicantStaNetworkHalAidlImpl networkHandle =
getCurrentNetworkRemoteHandle(ifaceName);
if (networkHandle == null) {
Log.e(TAG, "Roaming config matches a linked config, "
+ "but a linked network handle was not found.");
return false;
}
return networkHandle.select();
}
if (getCurrentNetworkId(ifaceName) != config.networkId) {
Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
+ "Current network ID: " + getCurrentNetworkId(ifaceName));
return connectToNetwork(ifaceName, config);
}
String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
Log.d(TAG, "roamToNetwork" + config.getProfileKey() + " (bssid " + bssid + ")");
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(ifaceName, "roamToNetwork");
if (networkHandle == null || !networkHandle.setBssid(bssid)) {
Log.e(TAG, "Failed to set new bssid on network: " + config.getProfileKey());
return false;
}
if (!reassociate(ifaceName)) {
Log.e(TAG, "Failed to trigger reassociate");
return false;
}
return true;
}
}
/**
* Clean HAL cached data for |networkId| in the framework.
*
* @param networkId network id of the network to be removed from supplicant.
*/
public void removeNetworkCachedData(int networkId) {
synchronized (mLock) {
Log.d(TAG, "Remove cached HAL data for config id " + networkId);
removePmkCacheEntry(networkId);
}
}
/**
* Clear HAL cached data 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) {
synchronized (mLock) {
mPmkCacheManager.remove(networkId, curMacAddress);
}
}
/**
* Remove all networks from supplicant
*
* @param ifaceName Name of the interface.
*/
public boolean removeAllNetworks(@NonNull String ifaceName) {
synchronized (mLock) {
int[] networks = listNetworks(ifaceName);
if (networks == null) {
Log.e(TAG, "removeAllNetworks failed, got null networks");
return false;
}
for (int id : networks) {
if (!removeNetwork(ifaceName, id)) {
Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
return false;
}
}
// Reset current network info.
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
mLinkedNetworkLocalAndRemoteConfigs.remove(ifaceName);
return true;
}
}
/**
* Disable the current network in supplicant
*
* @param ifaceName Name of the interface.
*/
public boolean disableCurrentNetwork(@NonNull String ifaceName) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(ifaceName, "disableCurrentNetwork");
if (networkHandle == null) {
return false;
}
return networkHandle.disable();
}
}
/**
* Set the currently configured network's bssid.
*
* @param ifaceName Name of the interface.
* @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
* @return true if succeeds, false otherwise.
*/
public boolean setCurrentNetworkBssid(@NonNull String ifaceName, String bssidStr) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(ifaceName, "setCurrentNetworkBssid");
if (networkHandle == null) {
return false;
}
return networkHandle.setBssid(bssidStr);
}
}
/**
* 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) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "getCurrentNetworkWpsNfcConfigurationToken");
if (networkHandle == null) {
return null;
}
return networkHandle.getWpsNfcConfigurationToken();
}
}
/**
* Get the eap anonymous identity for the currently configured network.
*
* @param ifaceName Name of the interface.
* @return anonymous identity string if succeeds, null otherwise.
*/
public String getCurrentNetworkEapAnonymousIdentity(@NonNull String ifaceName) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "getCurrentNetworkEapAnonymousIdentity");
if (networkHandle == null) {
return null;
}
return networkHandle.fetchEapAnonymousIdentity();
}
}
/**
* Send the eap identity response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param identity identity used for EAP-Identity
* @param encryptedIdentity encrypted identity used for EAP-AKA/EAP-SIM
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapIdentityResponse(
@NonNull String ifaceName, @NonNull String identity, String encryptedIdentity) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapIdentityResponse");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapIdentityResponse(identity, encryptedIdentity);
}
}
/**
* Send the eap sim gsm auth response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param paramsStr String to send.
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimGsmAuthResponse(
@NonNull String ifaceName, String paramsStr) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimGsmAuthResponse");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
}
}
/**
* 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 sendCurrentNetworkEapSimGsmAuthFailure(@NonNull String ifaceName) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimGsmAuthFailure");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapSimGsmAuthFailure();
}
}
/**
* Send the eap sim umts auth response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param paramsStr String to send.
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimUmtsAuthResponse(
@NonNull String ifaceName, String paramsStr) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimUmtsAuthResponse");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
}
}
/**
* Send the eap sim umts auts response for the currently configured network.
*
* @param ifaceName Name of the interface.
* @param paramsStr String to send.
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimUmtsAutsResponse(
@NonNull String ifaceName, String paramsStr) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimUmtsAutsResponse");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
}
}
/**
* 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 sendCurrentNetworkEapSimUmtsAuthFailure(@NonNull String ifaceName) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimUmtsAuthFailure");
if (networkHandle == null) {
return false;
}
return networkHandle.sendNetworkEapSimUmtsAuthFailure();
}
}
/**
* Adds a new network.
*
* @return SupplicantStaNetworkHalAidlImpl object for the new network, or null if the call fails
*/
private SupplicantStaNetworkHalAidlImpl addNetwork(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "addNetwork";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return null;
}
try {
ISupplicantStaNetwork network = iface.addNetwork();
// Get framework wrapper around the AIDL network object
return getStaNetworkHalMockable(ifaceName, network);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return null;
}
}
/**
* Remove network with specified network Id from supplicant.
*
* @return true if request is sent successfully, false otherwise.
*/
private boolean removeNetwork(@NonNull String ifaceName, int id) {
synchronized (mLock) {
final String methodStr = "removeNetwork";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.removeNetwork(id);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Creates a SupplicantStaNetworkHal wrapper around an AIDL ISupplicantStaNetwork object.
* Declared mockable for use in unit tests.
*
* @param ifaceName Name of the interface.
* @param network ISupplicantStaNetwork instance retrieved from AIDL.
* @return SupplicantStaNetworkHal object for the given network, or null if
* the call fails
*/
protected SupplicantStaNetworkHalAidlImpl getStaNetworkHalMockable(
@NonNull String ifaceName, ISupplicantStaNetwork network) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkWrapper =
new SupplicantStaNetworkHalAidlImpl(network, ifaceName, mContext,
mWifiMonitor, mWifiGlobals, getAdvancedCapabilities(ifaceName));
if (networkWrapper != null) {
networkWrapper.enableVerboseLogging(
mVerboseLoggingEnabled, mVerboseHalLoggingEnabled);
}
return networkWrapper;
}
}
private boolean registerCallback(
ISupplicantStaIface iface, ISupplicantStaIfaceCallback callback) {
synchronized (mLock) {
String methodStr = "registerCallback";
if (iface == null) {
return false;
}
try {
iface.registerCallback(callback);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Get list of id's of all networks controlled by supplicant.
*
* @return list of network id's, null if failed
*/
private int[] listNetworks(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "listNetworks";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return null;
}
try {
return iface.listNetworks();
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return null;
}
}
/**
* 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 setWpsDeviceName(@NonNull String ifaceName, String name) {
synchronized (mLock) {
final String methodStr = "setWpsDeviceName";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsDeviceName(name);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS device type.
*
* @param ifaceName Name of the interface.
* @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsDeviceType(@NonNull String ifaceName, String typeStr) {
synchronized (mLock) {
try {
Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
if (!match.find() || match.groupCount() != 3) {
Log.e(TAG, "Malformed WPS device type " + typeStr);
return false;
}
short categ = Short.parseShort(match.group(1));
byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
short subCateg = Short.parseShort(match.group(3));
byte[] bytes = new byte[8];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
byteBuffer.putShort(categ);
byteBuffer.put(oui);
byteBuffer.putShort(subCateg);
return setWpsDeviceType(ifaceName, bytes);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + typeStr, e);
return false;
}
}
}
private boolean setWpsDeviceType(@NonNull String ifaceName, byte[/* 8 */] type) {
synchronized (mLock) {
final String methodStr = "setWpsDeviceType";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsDeviceType(type);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS manufacturer.
*
* @param ifaceName Name of the interface.
* @param manufacturer String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsManufacturer(@NonNull String ifaceName, String manufacturer) {
synchronized (mLock) {
final String methodStr = "setWpsManufacturer";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsManufacturer(manufacturer);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS model name.
*
* @param ifaceName Name of the interface.
* @param modelName String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsModelName(@NonNull String ifaceName, String modelName) {
synchronized (mLock) {
final String methodStr = "setWpsModelName";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsModelName(modelName);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS model number.
*
* @param ifaceName Name of the interface.
* @param modelNumber String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsModelNumber(@NonNull String ifaceName, String modelNumber) {
synchronized (mLock) {
final String methodStr = "setWpsModelNumber";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsModelNumber(modelNumber);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS serial number.
*
* @param ifaceName Name of the interface.
* @param serialNumber String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsSerialNumber(@NonNull String ifaceName, String serialNumber) {
synchronized (mLock) {
final String methodStr = "setWpsSerialNumber";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsSerialNumber(serialNumber);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set WPS config methods
*
* @param ifaceName Name of the interface.
* @param configMethodsStr List of config methods.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsConfigMethods(@NonNull String ifaceName, String configMethodsStr) {
synchronized (mLock) {
int configMethodsMask = 0;
String[] configMethodsStrArr = configMethodsStr.split("\\s+");
for (int i = 0; i < configMethodsStrArr.length; i++) {
configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
}
return setWpsConfigMethods(ifaceName, configMethodsMask);
}
}
private boolean setWpsConfigMethods(@NonNull String ifaceName, int configMethods) {
synchronized (mLock) {
final String methodStr = "setWpsConfigMethods";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setWpsConfigMethods(configMethods);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* 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) {
synchronized (mLock) {
final String methodStr = "reassociate";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.reassociate();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* 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) {
synchronized (mLock) {
final String methodStr = "reconnect";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.reconnect();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* 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) {
synchronized (mLock) {
final String methodStr = "disconnect";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.disconnect();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Enable or disable power save mode.
*
* @param ifaceName Name of the interface.
* @param enable true to enable, false to disable.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setPowerSave(@NonNull String ifaceName, boolean enable) {
synchronized (mLock) {
final String methodStr = "setPowerSave";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setPowerSave(enable);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Initiate TDLS discover with the specified AP.
*
* @param ifaceName Name of the interface.
* @param macAddress MAC Address of the AP.
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsDiscover(@NonNull String ifaceName, String macAddress) {
synchronized (mLock) {
try {
return initiateTdlsDiscover(
ifaceName, NativeUtil.macAddressToByteArray(macAddress));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + macAddress, e);
return false;
}
}
}
private boolean initiateTdlsDiscover(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsDiscover";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateTdlsDiscover(macAddress);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Initiate TDLS setup with the specified AP.
*
* @param ifaceName Name of the interface.
* @param macAddress MAC Address of the AP.
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsSetup(@NonNull String ifaceName, String macAddress) {
synchronized (mLock) {
try {
return initiateTdlsSetup(ifaceName, NativeUtil.macAddressToByteArray(macAddress));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + macAddress, e);
return false;
}
}
}
private boolean initiateTdlsSetup(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsSetup";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateTdlsSetup(macAddress);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Initiate TDLS teardown with the specified AP.
*
* @param ifaceName Name of the interface.
* @param macAddress MAC Address of the AP.
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsTeardown(@NonNull String ifaceName, String macAddress) {
synchronized (mLock) {
try {
return initiateTdlsTeardown(
ifaceName, NativeUtil.macAddressToByteArray(macAddress));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + macAddress, e);
return false;
}
}
}
private boolean initiateTdlsTeardown(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsTeardown";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateTdlsTeardown(macAddress);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Request the specified ANQP elements |elements| from the specified AP |bssid|.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the AP
* @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
* @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateAnqpQuery(@NonNull String ifaceName, String bssid,
ArrayList<Short> infoElements,
ArrayList<Integer> hs20SubTypes) {
synchronized (mLock) {
try {
int[] infoElementsCast = new int[infoElements.size()];
int[] hs20SubTypesCast = new int[hs20SubTypes.size()];
for (int i = 0; i < infoElements.size(); i++) {
infoElementsCast[i] = infoElements.get(i);
}
for (int i = 0; i < hs20SubTypes.size(); i++) {
hs20SubTypesCast[i] = hs20SubTypes.get(i);
}
return initiateAnqpQuery(
ifaceName,
NativeUtil.macAddressToByteArray(bssid),
infoElementsCast, hs20SubTypesCast);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssid, e);
return false;
}
}
}
private boolean initiateAnqpQuery(@NonNull String ifaceName, byte[/* 6 */] macAddress,
int[] infoElements, int[] subTypes) {
synchronized (mLock) {
final String methodStr = "initiateAnqpQuery";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateAnqpQuery(macAddress, infoElements, subTypes);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Request Venue URL ANQP element from the specified AP |bssid|.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the AP
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateVenueUrlAnqpQuery(@NonNull String ifaceName, String bssid) {
synchronized (mLock) {
try {
return initiateVenueUrlAnqpQuery(
ifaceName, NativeUtil.macAddressToByteArray(bssid));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssid, e);
return false;
}
}
}
private boolean initiateVenueUrlAnqpQuery(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateVenueUrlAnqpQuery";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateVenueUrlAnqpQuery(macAddress);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Request the specified ANQP ICON from the specified AP |bssid|.
*
* @param ifaceName Name of the interface.
* @param bssid BSSID of the AP
* @param fileName Name of the file to request.
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateHs20IconQuery(@NonNull String ifaceName, String bssid, String fileName) {
synchronized (mLock) {
try {
return initiateHs20IconQuery(
ifaceName, NativeUtil.macAddressToByteArray(bssid), fileName);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssid, e);
return false;
}
}
}
private boolean initiateHs20IconQuery(@NonNull String ifaceName,
byte[/* 6 */] macAddress, String fileName) {
synchronized (mLock) {
final String methodStr = "initiateHs20IconQuery";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.initiateHs20IconQuery(macAddress, fileName);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Gets MAC Address from the 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) {
synchronized (mLock) {
final String methodStr = "getMacAddress";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return null;
}
try {
byte[] macAddr = iface.getMacAddress();
return NativeUtil.macAddressFromByteArray(macAddr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid MAC address value", e);
}
return null;
}
}
/**
* Start using the added RX filters.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean startRxFilter(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "startRxFilter";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startRxFilter();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Stop using the added RX filters.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean stopRxFilter(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "stopRxFilter";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.stopRxFilter();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Add an RX filter.
*
* @param ifaceName Name of the interface.
* @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
* {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
* @return true if request is sent successfully, false otherwise.
*/
public boolean addRxFilter(@NonNull String ifaceName, int type) {
synchronized (mLock) {
byte halType;
switch (type) {
case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
halType = RxFilterType.V4_MULTICAST;
break;
case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
halType = RxFilterType.V6_MULTICAST;
break;
default:
Log.e(TAG, "Invalid Rx Filter type: " + type);
return false;
}
return addRxFilter(ifaceName, halType);
}
}
private boolean addRxFilter(@NonNull String ifaceName, byte type) {
synchronized (mLock) {
final String methodStr = "addRxFilter";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.addRxFilter(type);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Remove an RX filter.
*
* @param ifaceName Name of the interface.
* @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
* {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
* @return true if request is sent successfully, false otherwise.
*/
public boolean removeRxFilter(@NonNull String ifaceName, int type) {
synchronized (mLock) {
byte halType;
switch (type) {
case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
halType = RxFilterType.V4_MULTICAST;
break;
case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
halType = RxFilterType.V6_MULTICAST;
break;
default:
Log.e(TAG, "Invalid Rx Filter type: " + type);
return false;
}
return removeRxFilter(ifaceName, halType);
}
}
private boolean removeRxFilter(@NonNull String ifaceName, byte type) {
synchronized (mLock) {
final String methodStr = "removeRxFilter";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.removeRxFilter(type);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set Bt co existense mode.
*
* @param ifaceName Name of the interface.
* @param mode one of the above {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_DISABLED},
* {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_ENABLED} or
* {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_SENSE}.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setBtCoexistenceMode(@NonNull String ifaceName, int mode) {
synchronized (mLock) {
byte halMode;
switch (mode) {
case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
halMode = BtCoexistenceMode.ENABLED;
break;
case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
halMode = BtCoexistenceMode.DISABLED;
break;
case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
halMode = BtCoexistenceMode.SENSE;
break;
default:
Log.e(TAG, "Invalid Bt Coex mode: " + mode);
return false;
}
return setBtCoexistenceMode(ifaceName, halMode);
}
}
private boolean setBtCoexistenceMode(@NonNull String ifaceName, byte mode) {
synchronized (mLock) {
final String methodStr = "setBtCoexistenceMode";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setBtCoexistenceMode(mode);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/** Enable or disable BT coexistence mode.
*
* @param ifaceName Name of the interface.
* @param enable true to enable, false to disable.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setBtCoexistenceScanModeEnabled(@NonNull String ifaceName, boolean enable) {
synchronized (mLock) {
final String methodStr = "setBtCoexistenceScanModeEnabled";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setBtCoexistenceScanModeEnabled(enable);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Enable or disable suspend mode optimizations.
*
* @param ifaceName Name of the interface.
* @param enable true to enable, false otherwise.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setSuspendModeEnabled(@NonNull String ifaceName, boolean enable) {
synchronized (mLock) {
final String methodStr = "setSuspendModeEnabled";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setSuspendModeEnabled(enable);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set country code.
*
* @param ifaceName Name of the interface.
* @param codeStr 2 byte ASCII string. For ex: US, CA.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setCountryCode(@NonNull String ifaceName, String codeStr) {
synchronized (mLock) {
if (TextUtils.isEmpty(codeStr)) {
return false;
}
byte[] countryCodeBytes = NativeUtil.stringToByteArray(codeStr);
if (countryCodeBytes.length != 2) {
return false;
}
return setCountryCode(ifaceName, countryCodeBytes);
}
}
private boolean setCountryCode(@NonNull String ifaceName, byte[/* 2 */] code) {
synchronized (mLock) {
final String methodStr = "setCountryCode";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setCountryCode(code);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Flush all previously configured HLPs.
*
* @param ifaceName Name of the interface.
* @return true if request is sent successfully, false otherwise.
*/
public boolean flushAllHlp(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "filsHlpFlushRequest";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.filsHlpFlushRequest();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set FILS HLP packet.
*
* @param ifaceName Name of the interface.
* @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, byte[] dst, byte[] hlpPacket) {
synchronized (mLock) {
final String methodStr = "filsHlpAddRequest";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.filsHlpAddRequest(dst, hlpPacket);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Start WPS pin registrar operation with the specified peer and pin.
*
* @param ifaceName Name of the interface.
* @param bssidStr 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 bssidStr, String pin) {
synchronized (mLock) {
if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) {
return false;
}
try {
return startWpsRegistrar(
ifaceName, NativeUtil.macAddressToByteArray(bssidStr), pin);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssidStr, e);
return false;
}
}
}
private boolean startWpsRegistrar(@NonNull String ifaceName, byte[/* 6 */] bssid, String pin) {
synchronized (mLock) {
final String methodStr = "startWpsRegistrar";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startWpsRegistrar(bssid, pin);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Start WPS pin display operation with the specified peer.
*
* @param ifaceName Name of the interface.
* @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsPbc(@NonNull String ifaceName, String bssidStr) {
synchronized (mLock) {
try {
return startWpsPbc(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssidStr, e);
return false;
}
}
}
private boolean startWpsPbc(@NonNull String ifaceName, byte[/* 6 */] bssid) {
synchronized (mLock) {
final String methodStr = "startWpsPbc";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startWpsPbc(bssid);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* 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) {
if (TextUtils.isEmpty(pin)) {
return false;
}
synchronized (mLock) {
final String methodStr = "startWpsPinKeypad";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startWpsPinKeypad(pin);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Start WPS pin display operation with the specified peer.
*
* @param ifaceName Name of the interface.
* @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
* @return new pin generated on success, null otherwise.
*/
public String startWpsPinDisplay(@NonNull String ifaceName, String bssidStr) {
synchronized (mLock) {
try {
return startWpsPinDisplay(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssidStr, e);
return null;
}
}
}
private String startWpsPinDisplay(@NonNull String ifaceName, byte[/* 6 */] bssid) {
synchronized (mLock) {
final String methodStr = "startWpsPinDisplay";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return null;
}
try {
return iface.startWpsPinDisplay(bssid);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return null;
}
}
/**
* 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) {
synchronized (mLock) {
final String methodStr = "cancelWps";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.cancelWps();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Sets whether to use external sim for SIM/USIM processing.
*
* @param ifaceName Name of the interface.
* @param useExternalSim true to enable, false otherwise.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setExternalSim(@NonNull String ifaceName, boolean useExternalSim) {
synchronized (mLock) {
final String methodStr = "setExternalSim";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setExternalSim(useExternalSim);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Enable/Disable auto reconnect to networks.
* Use this to prevent wpa_supplicant from trying to connect to networks
* on its own.
*
* @param enable true to enable, false to disable.
* @return true if no exceptions occurred, false otherwise
*/
public boolean enableAutoReconnect(@NonNull String ifaceName, boolean enable) {
synchronized (mLock) {
final String methodStr = "enableAutoReconnect";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.enableAutoReconnect(enable);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set the debug log level for wpa_supplicant
*
* @param turnOnVerbose Whether to turn on verbose logging or not.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setLogLevel(boolean turnOnVerbose) {
synchronized (mLock) {
int logLevel = turnOnVerbose
? DebugLevel.DEBUG
: DebugLevel.INFO;
return setDebugParams(logLevel, false,
turnOnVerbose && mWifiGlobals.getShowKeyVerboseLoggingModeEnabled());
}
}
/**
* Set debug parameters for the ISupplicant service.
*
* @param level Debug logging level for the supplicant.
* (one of |DebugLevel| values).
* @param showTimestamp Determines whether to show timestamps in logs or not.
* @param showKeys Determines whether to show keys in debug logs or not.
* CAUTION: Do not set this param in production code!
* @return true if no exceptions occurred, false otherwise
*/
private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
synchronized (mLock) {
final String methodStr = "setDebugParams";
if (!checkSupplicantAndLogFailure(methodStr)) {
return false;
}
try {
mISupplicant.setDebugParams(level, showTimestamp, showKeys);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* 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) {
synchronized (mLock) {
if (isStaHigherPriority) {
return setConcurrencyPriority(IfaceType.STA);
} else {
return setConcurrencyPriority(IfaceType.P2P);
}
}
}
private boolean setConcurrencyPriority(int type) {
synchronized (mLock) {
final String methodStr = "setConcurrencyPriority";
if (!checkSupplicantAndLogFailure(methodStr)) {
return false;
}
try {
mISupplicant.setConcurrencyPriority(type);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Returns false if mISupplicant is null and logs failure message
*/
private boolean checkSupplicantAndLogFailure(final String methodStr) {
synchronized (mLock) {
if (mISupplicant == null) {
Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
return false;
}
return true;
}
}
/**
* Returns specified STA iface if it exists. Otherwise, logs error and returns null.
*/
private ISupplicantStaIface checkStaIfaceAndLogFailure(
@NonNull String ifaceName, final String methodStr) {
synchronized (mLock) {
ISupplicantStaIface iface = getStaIface(ifaceName);
if (iface == null) {
Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null for "
+ "iface=" + ifaceName);
return null;
}
return iface;
}
}
/**
* Returns network belonging to the specified STA iface if it exists.
* Otherwise, logs error and returns null.
*/
private SupplicantStaNetworkHalAidlImpl checkStaNetworkAndLogFailure(
@NonNull String ifaceName, final String methodStr) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
getCurrentNetworkRemoteHandle(ifaceName);
if (networkHandle == null) {
Log.e(TAG, "Can't call " + methodStr + ", SupplicantStaNetwork for iface="
+ ifaceName + " is null.");
return null;
}
return networkHandle;
}
}
/**
* Helper function to log callback events
*/
protected void logCallback(final String methodStr) {
synchronized (mLock) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
}
}
}
private void handleRemoteException(RemoteException e, String methodStr) {
synchronized (mLock) {
clearState();
Log.e(TAG,
"ISupplicantStaIface." + methodStr + " failed with remote exception: ", e);
}
}
private void handleServiceSpecificException(ServiceSpecificException e, String methodStr) {
synchronized (mLock) {
Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with "
+ "service specific exception: ", e);
}
}
/**
* Converts the Wps config method string to the equivalent enum value.
*/
private static int stringToWpsConfigMethod(String configMethod) {
switch (configMethod) {
case "usba":
return WpsConfigMethods.USBA;
case "ethernet":
return WpsConfigMethods.ETHERNET;
case "label":
return WpsConfigMethods.LABEL;
case "display":
return WpsConfigMethods.DISPLAY;
case "int_nfc_token":
return WpsConfigMethods.INT_NFC_TOKEN;
case "ext_nfc_token":
return WpsConfigMethods.EXT_NFC_TOKEN;
case "nfc_interface":
return WpsConfigMethods.NFC_INTERFACE;
case "push_button":
return WpsConfigMethods.PUSHBUTTON;
case "keypad":
return WpsConfigMethods.KEYPAD;
case "virtual_push_button":
return WpsConfigMethods.VIRT_PUSHBUTTON;
case "physical_push_button":
return WpsConfigMethods.PHY_PUSHBUTTON;
case "p2ps":
return WpsConfigMethods.P2PS;
case "virtual_display":
return WpsConfigMethods.VIRT_DISPLAY;
case "physical_display":
return WpsConfigMethods.PHY_DISPLAY;
default:
throw new IllegalArgumentException(
"Invalid WPS config method: " + configMethod);
}
}
protected void addPmkCacheEntry(
String ifaceName, int networkId,
long expirationTimeInSec, ArrayList<Byte> serializedEntry) {
synchronized (mLock) {
String macAddressStr = getMacAddress(ifaceName);
try {
if (!mPmkCacheManager.add(MacAddress.fromString(macAddressStr),
networkId, expirationTimeInSec, serializedEntry)) {
Log.w(TAG, "Cannot add PMK cache for " + ifaceName);
}
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Cannot add PMK cache: " + ex);
}
}
}
protected void removePmkCacheEntry(int networkId) {
synchronized (mLock) {
mPmkCacheManager.remove(networkId);
}
}
/**
* Returns a bitmask of advanced capabilities: WPA3 SAE/SUITE B and OWE
* Bitmask used is:
* - WIFI_FEATURE_WPA3_SAE
* - WIFI_FEATURE_WPA3_SUITE_B
* - WIFI_FEATURE_OWE
*
* @return true if successful, false otherwise.
*/
public long getAdvancedCapabilities(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getAdvancedCapabilities";
long advancedCapabilities = 0;
int keyMgmtCapabilities = getKeyMgmtCapabilities(ifaceName);
advancedCapabilities |= WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS
| WIFI_FEATURE_DECORATED_IDENTITY;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": Passpoint T&C supported");
Log.v(TAG, methodStr + ": RFC 7542 decorated identity supported");
}
if ((keyMgmtCapabilities & KeyMgmtMask.SAE) != 0) {
advancedCapabilities |= WIFI_FEATURE_WPA3_SAE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": SAE supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.SUITE_B_192) != 0) {
advancedCapabilities |= WIFI_FEATURE_WPA3_SUITE_B;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": SUITE_B supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.OWE) != 0) {
advancedCapabilities |= WIFI_FEATURE_OWE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": OWE supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.DPP) != 0) {
advancedCapabilities |= WIFI_FEATURE_DPP
| WIFI_FEATURE_DPP_ENROLLEE_RESPONDER;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": DPP supported");
Log.v(TAG, methodStr + ": DPP ENROLLEE RESPONDER supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.WAPI_PSK) != 0) {
advancedCapabilities |= WIFI_FEATURE_WAPI;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": WAPI supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.FILS_SHA256) != 0) {
advancedCapabilities |= WIFI_FEATURE_FILS_SHA256;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": FILS_SHA256 supported");
}
}
if ((keyMgmtCapabilities & KeyMgmtMask.FILS_SHA384) != 0) {
advancedCapabilities |= WIFI_FEATURE_FILS_SHA384;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": FILS_SHA384 supported");
}
}
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": Capability flags = " + keyMgmtCapabilities);
}
return advancedCapabilities;
}
}
private int getKeyMgmtCapabilities(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getKeyMgmtCapabilities";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return 0;
}
try {
return iface.getKeyMgmtCapabilities();
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return 0;
}
}
/**
* Get the driver supported features through supplicant.
*
* @param ifaceName Name of the interface.
* @return bitmask defined by WifiManager.WIFI_FEATURE_*.
*/
public long getWpaDriverFeatureSet(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getWpaDriverFeatureSet";
int drvCapabilitiesMask = getWpaDriverCapabilities(ifaceName);
long featureSet = 0;
if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.MBO) != 0) {
featureSet |= WIFI_FEATURE_MBO;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": MBO supported");
}
if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.OCE) != 0) {
featureSet |= WIFI_FEATURE_OCE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": OCE supported");
}
}
}
if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.SAE_PK) != 0) {
featureSet |= WIFI_FEATURE_SAE_PK;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": SAE-PK supported");
}
}
if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.WFD_R2) != 0) {
featureSet |= WIFI_FEATURE_WFD_R2;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": WFD-R2 supported");
}
}
if ((drvCapabilitiesMask
& WpaDriverCapabilitiesMask.TRUST_ON_FIRST_USE) != 0) {
featureSet |= WIFI_FEATURE_TRUST_ON_FIRST_USE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": Trust-On-First-Use supported");
}
}
return featureSet;
}
}
private int getWpaDriverCapabilities(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getWpaDriverCapabilities";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return 0;
}
try {
return iface.getWpaDriverCapabilities();
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return 0;
}
}
private @WifiAnnotations.WifiStandard int getWifiStandard(int technology) {
switch(technology) {
case WifiTechnology.EHT:
return ScanResult.WIFI_STANDARD_11BE;
case WifiTechnology.HE:
return ScanResult.WIFI_STANDARD_11AX;
case WifiTechnology.VHT:
return ScanResult.WIFI_STANDARD_11AC;
case WifiTechnology.HT:
return ScanResult.WIFI_STANDARD_11N;
case WifiTechnology.LEGACY:
return ScanResult.WIFI_STANDARD_LEGACY;
default:
return ScanResult.WIFI_STANDARD_UNKNOWN;
}
}
private int getChannelBandwidth(int channelBandwidth) {
switch(channelBandwidth) {
case WifiChannelWidthInMhz.WIDTH_20:
return ScanResult.CHANNEL_WIDTH_20MHZ;
case WifiChannelWidthInMhz.WIDTH_40:
return ScanResult.CHANNEL_WIDTH_40MHZ;
case WifiChannelWidthInMhz.WIDTH_80:
return ScanResult.CHANNEL_WIDTH_80MHZ;
case WifiChannelWidthInMhz.WIDTH_160:
return ScanResult.CHANNEL_WIDTH_160MHZ;
case WifiChannelWidthInMhz.WIDTH_80P80:
return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
case WifiChannelWidthInMhz.WIDTH_320:
return ScanResult.CHANNEL_WIDTH_320MHZ;
default:
return ScanResult.CHANNEL_WIDTH_20MHZ;
}
}
private int frameworkToAidlDppAkm(int dppAkm) {
switch(dppAkm) {
case SupplicantStaIfaceHal.DppAkm.PSK:
return DppAkm.PSK;
case SupplicantStaIfaceHal.DppAkm.PSK_SAE:
return DppAkm.PSK_SAE;
case SupplicantStaIfaceHal.DppAkm.SAE:
return DppAkm.SAE;
case SupplicantStaIfaceHal.DppAkm.DPP:
return DppAkm.DPP;
default:
Log.e(TAG, "Invalid DppAkm received");
return -1;
}
}
private int frameworkToAidlDppCurve(int dppCurve) {
switch(dppCurve) {
case SupplicantStaIfaceHal.DppCurve.PRIME256V1:
return DppCurve.PRIME256V1;
case SupplicantStaIfaceHal.DppCurve.SECP384R1:
return DppCurve.SECP384R1;
case SupplicantStaIfaceHal.DppCurve.SECP521R1:
return DppCurve.SECP521R1;
case SupplicantStaIfaceHal.DppCurve.BRAINPOOLP256R1:
return DppCurve.BRAINPOOLP256R1;
case SupplicantStaIfaceHal.DppCurve.BRAINPOOLP384R1:
return DppCurve.BRAINPOOLP384R1;
case SupplicantStaIfaceHal.DppCurve.BRAINPOOLP512R1:
return DppCurve.BRAINPOOLP512R1;
default:
Log.e(TAG, "Invalid DppCurve received");
return -1;
}
}
private int frameworkToAidlDppNetRole(int dppNetRole) {
switch(dppNetRole) {
case SupplicantStaIfaceHal.DppNetRole.STA:
return DppNetRole.STA;
case SupplicantStaIfaceHal.DppNetRole.AP:
return DppNetRole.AP;
default:
Log.e(TAG, "Invalid DppNetRole received");
return -1;
}
}
protected byte dscpPolicyToAidlQosPolicyStatusCode(int status) {
switch (status) {
case NetworkAgent.DSCP_POLICY_STATUS_SUCCESS:
case NetworkAgent.DSCP_POLICY_STATUS_DELETED:
return QosPolicyStatusCode.QOS_POLICY_SUCCESS;
case NetworkAgent.DSCP_POLICY_STATUS_REQUEST_DECLINED:
return QosPolicyStatusCode.QOS_POLICY_REQUEST_DECLINED;
case NetworkAgent.DSCP_POLICY_STATUS_REQUESTED_CLASSIFIER_NOT_SUPPORTED:
return QosPolicyStatusCode.QOS_POLICY_CLASSIFIER_NOT_SUPPORTED;
case NetworkAgent.DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES:
return QosPolicyStatusCode.QOS_POLICY_INSUFFICIENT_RESOURCES;
default:
Log.e(TAG, "Invalid DSCP policy failure code received: " + status);
return QosPolicyStatusCode.QOS_POLICY_REQUEST_DECLINED;
}
}
protected static int halToFrameworkQosPolicyRequestType(byte requestType) {
switch (requestType) {
case QosPolicyRequestType.QOS_POLICY_ADD:
return SupplicantStaIfaceHal.QOS_POLICY_REQUEST_ADD;
case QosPolicyRequestType.QOS_POLICY_REMOVE:
return SupplicantStaIfaceHal.QOS_POLICY_REQUEST_REMOVE;
default:
Log.e(TAG, "Invalid QosPolicyRequestType received: " + requestType);
return -1;
}
}
private static boolean qosClassifierParamHasValue(int classifierParamMask, int paramBit) {
return (classifierParamMask & paramBit) != 0;
}
/**
* Convert from a HAL QosPolicyData object to a framework QosPolicy object.
*/
public static SupplicantStaIfaceHal.QosPolicyRequest halToFrameworkQosPolicy(
QosPolicyData halQosPolicy) {
QosPolicyClassifierParams classifierParams = halQosPolicy.classifierParams;
int classifierParamMask = classifierParams.classifierParamMask;
byte[] srcIp = null;
byte[] dstIp = null;
int srcPort = DscpPolicy.SOURCE_PORT_ANY;
int[] dstPortRange = new int[]{MIN_PORT_NUM, MAX_PORT_NUM};
int protocol = DscpPolicy.PROTOCOL_ANY;
boolean hasSrcIp = false;
boolean hasDstIp = false;
if (qosClassifierParamHasValue(classifierParamMask, QosPolicyClassifierParamsMask.SRC_IP)) {
hasSrcIp = true;
srcIp = classifierParams.srcIp;
}
if (qosClassifierParamHasValue(classifierParamMask, QosPolicyClassifierParamsMask.DST_IP)) {
hasDstIp = true;
dstIp = classifierParams.dstIp;
}
if (qosClassifierParamHasValue(classifierParamMask,
QosPolicyClassifierParamsMask.SRC_PORT)) {
srcPort = classifierParams.srcPort;
}
if (qosClassifierParamHasValue(classifierParamMask,
QosPolicyClassifierParamsMask.DST_PORT_RANGE)) {
dstPortRange[0] = classifierParams.dstPortRange.startPort;
dstPortRange[1] = classifierParams.dstPortRange.endPort;
}
if (qosClassifierParamHasValue(classifierParamMask,
QosPolicyClassifierParamsMask.PROTOCOL_NEXT_HEADER)) {
protocol = classifierParams.protocolNextHdr;
}
return new SupplicantStaIfaceHal.QosPolicyRequest(halQosPolicy.policyId,
halToFrameworkQosPolicyRequestType(halQosPolicy.requestType), halQosPolicy.dscp,
new SupplicantStaIfaceHal.QosPolicyClassifierParams(
hasSrcIp, srcIp, hasDstIp, dstIp, srcPort, dstPortRange, protocol));
}
/**
* Returns connection capabilities of the current network
*
* @param ifaceName Name of the interface.
* @return connection capabilities of the current network
*/
public WifiNative.ConnectionCapabilities getConnectionCapabilities(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getConnectionCapabilities";
WifiNative.ConnectionCapabilities capOut = new WifiNative.ConnectionCapabilities();
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return capOut;
}
try {
ConnectionCapabilities cap = iface.getConnectionCapabilities();
capOut.wifiStandard = getWifiStandard(cap.technology);
capOut.channelBandwidth = getChannelBandwidth(cap.channelBandwidth);
capOut.is11bMode = (cap.legacyMode == LegacyMode.B_MODE);
capOut.maxNumberTxSpatialStreams = cap.maxNumberTxSpatialStreams;
capOut.maxNumberRxSpatialStreams = cap.maxNumberRxSpatialStreams;
return capOut;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return capOut;
}
}
/**
* Returns connection MLO links info
*
* @param ifaceName Name of the interface.
* @return connection MLO links info
*/
public WifiNative.ConnectionMloLinksInfo getConnectionMloLinksInfo(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getConnectionMloLinksInfo";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return null;
}
try {
MloLinksInfo halInfo = iface.getConnectionMloLinksInfo();
if (halInfo == null) {
return null;
}
WifiNative.ConnectionMloLinksInfo nativeInfo =
new WifiNative.ConnectionMloLinksInfo();
nativeInfo.links = new WifiNative.ConnectionMloLink[halInfo.links.length];
for (int i = 0; i < halInfo.links.length; i++) {
nativeInfo.links[i].linkId = halInfo.links[i].linkId;
nativeInfo.links[i].staMacAddress = MacAddress.fromBytes(
halInfo.links[i].staLinkMacAddress);
}
return nativeInfo;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid STA Mac Address received from HAL");
return null;
}
return null;
}
}
/**
* Adds a DPP peer URI to the URI list.
*
* Returns an ID to be used later to refer to this URI (>0).
* On error, -1 is returned.
*/
public int addDppPeerUri(@NonNull String ifaceName, @NonNull String uri) {
synchronized (mLock) {
final String methodStr = "addDppPeerUri";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return -1;
}
try {
return iface.addDppPeerUri(uri);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return -1;
}
}
/**
* Removes a DPP URI to the URI list given an ID.
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean removeDppUri(@NonNull String ifaceName, int bootstrapId) {
synchronized (mLock) {
final String methodStr = "removeDppUri";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.removeDppUri(bootstrapId);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Stops/aborts DPP Initiator request
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean stopDppInitiator(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "stopDppInitiator";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.stopDppInitiator();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Starts DPP Configurator-Initiator request
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean startDppConfiguratorInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId, @NonNull String ssid, String password, String psk,
int netRole, int securityAkm, byte[] privEcKey) {
synchronized (mLock) {
final String methodStr = "startDppConfiguratorInitiator";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
byte[] key = iface.startDppConfiguratorInitiator(peerBootstrapId, ownBootstrapId,
ssid, password != null ? password : "", psk != null ? psk : "",
frameworkToAidlDppNetRole(netRole), frameworkToAidlDppAkm(securityAkm),
privEcKey != null ? privEcKey : new byte[] {});
if (key != null && key.length > 0 && mDppCallback != null) {
mDppCallback.onDppConfiguratorKeyUpdate(key);
}
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Starts DPP Enrollee-Initiator request
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean startDppEnrolleeInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId) {
synchronized (mLock) {
final String methodStr = "startDppEnrolleeInitiator";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startDppEnrolleeInitiator(peerBootstrapId, ownBootstrapId);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Generate a DPP QR code based boot strap info
*
* Returns DppResponderBootstrapInfo;
*/
public WifiNative.DppBootstrapQrCodeInfo generateDppBootstrapInfoForResponder(
@NonNull String ifaceName, String macAddress, @NonNull String deviceInfo,
int dppCurve) {
synchronized (mLock) {
final String methodStr = "generateDppBootstrapInfoForResponder";
WifiNative.DppBootstrapQrCodeInfo bootstrapInfoOut =
new WifiNative.DppBootstrapQrCodeInfo();
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return bootstrapInfoOut;
}
try {
DppResponderBootstrapInfo info = iface.generateDppBootstrapInfoForResponder(
NativeUtil.macAddressToByteArray(macAddress), deviceInfo,
frameworkToAidlDppCurve(dppCurve));
bootstrapInfoOut.bootstrapId = info.bootstrapId;
bootstrapInfoOut.listenChannel = info.listenChannel;
bootstrapInfoOut.uri = info.uri;
return bootstrapInfoOut;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return bootstrapInfoOut;
}
}
/**
* Starts DPP Enrollee-Responder request
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean startDppEnrolleeResponder(@NonNull String ifaceName, int listenChannel) {
synchronized (mLock) {
final String methodStr = "startDppEnrolleeResponder";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.startDppEnrolleeResponder(listenChannel);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Stops/aborts DPP Responder request.
*
* Returns true when operation is successful
* On error, false is returned.
*/
public boolean stopDppResponder(@NonNull String ifaceName, int ownBootstrapId) {
synchronized (mLock) {
final String methodStr = "stopDppResponder";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.stopDppResponder(ownBootstrapId);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Register callbacks for DPP events.
*
* @param dppCallback DPP callback object.
*/
public void registerDppCallback(WifiNative.DppEventCallback dppCallback) {
synchronized (mLock) {
mDppCallback = dppCallback;
}
}
protected WifiNative.DppEventCallback getDppCallback() {
synchronized (mLock) {
return mDppCallback;
}
}
/**
* Set MBO cellular data availability.
*
* @param ifaceName Name of the interface.
* @param available true means cellular data available, false otherwise.
* @return true is operation is successful, false otherwise.
*/
public boolean setMboCellularDataStatus(@NonNull String ifaceName, boolean available) {
synchronized (mLock) {
final String methodStr = "setMboCellularDataStatus";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setMboCellularDataStatus(available);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set whether the network-centric QoS policy feature is enabled or not for this interface.
*
* @param ifaceName name of the interface.
* @param isEnabled true if feature is enabled, false otherwise.
* @return true if operation is successful, false otherwise.
*/
public boolean setNetworkCentricQosPolicyFeatureEnabled(@NonNull String ifaceName,
boolean isEnabled) {
synchronized (mLock) {
String methodStr = "setNetworkCentricQosPolicyFeatureEnabled";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.setQosPolicyFeatureEnabled(isEnabled);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Check if we've roamed to a linked network and make the linked network the current network
* if we have.
*
* @param ifaceName Name of the interface.
* @param newNetworkId Network id of the new network we've roamed to. If fromFramework is
* {@code true}, this will be a framework network id. Otherwise, this will
* be a remote network id.
* @param fromFramework {@code true} if the network id is a framework network id, {@code false}
if the network id is a remote network id.
* @return true if we've roamed to a linked network, false if not.
*/
public boolean updateOnLinkedNetworkRoaming(
@NonNull String ifaceName, int newNetworkId, boolean fromFramework) {
synchronized (mLock) {
List<Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration>> linkedNetworkHandles =
mLinkedNetworkLocalAndRemoteConfigs.get(ifaceName);
SupplicantStaNetworkHalAidlImpl currentHandle =
getCurrentNetworkRemoteHandle(ifaceName);
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (linkedNetworkHandles == null || currentHandle == null || currentConfig == null) {
return false;
}
if (fromFramework ? currentConfig.networkId == newNetworkId
: currentHandle.getNetworkId() == newNetworkId) {
return false;
}
for (Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration> pair
: linkedNetworkHandles) {
if (fromFramework ? pair.second.networkId == newNetworkId
: pair.first.getNetworkId() == newNetworkId) {
Log.i(TAG, "Roamed to linked network, make linked network as current network");
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
return true;
}
}
return false;
}
}
/**
* Updates the linked networks for the current network and sends them to the supplicant.
*
* @param ifaceName Name of the interface.
* @param networkId Network id of the network to link the configurations to.
* @param linkedConfigurations Map of config profile key to config for linking.
* @return true if networks were successfully linked, false otherwise.
*/
public boolean updateLinkedNetworks(@NonNull String ifaceName, int networkId,
Map<String, WifiConfiguration> linkedConfigurations) {
synchronized (mLock) {
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
SupplicantStaNetworkHalAidlImpl currentHandle =
getCurrentNetworkRemoteHandle(ifaceName);
if (currentConfig == null || currentHandle == null) {
Log.e(TAG, "current network not configured yet.");
return false;
}
if (networkId != currentConfig.networkId) {
Log.e(TAG, "current config network id is not matching");
return false;
}
final int remoteNetworkId = currentHandle.getNetworkId();
if (remoteNetworkId == -1) {
Log.e(TAG, "current handle getNetworkId failed");
return false;
}
if (!removeAllNetworksExcept(ifaceName, remoteNetworkId)) {
Log.e(TAG, "couldn't remove non-current supplicant networks");
return false;
}
mLinkedNetworkLocalAndRemoteConfigs.remove(ifaceName);
if (linkedConfigurations == null || linkedConfigurations.size() == 0) {
Log.i(TAG, "cleared linked networks");
return true;
}
List<Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration>> linkedNetworkHandles =
new ArrayList<>();
linkedNetworkHandles.add(new Pair(currentHandle, currentConfig));
for (String linkedNetwork : linkedConfigurations.keySet()) {
Log.i(TAG, "add linked network: " + linkedNetwork);
Pair<SupplicantStaNetworkHalAidlImpl, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, linkedConfigurations.get(linkedNetwork));
if (pair == null) {
Log.e(TAG, "failed to add/save linked network: " + linkedNetwork);
return false;
}
pair.first.enable(true);
linkedNetworkHandles.add(pair);
}
mLinkedNetworkLocalAndRemoteConfigs.put(ifaceName, linkedNetworkHandles);
return true;
}
}
/**
* Remove all networks except the supplied network ID from supplicant
*
* @param ifaceName Name of the interface
* @param networkId network id to keep
*/
private boolean removeAllNetworksExcept(@NonNull String ifaceName, int networkId) {
synchronized (mLock) {
int[] networks = listNetworks(ifaceName);
if (networks == null) {
Log.e(TAG, "removeAllNetworksExcept failed, got null networks");
return false;
}
for (int id : networks) {
if (networkId == id) {
continue;
}
if (!removeNetwork(ifaceName, id)) {
Log.e(TAG, "removeAllNetworksExcept failed to remove network: " + id);
return false;
}
}
return true;
}
}
/**
* Gets the security params of the current network associated with this interface
*
* @param ifaceName Name of the interface
* @return Security params of the current network associated with the interface
*/
public SecurityParams getCurrentNetworkSecurityParams(@NonNull String ifaceName) {
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (currentConfig == null) {
return null;
}
return currentConfig.getNetworkSelectionStatus().getCandidateSecurityParams();
}
/**
* Sends a QoS policy response.
*
* @param ifaceName Name of the interface.
* @param qosPolicyRequestId Dialog token to identify the request.
* @param morePolicies Flag to indicate more QoS policies can be accommodated.
* @param qosPolicyStatusList List of framework QosPolicyStatus objects.
* @return true if response is sent successfully, false otherwise.
*/
public boolean sendQosPolicyResponse(String ifaceName, int qosPolicyRequestId,
boolean morePolicies,
@NonNull List<SupplicantStaIfaceHal.QosPolicyStatus> qosPolicyStatusList) {
synchronized (mLock) {
final String methodStr = "sendQosPolicyResponse";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
int index = 0;
QosPolicyStatus[] halPolicyStatusList = new QosPolicyStatus[qosPolicyStatusList.size()];
for (SupplicantStaIfaceHal.QosPolicyStatus frameworkPolicyStatus
: qosPolicyStatusList) {
if (frameworkPolicyStatus == null) {
return false;
}
QosPolicyStatus halPolicyStatus = new QosPolicyStatus();
halPolicyStatus.policyId = (byte) frameworkPolicyStatus.policyId;
halPolicyStatus.status = dscpPolicyToAidlQosPolicyStatusCode(
frameworkPolicyStatus.dscpPolicyStatus);
halPolicyStatusList[index] = halPolicyStatus;
index++;
}
try {
iface.sendQosPolicyResponse(qosPolicyRequestId, morePolicies, halPolicyStatusList);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Indicates the removal of all active QoS policies configured by the AP.
*
* @param ifaceName Name of the interface.
*/
public boolean removeAllQosPolicies(String ifaceName) {
final String methodStr = "removeAllQosPolicies";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.removeAllQosPolicies();
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
/**
* Generate DPP credential for network access
*
* @param ifaceName Name of the interface.
* @param ssid ssid of the network
* @param privEcKey Private EC Key for DPP Configurator
* Returns true when operation is successful. On error, false is returned.
*/
public boolean generateSelfDppConfiguration(@NonNull String ifaceName, @NonNull String ssid,
byte[] privEcKey) {
synchronized (mLock) {
final String methodStr = "generateSelfDppConfiguration";
ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
try {
iface.generateSelfDppConfiguration(
NativeUtil.removeEnclosingQuotes(ssid), privEcKey);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (ServiceSpecificException e) {
handleServiceSpecificException(e, methodStr);
}
return false;
}
}
/**
* Set the currently configured network's anonymous identity.
*
* @param ifaceName Name of the interface.
* @param anonymousIdentity the anonymouns identity.
* @return true if succeeds, false otherwise.
*/
public boolean setEapAnonymousIdentity(@NonNull String ifaceName, String anonymousIdentity) {
synchronized (mLock) {
SupplicantStaNetworkHalAidlImpl networkHandle =
checkStaNetworkAndLogFailure(ifaceName, "setEapAnonymousIdentity");
if (networkHandle == null) return false;
if (anonymousIdentity == null) return false;
return networkHandle.setEapAnonymousIdentity(anonymousIdentity.getBytes());
}
}
}