blob: a3953cd6fdde4e99aa2110c0ba58cee5c731bc98 [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.aware;
import android.hardware.wifi.V1_0.IWifiNanIface;
import android.hardware.wifi.V1_0.NanBandIndex;
import android.hardware.wifi.V1_0.NanBandSpecificConfig;
import android.hardware.wifi.V1_0.NanCipherSuiteType;
import android.hardware.wifi.V1_0.NanConfigRequest;
import android.hardware.wifi.V1_0.NanEnableRequest;
import android.hardware.wifi.V1_0.NanInitiateDataPathRequest;
import android.hardware.wifi.V1_0.NanMatchAlg;
import android.hardware.wifi.V1_0.NanPublishRequest;
import android.hardware.wifi.V1_0.NanRespondToDataPathIndicationRequest;
import android.hardware.wifi.V1_0.NanSubscribeRequest;
import android.hardware.wifi.V1_0.NanTransmitFollowupRequest;
import android.hardware.wifi.V1_0.NanTxType;
import android.hardware.wifi.V1_0.WifiStatus;
import android.hardware.wifi.V1_0.WifiStatusCode;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.os.RemoteException;
import android.util.Log;
import libcore.util.HexEncoding;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Translates Wi-Fi Aware requests from the framework to the HAL (HIDL).
*
* Delegates the management of the NAN interface to WifiAwareNativeManager.
*/
public class WifiAwareNativeApi {
private static final String TAG = "WifiAwareNativeApi";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
private final WifiAwareNativeManager mHal;
public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) {
mHal = wifiAwareNativeManager;
}
/**
* Query the firmware's capabilities.
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
*/
public boolean getCapabilities(short transactionId) {
if (VDBG) Log.v(TAG, "getCapabilities: transactionId=" + transactionId);
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "getCapabilities: null interface");
return false;
}
try {
WifiStatus status = iface.getCapabilitiesRequest(transactionId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "getCapabilities: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "getCapabilities: exception: " + e);
return false;
}
}
/**
* Enable and configure Aware.
*
* @param transactionId Transaction ID for the transaction - used in the
* async callback to match with the original request.
* @param configRequest Requested Aware configuration.
* @param notifyIdentityChange Indicates whether or not to get address change callbacks.
* @param initialConfiguration Specifies whether initial configuration
* (true) or an update (false) to the configuration.
*/
public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest,
boolean notifyIdentityChange, boolean initialConfiguration) {
if (VDBG) {
Log.v(TAG, "enableAndConfigure: transactionId=" + transactionId + ", configRequest="
+ configRequest + ", notifyIdentityChange=" + notifyIdentityChange
+ ", initialConfiguration=" + initialConfiguration);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "enableAndConfigure: null interface");
return false;
}
try {
WifiStatus status;
if (initialConfiguration) {
// translate framework to HIDL configuration
NanEnableRequest req = new NanEnableRequest();
req.operateInBand[NanBandIndex.NAN_BAND_24GHZ] = true;
req.operateInBand[NanBandIndex.NAN_BAND_5GHZ] = configRequest.mSupport5gBand;
req.hopCountMax = 2;
req.configParams.masterPref = (byte) configRequest.mMasterPreference;
req.configParams.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
req.configParams.disableStartedClusterIndication = !notifyIdentityChange;
req.configParams.disableJoinedClusterIndication = !notifyIdentityChange;
req.configParams.includePublishServiceIdsInBeacon = true;
req.configParams.numberOfPublishServiceIdsInBeacon = 0;
req.configParams.includeSubscribeServiceIdsInBeacon = true;
req.configParams.numberOfSubscribeServiceIdsInBeacon = 0;
req.configParams.rssiWindowSize = 8;
req.configParams.macAddressRandomizationIntervalSec = 1800;
NanBandSpecificConfig config24 = new NanBandSpecificConfig();
config24.rssiClose = 60;
config24.rssiMiddle = 70;
config24.rssiCloseProximity = 60;
config24.dwellTimeMs = (byte) 200;
config24.scanPeriodSec = 20;
if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
== ConfigRequest.DW_INTERVAL_NOT_INIT) {
config24.validDiscoveryWindowIntervalVal = false;
} else {
config24.validDiscoveryWindowIntervalVal = true;
config24.discoveryWindowIntervalVal =
(byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_24GHZ];
}
req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
NanBandSpecificConfig config5 = new NanBandSpecificConfig();
config5.rssiClose = 60;
config5.rssiMiddle = 75;
config5.rssiCloseProximity = 60;
config5.dwellTimeMs = (byte) 200;
config5.scanPeriodSec = 20;
if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
== ConfigRequest.DW_INTERVAL_NOT_INIT) {
config5.validDiscoveryWindowIntervalVal = false;
} else {
config5.validDiscoveryWindowIntervalVal = true;
config5.discoveryWindowIntervalVal =
(byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_5GHZ];
}
req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
req.debugConfigs.validClusterIdVals = true;
req.debugConfigs.clusterIdTopRangeVal = (short) configRequest.mClusterHigh;
req.debugConfigs.clusterIdBottomRangeVal = (short) configRequest.mClusterLow;
req.debugConfigs.validIntfAddrVal = false;
req.debugConfigs.validOuiVal = false;
req.debugConfigs.ouiVal = 0;
req.debugConfigs.validRandomFactorForceVal = false;
req.debugConfigs.randomFactorForceVal = 0;
req.debugConfigs.validHopCountForceVal = false;
req.debugConfigs.hopCountForceVal = 0;
req.debugConfigs.validDiscoveryChannelVal = false;
req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_24GHZ] = 0;
req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_5GHZ] = 0;
req.debugConfigs.validUseBeaconsInBandVal = false;
req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
req.debugConfigs.validUseSdfInBandVal = false;
req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
status = iface.enableRequest(transactionId, req);
} else {
NanConfigRequest req = new NanConfigRequest();
req.masterPref = (byte) configRequest.mMasterPreference;
req.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
req.disableStartedClusterIndication = !notifyIdentityChange;
req.disableJoinedClusterIndication = !notifyIdentityChange;
req.includePublishServiceIdsInBeacon = true;
req.numberOfPublishServiceIdsInBeacon = 0;
req.includeSubscribeServiceIdsInBeacon = true;
req.numberOfSubscribeServiceIdsInBeacon = 0;
req.rssiWindowSize = 8;
req.macAddressRandomizationIntervalSec = 1800;
NanBandSpecificConfig config24 = new NanBandSpecificConfig();
config24.rssiClose = 60;
config24.rssiMiddle = 70;
config24.rssiCloseProximity = 60;
config24.dwellTimeMs = (byte) 200;
config24.scanPeriodSec = 20;
if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
== ConfigRequest.DW_INTERVAL_NOT_INIT) {
config24.validDiscoveryWindowIntervalVal = false;
} else {
config24.validDiscoveryWindowIntervalVal = true;
config24.discoveryWindowIntervalVal =
(byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_24GHZ];
}
req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
NanBandSpecificConfig config5 = new NanBandSpecificConfig();
config5.rssiClose = 60;
config5.rssiMiddle = 75;
config5.rssiCloseProximity = 60;
config5.dwellTimeMs = (byte) 200;
config5.scanPeriodSec = 20;
if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
== ConfigRequest.DW_INTERVAL_NOT_INIT) {
config5.validDiscoveryWindowIntervalVal = false;
} else {
config5.validDiscoveryWindowIntervalVal = true;
config5.discoveryWindowIntervalVal =
(byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_5GHZ];
}
req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
status = iface.configRequest(transactionId, req);
}
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "enableAndConfigure: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "enableAndConfigure: exception: " + e);
return false;
}
}
/**
* Disable Aware.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
*/
public boolean disable(short transactionId) {
if (VDBG) Log.d(TAG, "disable");
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "disable: null interface");
return false;
}
try {
WifiStatus status = iface.disableRequest(transactionId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "disable: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "disable: exception: " + e);
return false;
}
}
/**
* Start or modify a service publish session.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
* @param publishId ID of the requested session - 0 to request a new publish
* session.
* @param publishConfig Configuration of the discovery session.
*/
public boolean publish(short transactionId, int publishId, PublishConfig publishConfig) {
if (VDBG) {
Log.d(TAG, "publish: transactionId=" + transactionId + ", config=" + publishConfig);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "publish: null interface");
return false;
}
NanPublishRequest req = new NanPublishRequest();
req.baseConfigs.sessionId = 0;
req.baseConfigs.ttlSec = (short) publishConfig.mTtlSec;
req.baseConfigs.discoveryWindowPeriod = 1;
req.baseConfigs.discoveryCount = (byte) publishConfig.mPublishCount;
convertLcByteToUcByteArray(publishConfig.mServiceName, req.baseConfigs.serviceName);
// TODO: what's the right value on publish?
req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
convertLcByteToUcByteArray(publishConfig.mServiceSpecificInfo,
req.baseConfigs.serviceSpecificInfo);
convertLcByteToUcByteArray(publishConfig.mMatchFilter,
publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED
? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
req.baseConfigs.useRssiThreshold = false;
req.baseConfigs.disableDiscoveryTerminationIndication =
!publishConfig.mEnableTerminateNotification;
req.baseConfigs.disableMatchExpirationIndication = true;
req.baseConfigs.disableFollowupReceivedIndication = false;
// TODO: configure ranging and security
req.baseConfigs.securityEnabledInNdp = false;
req.baseConfigs.rangingRequired = false;
req.autoAcceptDataPathRequests = false;
req.publishType = publishConfig.mPublishType;
req.txType = NanTxType.BROADCAST;
try {
WifiStatus status = iface.startPublishRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "publish: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "publish: exception: " + e);
return false;
}
}
/**
* Start or modify a service subscription session.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
* @param subscribeId ID of the requested session - 0 to request a new
* subscribe session.
* @param subscribeConfig Configuration of the discovery session.
*/
public boolean subscribe(short transactionId, int subscribeId,
SubscribeConfig subscribeConfig) {
if (VDBG) {
Log.d(TAG, "subscribe: transactionId=" + transactionId + ", config=" + subscribeConfig);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "subscribe: null interface");
return false;
}
NanSubscribeRequest req = new NanSubscribeRequest();
req.baseConfigs.sessionId = 0;
req.baseConfigs.ttlSec = (short) subscribeConfig.mTtlSec;
req.baseConfigs.discoveryWindowPeriod = 1;
req.baseConfigs.discoveryCount = (byte) subscribeConfig.mSubscribeCount;
convertLcByteToUcByteArray(subscribeConfig.mServiceName, req.baseConfigs.serviceName);
req.baseConfigs.discoveryMatchIndicator = subscribeConfig.mMatchStyle;
convertLcByteToUcByteArray(subscribeConfig.mServiceSpecificInfo,
req.baseConfigs.serviceSpecificInfo);
convertLcByteToUcByteArray(subscribeConfig.mMatchFilter,
subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE
? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
req.baseConfigs.useRssiThreshold = false;
req.baseConfigs.disableDiscoveryTerminationIndication =
!subscribeConfig.mEnableTerminateNotification;
req.baseConfigs.disableMatchExpirationIndication = true;
req.baseConfigs.disableFollowupReceivedIndication = false;
// TODO: configure ranging and security
req.baseConfigs.securityEnabledInNdp = false;
req.baseConfigs.rangingRequired = false;
req.subscribeType = subscribeConfig.mSubscribeType;
try {
WifiStatus status = iface.startSubscribeRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "subscribe: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "subscribe: exception: " + e);
return false;
}
}
/**
* Send a message through an existing discovery session.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
* @param pubSubId ID of the existing publish/subscribe session.
* @param requestorInstanceId ID of the peer to communicate with - obtained
* through a previous discovery (match) operation with that peer.
* @param dest MAC address of the peer to communicate with - obtained
* together with requestorInstanceId.
* @param message Message.
* @param messageId Arbitary integer from host (not sent to HAL - useful for
* testing/debugging at this level)
*/
public boolean sendMessage(short transactionId, int pubSubId, int requestorInstanceId,
byte[] dest, byte[] message, int messageId) {
if (VDBG) {
Log.d(TAG,
"sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
+ ", requestorInstanceId=" + requestorInstanceId + ", dest="
+ String.valueOf(HexEncoding.encode(dest)) + ", messageId="
+ messageId);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "sendMessage: null interface");
return false;
}
NanTransmitFollowupRequest req = new NanTransmitFollowupRequest();
req.discoverySessionId = (byte) pubSubId;
req.peerId = requestorInstanceId;
copyArray(dest, req.addr);
req.isHighPriority = false;
req.shouldUseDiscoveryWindow = true;
convertLcByteToUcByteArray(message, req.serviceSpecificInfo);
req.disableFollowupResultIndication = false;
try {
WifiStatus status = iface.transmitFollowupRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "sendMessage: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "sendMessage: exception: " + e);
return false;
}
}
/**
* Terminate a publish discovery session.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
* @param pubSubId ID of the publish/subscribe session - obtained when
* creating a session.
*/
public boolean stopPublish(short transactionId, int pubSubId) {
if (VDBG) {
Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "stopPublish: null interface");
return false;
}
try {
WifiStatus status = iface.stopPublishRequest(transactionId, (byte) pubSubId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "stopPublish: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "stopPublish: exception: " + e);
return false;
}
}
/**
* Terminate a subscribe discovery session.
*
* @param transactionId transactionId Transaction ID for the transaction -
* used in the async callback to match with the original request.
* @param pubSubId ID of the publish/subscribe session - obtained when
* creating a session.
*/
public boolean stopSubscribe(short transactionId, int pubSubId) {
if (VDBG) {
Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "stopSubscribe: null interface");
return false;
}
try {
WifiStatus status = iface.stopSubscribeRequest(transactionId, (byte) pubSubId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "stopSubscribe: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "stopSubscribe: exception: " + e);
return false;
}
}
/**
* Create a Aware network interface. This only creates the Linux interface - it doesn't actually
* create the data connection.
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
* @param interfaceName The name of the interface, e.g. "aware0".
*/
public boolean createAwareNetworkInterface(short transactionId, String interfaceName) {
if (VDBG) {
Log.v(TAG, "createAwareNetworkInterface: transactionId=" + transactionId + ", "
+ "interfaceName=" + interfaceName);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "createAwareNetworkInterface: null interface");
return false;
}
try {
WifiStatus status = iface.createDataInterfaceRequest(transactionId, interfaceName);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "createAwareNetworkInterface: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "createAwareNetworkInterface: exception: " + e);
return false;
}
}
/**
* Deletes a Aware network interface. The data connection can (should?) be torn down previously.
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
* @param interfaceName The name of the interface, e.g. "aware0".
*/
public boolean deleteAwareNetworkInterface(short transactionId, String interfaceName) {
if (VDBG) {
Log.v(TAG, "deleteAwareNetworkInterface: transactionId=" + transactionId + ", "
+ "interfaceName=" + interfaceName);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "deleteAwareNetworkInterface: null interface");
return false;
}
try {
WifiStatus status = iface.deleteDataInterfaceRequest(transactionId, interfaceName);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "deleteAwareNetworkInterface: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "deleteAwareNetworkInterface: exception: " + e);
return false;
}
}
/**
* Initiates setting up a data-path between device and peer.
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
* @param peerId ID of the peer ID to associate the data path with. A value of 0
* indicates that not associated with an existing session.
* @param channelRequestType Indicates whether the specified channel is available, if available
* requested or forced (resulting in failure if cannot be
* accommodated).
* @param channel The channel on which to set up the data-path.
* @param peer The MAC address of the peer to create a connection with.
* @param interfaceName The interface on which to create the data connection.
* @param pmk Pairwise master key (PMK, see IEEE 802.11i). Null value means an unsecured (open)
* datapath.
* @param capabilities The capabilities of the firmware.
*/
public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType,
int channel, byte[] peer, String interfaceName, byte[] pmk, Capabilities capabilities) {
if (VDBG) {
Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId
+ ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ ", peer=" + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName="
+ interfaceName);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "initiateDataPath: null interface");
return false;
}
if (capabilities == null) {
Log.e(TAG, "initiateDataPath: null capabilities");
return false;
}
NanInitiateDataPathRequest req = new NanInitiateDataPathRequest();
req.peerId = peerId;
copyArray(peer, req.peerDiscMacAddr);
req.channelRequestType = channelRequestType;
req.channel = channel;
req.ifaceName = interfaceName;
if (pmk == null || pmk.length == 0) {
req.securityRequired = false;
} else {
req.securityRequired = true;
req.cipherType = getStrongestCipherSuiteType(capabilities.supportedCipherSuites);
convertLcByteToUcByteArray(pmk, req.pmk);
}
try {
WifiStatus status = iface.initiateDataPathRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "initiateDataPath: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "initiateDataPath: exception: " + e);
return false;
}
}
/**
* Responds to a data request from a peer.
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
* @param accept Accept (true) or reject (false) the original call.
* @param ndpId The NDP (Aware data path) ID. Obtained from the request callback.
* @param interfaceName The interface on which the data path will be setup. Obtained from the
* request callback.
* @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path. A null indicates
* an unsecure (open) link.
* @param capabilities The capabilities of the firmware.
*/
public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId,
String interfaceName, byte[] pmk, Capabilities capabilities) {
if (VDBG) {
Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
+ accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "respondToDataPathRequest: null interface");
return false;
}
if (capabilities == null) {
Log.e(TAG, "initiateDataPath: null capabilities");
return false;
}
NanRespondToDataPathIndicationRequest req = new NanRespondToDataPathIndicationRequest();
req.acceptRequest = accept;
req.ndpInstanceId = ndpId;
req.ifaceName = interfaceName;
if (pmk == null || pmk.length == 0) {
req.securityRequired = false;
} else {
req.securityRequired = true;
req.cipherType = getStrongestCipherSuiteType(capabilities.supportedCipherSuites);
convertLcByteToUcByteArray(pmk, req.pmk);
}
try {
WifiStatus status = iface.respondToDataPathIndicationRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "respondToDataPathRequest: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "respondToDataPathRequest: exception: " + e);
return false;
}
}
/**
* Terminate an existing data-path (does not delete the interface).
*
* @param transactionId Transaction ID for the transaction - used in the async callback to
* match with the original request.
* @param ndpId The NDP (Aware data path) ID to be terminated.
*/
public boolean endDataPath(short transactionId, int ndpId) {
if (VDBG) {
Log.v(TAG, "endDataPath: transactionId=" + transactionId + ", ndpId=" + ndpId);
}
IWifiNanIface iface = mHal.getWifiNanIface();
if (iface == null) {
Log.e(TAG, "endDataPath: null interface");
return false;
}
try {
WifiStatus status = iface.terminateDataPathRequest(transactionId, ndpId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
Log.e(TAG, "endDataPath: error: " + statusString(status));
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "endDataPath: exception: " + e);
return false;
}
}
// utilities
/**
* Returns the strongest supported cipher suite.
*
* Baseline is very simple: 256 > 128 > 0.
*/
private int getStrongestCipherSuiteType(int supportedCipherSuites) {
if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_256_MASK) != 0) {
return NanCipherSuiteType.SHARED_KEY_256_MASK;
}
if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_128_MASK) != 0) {
return NanCipherSuiteType.SHARED_KEY_128_MASK;
}
return NanCipherSuiteType.NONE;
}
/**
* Converts a byte[] to an ArrayList<Byte>. Fills in the entries of the 'to' array if
* provided (non-null), otherwise creates and returns a new ArrayList<>.
*
* @param from The input byte[] to convert from.
* @param to An optional ArrayList<> to fill in from 'from'.
*
* @return A newly allocated ArrayList<> if 'to' is null, otherwise null.
*/
private ArrayList<Byte> convertLcByteToUcByteArray(byte[] from, ArrayList<Byte> to) {
if (from == null) {
from = new byte[0];
}
if (to == null) {
to = new ArrayList<>(from.length);
} else {
to.ensureCapacity(from.length);
}
for (int i = 0; i < from.length; ++i) {
to.add(from[i]);
}
return to;
}
private void copyArray(byte[] from, byte[] to) {
if (from == null || to == null || from.length != to.length) {
Log.e(TAG, "copyArray error: from=" + from + ", to=" + to);
return;
}
for (int i = 0; i < from.length; ++i) {
to[i] = from[i];
}
}
private static String statusString(WifiStatus status) {
if (status == null) {
return "status=null";
}
StringBuilder sb = new StringBuilder();
sb.append(status.code).append(" (").append(status.description).append(")");
return sb.toString();
}
/**
* Dump the internal state of the class.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mHal.dump(fd, pw, args);
}
}