blob: 881d0b8eba0f2c89651ddad383639163065aae4d [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.content.Context;
import android.hardware.wifi.V1_0.NanDataPathChannelCfg;
import android.net.ConnectivityManager;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.wifi.aware.WifiAwareManager;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import libcore.util.HexEncoding;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Manages Aware data-path lifetime: interface creation/deletion, data-path setup and tear-down.
* The Aware network configuration is:
* - transport = TRANSPORT_WIFI_AWARE
* - capabilities = NET_CAPABILITY_NOT_VPN
* - network specifier generated by DiscoverySession.createNetworkSpecifier(...) or
* WifiAwareManager.createNetworkSpecifier(...).
* The network specifier is encoded as a JSON string with the key combos described in
* {@link WifiAwareManager} as {@code NETWORK_SPECIFIER_TYPE_*}.
*/
public class WifiAwareDataPathStateManager {
private static final String TAG = "WifiAwareDataPathStMgr";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
private static final String AWARE_INTERFACE_PREFIX = "aware_data";
private static final String NETWORK_TAG = "WIFI_AWARE_FACTORY";
private static final String AGENT_TAG_PREFIX = "WIFI_AWARE_AGENT_";
private static final int NETWORK_FACTORY_SCORE_AVAIL = 1;
private static final int NETWORK_FACTORY_BANDWIDTH_AVAIL = 1;
private static final int NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL = 1;
private final WifiAwareStateManager mMgr;
private final NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper();
private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
private final Set<String> mInterfaces = new HashSet<>();
private final Map<String, AwareNetworkRequestInformation> mNetworkRequestsCache =
new ArrayMap<>();
private Context mContext;
private Looper mLooper;
private WifiAwareNetworkFactory mNetworkFactory;
private INetworkManagementService mNwService;
public WifiAwareDataPathStateManager(WifiAwareStateManager mgr) {
mMgr = mgr;
}
/**
* Initialize the Aware data-path state manager. Specifically register the network factory with
* connectivity service.
*/
public void start(Context context, Looper looper) {
if (VDBG) Log.v(TAG, "start");
mContext = context;
mLooper = looper;
mNetworkCapabilitiesFilter.clearAll();
mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
mNetworkCapabilitiesFilter
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
mNetworkCapabilitiesFilter
.setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
mNetworkCapabilitiesFilter.setSignalStrength(NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL);
mNetworkFactory = new WifiAwareNetworkFactory(looper, context, mNetworkCapabilitiesFilter);
mNetworkFactory.setScoreFilter(NETWORK_FACTORY_SCORE_AVAIL);
mNetworkFactory.register();
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
}
private Map.Entry<String, AwareNetworkRequestInformation> getNetworkRequestByNdpId(int ndpId) {
for (Map.Entry<String, AwareNetworkRequestInformation> entry : mNetworkRequestsCache
.entrySet()) {
if (entry.getValue().ndpId == ndpId) {
return entry;
}
}
return null;
}
/**
* Create all Aware data-path interfaces which are possible on the device - based on the
* capabilities of the firmware.
*/
public void createAllInterfaces() {
if (VDBG) Log.v(TAG, "createAllInterfaces");
if (mMgr.getCapabilities() == null) {
Log.e(TAG, "createAllInterfaces: capabilities aren't initialized yet!");
return;
}
for (int i = 0; i < mMgr.getCapabilities().maxNdiInterfaces; ++i) {
String name = AWARE_INTERFACE_PREFIX + i;
if (mInterfaces.contains(name)) {
Log.e(TAG, "createAllInterfaces(): interface already up, " + name
+ ", possibly failed to delete - deleting/creating again to be safe");
mMgr.deleteDataPathInterface(name);
// critical to remove so that don't get infinite loop if the delete fails again
mInterfaces.remove(name);
}
mMgr.createDataPathInterface(name);
}
}
/**
* Delete all Aware data-path interfaces which are currently up.
*/
public void deleteAllInterfaces() {
if (VDBG) Log.v(TAG, "deleteAllInterfaces");
for (String name : mInterfaces) {
mMgr.deleteDataPathInterface(name);
}
}
/**
* Called when firmware indicates the an interface was created.
*/
public void onInterfaceCreated(String interfaceName) {
if (VDBG) Log.v(TAG, "onInterfaceCreated: interfaceName=" + interfaceName);
if (mInterfaces.contains(interfaceName)) {
Log.w(TAG, "onInterfaceCreated: already contains interface -- " + interfaceName);
}
mInterfaces.add(interfaceName);
}
/**
* Called when firmware indicates the an interface was deleted.
*/
public void onInterfaceDeleted(String interfaceName) {
if (VDBG) Log.v(TAG, "onInterfaceDeleted: interfaceName=" + interfaceName);
if (!mInterfaces.contains(interfaceName)) {
Log.w(TAG, "onInterfaceDeleted: interface not on list -- " + interfaceName);
}
mInterfaces.remove(interfaceName);
}
/**
* Response to initiating data-path request. Indicates that request is successful (not
* complete!) and is now in progress.
*
* @param networkSpecifier The network specifier provided as part of the initiate request.
* @param ndpId The ID assigned to the data-path.
*/
public void onDataPathInitiateSuccess(String networkSpecifier, int ndpId) {
if (VDBG) {
Log.v(TAG,
"onDataPathInitiateSuccess: networkSpecifier=" + networkSpecifier + ", ndpId="
+ ndpId);
}
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
if (nnri == null) {
Log.w(TAG, "onDataPathInitiateSuccess: network request not found for networkSpecifier="
+ networkSpecifier);
mMgr.endDataPath(ndpId);
return;
}
if (nnri.state
!= AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
Log.w(TAG, "onDataPathInitiateSuccess: network request in incorrect state: state="
+ nnri.state);
mNetworkRequestsCache.remove(networkSpecifier);
mMgr.endDataPath(ndpId);
return;
}
nnri.state = AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM;
nnri.ndpId = ndpId;
}
/**
* Response to an attempt to set up a data-path (on the initiator side).
*
* @param networkSpecifier The network specifier provided as part of the initiate request.
* @param reason Failure reason.
*/
public void onDataPathInitiateFail(String networkSpecifier, int reason) {
if (VDBG) {
Log.v(TAG,
"onDataPathInitiateFail: networkSpecifier=" + networkSpecifier + ", reason="
+ reason);
}
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.remove(networkSpecifier);
if (nnri == null) {
Log.w(TAG, "onDataPathInitiateFail: network request not found for networkSpecifier="
+ networkSpecifier);
return;
}
if (nnri.state
!= AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
Log.w(TAG, "onDataPathInitiateFail: network request in incorrect state: state="
+ nnri.state);
}
mNetworkRequestsCache.remove(networkSpecifier);
}
/**
* Notification (unsolicited/asynchronous) that a peer has requested to set up a data-path
* connection with us.
*
* @param pubSubId The ID of the discovery session context for the data-path - or 0 if not
* related to a discovery session.
* @param mac The discovery MAC address of the peer.
* @param ndpId The locally assigned ID for the data-path.
* @return The network specifier of the data-path (or null if none/error)
*/
public String onDataPathRequest(int pubSubId, byte[] mac, int ndpId) {
if (VDBG) {
Log.v(TAG,
"onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
HexEncoding.encode(mac)) + ", ndpId=" + ndpId);
}
String networkSpecifier = null;
AwareNetworkRequestInformation nnri = null;
for (Map.Entry<String, AwareNetworkRequestInformation> entry : mNetworkRequestsCache
.entrySet()) {
/*
* Checking that the incoming request (from the Initiator) matches the request
* we (the Responder) already have set up. The rules are:
* - The discovery session (pub/sub ID) must match.
* - The peer MAC address (if specified - i.e. non-null) must match. A null peer MAC ==
* accept (otherwise matching) requests from any peer MAC.
*/
if (entry.getValue().pubSubId != 0 && entry.getValue().pubSubId != pubSubId) {
continue;
}
if (entry.getValue().peerDiscoveryMac != null && !Arrays.equals(
entry.getValue().peerDiscoveryMac, mac)) {
continue;
}
networkSpecifier = entry.getKey();
nnri = entry.getValue();
break;
}
if (nnri == null) {
Log.w(TAG, "onDataPathRequest: can't find a request with specified pubSubId=" + pubSubId
+ ", mac=" + String.valueOf(HexEncoding.encode(mac)));
if (DBG) {
Log.d(TAG, "onDataPathRequest: network request cache = " + mNetworkRequestsCache);
}
mMgr.respondToDataPathRequest(false, ndpId, "", null);
return null;
}
if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
Log.w(TAG, "onDataPathRequest: request " + networkSpecifier + " is incorrect state="
+ nnri.state);
mMgr.respondToDataPathRequest(false, ndpId, "", null);
mNetworkRequestsCache.remove(networkSpecifier);
return null;
}
nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE;
nnri.ndpId = ndpId;
nnri.interfaceName = selectInterfaceForRequest(nnri);
mMgr.respondToDataPathRequest(true, ndpId, nnri.interfaceName, nnri.pmk);
return networkSpecifier;
}
/**
* Called on the RESPONDER when the response to data-path request has been completed.
*
* @param ndpId The ID of the data-path (NDP)
* @param success Whether or not the 'RespondToDataPathRequest' operation was a success.
*/
public void onRespondToDataPathRequest(int ndpId, boolean success) {
if (VDBG) {
Log.v(TAG, "onRespondToDataPathRequest: ndpId=" + ndpId + ", success=" + success);
}
String networkSpecifier = null;
AwareNetworkRequestInformation nnri = null;
for (Map.Entry<String, AwareNetworkRequestInformation> entry : mNetworkRequestsCache
.entrySet()) {
if (entry.getValue().ndpId == ndpId) {
networkSpecifier = entry.getKey();
nnri = entry.getValue();
break;
}
}
if (nnri == null) {
Log.w(TAG, "onRespondToDataPathRequest: can't find a request with specified ndpId="
+ ndpId);
if (DBG) {
Log.d(TAG, "onRespondToDataPathRequest: network request cache = "
+ mNetworkRequestsCache);
}
return;
}
if (!success) {
Log.w(TAG, "onRespondToDataPathRequest: request " + networkSpecifier
+ " failed responding");
mMgr.endDataPath(ndpId);
mNetworkRequestsCache.remove(networkSpecifier);
return;
}
if (nnri.state
!= AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE) {
Log.w(TAG, "onRespondToDataPathRequest: request " + networkSpecifier
+ " is incorrect state=" + nnri.state);
mMgr.endDataPath(ndpId);
mNetworkRequestsCache.remove(networkSpecifier);
return;
}
nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM;
}
/**
* Notification (unsolicited/asynchronous) that the data-path (which we've been setting up)
* is possibly (if {@code accept} is {@code true}) ready for use from the firmware's
* perspective - now can do L3 configuration.
*
* @param ndpId Id of the data-path
* @param mac The MAC address of the peer's data-path (not discovery interface). Only
* valid
* if {@code accept} is {@code true}.
* @param accept Indicates whether the data-path setup has succeeded (been accepted) or
* failed (been rejected).
* @param reason If {@code accept} is {@code false} provides a reason code for the
* rejection/failure.
* @param message The message provided by the peer as part of the data-path setup
* process.
* @return The network specifier of the data-path or a null if none/error.
*/
public String onDataPathConfirm(int ndpId, byte[] mac, boolean accept, int reason,
byte[] message) {
if (VDBG) {
Log.v(TAG, "onDataPathConfirm: ndpId=" + ndpId + ", mac=" + String.valueOf(
HexEncoding.encode(mac)) + ", accept=" + accept + ", reason=" + reason);
}
Map.Entry<String, AwareNetworkRequestInformation> nnriE = getNetworkRequestByNdpId(ndpId);
if (nnriE == null) {
Log.w(TAG, "onDataPathConfirm: network request not found for ndpId=" + ndpId);
if (accept) {
mMgr.endDataPath(ndpId);
}
return null;
}
String networkSpecifier = nnriE.getKey();
AwareNetworkRequestInformation nnri = nnriE.getValue();
// validate state
if (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& nnri.state != AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM) {
Log.w(TAG, "onDataPathConfirm: INITIATOR in invalid state=" + nnri.state);
mNetworkRequestsCache.remove(networkSpecifier);
if (accept) {
mMgr.endDataPath(ndpId);
}
return networkSpecifier;
}
if (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER
&& nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM) {
Log.w(TAG, "onDataPathConfirm: RESPONDER in invalid state=" + nnri.state);
mNetworkRequestsCache.remove(networkSpecifier);
if (accept) {
mMgr.endDataPath(ndpId);
}
return networkSpecifier;
}
if (accept) {
nnri.state = (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)
? AwareNetworkRequestInformation.STATE_INITIATOR_CONFIRMED
: AwareNetworkRequestInformation.STATE_RESPONDER_CONFIRMED;
nnri.peerDataMac = mac;
NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0,
NETWORK_TAG, "");
NetworkCapabilities networkCapabilities = new NetworkCapabilities(
mNetworkCapabilitiesFilter);
LinkProperties linkProperties = new LinkProperties();
try {
mNwService.setInterfaceUp(nnri.interfaceName);
mNwService.enableIpv6(nnri.interfaceName);
} catch (Exception e) { // NwService throws runtime exceptions for errors
Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": can't configure network - "
+ e);
mMgr.endDataPath(ndpId);
return networkSpecifier;
}
if (!mNiWrapper.configureAgentProperties(nnri, networkSpecifier, ndpId, networkInfo,
networkCapabilities, linkProperties)) {
return networkSpecifier;
}
nnri.networkAgent = new WifiAwareNetworkAgent(mLooper, mContext,
AGENT_TAG_PREFIX + nnri.ndpId,
new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORK_TAG, ""),
networkCapabilities, new LinkProperties(), NETWORK_FACTORY_SCORE_AVAIL,
networkSpecifier, ndpId);
nnri.networkAgent.sendNetworkInfo(networkInfo);
nnri.networkAgent.sendLinkProperties(linkProperties);
} else {
if (DBG) {
Log.d(TAG, "onDataPathConfirm: data-path for networkSpecifier=" + networkSpecifier
+ " rejected - reason=" + reason);
}
mNetworkRequestsCache.remove(networkSpecifier);
}
return networkSpecifier;
}
/**
* Notification (unsolicited/asynchronous) from the firmware that the specified data-path has
* been terminated.
*
* @param ndpId The ID of the terminated data-path.
*/
public void onDataPathEnd(int ndpId) {
if (VDBG) Log.v(TAG, "onDataPathEnd: ndpId=" + ndpId);
Map.Entry<String, AwareNetworkRequestInformation> nnriE = getNetworkRequestByNdpId(ndpId);
if (nnriE == null) {
if (DBG) {
Log.d(TAG, "onDataPathEnd: network request not found for ndpId=" + ndpId);
}
return;
}
tearDownInterface(nnriE.getValue());
mNetworkRequestsCache.remove(nnriE.getKey());
}
/**
* Called whenever Aware comes down. Clean up all pending and up network requeests and agents.
*/
public void onAwareDownCleanupDataPaths() {
if (VDBG) Log.v(TAG, "onAwareDownCleanupDataPaths");
for (AwareNetworkRequestInformation nnri : mNetworkRequestsCache.values()) {
tearDownInterface(nnri);
}
mNetworkRequestsCache.clear();
}
/**
* Called when timed-out waiting for confirmation of the data-path setup (i.e.
* onDataPathConfirm). Started on the initiator when executing the request for the data-path
* and on the responder when received a request for data-path (in both cases only on success
* - i.e. when we're proceeding with data-path setup).
*/
public void handleDataPathTimeout(String networkSpecifier) {
if (VDBG) Log.v(TAG, "handleDataPathTimeout: networkSpecifier=" + networkSpecifier);
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.remove(networkSpecifier);
if (nnri == null) {
if (DBG) {
Log.d(TAG,
"handleDataPathTimeout: network request not found for networkSpecifier="
+ networkSpecifier);
}
return;
}
mMgr.endDataPath(nnri.ndpId);
}
private class WifiAwareNetworkFactory extends NetworkFactory {
WifiAwareNetworkFactory(Looper looper, Context context, NetworkCapabilities filter) {
super(looper, context, NETWORK_TAG, filter);
}
@Override
public boolean acceptRequest(NetworkRequest request, int score) {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request + ", score="
+ score);
}
if (!mMgr.isUsageEnabled()) {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " -- Aware disabled");
}
return false;
}
if (mInterfaces.isEmpty()) {
Log.w(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " -- No Aware interfaces are up");
return false;
}
String networkSpecifier = request.networkCapabilities.getNetworkSpecifier();
if (TextUtils.isEmpty(networkSpecifier)) {
Log.w(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " - empty (or null) NetworkSpecifier");
return false;
}
// look up specifier - are we being called again?
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
if (nnri != null) {
if (DBG) {
Log.d(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " - already in cache!?");
}
// seems to happen after a network agent is created - trying to rematch all
// requests again!?
return true;
}
// parse network specifier (JSON) & cache
// TODO: validate that the client ID actually comes from the correct process and is
// not faked?
nnri = AwareNetworkRequestInformation.parseNetworkSpecifier(networkSpecifier, mMgr);
if (nnri == null) {
Log.e(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " - can't parse network specifier");
return false;
}
mNetworkRequestsCache.put(networkSpecifier, nnri);
return true;
}
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ networkRequest + ", score=" + score);
}
String networkSpecifier = networkRequest.networkCapabilities.getNetworkSpecifier();
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
if (nnri == null) {
Log.e(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ networkRequest + " not in cache!?");
return;
}
if (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
if (nnri.state != AwareNetworkRequestInformation.STATE_INITIATOR_IDLE) {
if (DBG) {
Log.d(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ networkRequest + " - already in progress");
// TODO: understand how/when can be called again/while in progress (seems
// to be related to score re-calculation after a network agent is created)
}
return;
}
nnri.interfaceName = selectInterfaceForRequest(nnri);
mMgr.initiateDataPathSetup(networkSpecifier, nnri.peerId,
NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP, selectChannelForRequest(nnri),
nnri.peerDiscoveryMac, nnri.interfaceName, nnri.pmk);
nnri.state =
AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE;
} else {
if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_IDLE) {
if (DBG) {
Log.d(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ networkRequest + " - already in progress");
// TODO: understand how/when can be called again/while in progress (seems
// to be related to score re-calculation after a network agent is created)
}
return;
}
nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST;
}
}
@Override
protected void releaseNetworkFor(NetworkRequest networkRequest) {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ networkRequest);
}
String networkSpecifier = networkRequest.networkCapabilities.getNetworkSpecifier();
AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
if (nnri == null) {
Log.e(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ networkRequest + " not in cache!?");
return;
}
if (nnri.networkAgent != null) {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ networkRequest + ", nnri=" + nnri
+ ": agent already created - deferring ending data-path to agent"
+ ".unwanted()");
}
return;
}
if (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && nnri.state
> AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
mMgr.endDataPath(nnri.ndpId);
}
if (nnri.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER && nnri.state
> AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
mMgr.endDataPath(nnri.ndpId);
}
// Will get a callback (on both initiator and responder) when data-path actually
// terminated. At that point will inform the agent and will clear the cache.
}
}
private class WifiAwareNetworkAgent extends NetworkAgent {
private NetworkInfo mNetworkInfo;
private String mNetworkSpecifier;
private int mNdpId;
WifiAwareNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, String networkSpecifier,
int ndpId) {
super(looper, context, logTag, ni, nc, lp, score);
mNetworkInfo = ni;
mNetworkSpecifier = networkSpecifier;
mNdpId = ndpId;
}
@Override
protected void unwanted() {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkAgent.unwanted: networkSpecifier=" + mNetworkSpecifier
+ ", ndpId=" + mNdpId);
}
mMgr.endDataPath(mNdpId);
// Will get a callback (on both initiator and responder) when data-path actually
// terminated. At that point will inform the agent and will clear the cache.
}
void reconfigureAgentAsDisconnected() {
if (VDBG) {
Log.v(TAG, "WifiAwareNetworkAgent.reconfigureAgentAsDisconnected: networkSpecifier="
+ mNetworkSpecifier + ", ndpId=" + mNdpId);
}
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, "");
sendNetworkInfo(mNetworkInfo);
}
}
private void tearDownInterface(AwareNetworkRequestInformation nnri) {
if (VDBG) Log.v(TAG, "tearDownInterface: nnri=" + nnri);
if (nnri.interfaceName != null && !nnri.interfaceName.isEmpty()) {
try {
mNwService.setInterfaceDown(nnri.interfaceName);
} catch (Exception e) { // NwService throws runtime exceptions for errors
Log.e(TAG,
"tearDownInterface: nnri=" + nnri + ": can't bring interface down - " + e);
}
}
if (nnri.networkAgent != null) {
nnri.networkAgent.reconfigureAgentAsDisconnected();
}
}
/**
* Select one of the existing interfaces for the new network request.
*
* TODO: for now there is only a single interface - simply pick it.
*/
private String selectInterfaceForRequest(AwareNetworkRequestInformation req) {
Iterator<String> it = mInterfaces.iterator();
if (it.hasNext()) {
return it.next();
}
Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!");
return "";
}
/**
* Select a channel for the network request.
*
* TODO: for now simply select channel 6
*/
private int selectChannelForRequest(AwareNetworkRequestInformation req) {
return 2437;
}
/**
* Aware network request. State object: contains network request information/state through its
* lifetime.
*/
@VisibleForTesting
public static class AwareNetworkRequestInformation {
static final int STATE_INITIATOR_IDLE = 100;
static final int STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE = 101;
static final int STATE_INITIATOR_WAIT_FOR_CONFIRM = 102;
static final int STATE_INITIATOR_CONFIRMED = 103;
static final int STATE_RESPONDER_IDLE = 200;
static final int STATE_RESPONDER_WAIT_FOR_REQUEST = 201;
static final int STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE = 202;
static final int STATE_RESPONDER_WAIT_FOR_CONFIRM = 203;
static final int STATE_RESPONDER_CONFIRMED = 204;
public int state;
public int role;
public int uid;
public String interfaceName;
public int pubSubId = 0;
public int peerId = 0;
public byte[] peerDiscoveryMac = null;
public byte[] pmk = null;
public int ndpId;
public byte[] peerDataMac;
public WifiAwareNetworkAgent networkAgent;
static AwareNetworkRequestInformation parseNetworkSpecifier(String networkSpecifier,
WifiAwareStateManager mgr) {
int type, role, uid, clientId, sessionId = 0, peerId = 0, pubSubId = 0;
byte[] peerMac = null;
byte[] pmk = null;
if (VDBG) {
Log.v(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier);
}
try {
JSONObject jsonObject = new JSONObject(networkSpecifier);
// type: always present
type = jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_TYPE);
if (type < 0 || type > WifiAwareManager.NETWORK_SPECIFIER_TYPE_MAX_VALID) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ ", invalid 'type' value");
return null;
}
// role: always present
role = jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE);
if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid 'role' value");
return null;
}
if (role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& type != WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB
&& type != WifiAwareManager.NETWORK_SPECIFIER_TYPE_OOB) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid 'type' value for INITIATOR (only IB and OOB are "
+ "permitted)");
return null;
}
// clientId: always present
clientId = jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID);
// sessionId: present for types IB, IB_ANY_PEER
if (type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB
|| type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER) {
sessionId = jsonObject.getInt(
WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID);
}
// peer Id: present for type IB
if (type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB) {
peerId = jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID);
}
// peerMac: present for type OOB
if (type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_OOB) {
try {
peerMac = HexEncoding.decode(jsonObject.getString(
WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
false);
if (peerMac == null || peerMac.length != 6) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid peer MAC address - null or not 6 bytes");
return null;
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier="
+ networkSpecifier + " -- invalid peer MAC address -- e=" + e);
return null;
}
}
// pmk: always present (though can be an empty array - equivalent to null)
pmk = Base64.decode(
jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK),
Base64.DEFAULT);
if (pmk != null && pmk.length == 0) {
pmk = null;
}
} catch (JSONException e) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid JSON format -- e=" + e);
return null;
}
// look up network specifier information in Aware state manager
WifiAwareClientState client = mgr.getClient(clientId);
if (client == null) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- not client with this id -- clientId=" + clientId);
return null;
}
uid = client.getUid();
// validate the role (if session ID provided: i.e. session 1xx)
if (type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB
|| type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER) {
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG,
"parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- no session with this id -- sessionId=" + sessionId);
return null;
}
if ((session.isPublishSession()
&& role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) || (
!session.isPublishSession()
&& role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid role for session type");
return null;
}
if (type == WifiAwareManager.NETWORK_SPECIFIER_TYPE_IB) {
pubSubId = session.getPubSubId();
String peerMacStr = session.getMac(peerId, null);
if (peerMacStr == null) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- no MAC address associated with this peer id -- peerId="
+ peerId);
return null;
}
try {
peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
if (peerMac == null || peerMac.length != 6) {
Log.e(TAG, "parseNetworkSpecifier: networkSpecifier="
+ networkSpecifier + " -- invalid peer MAC address");
return null;
}
} catch (IllegalArgumentException e) {
Log.e(TAG,
"parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ " -- invalid peer MAC address -- e=" + e);
return null;
}
}
}
// create container and populate
AwareNetworkRequestInformation nnri = new AwareNetworkRequestInformation();
nnri.state = (role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)
? AwareNetworkRequestInformation.STATE_INITIATOR_IDLE
: AwareNetworkRequestInformation.STATE_RESPONDER_IDLE;
nnri.role = role;
nnri.uid = uid;
nnri.pubSubId = pubSubId;
nnri.peerId = peerId;
nnri.peerDiscoveryMac = peerMac;
nnri.pmk = pmk;
return nnri;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("AwareNetworkRequestInformation: ");
sb.append("state=").append(state).append(", role=").append(role).append(
", uid=").append(uid).append(", interfaceName=").append(interfaceName).append(
", pubSubId=").append(pubSubId).append(", peerDiscoveryMac=").append(
peerDiscoveryMac == null ? ""
: String.valueOf(HexEncoding.encode(peerDiscoveryMac))).append(
", ndpId=").append(ndpId).append(", peerDataMac=").append(
peerDataMac == null ? "" : String.valueOf(HexEncoding.encode(peerDataMac)));
return sb.toString();
}
}
/**
* Enables mocking.
*/
@VisibleForTesting
public class NetworkInterfaceWrapper {
/**
* Configures network agent properties: link-local address, connected status, interface
* name. Delegated to enable mocking.
*/
public boolean configureAgentProperties(AwareNetworkRequestInformation nnri,
String networkSpecifier, int ndpId, NetworkInfo networkInfo,
NetworkCapabilities networkCapabilities, LinkProperties linkProperties) {
// find link-local address
InetAddress linkLocal = null;
NetworkInterface ni;
try {
ni = NetworkInterface.getByName(nnri.interfaceName);
} catch (SocketException e) {
Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri
+ ": can't get network interface - " + e);
mMgr.endDataPath(ndpId);
return false;
}
if (ni == null) {
Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri
+ ": can't get network interface (null)");
mMgr.endDataPath(ndpId);
return false;
}
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress ip = addresses.nextElement();
if (ip instanceof Inet6Address && ip.isLinkLocalAddress()) {
linkLocal = ip;
break;
}
}
if (linkLocal == null) {
Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": no link local addresses");
mMgr.endDataPath(ndpId);
return false;
}
// configure agent
networkInfo.setIsAvailable(true);
networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
networkCapabilities.setNetworkSpecifier(networkSpecifier);
linkProperties.setInterfaceName(nnri.interfaceName);
linkProperties.addLinkAddress(new LinkAddress(linkLocal, 64));
linkProperties.addRoute(
new RouteInfo(new IpPrefix("fe80::/64"), null, nnri.interfaceName));
return true;
}
}
/**
* Dump the internal state of the class.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("WifiAwareDataPathStateManager:");
pw.println(" mInterfaces: " + mInterfaces);
pw.println(" mNetworkCapabilitiesFilter: " + mNetworkCapabilitiesFilter);
pw.println(" mNetworkRequestsCache: " + mNetworkRequestsCache);
pw.println(" mNetworkFactory:");
mNetworkFactory.dump(fd, pw, args);
}
}