blob: 891595f8dde38d4105a3474db647b5e9201c7bf3 [file] [log] [blame]
/*
* Copyright (C) 2016 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.p2p;
import android.annotation.NonNull;
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.ISupplicantP2pIface;
import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork;
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.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pGroupList;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
import android.os.HwRemoteBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
import com.android.server.wifi.util.NativeUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events
*
* {@hide}
*/
public class SupplicantP2pIfaceHal {
private static final String TAG = "SupplicantP2pIfaceHal";
private static boolean sVerboseLoggingEnabled = true;
private static final int RESULT_NOT_VALID = -1;
private static final int DEFAULT_GROUP_OWNER_INTENT = 6;
private static final int DEFAULT_OPERATING_CLASS = 81;
/**
* 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 Object mLock = new Object();
// Supplicant HAL HIDL interface objects
private IServiceManager mIServiceManager = null;
private ISupplicant mISupplicant = null;
private ISupplicantIface mHidlSupplicantIface = null;
private ISupplicantP2pIface mISupplicantP2pIface = null;
private final IServiceNotification mServiceNotificationCallback =
new IServiceNotification.Stub() {
public void onRegistration(String fqName, String name, boolean preexisting) {
synchronized (mLock) {
if (sVerboseLoggingEnabled) {
Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
+ ", " + name + " preexisting=" + preexisting);
}
if (!initSupplicantService()) {
Log.e(TAG, "initalizing ISupplicant failed.");
supplicantServiceDiedHandler();
} else {
Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
}
}
}
};
private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
cookie -> {
Log.w(TAG, "IServiceManager died: cookie=" + cookie);
synchronized (mLock) {
supplicantServiceDiedHandler();
mIServiceManager = null; // Will need to register a new ServiceNotification
}
};
private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
cookie -> {
Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
synchronized (mLock) {
supplicantServiceDiedHandler();
}
};
private final WifiP2pMonitor mMonitor;
private SupplicantP2pIfaceCallback mCallback = null;
public SupplicantP2pIfaceHal(WifiP2pMonitor monitor) {
mMonitor = monitor;
}
private boolean linkToServiceManagerDeath() {
if (mIServiceManager == null) return false;
try {
if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
supplicantServiceDiedHandler();
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;
}
/**
* Enable verbose logging for all sub modules.
*/
public static void enableVerboseLogging(int verbose) {
sVerboseLoggingEnabled = verbose > 0;
SupplicantP2pIfaceCallback.enableVerboseLogging(verbose);
}
/**
* Registers a service notification for the ISupplicant service, which triggers intialization of
* the ISupplicantP2pIface
* @return true if the service notification was successfully registered
*/
public boolean initialize() {
if (sVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback.");
synchronized (mLock) {
if (mIServiceManager != null) {
Log.i(TAG, "Supplicant HAL already initialized.");
// Already have an IServiceManager and serviceNotification registered, don't
// don't register another.
return true;
}
mISupplicant = null;
mISupplicantP2pIface = null;
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;
}
// Successful completion by the end of the 'try' block. This will prevent reporting
// proper initialization after exception is caught.
return true;
} catch (RemoteException e) {
Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
+ e);
supplicantServiceDiedHandler();
}
return false;
}
}
private boolean linkToSupplicantDeath() {
if (mISupplicant == null) return false;
try {
if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
supplicantServiceDiedHandler();
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;
}
if (mISupplicant == null) {
Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
return false;
}
if (!linkToSupplicantDeath()) {
return false;
}
}
return true;
}
private boolean linkToSupplicantP2pIfaceDeath() {
if (mISupplicantP2pIface == null) return false;
try {
if (!mISupplicantP2pIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
Log.wtf(TAG, "Error on linkToDeath on ISupplicantP2pIface");
supplicantServiceDiedHandler();
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface.linkToDeath exception", e);
return false;
}
return true;
}
/**
* Setup the P2p iface.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
public boolean setupIface(@NonNull String ifaceName) {
synchronized (mLock) {
if (mISupplicantP2pIface != null) return false;
ISupplicantIface ifaceHwBinder;
if (isV1_1()) {
ifaceHwBinder = addIfaceV1_1(ifaceName);
} else {
ifaceHwBinder = getIfaceV1_0(ifaceName);
}
if (ifaceHwBinder == null) {
Log.e(TAG, "initSupplicantP2pIface got null iface");
return false;
}
mISupplicantP2pIface = getP2pIfaceMockable(ifaceHwBinder);
if (!linkToSupplicantP2pIfaceDeath()) {
return false;
}
if (mISupplicantP2pIface != null && mMonitor != null) {
mCallback = new SupplicantP2pIfaceCallback(ifaceName, mMonitor);
if (!registerCallback(mCallback)) {
Log.e(TAG, "Callback registration failed. Initialization incomplete.");
return false;
}
}
return true;
}
}
private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) {
/** 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);
return null;
}
if (supplicantIfaces.size() == 0) {
Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
supplicantServiceDiedHandler();
return null;
}
SupplicantResult<ISupplicantIface> supplicantIface =
new SupplicantResult("getInterface()");
for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
if (ifaceInfo.type == IfaceType.P2P && 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.setResult(status, iface);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.getInterface exception: " + e);
supplicantServiceDiedHandler();
return null;
}
break;
}
}
return supplicantIface.getResult();
}
private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) {
synchronized (mLock) {
ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
ifaceInfo.name = ifaceName;
ifaceInfo.type = IfaceType.P2P;
SupplicantResult<ISupplicantIface> supplicantIface =
new SupplicantResult("addInterface(" + ifaceInfo + ")");
try {
android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 =
getSupplicantMockableV1_1();
if (supplicant_v1_1 == null) {
Log.e(TAG, "Can't call addIface: ISupplicantP2pIface is null");
return null;
}
supplicant_v1_1.addInterface(ifaceInfo,
(SupplicantStatus status, ISupplicantIface iface) -> {
if (status.code != SupplicantStatusCode.SUCCESS
&& status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) {
Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
return;
}
supplicantIface.setResult(status, iface);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.addInterface exception: " + e);
supplicantServiceDiedHandler();
return null;
}
return supplicantIface.getResult();
}
}
/**
* Teardown the P2P interface.
*
* @param ifaceName Name of the interface.
* @return true on success, false otherwise.
*/
public boolean teardownIface(@NonNull String ifaceName) {
synchronized (mLock) {
if (mISupplicantP2pIface == null) return false;
// Only supported for V1.1
if (isV1_1()) {
return removeIfaceV1_1(ifaceName);
}
return true;
}
}
/**
* Remove the P2p iface.
*
* @return true on success, false otherwise.
*/
private boolean removeIfaceV1_1(@NonNull String ifaceName) {
synchronized (mLock) {
try {
android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 =
getSupplicantMockableV1_1();
if (supplicant_v1_1 == null) {
Log.e(TAG, "Can't call removeIface: ISupplicantP2pIface is null");
return false;
}
ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo();
ifaceInfo.name = ifaceName;
ifaceInfo.type = IfaceType.P2P;
SupplicantStatus status = supplicant_v1_1.removeInterface(ifaceInfo);
if (status.code != SupplicantStatusCode.SUCCESS) {
Log.e(TAG, "Failed to remove iface " + status.code);
return false;
}
mCallback = null;
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.removeInterface exception: " + e);
supplicantServiceDiedHandler();
return false;
}
mISupplicantP2pIface = null;
return true;
}
}
private void supplicantServiceDiedHandler() {
synchronized (mLock) {
mISupplicant = null;
mISupplicantP2pIface = null;
}
}
/**
* Signals whether Initialization completed successfully.
*/
public boolean isInitializationStarted() {
synchronized (mLock) {
return mIServiceManager != null;
}
}
/**
* Signals whether Initialization completed successfully. Only necessary for testing, is not
* needed to guard calls etc.
*/
public boolean isInitializationComplete() {
return mISupplicant != null;
}
/**
* Wrapper functions to access static HAL methods, created to be mockable in unit tests
*/
protected IServiceManager getServiceManagerMockable() throws RemoteException {
return IServiceManager.getService();
}
protected ISupplicant getSupplicantMockable() throws RemoteException {
try {
return ISupplicant.getService();
} catch (NoSuchElementException e) {
Log.e(TAG, "Failed to get ISupplicant", e);
return null;
}
}
protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1()
throws RemoteException {
synchronized (mLock) {
try {
return android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom(
ISupplicant.getService());
} catch (NoSuchElementException e) {
Log.e(TAG, "Failed to get ISupplicant", e);
return null;
}
}
}
protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) {
return ISupplicantP2pIface.asInterface(iface.asBinder());
}
protected android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface
getP2pIfaceMockableV1_2() {
if (mISupplicantP2pIface == null) return null;
return android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface.castFrom(
mISupplicantP2pIface);
}
protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) {
return ISupplicantP2pNetwork.asInterface(network.asBinder());
}
/**
* Check if the device is running V1_1 supplicant service.
* @return
*/
private boolean isV1_1() {
synchronized (mLock) {
try {
return (getSupplicantMockableV1_1() != null);
} catch (RemoteException e) {
Log.e(TAG, "ISupplicant.getService exception: " + e);
supplicantServiceDiedHandler();
return false;
}
}
}
protected static void logd(String s) {
if (sVerboseLoggingEnabled) Log.d(TAG, s);
}
protected static void logCompletion(String operation, SupplicantStatus status) {
if (status == null) {
Log.w(TAG, operation + " failed: no status code returned.");
} else if (status.code == SupplicantStatusCode.SUCCESS) {
logd(operation + " completed successfully.");
} else {
Log.w(TAG, operation + " failed: " + status.code + " (" + status.debugMessage + ")");
}
}
/**
* Returns false if SupplicantP2pIface is null, and logs failure to call methodStr
*/
private boolean checkSupplicantP2pIfaceAndLogFailure(String method) {
if (mISupplicantP2pIface == null) {
Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
return false;
}
return true;
}
/**
* Returns false if SupplicantP2pIface is null, and logs failure to call methodStr
*/
private boolean checkSupplicantP2pIfaceAndLogFailureV1_2(String method) {
if (getP2pIfaceMockableV1_2() == null) {
Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
return false;
}
return true;
}
private int wpsInfoToConfigMethod(int info) {
switch (info) {
case WpsInfo.PBC:
return ISupplicantP2pIface.WpsProvisionMethod.PBC;
case WpsInfo.DISPLAY:
return ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
case WpsInfo.KEYPAD:
case WpsInfo.LABEL:
return ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
default:
Log.e(TAG, "Unsupported WPS provision method: " + info);
return RESULT_NOT_VALID;
}
}
/**
* Retrieves the name of the network interface.
*
* @return name Name of the network interface, e.g., wlan0
*/
public String getName() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getName")) return null;
SupplicantResult<String> result = new SupplicantResult("getName()");
try {
mISupplicantP2pIface.getName(
(SupplicantStatus status, String name) -> {
result.setResult(status, name);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.getResult();
}
}
/**
* Register for callbacks from this interface.
*
* These callbacks are invoked for events that are specific to this interface.
* Registration of multiple callback objects is supported. These objects must
* be automatically deleted when the corresponding client process is dead or
* if this interface is removed.
*
* @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL
* interface object.
* @return boolean value indicating whether operation was successful.
*/
public boolean registerCallback(ISupplicantP2pIfaceCallback receiver) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("registerCallback")) return false;
SupplicantResult<Void> result = new SupplicantResult("registerCallback()");
try {
result.setResult(mISupplicantP2pIface.registerCallback(receiver));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Initiate a P2P service discovery with a (optional) timeout.
*
* @param timeout Max time to be spent is peforming discovery.
* Set to 0 to indefinely continue discovery untill and explicit
* |stopFind| is sent.
* @return boolean value indicating whether operation was successful.
*/
public boolean find(int timeout) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("find")) return false;
if (timeout < 0) {
Log.e(TAG, "Invalid timeout value: " + timeout);
return false;
}
SupplicantResult<Void> result = new SupplicantResult("find(" + timeout + ")");
try {
result.setResult(mISupplicantP2pIface.find(timeout));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Stop an ongoing P2P service discovery.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean stopFind() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("stopFind")) return false;
SupplicantResult<Void> result = new SupplicantResult("stopFind()");
try {
result.setResult(mISupplicantP2pIface.stopFind());
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Flush P2P peer table and state.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean flush() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("flush")) return false;
SupplicantResult<Void> result = new SupplicantResult("flush()");
try {
result.setResult(mISupplicantP2pIface.flush());
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* This command can be used to flush all services from the
* device.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean serviceFlush() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("serviceFlush")) return false;
SupplicantResult<Void> result = new SupplicantResult("serviceFlush()");
try {
result.setResult(mISupplicantP2pIface.flushServices());
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Turn on/off power save mode for the interface.
*
* @param groupIfName Group interface name to use.
* @param enable Indicate if power save is to be turned on/off.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean setPowerSave(String groupIfName, boolean enable) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setPowerSave")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"setPowerSave(" + groupIfName + ", " + enable + ")");
try {
result.setResult(mISupplicantP2pIface.setPowerSave(groupIfName, enable));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set the Maximum idle time in seconds for P2P groups.
* This value controls how long a P2P group is maintained after there
* is no other members in the group. As a group owner, this means no
* associated stations in the group. As a P2P client, this means no
* group owner seen in scan results.
*
* @param groupIfName Group interface name to use.
* @param timeoutInSec Timeout value in seconds.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean setGroupIdle(String groupIfName, int timeoutInSec) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setGroupIdle")) return false;
// Basic checking here. Leave actual parameter validation to supplicant.
if (timeoutInSec < 0) {
Log.e(TAG, "Invalid group timeout value " + timeoutInSec);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"setGroupIdle(" + groupIfName + ", " + timeoutInSec + ")");
try {
result.setResult(mISupplicantP2pIface.setGroupIdle(groupIfName, timeoutInSec));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set the postfix to be used for P2P SSID's.
*
* @param postfix String to be appended to SSID.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean setSsidPostfix(String postfix) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return false;
// Basic checking here. Leave actual parameter validation to supplicant.
if (postfix == null) {
Log.e(TAG, "Invalid SSID postfix value (null).");
return false;
}
SupplicantResult<Void> result = new SupplicantResult("setSsidPostfix(" + postfix + ")");
try {
result.setResult(mISupplicantP2pIface.setSsidPostfix(
NativeUtil.decodeSsid("\"" + postfix + "\"")));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
} catch (IllegalArgumentException e) {
Log.e(TAG, "Could not decode SSID.", e);
return false;
}
return result.isSuccess();
}
}
/**
* Start P2P group formation with a discovered P2P peer. This includes
* optional group owner negotiation, group interface setup, provisioning,
* and establishing data connection.
*
* @param config Configuration to use to connect to remote device.
* @param joinExistingGroup Indicates that this is a command to join an
* existing group as a client. It skips the group owner negotiation
* part. This must send a Provision Discovery Request message to the
* target group owner before associating for WPS provisioning.
*
* @return String containing generated pin, if selected provision method
* uses PIN.
*/
public String connect(WifiP2pConfig config, boolean joinExistingGroup) {
if (config == null) return null;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return null;
if (config == null) {
Log.e(TAG, "Could not connect: null config.");
return null;
}
if (config.deviceAddress == null) {
Log.e(TAG, "Could not parse null mac address.");
return null;
}
if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) {
Log.e(TAG, "Expected empty pin for PBC.");
return null;
}
byte[] peerAddress = null;
try {
peerAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse peer mac address.", e);
return null;
}
int provisionMethod = wpsInfoToConfigMethod(config.wps.setup);
if (provisionMethod == RESULT_NOT_VALID) {
Log.e(TAG, "Invalid WPS config method: " + config.wps.setup);
return null;
}
// NOTE: preSelectedPin cannot be null, otherwise hal would crash.
String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin;
boolean persistent = (config.netId == WifiP2pGroup.PERSISTENT_NET_ID);
int goIntent = 0;
if (!joinExistingGroup) {
int groupOwnerIntent = config.groupOwnerIntent;
if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
}
goIntent = groupOwnerIntent;
}
SupplicantResult<String> result = new SupplicantResult(
"connect(" + config.deviceAddress + ")");
try {
mISupplicantP2pIface.connect(
peerAddress, provisionMethod, preSelectedPin, joinExistingGroup,
persistent, goIntent,
(SupplicantStatus status, String generatedPin) -> {
result.setResult(status, generatedPin);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.getResult();
}
}
/**
* Cancel an ongoing P2P group formation and joining-a-group related
* operation. This operation unauthorizes the specific peer device (if any
* had been authorized to start group formation), stops P2P find (if in
* progress), stops pending operations for join-a-group, and removes the
* P2P group interface (if one was used) that is in the WPS provisioning
* step. If the WPS provisioning step has been completed, the group is not
* terminated.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean cancelConnect() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("cancelConnect")) return false;
SupplicantResult<Void> result = new SupplicantResult("cancelConnect()");
try {
result.setResult(mISupplicantP2pIface.cancelConnect());
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Send P2P provision discovery request to the specified peer. The
* parameters for this command are the P2P device address of the peer and the
* desired configuration method.
*
* @param config Config class describing peer setup.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean provisionDiscovery(WifiP2pConfig config) {
if (config == null) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("provisionDiscovery")) return false;
int targetMethod = wpsInfoToConfigMethod(config.wps.setup);
if (targetMethod == RESULT_NOT_VALID) {
Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup);
return false;
}
if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) {
// We are doing display, so provision discovery is keypad.
targetMethod = ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
} else if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.KEYPAD) {
// We are doing keypad, so provision discovery is display.
targetMethod = ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
}
if (config.deviceAddress == null) {
Log.e(TAG, "Cannot parse null mac address.");
return false;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse peer mac address.", e);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"provisionDiscovery(" + config.deviceAddress + ", " + config.wps.setup + ")");
try {
result.setResult(mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Invite a device to a persistent group.
* If the peer device is the group owner of the persistent group, the peer
* parameter is not needed. Otherwise it is used to specify which
* device to invite. |goDeviceAddress| parameter may be used to override
* the group owner device address for Invitation Request should it not be
* known for some reason (this should not be needed in most cases).
*
* @param group Group object to use.
* @param peerAddress MAC address of the device to invite.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean invite(WifiP2pGroup group, String peerAddress) {
if (TextUtils.isEmpty(peerAddress)) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("invite")) return false;
if (group == null) {
Log.e(TAG, "Cannot invite to null group.");
return false;
}
if (group.getOwner() == null) {
Log.e(TAG, "Cannot invite to group with null owner.");
return false;
}
if (group.getOwner().deviceAddress == null) {
Log.e(TAG, "Group owner has no mac address.");
return false;
}
byte[] ownerMacAddress = null;
try {
ownerMacAddress = NativeUtil.macAddressToByteArray(group.getOwner().deviceAddress);
} catch (Exception e) {
Log.e(TAG, "Group owner mac address parse error.", e);
return false;
}
if (peerAddress == null) {
Log.e(TAG, "Cannot parse peer mac address.");
return false;
}
byte[] peerMacAddress;
try {
peerMacAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Peer mac address parse error.", e);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"invite(" + group.getInterface() + ", " + group.getOwner().deviceAddress
+ ", " + peerAddress + ")");
try {
result.setResult(mISupplicantP2pIface.invite(
group.getInterface(), ownerMacAddress, peerMacAddress));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Reject connection attempt from a peer (specified with a device
* address). This is a mechanism to reject a pending group owner negotiation
* with a peer and request to automatically block any further connection or
* discovery of the peer.
*
* @param peerAddress MAC address of the device to reject.
*
* @return boolean value indicating whether operation was successful.
*/
public boolean reject(String peerAddress) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("reject")) return false;
if (peerAddress == null) {
Log.e(TAG, "Cannot parse rejected peer's mac address.");
return false;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse peer mac address.", e);
return false;
}
SupplicantResult<Void> result =
new SupplicantResult("reject(" + peerAddress + ")");
try {
result.setResult(mISupplicantP2pIface.reject(macAddress));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Gets the MAC address of the device.
*
* @return MAC address of the device.
*/
public String getDeviceAddress() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getDeviceAddress")) return null;
SupplicantResult<String> result = new SupplicantResult("getDeviceAddress()");
try {
mISupplicantP2pIface.getDeviceAddress((SupplicantStatus status, byte[] address) -> {
String parsedAddress = null;
try {
parsedAddress = NativeUtil.macAddressFromByteArray(address);
} catch (Exception e) {
Log.e(TAG, "Could not process reported address.", e);
}
result.setResult(status, parsedAddress);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
return null;
}
return result.getResult();
}
}
/**
* Gets the operational SSID of the device.
*
* @param address MAC address of the peer.
*
* @return SSID of the device.
*/
public String getSsid(String address) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getSsid")) return null;
if (address == null) {
Log.e(TAG, "Cannot parse peer mac address.");
return null;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(address);
} catch (Exception e) {
Log.e(TAG, "Could not parse mac address.", e);
return null;
}
SupplicantResult<String> result =
new SupplicantResult("getSsid(" + address + ")");
try {
mISupplicantP2pIface.getSsid(
macAddress, (SupplicantStatus status, ArrayList<Byte> ssid) -> {
String ssidString = null;
if (ssid != null) {
try {
ssidString = NativeUtil.removeEnclosingQuotes(
NativeUtil.encodeSsid(ssid));
} catch (Exception e) {
Log.e(TAG, "Could not encode SSID.", e);
}
}
result.setResult(status, ssidString);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
return null;
}
return result.getResult();
}
}
/**
* Reinvoke a device from a persistent group.
*
* @param networkId Used to specify the persistent group.
* @param peerAddress MAC address of the device to reinvoke.
*
* @return true, if operation was successful.
*/
public boolean reinvoke(int networkId, String peerAddress) {
if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false;
if (peerAddress == null) {
Log.e(TAG, "Cannot parse peer mac address.");
return false;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse mac address.", e);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"reinvoke(" + networkId + ", " + peerAddress + ")");
try {
result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set up a P2P group owner manually (i.e., without group owner
* negotiation with a specific peer). This is also known as autonomous
* group owner.
*
* @param networkId Used to specify the restart of a persistent group.
* @param isPersistent Used to request a persistent group to be formed.
*
* @return true, if operation was successful.
*/
public boolean groupAdd(int networkId, boolean isPersistent) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false;
SupplicantResult<Void> result =
new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")");
try {
result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set up a P2P group as Group Owner or join a group with a configuration.
*
* @param networkName SSID of the group to be formed
* @param passphrase passphrase of the group to be formed
* @param isPersistent Used to request a persistent group to be formed.
* @param freq prefered frequencty or band of the group to be formed
* @param peerAddress peerAddress Group Owner MAC address, only applied for Group Client.
* If the MAC is "00:00:00:00:00:00", the device will try to find a peer
* whose SSID matches ssid.
* @param join join a group or create a group
*
* @return true, if operation was successful.
*/
public boolean groupAdd(String networkName, String passphrase,
boolean isPersistent, int freq, String peerAddress, boolean join) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailureV1_2("groupAdd_1_2")) return false;
java.util.ArrayList<Byte> ssid = NativeUtil.decodeSsid("\"" + networkName + "\"");
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse mac address.", e);
return false;
}
android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 =
getP2pIfaceMockableV1_2();
SupplicantResult<Void> result =
new SupplicantResult("groupAdd(" + networkName + ", "
+ (TextUtils.isEmpty(passphrase) ? "<Empty>" : "<Non-Empty>")
+ ", " + isPersistent + ", " + freq
+ ", " + peerAddress + ", " + join + ")");
try {
result.setResult(ifaceV12.addGroup_1_2(
ssid, passphrase, isPersistent, freq, macAddress, join));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set up a P2P group owner manually.
* This is a helper method that invokes groupAdd(networkId, isPersistent) internally.
*
* @param isPersistent Used to request a persistent group to be formed.
*
* @return true, if operation was successful.
*/
public boolean groupAdd(boolean isPersistent) {
// Supplicant expects networkId to be -1 if not supplied.
return groupAdd(-1, isPersistent);
}
/**
* Terminate a P2P group. If a new virtual network interface was used for
* the group, it must also be removed. The network interface name of the
* group interface is used as a parameter for this command.
*
* @param groupName Group interface name to use.
*
* @return true, if operation was successful.
*/
public boolean groupRemove(String groupName) {
if (TextUtils.isEmpty(groupName)) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false;
SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")");
try {
result.setResult(mISupplicantP2pIface.removeGroup(groupName));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Gets the capability of the group which the device is a
* member of.
*
* @param peerAddress MAC address of the peer.
*
* @return combination of |GroupCapabilityMask| values.
*/
public int getGroupCapability(String peerAddress) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) {
return RESULT_NOT_VALID;
}
if (peerAddress == null) {
Log.e(TAG, "Cannot parse peer mac address.");
return RESULT_NOT_VALID;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Could not parse group address.", e);
return RESULT_NOT_VALID;
}
SupplicantResult<Integer> capability = new SupplicantResult(
"getGroupCapability(" + peerAddress + ")");
try {
mISupplicantP2pIface.getGroupCapability(
macAddress, (SupplicantStatus status, int cap) -> {
capability.setResult(status, cap);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (!capability.isSuccess()) {
return RESULT_NOT_VALID;
}
return capability.getResult();
}
}
/**
* Configure Extended Listen Timing.
*
* If enabled, listen state must be entered every |intervalInMillis| for at
* least |periodInMillis|. Both values have acceptable range of 1-65535
* (with interval obviously having to be larger than or equal to duration).
* If the P2P module is not idle at the time the Extended Listen Timing
* timeout occurs, the Listen State operation must be skipped.
*
* @param enable Enables or disables listening.
* @param periodInMillis Period in milliseconds.
* @param intervalInMillis Interval in milliseconds.
*
* @return true, if operation was successful.
*/
public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) {
if (enable && intervalInMillis < periodInMillis) {
return false;
}
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false;
// If listening is disabled, wpa supplicant expects zeroes.
if (!enable) {
periodInMillis = 0;
intervalInMillis = 0;
}
// Verify that the integers are not negative. Leave actual parameter validation to
// supplicant.
if (periodInMillis < 0 || intervalInMillis < 0) {
Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis
+ ", " + intervalInMillis);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")");
try {
result.setResult(
mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set P2P Listen channel and operating chanel.
*
* @param listenChannel Wifi channel. eg, 1, 6, 11.
* @param operatingChannel Wifi channel. eg, 1, 6, 11.
*
* @return true, if operation was successful.
*/
public boolean setListenChannel(int listenChannel, int operatingChannel) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false;
if (listenChannel >= 1 && listenChannel <= 11) {
SupplicantResult<Void> result = new SupplicantResult(
"setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")");
try {
result.setResult(mISupplicantP2pIface.setListenChannel(
listenChannel, DEFAULT_OPERATING_CLASS));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (!result.isSuccess()) {
return false;
}
} else if (listenChannel != 0) {
// listenChannel == 0 does not set any listen channel.
return false;
}
if (operatingChannel >= 0 && operatingChannel <= 165) {
ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
// operatingChannel == 0 enables all freqs.
if (operatingChannel >= 1 && operatingChannel <= 165) {
int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5;
ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange();
range1.min = 1000;
range1.max = freq - 5;
ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange();
range2.min = freq + 5;
range2.max = 6000;
ranges.add(range1);
ranges.add(range2);
}
SupplicantResult<Void> result = new SupplicantResult(
"setDisallowedFrequencies(" + ranges + ")");
try {
result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
return false;
}
}
/**
* This command can be used to add a upnp/bonjour service.
*
* @param servInfo List of service queries.
*
* @return true, if operation was successful.
*/
public boolean serviceAdd(WifiP2pServiceInfo servInfo) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false;
if (servInfo == null) {
Log.e(TAG, "Null service info passed.");
return false;
}
for (String s : servInfo.getSupplicantQueryList()) {
if (s == null) {
Log.e(TAG, "Invalid service description (null).");
return false;
}
String[] data = s.split(" ");
if (data.length < 3) {
Log.e(TAG, "Service specification invalid: " + s);
return false;
}
SupplicantResult<Void> result = null;
try {
if ("upnp".equals(data[0])) {
int version = 0;
try {
version = Integer.parseInt(data[1], 16);
} catch (NumberFormatException e) {
Log.e(TAG, "UPnP Service specification invalid: " + s, e);
return false;
}
result = new SupplicantResult(
"addUpnpService(" + data[1] + ", " + data[2] + ")");
result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2]));
} else if ("bonjour".equals(data[0])) {
if (data[1] != null && data[2] != null) {
ArrayList<Byte> request = null;
ArrayList<Byte> response = null;
try {
request = NativeUtil.byteArrayToArrayList(
NativeUtil.hexStringToByteArray(data[1]));
response = NativeUtil.byteArrayToArrayList(
NativeUtil.hexStringToByteArray(data[2]));
} catch (Exception e) {
Log.e(TAG, "Invalid bonjour service description.");
return false;
}
result = new SupplicantResult(
"addBonjourService(" + data[1] + ", " + data[2] + ")");
result.setResult(
mISupplicantP2pIface.addBonjourService(request, response));
}
} else {
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (result == null || !result.isSuccess()) return false;
}
return true;
}
}
/**
* This command can be used to remove a upnp/bonjour service.
*
* @param servInfo List of service queries.
*
* @return true, if operation was successful.
*/
public boolean serviceRemove(WifiP2pServiceInfo servInfo) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false;
if (servInfo == null) {
Log.e(TAG, "Null service info passed.");
return false;
}
for (String s : servInfo.getSupplicantQueryList()) {
if (s == null) {
Log.e(TAG, "Invalid service description (null).");
return false;
}
String[] data = s.split(" ");
if (data.length < 3) {
Log.e(TAG, "Service specification invalid: " + s);
return false;
}
SupplicantResult<Void> result = null;
try {
if ("upnp".equals(data[0])) {
int version = 0;
try {
version = Integer.parseInt(data[1], 16);
} catch (NumberFormatException e) {
Log.e(TAG, "UPnP Service specification invalid: " + s, e);
return false;
}
result = new SupplicantResult(
"removeUpnpService(" + data[1] + ", " + data[2] + ")");
result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2]));
} else if ("bonjour".equals(data[0])) {
if (data[1] != null) {
ArrayList<Byte> request = null;
try {
request = NativeUtil.byteArrayToArrayList(
NativeUtil.hexStringToByteArray(data[1]));
} catch (Exception e) {
Log.e(TAG, "Invalid bonjour service description.");
return false;
}
result = new SupplicantResult("removeBonjourService(" + data[1] + ")");
result.setResult(mISupplicantP2pIface.removeBonjourService(request));
}
} else {
Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]);
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (result == null || !result.isSuccess()) return false;
}
return true;
}
}
/**
* Schedule a P2P service discovery request. The parameters for this command
* are the device address of the peer device (or 00:00:00:00:00:00 for
* wildcard query that is sent to every discovered P2P peer that supports
* service discovery) and P2P Service Query TLV(s) as hexdump.
*
* @param peerAddress MAC address of the device to discover.
* @param query Hex dump of the query data.
* @return identifier Identifier for the request. Can be used to cancel the
* request.
*/
public String requestServiceDiscovery(String peerAddress, String query) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null;
if (peerAddress == null) {
Log.e(TAG, "Cannot parse peer mac address.");
return null;
}
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(peerAddress);
} catch (Exception e) {
Log.e(TAG, "Could not process peer MAC address.", e);
return null;
}
if (query == null) {
Log.e(TAG, "Cannot parse service discovery query: " + query);
return null;
}
ArrayList<Byte> binQuery = null;
try {
binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query));
} catch (Exception e) {
Log.e(TAG, "Could not parse service query.", e);
return null;
}
SupplicantResult<Long> result = new SupplicantResult(
"requestServiceDiscovery(" + peerAddress + ", " + query + ")");
try {
mISupplicantP2pIface.requestServiceDiscovery(
macAddress, binQuery,
(SupplicantStatus status, long identifier) -> {
result.setResult(status, new Long(identifier));
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
Long value = result.getResult();
if (value == null) return null;
return value.toString();
}
}
/**
* Cancel a previous service discovery request.
*
* @param identifier Identifier for the request to cancel.
* @return true, if operation was successful.
*/
public boolean cancelServiceDiscovery(String identifier) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false;
if (identifier == null) {
Log.e(TAG, "cancelServiceDiscovery requires a valid tag.");
return false;
}
long id = 0;
try {
id = Long.parseLong(identifier);
} catch (NumberFormatException e) {
Log.e(TAG, "Service discovery identifier invalid: " + identifier, e);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"cancelServiceDiscovery(" + identifier + ")");
try {
result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Send driver command to set Miracast mode.
*
* @param mode Mode of Miracast.
* @return true, if operation was successful.
*/
public boolean setMiracastMode(int mode) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false;
byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED;
switch (mode) {
case WifiP2pManager.MIRACAST_SOURCE:
targetMode = ISupplicantP2pIface.MiracastMode.SOURCE;
break;
case WifiP2pManager.MIRACAST_SINK:
targetMode = ISupplicantP2pIface.MiracastMode.SINK;
break;
}
SupplicantResult<Void> result = new SupplicantResult(
"setMiracastMode(" + mode + ")");
try {
result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Initiate WPS Push Button setup.
* The PBC operation requires that a button is also pressed at the
* AP/Registrar at about the same time (2 minute window).
*
* @param groupIfName Group interface name to use.
* @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
* @return true, if operation was successful.
*/
public boolean startWpsPbc(String groupIfName, String bssid) {
if (TextUtils.isEmpty(groupIfName)) {
Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")");
return false;
}
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false;
// Null values should be fine, since bssid can be empty.
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(bssid);
} catch (Exception e) {
Log.e(TAG, "Could not parse BSSID.", e);
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"startWpsPbc(" + groupIfName + ", " + bssid + ")");
try {
result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Initiate WPS Pin Keypad setup.
*
* @param groupIfName Group interface name to use.
* @param pin 8 digit pin to be used.
* @return true, if operation was successful.
*/
public boolean startWpsPinKeypad(String groupIfName, String pin) {
if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false;
if (groupIfName == null) {
Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
return false;
}
if (pin == null) {
Log.e(TAG, "PIN required when requesting WPS KEYPAD.");
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"startWpsPinKeypad(" + groupIfName + ", " + pin + ")");
try {
result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Initiate WPS Pin Display setup.
*
* @param groupIfName Group interface name to use.
* @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
* @return generated pin if operation was successful, null otherwise.
*/
public String startWpsPinDisplay(String groupIfName, String bssid) {
if (TextUtils.isEmpty(groupIfName)) return null;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null;
if (groupIfName == null) {
Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
return null;
}
// Null values should be fine, since bssid can be empty.
byte[] macAddress = null;
try {
macAddress = NativeUtil.macAddressToByteArray(bssid);
} catch (Exception e) {
Log.e(TAG, "Could not parse BSSID.", e);
return null;
}
SupplicantResult<String> result = new SupplicantResult(
"startWpsPinDisplay(" + groupIfName + ", " + bssid + ")");
try {
mISupplicantP2pIface.startWpsPinDisplay(
groupIfName, macAddress,
(SupplicantStatus status, String generatedPin) -> {
result.setResult(status, generatedPin);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.getResult();
}
}
/**
* Cancel any ongoing WPS operations.
*
* @param groupIfName Group interface name to use.
* @return true, if operation was successful.
*/
public boolean cancelWps(String groupIfName) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false;
if (groupIfName == null) {
Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"cancelWps(" + groupIfName + ")");
try {
result.setResult(mISupplicantP2pIface.cancelWps(groupIfName));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Enable/Disable Wifi Display.
*
* @param enable true to enable, false to disable.
* @return true, if operation was successful.
*/
public boolean enableWfd(boolean enable) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"enableWfd(" + enable + ")");
try {
result.setResult(mISupplicantP2pIface.enableWfd(enable));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set Wifi Display device info.
*
* @param info WFD device info as described in section 5.1.2 of WFD technical
* specification v1.0.0.
* @return true, if operation was successful.
*/
public boolean setWfdDeviceInfo(String info) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false;
if (info == null) {
Log.e(TAG, "Cannot parse null WFD info string.");
return false;
}
byte[] wfdInfo = null;
try {
wfdInfo = NativeUtil.hexStringToByteArray(info);
} catch (Exception e) {
Log.e(TAG, "Could not parse WFD Device Info string.");
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"setWfdDeviceInfo(" + info + ")");
try {
result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Remove network with provided id.
*
* @param networkId Id of the network to lookup.
* @return true, if operation was successful.
*/
public boolean removeNetwork(int networkId) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"removeNetwork(" + networkId + ")");
try {
result.setResult(mISupplicantP2pIface.removeNetwork(networkId));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* List the networks saved in wpa_supplicant.
*
* @return List of network ids.
*/
private List<Integer> listNetworks() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null;
SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()");
try {
mISupplicantP2pIface.listNetworks(
(SupplicantStatus status, ArrayList<Integer> networkIds) -> {
result.setResult(status, networkIds);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.getResult();
}
}
/**
* Get the supplicant P2p network object for the specified network ID.
*
* @param networkId Id of the network to lookup.
* @return ISupplicantP2pNetwork instance on success, null on failure.
*/
private ISupplicantP2pNetwork getNetwork(int networkId) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null;
SupplicantResult<ISupplicantNetwork> result =
new SupplicantResult("getNetwork(" + networkId + ")");
try {
mISupplicantP2pIface.getNetwork(
networkId,
(SupplicantStatus status, ISupplicantNetwork network) -> {
result.setResult(status, network);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (result.getResult() == null) {
Log.e(TAG, "getNetwork got null network");
return null;
}
return getP2pNetworkMockable(result.getResult());
}
}
/**
* Get the persistent group list from wpa_supplicant's p2p mgmt interface
*
* @param groups WifiP2pGroupList to store persistent groups in
* @return true, if list has been modified.
*/
public boolean loadGroups(WifiP2pGroupList groups) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false;
List<Integer> networkIds = listNetworks();
if (networkIds == null || networkIds.isEmpty()) {
return false;
}
for (Integer networkId : networkIds) {
ISupplicantP2pNetwork network = getNetwork(networkId);
if (network == null) {
Log.e(TAG, "Failed to retrieve network object for " + networkId);
continue;
}
SupplicantResult<Boolean> resultIsCurrent =
new SupplicantResult("isCurrent(" + networkId + ")");
try {
network.isCurrent(
(SupplicantStatus status, boolean isCurrent) -> {
resultIsCurrent.setResult(status, isCurrent);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
/** Skip the current network, if we're somehow getting networks from the p2p GO
interface, instead of p2p mgmt interface*/
if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) {
Log.i(TAG, "Skipping current network");
continue;
}
WifiP2pGroup group = new WifiP2pGroup();
group.setNetworkId(networkId);
// Now get the ssid, bssid and other flags for this network.
SupplicantResult<ArrayList> resultSsid =
new SupplicantResult("getSsid(" + networkId + ")");
try {
network.getSsid(
(SupplicantStatus status, ArrayList<Byte> ssid) -> {
resultSsid.setResult(status, ssid);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (resultSsid.isSuccess() && resultSsid.getResult() != null
&& !resultSsid.getResult().isEmpty()) {
group.setNetworkName(NativeUtil.removeEnclosingQuotes(
NativeUtil.encodeSsid(resultSsid.getResult())));
}
SupplicantResult<byte[]> resultBssid =
new SupplicantResult("getBssid(" + networkId + ")");
try {
network.getBssid(
(SupplicantStatus status, byte[] bssid) -> {
resultBssid.setResult(status, bssid);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) {
WifiP2pDevice device = new WifiP2pDevice();
device.deviceAddress =
NativeUtil.macAddressFromByteArray(resultBssid.getResult());
group.setOwner(device);
}
SupplicantResult<Boolean> resultIsGo =
new SupplicantResult("isGo(" + networkId + ")");
try {
network.isGo(
(SupplicantStatus status, boolean isGo) -> {
resultIsGo.setResult(status, isGo);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (resultIsGo.isSuccess()) {
group.setIsGroupOwner(resultIsGo.getResult());
}
groups.add(group);
}
}
return true;
}
/**
* Set WPS device name.
*
* @param name String to be set.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsDeviceName(String name) {
if (name == null) {
return false;
}
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"setWpsDeviceName(" + name + ")");
try {
result.setResult(mISupplicantP2pIface.setWpsDeviceName(name));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Set WPS device type.
*
* @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsDeviceType(String typeStr) {
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);
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"setWpsDeviceType(" + typeStr + ")");
try {
result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + typeStr, e);
return false;
}
}
/**
* Set WPS config methods
*
* @param configMethodsStr List of config methods.
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsConfigMethods(String configMethodsStr) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false;
SupplicantResult<Void> result =
new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")");
short configMethodsMask = 0;
String[] configMethodsStrArr = configMethodsStr.split("\\s+");
for (int i = 0; i < configMethodsStrArr.length; i++) {
configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
}
try {
result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Get NFC handover request message.
*
* @return select message if created successfully, null otherwise.
*/
public String getNfcHandoverRequest() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null;
SupplicantResult<ArrayList> result = new SupplicantResult(
"getNfcHandoverRequest()");
try {
mISupplicantP2pIface.createNfcHandoverRequestMessage(
(SupplicantStatus status, ArrayList<Byte> message) -> {
result.setResult(status, message);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (!result.isSuccess()) {
return null;
}
return NativeUtil.hexStringFromByteArray(
NativeUtil.byteArrayFromArrayList(result.getResult()));
}
}
/**
* Get NFC handover select message.
*
* @return select message if created successfully, null otherwise.
*/
public String getNfcHandoverSelect() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null;
SupplicantResult<ArrayList> result = new SupplicantResult(
"getNfcHandoverSelect()");
try {
mISupplicantP2pIface.createNfcHandoverSelectMessage(
(SupplicantStatus status, ArrayList<Byte> message) -> {
result.setResult(status, message);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (!result.isSuccess()) {
return null;
}
return NativeUtil.hexStringFromByteArray(
NativeUtil.byteArrayFromArrayList(result.getResult()));
}
}
/**
* Report NFC handover select message.
*
* @return true if reported successfully, false otherwise.
*/
public boolean initiatorReportNfcHandover(String selectMessage) {
if (selectMessage == null) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"initiatorReportNfcHandover(" + selectMessage + ")");
try {
result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation(
NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
selectMessage))));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + selectMessage, e);
return false;
}
return result.isSuccess();
}
}
/**
* Report NFC handover request message.
*
* @return true if reported successfully, false otherwise.
*/
public boolean responderReportNfcHandover(String requestMessage) {
if (requestMessage == null) return false;
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false;
SupplicantResult<Void> result = new SupplicantResult(
"responderReportNfcHandover(" + requestMessage + ")");
try {
result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse(
NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
requestMessage))));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + requestMessage, e);
return false;
}
return result.isSuccess();
}
}
/**
* Set the client list for the provided network.
*
* @param networkId Id of the network.
* @param clientListStr Space separated list of clients.
* @return true, if operation was successful.
*/
public boolean setClientList(int networkId, String clientListStr) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false;
if (TextUtils.isEmpty(clientListStr)) {
Log.e(TAG, "Invalid client list");
return false;
}
ISupplicantP2pNetwork network = getNetwork(networkId);
if (network == null) {
Log.e(TAG, "Invalid network id ");
return false;
}
SupplicantResult<Void> result = new SupplicantResult(
"setClientList(" + networkId + ", " + clientListStr + ")");
try {
ArrayList<byte[]> clients = new ArrayList<>();
for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) {
clients.add(NativeUtil.macAddressToByteArray(clientStr));
}
result.setResult(network.setClientList(clients));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
} catch (IllegalArgumentException e) {
Log.e(TAG, "Illegal argument " + clientListStr, e);
return false;
}
return result.isSuccess();
}
}
/**
* Set the client list for the provided network.
*
* @param networkId Id of the network.
* @return Space separated list of clients if successfull, null otherwise.
*/
public String getClientList(int networkId) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null;
ISupplicantP2pNetwork network = getNetwork(networkId);
if (network == null) {
Log.e(TAG, "Invalid network id ");
return null;
}
SupplicantResult<ArrayList> result = new SupplicantResult(
"getClientList(" + networkId + ")");
try {
network.getClientList(
(SupplicantStatus status, ArrayList<byte[]> clients) -> {
result.setResult(status, clients);
});
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
if (!result.isSuccess()) {
return null;
}
ArrayList<byte[]> clients = result.getResult();
return clients.stream()
.map(NativeUtil::macAddressFromByteArray)
.collect(Collectors.joining(" "));
}
}
/**
* Persist the current configurations to disk.
*
* @return true, if operation was successful.
*/
public boolean saveConfig() {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false;
SupplicantResult<Void> result = new SupplicantResult("saveConfig()");
try {
result.setResult(mISupplicantP2pIface.saveConfig());
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* Enable/Disable P2P MAC randomization.
*
* @param enable true to enable, false to disable.
* @return true, if operation was successful.
*/
public boolean setMacRandomization(boolean enable) {
synchronized (mLock) {
if (!checkSupplicantP2pIfaceAndLogFailureV1_2("setMacRandomization")) return false;
android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 =
getP2pIfaceMockableV1_2();
SupplicantResult<Void> result = new SupplicantResult(
"setMacRandomization(" + enable + ")");
try {
result.setResult(ifaceV12.setMacRandomization(enable));
} catch (RemoteException e) {
Log.e(TAG, "ISupplicantP2pIface exception: " + e);
supplicantServiceDiedHandler();
}
return result.isSuccess();
}
}
/**
* 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);
}
}
/** Container class allowing propagation of status and/or value
* from callbacks.
*
* Primary purpose is to allow callback lambdas to provide results
* to parent methods.
*/
private static class SupplicantResult<E> {
private String mMethodName;
private SupplicantStatus mStatus;
private E mValue;
SupplicantResult(String methodName) {
mMethodName = methodName;
mStatus = null;
mValue = null;
logd("entering " + mMethodName);
}
public void setResult(SupplicantStatus status, E value) {
logCompletion(mMethodName, status);
logd("leaving " + mMethodName + " with result = " + value);
mStatus = status;
mValue = value;
}
public void setResult(SupplicantStatus status) {
logCompletion(mMethodName, status);
logd("leaving " + mMethodName);
mStatus = status;
}
public boolean isSuccess() {
return (mStatus != null
&& (mStatus.code == SupplicantStatusCode.SUCCESS
|| mStatus.code == SupplicantStatusCode.FAILURE_IFACE_EXISTS));
}
public E getResult() {
return (isSuccess() ? mValue : null);
}
}
}