blob: b587eed3844473896cb9f8c219ea9d296a646ecb [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP;
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_WAPI;
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_0.WifiChannelWidthInMhz;
import android.hardware.wifi.supplicant.V1_0.ISupplicant;
import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
import android.hardware.wifi.supplicant.V1_0.IfaceType;
import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
import android.hardware.wifi.supplicant.V1_3.ConnectionCapabilities;
import android.hardware.wifi.supplicant.V1_3.WifiTechnology;
import android.hardware.wifi.supplicant.V1_3.WpaDriverCapabilitiesMask;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.net.MacAddress;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiAnnotations.WifiStandard;
import android.net.wifi.WifiConfiguration;
import android.os.Handler;
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.WifiNative.DppEventCallback;
import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler;
import com.android.server.wifi.util.GeneralUtil.Mutable;
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.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.concurrent.ThreadSafe;
/**
* Hal calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
* To maintain thread-safety, the locking protocol is that every non-static method (regardless of
* access level) acquires mLock.
*/
@ThreadSafe
public class SupplicantStaIfaceHal {
private static final String TAG = "SupplicantStaIfaceHal";
@VisibleForTesting
public static final String HAL_INSTANCE_NAME = "default";
@VisibleForTesting
public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L;
@VisibleForTesting
static final String PMK_CACHE_EXPIRATION_ALARM_TAG = "PMK_CACHE_EXPIRATION_TIMER";
/**
* 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 final Object mLock = new Object();
private boolean mVerboseLoggingEnabled = false;
// Supplicant HAL interface objects
private IServiceManager mIServiceManager = null;
private ISupplicant mISupplicant;
private HashMap<String, ISupplicantStaIface> mISupplicantStaIfaces = new HashMap<>();
private HashMap<String, ISupplicantStaIfaceCallback> mISupplicantStaIfaceCallbacks =
new HashMap<>();
private HashMap<String, SupplicantStaNetworkHal> mCurrentNetworkRemoteHandles = new HashMap<>();
private HashMap<String, WifiConfiguration> mCurrentNetworkLocalConfigs = new HashMap<>();
@VisibleForTesting
HashMap<Integer, PmkCacheStoreData> mPmkCacheEntries = new HashMap<>();
private SupplicantDeathEventHandler mDeathEventHandler;
private ServiceManagerDeathRecipient mServiceManagerDeathRecipient;
private SupplicantDeathRecipient mSupplicantDeathRecipient;
// Death recipient cookie registered for current supplicant instance.
private long mDeathRecipientCookie = 0;
private final Context mContext;
private final WifiMonitor mWifiMonitor;
private final FrameworkFacade mFrameworkFacade;
private final Handler mEventHandler;
private DppEventCallback mDppCallback = null;
private final Clock mClock;
private final WifiMetrics mWifiMetrics;
private final IServiceNotification mServiceNotificationCallback =
new IServiceNotification.Stub() {
public void onRegistration(String fqName, String name, boolean preexisting) {
synchronized (mLock) {
if (mVerboseLoggingEnabled) {
Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
+ ", " + name + " preexisting=" + preexisting);
}
if (!initSupplicantService()) {
Log.e(TAG, "initalizing ISupplicant failed.");
supplicantServiceDiedHandler(mDeathRecipientCookie);
} else {
Log.i(TAG, "Completed initialization of ISupplicant.");
}
}
}
};
private class ServiceManagerDeathRecipient implements DeathRecipient {
@Override
public void serviceDied(long cookie) {
mEventHandler.post(() -> {
synchronized (mLock) {
Log.w(TAG, "IServiceManager died: cookie=" + cookie);
supplicantServiceDiedHandler(mDeathRecipientCookie);
mIServiceManager = null; // Will need to register a new ServiceNotification
}
});
}
}
private class SupplicantDeathRecipient implements DeathRecipient {
@Override
public void serviceDied(long cookie) {
mEventHandler.post(() -> {
synchronized (mLock) {
Log.w(TAG, "ISupplicant died: cookie=" + cookie);
supplicantServiceDiedHandler(cookie);
}
});
}
}
@VisibleForTesting
static class PmkCacheStoreData {
public long expirationTimeInSec;
public ArrayList<Byte> data;
public MacAddress macAddress;
PmkCacheStoreData(long timeInSec, ArrayList<Byte> serializedData, MacAddress macAddress) {
expirationTimeInSec = timeInSec;
data = serializedData;
this.macAddress = macAddress;
}
}
public SupplicantStaIfaceHal(Context context, WifiMonitor monitor,
FrameworkFacade frameworkFacade, Handler handler,
Clock clock, WifiMetrics wifiMetrics) {
mContext = context;
mWifiMonitor = monitor;
mFrameworkFacade = frameworkFacade;
mEventHandler = handler;
mClock = clock;
mWifiMetrics = wifiMetrics;
mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient();
mSupplicantDeathRecipient = new SupplicantDeathRecipient();
}
/**
* Enable/Disable verbose logging.
*
* @param enable true to enable, false to disable.
*/
void enableVerboseLogging(boolean enable) {
synchronized (mLock) {
mVerboseLoggingEnabled = enable;
}
}
protected boolean isVerboseLoggingEnabled() {
return mVerboseLoggingEnabled;
}
private boolean linkToServiceManagerDeath() {
synchronized (mLock) {
if (mIServiceManager == null) return false;
try {
if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
supplicantServiceDiedHandler(mDeathRecipientCookie);
mIServiceManager = null; // Will need to register a new ServiceNotification
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "IServiceManager.linkToDeath exception", e);
return false;
}
return true;
}
}
/**
* Registers a service notification for the ISupplicant service, which triggers initialization
* of the ISupplicantStaIface
* @return true if the service notification was successfully registered
*/
public boolean initialize() {
synchronized (mLock) {
if (mVerboseLoggingEnabled) {
Log.i(TAG, "Registering ISupplicant service ready callback.");
}
mISupplicant = null;
mISupplicantStaIfaces.clear();
if (mIServiceManager != null) {
// Already have an IServiceManager and serviceNotification registered, don't
// don't register another.
return true;
}
try {
mIServiceManager = getServiceManagerMockable();
if (mIServiceManager == null) {
Log.e(TAG, "Failed to get HIDL Service Manager");
return false;
}
if (!linkToServiceManagerDeath()) {
return false;
}
/* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
exists */
if (!mIServiceManager.registerForNotifications(
ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
Log.e(TAG, "Failed to register for notifications to "
+ ISupplicant.kInterfaceName);
mIServiceManager = null; // Will need to register a new ServiceNotification
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
+ e);
supplicantServiceDiedHandler(mDeathRecipientCookie);
}
return true;
}
}
private boolean linkToSupplicantDeath(
DeathRecipient deathRecipient, long cookie) {
synchronized (mLock) {
if (mISupplicant == null) return false;
try {
if (!mISupplicant.linkToDeath(deathRecipient, cookie)) {
Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
supplicantServiceDiedHandler(mDeathRecipientCookie);
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.linkToDeath exception", e);
return false;
}
return true;
}
}
private boolean initSupplicantService() {
synchronized (mLock) {
try {
mISupplicant = getSupplicantMockable();
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.getService exception: " + e);
return false;
} catch (NoSuchElementException e) {
Log.e(TAG, "ISupplicant.getService exception: " + e);
return false;
}
if (mISupplicant == null) {
Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
return false;
}
if (!linkToSupplicantDeath(mSupplicantDeathRecipient, ++mDeathRecipientCookie)) {
return false;
}
}
return true;
}
protected int getCurrentNetworkId(@NonNull String ifaceName) {
synchronized (mLock) {
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (currentConfig == null) {
return WifiConfiguration.INVALID_NETWORK_ID;
}
return currentConfig.networkId;
}
}
private boolean trySetupStaIfaceV1_3(@NonNull String ifaceName,
@NonNull ISupplicantStaIface iface) throws RemoteException {
if (!isV1_3()) return false;
SupplicantStaIfaceHalCallbackV1_3 callbackV13 =
new SupplicantStaIfaceHalCallbackV1_3(ifaceName);
if (!registerCallbackV1_3(getStaIfaceMockableV1_3(iface), callbackV13)) {
throw new RemoteException("Init StaIface V1_3 failed.");
}
/* keep this in a store to avoid recycling by garbage collector. */
mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV13);
return true;
}
private boolean trySetupStaIfaceV1_2(@NonNull String ifaceName,
@NonNull ISupplicantStaIface iface) throws RemoteException {
if (!isV1_2()) return false;
/* try newer version fist. */
if (trySetupStaIfaceV1_3(ifaceName, iface)) {
logd("Newer HAL is found, skip V1_2 remaining init flow.");
return true;
}
SupplicantStaIfaceHalCallbackV1_2 callbackV12 =
new SupplicantStaIfaceHalCallbackV1_2(ifaceName);
if (!registerCallbackV1_2(getStaIfaceMockableV1_2(iface), callbackV12)) {
throw new RemoteException("Init StaIface V1_2 failed.");
}
/* keep this in a store to avoid recycling by garbage collector. */
mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV12);
return true;
}
private boolean trySetupStaIfaceV1_1(@NonNull String ifaceName,
@NonNull ISupplicantStaIface iface) throws RemoteException {
if (!isV1_1()) return false;
/* try newer version fist. */
if (trySetupStaIfaceV1_2(ifaceName, iface)) {
logd("Newer HAL is found, skip V1_1 remaining init flow.");
return true;
}
SupplicantStaIfaceHalCallbackV1_1 callbackV11 =
new SupplicantStaIfaceHalCallbackV1_1(ifaceName);
if (!registerCallbackV1_1(getStaIfaceMockableV1_1(iface), callbackV11)) {
throw new RemoteException("Init StaIface V1_1 failed.");
}
/* keep this in a store to avoid recycling by garbage collector. */
mISupplicantStaIfaceCallbacks.put(ifaceName, callbackV11);
return true;
}
/**
* Helper function to set up StaIface with different HAL version.
*
* This helper function would try newer version recursively.
* Once the latest version is found, it would register the callback
* of the latest version and skip unnecessary older HAL init flow.
*
* New version callback will be extended from the older one, as a result,
* older callback is always created regardless of the latest version.
*
* Uprev steps:
* 1. add new helper function trySetupStaIfaceV1_Y.
* 2. call newly added function in trySetupStaIfaceV1_X (X should be Y-1).
*/
private ISupplicantStaIface setupStaIface(@NonNull String ifaceName,
@NonNull ISupplicantIface ifaceHwBinder) throws RemoteException {
/* Prepare base type for later cast. */
ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder);
/* try newer version first. */
if (trySetupStaIfaceV1_1(ifaceName, iface)) {
logd("Newer HAL is found, skip V1_0 remaining init flow.");
return iface;
}
SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName);
if (!registerCallback(iface, callback)) {
throw new RemoteException("Init StaIface V1_0 failed.");
}
/* keep this in a store to avoid recycling by garbage collector. */
mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
return iface;
}
/**
* 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) {
final String methodStr = "setupIface";
if (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) != null) return false;
ISupplicantIface ifaceHwBinder;
if (isV1_1()) {
ifaceHwBinder = addIfaceV1_1(ifaceName);
} else {
ifaceHwBinder = getIfaceV1_0(ifaceName);
}
if (ifaceHwBinder == null) {
Log.e(TAG, "setupIface got null iface");
return false;
}
try {
ISupplicantStaIface iface = setupStaIface(ifaceName, ifaceHwBinder);
mISupplicantStaIfaces.put(ifaceName, iface);
} catch (RemoteException e) {
loge("setup StaIface failed: " + e.toString());
return false;
}
return true;
}
/**
* Get a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) {
synchronized (mLock) {
if (mISupplicant == null) {
return null;
}
/** List all supplicant Ifaces */
final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
try {
mISupplicant.listInterfaces((SupplicantStatus status,
ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
if (status.code != SupplicantStatusCode.SUCCESS) {
Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
return;
}
supplicantIfaces.addAll(ifaces);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
handleRemoteException(e, "listInterfaces");
return null;
}
if (supplicantIfaces.size() == 0) {
Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
return null;
}
Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
if (ifaceInfo.type == IfaceType.STA && ifaceName.equals(ifaceInfo.name)) {
try {
mISupplicant.getInterface(ifaceInfo,
(SupplicantStatus status, ISupplicantIface iface) -> {
if (status.code != SupplicantStatusCode.SUCCESS) {
Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
return;
}
supplicantIface.value = iface;
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.getInterface exception: " + e);
handleRemoteException(e, "getInterface");
return null;
}
break;
}
}
return supplicantIface.value;
}
}
/**
* Create a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) {
synchronized (mLock) {
ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
ifaceInfo.name = ifaceName;
ifaceInfo.type = IfaceType.STA;
Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
try {
getSupplicantMockableV1_1().addInterface(ifaceInfo,
(SupplicantStatus status, ISupplicantIface iface) -> {
if (status.code != SupplicantStatusCode.SUCCESS
&& status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) {
Log.e(TAG, "Failed to create ISupplicantIface " + status.code);
return;
}
supplicantIface.value = iface;
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.addInterface exception: " + e);
handleRemoteException(e, "addInterface");
return null;
} catch (NoSuchElementException e) {
Log.e(TAG, "ISupplicant.addInterface exception: " + e);
handleNoSuchElementException(e, "addInterface");
return null;
} catch (IllegalArgumentException e) {
handleIllegalArgumentException(e, "addInterface");
Log.e(TAG, "ISupplicant.addInterface exception: " + e);
return null;
}
return supplicantIface.value;
}
}
/**
* 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 (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) == null) return false;
if (isV1_1()) {
if (!removeIfaceV1_1(ifaceName)) {
Log.e(TAG, "Failed to remove iface = " + ifaceName);
return false;
}
}
if (mISupplicantStaIfaces.remove(ifaceName) == null) {
Log.e(TAG, "Trying to teardown unknown inteface");
return false;
}
mISupplicantStaIfaceCallbacks.remove(ifaceName);
return true;
}
}
/**
* Remove a STA interface for the specified iface name.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
private boolean removeIfaceV1_1(@NonNull String ifaceName) {
synchronized (mLock) {
try {
ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
ifaceInfo.name = ifaceName;
ifaceInfo.type = IfaceType.STA;
SupplicantStatus status = getSupplicantMockableV1_1().removeInterface(ifaceInfo);
if (status.code != SupplicantStatusCode.SUCCESS) {
Log.e(TAG, "Failed to remove iface " + status.code);
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.removeInterface exception: " + e);
handleRemoteException(e, "removeInterface");
return false;
} catch (NoSuchElementException e) {
Log.e(TAG, "ISupplicant.removeInterface exception: " + e);
handleNoSuchElementException(e, "removeInterface");
return false;
}
return true;
}
}
/**
* Registers a death notification for supplicant.
* @return Returns true on success.
*/
public boolean registerDeathHandler(@NonNull SupplicantDeathEventHandler handler) {
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() {
if (mDeathEventHandler == null) {
Log.e(TAG, "No Death handler present");
}
mDeathEventHandler = null;
return true;
}
private void clearState() {
synchronized (mLock) {
mISupplicant = null;
mISupplicantStaIfaces.clear();
mCurrentNetworkLocalConfigs.clear();
mCurrentNetworkRemoteHandles.clear();
}
}
private void supplicantServiceDiedHandler(long cookie) {
synchronized (mLock) {
if (mDeathRecipientCookie != cookie) {
Log.i(TAG, "Ignoring stale death recipient notification");
return;
}
for (String ifaceName : mISupplicantStaIfaces.keySet()) {
mWifiMonitor.broadcastSupplicantDisconnectionEvent(ifaceName);
}
clearState();
if (mDeathEventHandler != null) {
mDeathEventHandler.onDeath();
}
}
}
/**
* Signals whether Initialization completed successfully.
*/
public boolean isInitializationStarted() {
synchronized (mLock) {
return mIServiceManager != null;
}
}
/**
* Signals whether Initialization completed successfully.
*/
public boolean isInitializationComplete() {
synchronized (mLock) {
return mISupplicant != null;
}
}
/**
* Start the supplicant daemon for V1_1 service.
*
* @return true on success, false otherwise.
*/
private boolean startDaemon_V1_1() {
synchronized (mLock) {
try {
// This should startup supplicant daemon using the lazy start HAL mechanism.
getSupplicantMockableV1_1();
} catch (RemoteException e) {
Log.e(TAG, "Exception while trying to start supplicant: "
+ e);
supplicantServiceDiedHandler(mDeathRecipientCookie);
return false;
} catch (NoSuchElementException e) {
// We're starting the daemon, so expect |NoSuchElementException|.
Log.d(TAG, "Successfully triggered start of supplicant using HIDL");
}
return true;
}
}
/**
* Start the supplicant daemon.
*
* @return true on success, false otherwise.
*/
public boolean startDaemon() {
synchronized (mLock) {
if (isV1_1()) {
Log.i(TAG, "Starting supplicant using HIDL");
return startDaemon_V1_1();
} else {
Log.i(TAG, "Starting supplicant using init");
mFrameworkFacade.startSupplicant();
return true;
}
}
}
/**
* Terminate the supplicant daemon for V1_1 service.
*/
private void terminate_V1_1() {
synchronized (mLock) {
final String methodStr = "terminate";
if (!checkSupplicantAndLogFailure(methodStr)) return;
try {
getSupplicantMockableV1_1().terminate();
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
} catch (NoSuchElementException e) {
handleNoSuchElementException(e, methodStr);
}
}
}
/**
* Terminate the supplicant daemon & wait for it's death.
*/
public void terminate() {
synchronized (mLock) {
// Register for a new death listener to block until supplicant is dead.
final long waitForDeathCookie = new Random().nextLong();
final CountDownLatch waitForDeathLatch = new CountDownLatch(1);
linkToSupplicantDeath((cookie) -> {
Log.d(TAG, "ISupplicant died: cookie=" + cookie);
if (cookie != waitForDeathCookie) return;
waitForDeathLatch.countDown();
}, waitForDeathCookie);
if (isV1_1()) {
Log.i(TAG, "Terminating supplicant using HIDL");
terminate_V1_1();
} else {
Log.i(TAG, "Terminating supplicant using init");
mFrameworkFacade.stopSupplicant();
}
// Now wait for death listener callback to confirm that it's dead.
try {
if (!waitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
Log.w(TAG, "Timed out waiting for confirmation of supplicant death");
}
} catch (InterruptedException e) {
Log.w(TAG, "Failed to wait for supplicant death");
}
}
}
/**
* Wrapper functions to access static HAL methods, created to be mockable in unit tests
*/
protected IServiceManager getServiceManagerMockable() throws RemoteException {
synchronized (mLock) {
return IServiceManager.getService();
}
}
protected ISupplicant getSupplicantMockable() throws RemoteException, NoSuchElementException {
synchronized (mLock) {
ISupplicant iSupplicant = ISupplicant.getService();
if (iSupplicant == null) {
throw new NoSuchElementException("Cannot get root service.");
}
return iSupplicant;
}
}
protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1()
throws RemoteException, NoSuchElementException {
synchronized (mLock) {
android.hardware.wifi.supplicant.V1_1.ISupplicant iSupplicantDerived =
android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom(
getSupplicantMockable());
if (iSupplicantDerived == null) {
throw new NoSuchElementException("Cannot cast to V1.1 service.");
}
return iSupplicantDerived;
}
}
protected android.hardware.wifi.supplicant.V1_2.ISupplicant getSupplicantMockableV1_2()
throws RemoteException, NoSuchElementException {
synchronized (mLock) {
android.hardware.wifi.supplicant.V1_2.ISupplicant iSupplicantDerived =
android.hardware.wifi.supplicant.V1_2.ISupplicant.castFrom(
getSupplicantMockable());
if (iSupplicantDerived == null) {
throw new NoSuchElementException("Cannot cast to V1.1 service.");
}
return iSupplicantDerived;
}
}
protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
synchronized (mLock) {
return ISupplicantStaIface.asInterface(iface.asBinder());
}
}
protected android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface
getStaIfaceMockableV1_1(ISupplicantIface iface) {
synchronized (mLock) {
return android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface
.asInterface(iface.asBinder());
}
}
protected android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface
getStaIfaceMockableV1_2(ISupplicantIface iface) {
synchronized (mLock) {
return android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface
.asInterface(iface.asBinder());
}
}
protected android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface
getStaIfaceMockableV1_3(ISupplicantIface iface) {
synchronized (mLock) {
return android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface
.asInterface(iface.asBinder());
}
}
/**
* Uses the IServiceManager to check if the device is running V1_1 of the HAL from the VINTF for
* the device.
* @return true if supported, false otherwise.
*/
private boolean isV1_1() {
return checkHalVersionByInterfaceName(
android.hardware.wifi.supplicant.V1_1.ISupplicant.kInterfaceName);
}
/**
* Uses the IServiceManager to check if the device is running V1_2 of the HAL from the VINTF for
* the device.
* @return true if supported, false otherwise.
*/
private boolean isV1_2() {
return checkHalVersionByInterfaceName(
android.hardware.wifi.supplicant.V1_2.ISupplicant.kInterfaceName);
}
/**
* Uses the IServiceManager to check if the device is running V1_3 of the HAL from the VINTF for
* the device.
* @return true if supported, false otherwise.
*/
private boolean isV1_3() {
return checkHalVersionByInterfaceName(
android.hardware.wifi.supplicant.V1_3.ISupplicant.kInterfaceName);
}
private boolean checkHalVersionByInterfaceName(String interfaceName) {
if (interfaceName == null) {
return false;
}
synchronized (mLock) {
if (mIServiceManager == null) {
Log.e(TAG, "checkHalVersionByInterfaceName: called but mServiceManager is null");
return false;
}
try {
return (mIServiceManager.getTransport(
interfaceName,
HAL_INSTANCE_NAME)
!= IServiceManager.Transport.EMPTY);
} catch (RemoteException e) {
Log.e(TAG, "Exception while operating on IServiceManager: " + e);
handleRemoteException(e, "getTransport");
return false;
}
}
}
/**
* Helper method to look up the network object for the specified iface.
*/
private ISupplicantStaIface getStaIface(@NonNull String ifaceName) {
return mISupplicantStaIfaces.get(ifaceName);
}
/**
* Helper method to look up the network object for the specified iface.
*/
private SupplicantStaNetworkHal getCurrentNetworkRemoteHandle(@NonNull String ifaceName) {
return mCurrentNetworkRemoteHandles.get(ifaceName);
}
/**
* Helper method to look up the network config or the specified iface.
*/
protected WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) {
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<SupplicantStaNetworkHal, WifiConfiguration>
addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
synchronized (mLock) {
logi("addSupplicantStaNetwork via HIDL");
if (config == null) {
loge("Cannot add NULL network!");
return null;
}
SupplicantStaNetworkHal network = addNetwork(ifaceName);
if (network == null) {
loge("Failed to add a 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) {
loge("Failed to save variables for: " + config.getKey());
if (!removeAllNetworks(ifaceName)) {
loge("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 {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
logd("connectToNetwork " + config.getKey());
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
String networkSelectionBSSIDCurrent =
currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
logd("Network is already saved, will not trigger remove and add operation.");
} else {
logd("Network is already saved, but need to update BSSID.");
if (!setCurrentNetworkBssid(
ifaceName,
config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
loge("Failed to set current network BSSID.");
return false;
}
mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
}
} else {
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove existing networks");
return false;
}
Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, config);
if (pair == null) {
loge("Failed to add/save network configuration: " + config.getKey());
return false;
}
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
}
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
if (networkHandle == null) {
loge("No valid remote network handle for network configuration: "
+ config.getKey());
return false;
}
PmkCacheStoreData pmkData = mPmkCacheEntries.get(config.networkId);
if (pmkData != null
&& !WifiConfigurationUtil.isConfigForPskNetwork(config)
&& pmkData.expirationTimeInSec > mClock.getElapsedSinceBootMillis() / 1000) {
logi("Set PMK cache for config id " + config.networkId);
if (networkHandle.setPmkCache(pmkData.data)) {
mWifiMetrics.setConnectionPmkCache(true);
}
}
if (!networkHandle.select()) {
loge("Failed to select network configuration: " + config.getKey());
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 the same network as we currently have
* configured.
* 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 (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();
logd("roamToNetwork" + config.getKey() + " (bssid " + bssid + ")");
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(ifaceName, "roamToNetwork");
if (networkHandle == null || !networkHandle.setBssid(bssid)) {
loge("Failed to set new bssid on network: " + config.getKey());
return false;
}
if (!reassociate(ifaceName)) {
loge("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) {
logd("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) {
PmkCacheStoreData pmkData = mPmkCacheEntries.get(networkId);
if (pmkData == null) return;
if (curMacAddress.equals(pmkData.macAddress)) return;
removeNetworkCachedData(networkId);
}
}
/**
* Remove all networks from supplicant
*
* @param ifaceName Name of the interface.
*/
public boolean removeAllNetworks(@NonNull String ifaceName) {
synchronized (mLock) {
ArrayList<Integer> 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. Probably not needed once we add support to remove/reset
// current network on receiving disconnection event from supplicant (b/32898136).
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
return true;
}
}
/**
* 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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
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) {
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(
ifaceName, "sendCurrentNetworkEapSimUmtsAuthFailure");
if (networkHandle == null) return false;
return networkHandle.sendNetworkEapSimUmtsAuthFailure();
}
}
/**
* Adds a new network.
*
* @return The ISupplicantNetwork object for the new network, or null if the call fails
*/
private SupplicantStaNetworkHal addNetwork(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "addNetwork";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
try {
iface.addNetwork((SupplicantStatus status,
ISupplicantNetwork network) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
newNetwork.value = network;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
if (newNetwork.value != null) {
return getStaNetworkMockable(
ifaceName,
ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
} else {
return null;
}
}
}
/**
* Remove network from supplicant with network Id
*
* @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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.removeNetwork(id);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
/**
* Use this to mock the creation of SupplicantStaNetworkHal instance.
*
* @param ifaceName Name of the interface.
* @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
* @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
* the call fails
*/
protected SupplicantStaNetworkHal getStaNetworkMockable(
@NonNull String ifaceName, ISupplicantStaNetwork iSupplicantStaNetwork) {
synchronized (mLock) {
SupplicantStaNetworkHal network =
new SupplicantStaNetworkHal(iSupplicantStaNetwork, ifaceName, mContext,
mWifiMonitor);
if (network != null) {
network.enableVerboseLogging(mVerboseLoggingEnabled);
}
return network;
}
}
/**
* @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
* the call fails
*/
private SupplicantStaNetworkHal getNetwork(@NonNull String ifaceName, int id) {
synchronized (mLock) {
final String methodStr = "getNetwork";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
try {
iface.getNetwork(id, (SupplicantStatus status, ISupplicantNetwork network) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
gotNetwork.value = network;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
if (gotNetwork.value != null) {
return getStaNetworkMockable(
ifaceName,
ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
} else {
return null;
}
}
}
/** See ISupplicantStaNetwork.hal for documentation */
private boolean registerCallback(
ISupplicantStaIface iface, ISupplicantStaIfaceCallback callback) {
synchronized (mLock) {
final String methodStr = "registerCallback";
if (iface == null) return false;
try {
SupplicantStatus status = iface.registerCallback(callback);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
private boolean registerCallbackV1_1(
android.hardware.wifi.supplicant.V1_1.ISupplicantStaIface iface,
android.hardware.wifi.supplicant.V1_1.ISupplicantStaIfaceCallback callback) {
synchronized (mLock) {
String methodStr = "registerCallback_1_1";
if (iface == null) return false;
try {
SupplicantStatus status = iface.registerCallback_1_1(callback);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
private boolean registerCallbackV1_2(
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface iface,
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIfaceCallback callback) {
synchronized (mLock) {
String methodStr = "registerCallback_1_2";
if (iface == null) return false;
try {
SupplicantStatus status = iface.registerCallback_1_2(callback);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
private boolean registerCallbackV1_3(
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface iface,
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback callback) {
synchronized (mLock) {
String methodStr = "registerCallback_1_3";
if (iface == null) return false;
try {
SupplicantStatus status = iface.registerCallback_1_3(callback);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
/**
* @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
* null if the call fails
*/
private java.util.ArrayList<Integer> listNetworks(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "listNetworks";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
try {
iface.listNetworks((SupplicantStatus status, ArrayList<Integer> networkIds) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
networkIdList.value = networkIds;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return networkIdList.value;
}
}
/**
* 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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsDeviceName(name);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsDeviceType(type);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsManufacturer(manufacturer);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsModelName(modelName);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsModelNumber(modelNumber);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsSerialNumber(serialNumber);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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) {
short 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, short configMethods) {
synchronized (mLock) {
final String methodStr = "setWpsConfigMethods";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setWpsConfigMethods(configMethods);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.reassociate();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.reconnect();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.disconnect();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setPowerSave(enable);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean initiateTdlsDiscover(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsDiscover";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.initiateTdlsDiscover(macAddress);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean initiateTdlsSetup(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsSetup";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.initiateTdlsSetup(macAddress);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean initiateTdlsTeardown(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
synchronized (mLock) {
final String methodStr = "initiateTdlsTeardown";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.initiateTdlsTeardown(macAddress);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 {
return initiateAnqpQuery(
ifaceName,
NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + bssid, e);
return false;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean initiateAnqpQuery(@NonNull String ifaceName, byte[/* 6 */] macAddress,
java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
synchronized (mLock) {
final String methodStr = "initiateAnqpQuery";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.initiateAnqpQuery(
macAddress, infoElements, subTypes);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean initiateHs20IconQuery(@NonNull String ifaceName,
byte[/* 6 */] macAddress, String fileName) {
synchronized (mLock) {
final String methodStr = "initiateHs20IconQuery";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.initiateHs20IconQuery(macAddress, fileName);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
/**
* Makes a callback to HIDL to getMacAddress from supplicant
*
* @param ifaceName Name of the interface.
* @return string containing the MAC address, or null on a failed call
*/
public String getMacAddress(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "getMacAddress";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<String> gotMac = new Mutable<>();
try {
iface.getMacAddress((SupplicantStatus status,
byte[/* 6 */] macAddr) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return gotMac.value;
}
}
/**
* 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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.startRxFilter();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.stopRxFilter();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
break;
case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
halType = ISupplicantStaIface.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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.addRxFilter(type);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
break;
case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
halType = ISupplicantStaIface.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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.removeRxFilter(type);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
break;
case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
break;
case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
halMode = ISupplicantStaIface.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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setBtCoexistenceMode(mode);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status =
iface.setBtCoexistenceScanModeEnabled(enable);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setSuspendModeEnabled(enable);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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);
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean setCountryCode(@NonNull String ifaceName, byte[/* 2 */] code) {
synchronized (mLock) {
final String methodStr = "setCountryCode";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setCountryCode(code);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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";
if (isV1_3()) {
ISupplicantStaIface iface =
checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": ISupplicantStaIface is null, cannot flushAllHlp");
return false;
}
try {
SupplicantStatus status = staIfaceV13.filsHlpFlushRequest();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
} else {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
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";
if (isV1_3()) {
ISupplicantStaIface iface =
checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": ISupplicantStaIface is null, cannot addHlpReq");
return false;
}
try {
ArrayList<Byte> payload = NativeUtil.byteArrayToArrayList(hlpPacket);
SupplicantStatus status = staIfaceV13.filsHlpAddRequest(dst, payload);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
} else {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean startWpsRegistrar(@NonNull String ifaceName, byte[/* 6 */] bssid, String pin) {
synchronized (mLock) {
final String methodStr = "startWpsRegistrar";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.startWpsRegistrar(bssid, pin);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private boolean startWpsPbc(@NonNull String ifaceName, byte[/* 6 */] bssid) {
synchronized (mLock) {
final String methodStr = "startWpsPbc";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.startWpsPbc(bssid);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.startWpsPinKeypad(pin);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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;
}
}
}
/** See ISupplicantStaIface.hal for documentation */
private String startWpsPinDisplay(@NonNull String ifaceName, byte[/* 6 */] bssid) {
synchronized (mLock) {
final String methodStr = "startWpsPinDisplay";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
final Mutable<String> gotPin = new Mutable<>();
try {
iface.startWpsPinDisplay(bssid,
(SupplicantStatus status, String pin) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
gotPin.value = pin;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return gotPin.value;
}
}
/**
* 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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.cancelWps();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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 = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.setExternalSim(useExternalSim);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
/** See ISupplicant.hal for documentation */
public boolean enableAutoReconnect(@NonNull String ifaceName, boolean enable) {
synchronized (mLock) {
final String methodStr = "enableAutoReconnect";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.enableAutoReconnect(enable);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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
? ISupplicant.DebugLevel.DEBUG
: ISupplicant.DebugLevel.INFO;
return setDebugParams(logLevel, false, false);
}
}
/** See ISupplicant.hal for documentation */
private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
synchronized (mLock) {
final String methodStr = "setDebugParams";
if (!checkSupplicantAndLogFailure(methodStr)) return false;
try {
SupplicantStatus status =
mISupplicant.setDebugParams(level, showTimestamp, showKeys);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(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);
}
}
}
/** See ISupplicant.hal for documentation */
private boolean setConcurrencyPriority(int type) {
synchronized (mLock) {
final String methodStr = "setConcurrencyPriority";
if (!checkSupplicantAndLogFailure(methodStr)) return false;
try {
SupplicantStatus status = mISupplicant.setConcurrencyPriority(type);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
/**
* Returns false if Supplicant is null, and logs failure to call methodStr
*/
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 false if SupplicantStaIface is null, and logs failure to call methodStr
*/
private ISupplicantStaIface checkSupplicantStaIfaceAndLogFailure(
@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");
return null;
}
return iface;
}
}
/**
* Returns false if SupplicantStaNetwork is null, and logs failure to call methodStr
*/
private SupplicantStaNetworkHal checkSupplicantStaNetworkAndLogFailure(
@NonNull String ifaceName, final String methodStr) {
synchronized (mLock) {
SupplicantStaNetworkHal networkHal = getCurrentNetworkRemoteHandle(ifaceName);
if (networkHal == null) {
Log.e(TAG, "Can't call " + methodStr + ", SupplicantStaNetwork is null");
return null;
}
return networkHal;
}
}
/**
* Returns true if provided status code is SUCCESS, logs debug message and returns false
* otherwise
*/
private boolean checkStatusAndLogFailure(SupplicantStatus status,
final String methodStr) {
synchronized (mLock) {
if (status == null || status.code != SupplicantStatusCode.SUCCESS) {
Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: " + status);
return false;
} else {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
}
return true;
}
}
}
/**
* Helper function to log callbacks.
*/
protected void logCallback(final String methodStr) {
synchronized (mLock) {
if (mVerboseLoggingEnabled) {
Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
}
}
}
private void handleNoSuchElementException(NoSuchElementException e, String methodStr) {
synchronized (mLock) {
clearState();
Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
}
}
private void handleRemoteException(RemoteException e, String methodStr) {
synchronized (mLock) {
clearState();
Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
}
}
private void handleIllegalArgumentException(IllegalArgumentException e, String methodStr) {
synchronized (mLock) {
clearState();
Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
}
}
/**
* Converts the Wps config method string to the equivalent enum value.
*/
private static short 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 class SupplicantStaIfaceHalCallback extends SupplicantStaIfaceCallbackImpl {
SupplicantStaIfaceHalCallback(@NonNull String ifaceName) {
super(SupplicantStaIfaceHal.this, ifaceName, mLock, mWifiMonitor);
}
}
protected class SupplicantStaIfaceHalCallbackV1_1 extends SupplicantStaIfaceCallbackV1_1Impl {
SupplicantStaIfaceHalCallbackV1_1(@NonNull String ifaceName) {
super(SupplicantStaIfaceHal.this, ifaceName, mLock, mWifiMonitor);
}
}
protected class SupplicantStaIfaceHalCallbackV1_2 extends SupplicantStaIfaceCallbackV1_2Impl {
SupplicantStaIfaceHalCallbackV1_2(@NonNull String ifaceName) {
super(SupplicantStaIfaceHal.this, ifaceName, mContext);
}
}
protected class SupplicantStaIfaceHalCallbackV1_3 extends SupplicantStaIfaceCallbackV1_3Impl {
SupplicantStaIfaceHalCallbackV1_3(@NonNull String ifaceName) {
super(SupplicantStaIfaceHal.this, ifaceName, mWifiMonitor);
}
}
protected void addPmkCacheEntry(
String ifaceName,
int networkId, long expirationTimeInSec, ArrayList<Byte> serializedEntry) {
String macAddressStr = getMacAddress(ifaceName);
if (macAddressStr == null) {
Log.w(TAG, "Omit PMK cache due to no valid MAC address on " + ifaceName);
return;
}
try {
MacAddress macAddress = MacAddress.fromString(macAddressStr);
mPmkCacheEntries.put(networkId,
new PmkCacheStoreData(expirationTimeInSec, serializedEntry, macAddress));
updatePmkCacheExpiration();
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Invalid MAC address string " + macAddressStr);
}
}
protected void removePmkCacheEntry(int networkId) {
if (mPmkCacheEntries.remove(networkId) != null) {
updatePmkCacheExpiration();
}
}
private void updatePmkCacheExpiration() {
synchronized (mLock) {
mEventHandler.removeCallbacksAndMessages(PMK_CACHE_EXPIRATION_ALARM_TAG);
long elapseTimeInSecond = mClock.getElapsedSinceBootMillis() / 1000;
long nextUpdateTimeInSecond = Long.MAX_VALUE;
logd("Update PMK cache expiration at " + elapseTimeInSecond);
Iterator<Map.Entry<Integer, PmkCacheStoreData>> iter =
mPmkCacheEntries.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Integer, PmkCacheStoreData> entry = iter.next();
if (entry.getValue().expirationTimeInSec <= elapseTimeInSecond) {
logd("Config " + entry.getKey() + " PMK is expired.");
iter.remove();
} else if (entry.getValue().expirationTimeInSec <= 0) {
logd("Config " + entry.getKey() + " PMK expiration time is invalid.");
iter.remove();
} else if (nextUpdateTimeInSecond > entry.getValue().expirationTimeInSec) {
nextUpdateTimeInSecond = entry.getValue().expirationTimeInSec;
}
}
// No need to arrange next update since there is no valid PMK in the cache.
if (nextUpdateTimeInSecond == Long.MAX_VALUE) {
return;
}
logd("PMK cache next expiration time: " + nextUpdateTimeInSecond);
long delayedTimeInMs = (nextUpdateTimeInSecond - elapseTimeInSecond) * 1000;
mEventHandler.postDelayed(
() -> {
updatePmkCacheExpiration();
},
PMK_CACHE_EXPIRATION_ALARM_TAG,
(delayedTimeInMs > 0) ? delayedTimeInMs : 0);
}
}
private static void logd(String s) {
Log.d(TAG, s);
}
private static void logi(String s) {
Log.i(TAG, s);
}
private static void loge(String s) {
Log.e(TAG, s);
}
/**
* Returns a bitmask of advanced key management capabilities: WPA3 SAE/SUITE B and OWE
* Bitmask used is:
* - WIFI_FEATURE_WPA3_SAE
* - WIFI_FEATURE_WPA3_SUITE_B
* - WIFI_FEATURE_OWE
*
* This is a v1.2+ HAL feature.
* On error, or if these features are not supported, 0 is returned.
*/
public long getAdvancedKeyMgmtCapabilities(@NonNull String ifaceName) {
final String methodStr = "getAdvancedKeyMgmtCapabilities";
long advancedCapabilities = 0;
int keyMgmtCapabilities = getKeyMgmtCapabilities(ifaceName);
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork
.KeyMgmtMask.SAE) != 0) {
advancedCapabilities |= WIFI_FEATURE_WPA3_SAE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": SAE supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork
.KeyMgmtMask.SUITE_B_192) != 0) {
advancedCapabilities |= WIFI_FEATURE_WPA3_SUITE_B;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": SUITE_B supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork
.KeyMgmtMask.OWE) != 0) {
advancedCapabilities |= WIFI_FEATURE_OWE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": OWE supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork
.KeyMgmtMask.DPP) != 0) {
advancedCapabilities |= WIFI_FEATURE_DPP;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": DPP supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork
.KeyMgmtMask.WAPI_PSK) != 0) {
advancedCapabilities |= WIFI_FEATURE_WAPI;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": WAPI supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork
.KeyMgmtMask.FILS_SHA256) != 0) {
advancedCapabilities |= WIFI_FEATURE_FILS_SHA256;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": FILS_SHA256 supported");
}
}
if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork
.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_1_3(@NonNull String ifaceName) {
final String methodStr = "getKeyMgmtCapabilities_1_3";
MutableInt keyMgmtMask = new MutableInt(0);
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return 0;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": ISupplicantStaIface V1.3 is null, cannot get advanced capabilities");
return 0;
}
try {
// Support for new key management types; WAPI_PSK, WAPI_CERT
// Requires HAL v1.3 or higher
staIfaceV13.getKeyMgmtCapabilities_1_3(
(SupplicantStatus statusInternal, int keyMgmtMaskInternal) -> {
if (statusInternal.code == SupplicantStatusCode.SUCCESS) {
keyMgmtMask.value = keyMgmtMaskInternal;
}
checkStatusAndLogFailure(statusInternal, methodStr);
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return keyMgmtMask.value;
}
private int getKeyMgmtCapabilities(@NonNull String ifaceName) {
final String methodStr = "getKeyMgmtCapabilities";
MutableBoolean status = new MutableBoolean(false);
MutableInt keyMgmtMask = new MutableInt(0);
if (isV1_3()) {
keyMgmtMask.value = getKeyMgmtCapabilities_1_3(ifaceName);
} else if (isV1_2()) {
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return 0;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr
+ ": ISupplicantStaIface is null, cannot get advanced capabilities");
return 0;
}
try {
// Support for new key management types; SAE, SUITE_B, OWE
// Requires HAL v1.2 or higher
staIfaceV12.getKeyMgmtCapabilities(
(SupplicantStatus statusInternal, int keyMgmtMaskInternal) -> {
status.value = statusInternal.code == SupplicantStatusCode.SUCCESS;
if (status.value) {
keyMgmtMask.value = keyMgmtMaskInternal;
}
checkStatusAndLogFailure(statusInternal, methodStr);
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
} else {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
}
// 0 is returned in case of an error
return keyMgmtMask.value;
}
/**
* 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) {
final String methodStr = "getWpaDriverFeatureSet";
MutableInt drvCapabilitiesMask = new MutableInt(0);
long featureSet = 0;
if (isV1_3()) {
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return 0;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": SupplicantStaIface is null, cannot get wpa driver features");
return 0;
}
try {
staIfaceV13.getWpaDriverCapabilities(
(SupplicantStatus statusInternal, int drvCapabilities) -> {
if (statusInternal.code == SupplicantStatusCode.SUCCESS) {
drvCapabilitiesMask.value = drvCapabilities;
}
checkStatusAndLogFailure(statusInternal, methodStr);
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
} else {
Log.i(TAG, "Method " + methodStr + " is not supported in existing HAL");
return 0;
}
if ((drvCapabilitiesMask.value & WpaDriverCapabilitiesMask.MBO) != 0) {
featureSet |= WIFI_FEATURE_MBO;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": MBO supported");
}
if ((drvCapabilitiesMask.value
& WpaDriverCapabilitiesMask.OCE) != 0) {
featureSet |= WIFI_FEATURE_OCE;
if (mVerboseLoggingEnabled) {
Log.v(TAG, methodStr + ": OCE supported");
}
}
}
return featureSet;
}
private @WifiStandard int getWifiStandardFromCap(ConnectionCapabilities capa) {
switch(capa.technology) {
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 getChannelBandwidthFromCap(ConnectionCapabilities cap) {
switch(cap.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;
default:
return ScanResult.CHANNEL_WIDTH_20MHZ;
}
}
/**
* Returns connection capabilities of the current network
*
* This is a v1.3+ HAL feature.
* @param ifaceName Name of the interface.
* @return connection capabilities of the current network
*/
public WifiNative.ConnectionCapabilities getConnectionCapabilities(@NonNull String ifaceName) {
final String methodStr = "getConnectionCapabilities";
WifiNative.ConnectionCapabilities capOut = new WifiNative.ConnectionCapabilities();
if (isV1_3()) {
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return capOut;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": SupplicantStaIface is null, cannot get Connection Capabilities");
return capOut;
}
try {
staIfaceV13.getConnectionCapabilities(
(SupplicantStatus statusInternal, ConnectionCapabilities cap) -> {
if (statusInternal.code == SupplicantStatusCode.SUCCESS) {
capOut.wifiStandard = getWifiStandardFromCap(cap);
capOut.channelBandwidth = getChannelBandwidthFromCap(cap);
capOut.maxNumberTxSpatialStreams = cap.maxNumberTxSpatialStreams;
capOut.maxNumberRxSpatialStreams = cap.maxNumberRxSpatialStreams;
}
checkStatusAndLogFailure(statusInternal, methodStr);
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
} else {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
}
return capOut;
}
/**
* Adds a DPP peer URI to the URI list.
*
* This is a v1.2+ HAL feature.
* Returns an ID to be used later to refer to this URI (>0).
* On error, or if these features are not supported, -1 is returned.
*/
public int addDppPeerUri(@NonNull String ifaceName, @NonNull String uri) {
final String methodStr = "addDppPeerUri";
MutableBoolean status = new MutableBoolean(false);
MutableInt bootstrapId = new MutableInt(-1);
if (!isV1_2()) {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
return -1;
}
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return -1;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr + ": ISupplicantStaIface is null");
return -1;
}
try {
// Support for DPP (Easy connect)
// Requires HAL v1.2 or higher
staIfaceV12.addDppPeerUri(uri,
(SupplicantStatus statusInternal, int bootstrapIdInternal) -> {
status.value = statusInternal.code == SupplicantStatusCode.SUCCESS;
if (status.value) {
bootstrapId.value = bootstrapIdInternal;
}
checkStatusAndLogFailure(statusInternal, methodStr);
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return -1;
}
return bootstrapId.value;
}
/**
* Removes a DPP URI to the URI list given an ID.
*
* This is a v1.2+ HAL feature.
* Returns true when operation is successful
* On error, or if these features are not supported, false is returned.
*/
public boolean removeDppUri(@NonNull String ifaceName, int bootstrapId) {
final String methodStr = "removeDppUri";
if (!isV1_2()) {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
return false;
}
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr + ": ISupplicantStaIface is null");
return false;
}
try {
// Support for DPP (Easy connect)
// Requires HAL v1.2 or higher
SupplicantStatus status = staIfaceV12.removeDppUri(bootstrapId);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return false;
}
/**
* Stops/aborts DPP Initiator request
*
* This is a v1.2+ HAL feature.
* Returns true when operation is successful
* On error, or if these features are not supported, false is returned.
*/
public boolean stopDppInitiator(@NonNull String ifaceName) {
final String methodStr = "stopDppInitiator";
if (!isV1_2()) {
return false;
}
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr + ": ISupplicantStaIface is null");
return false;
}
try {
// Support for DPP (Easy connect)
// Requires HAL v1.2 or higher
SupplicantStatus status = staIfaceV12.stopDppInitiator();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return false;
}
/**
* Starts DPP Configurator-Initiator request
*
* This is a v1.2+ HAL feature.
* Returns true when operation is successful
* On error, or if these features are not supported, false is returned.
*/
public boolean startDppConfiguratorInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId, @NonNull String ssid, String password, String psk,
int netRole, int securityAkm) {
final String methodStr = "startDppConfiguratorInitiator";
if (!isV1_2()) {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
return false;
}
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr + ": ISupplicantStaIface is null");
return false;
}
try {
// Support for DPP (Easy connect)
// Requires HAL v1.2 or higher
SupplicantStatus status = staIfaceV12.startDppConfiguratorInitiator(peerBootstrapId,
ownBootstrapId, ssid, password != null ? password : "", psk != null ? psk : "",
netRole, securityAkm);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return false;
}
/**
* Starts DPP Enrollee-Initiator request
*
* This is a v1.2+ HAL feature.
* Returns true when operation is successful
* On error, or if these features are not supported, false is returned.
*/
public boolean startDppEnrolleeInitiator(@NonNull String ifaceName, int peerBootstrapId,
int ownBootstrapId) {
final String methodStr = "startDppEnrolleeInitiator";
if (!isV1_2()) {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
return false;
}
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.2 supplicant STA Interface
android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface staIfaceV12 =
getStaIfaceMockableV1_2(iface);
if (staIfaceV12 == null) {
Log.e(TAG, methodStr + ": ISupplicantStaIface is null");
return false;
}
try {
// Support for DPP (Easy connect)
// Requires HAL v1.2 or higher
SupplicantStatus status = staIfaceV12.startDppEnrolleeInitiator(peerBootstrapId,
ownBootstrapId);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return false;
}
/**
* Register callbacks for DPP events.
*
* @param dppCallback DPP callback object.
*/
public void registerDppCallback(DppEventCallback dppCallback) {
mDppCallback = dppCallback;
}
protected DppEventCallback getDppCallback() {
return mDppCallback;
}
/**
* Set MBO cellular data availability.
*
* @param ifaceName Name of the interface.
* @param available true means cellular data available, false otherwise.
* @return None.
*/
public boolean setMboCellularDataStatus(@NonNull String ifaceName, boolean available) {
final String methodStr = "setMboCellularDataStatus";
if (isV1_3()) {
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) {
return false;
}
// Get a v1.3 supplicant STA Interface
android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 =
getStaIfaceMockableV1_3(iface);
if (staIfaceV13 == null) {
Log.e(TAG, methodStr
+ ": SupplicantStaIface is null, cannot update cell status");
return false;
}
try {
SupplicantStatus status = staIfaceV13.setMboCellularDataStatus(available);
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
} else {
Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL");
return false;
}
return false;
}
}