/* * 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.MatchAllNetworkSpecifier; import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.RouteInfo; import android.net.wifi.aware.WifiAwareManager; import android.net.wifi.aware.WifiAwareNetworkSpecifier; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.ServiceManager; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import libcore.util.HexEncoding; 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(...). */ 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 mInterfaces = new HashSet<>(); private final Map 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(new MatchAllNetworkSpecifier()); 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 getNetworkRequestByNdpId(int ndpId) { for (Map.Entry 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(WifiAwareNetworkSpecifier 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(WifiAwareNetworkSpecifier 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 WifiAwareNetworkSpecifier onDataPathRequest(int pubSubId, byte[] mac, int ndpId) { if (VDBG) { Log.v(TAG, "onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf( HexEncoding.encode(mac)) + ", ndpId=" + ndpId); } WifiAwareNetworkSpecifier networkSpecifier = null; AwareNetworkRequestInformation nnri = null; for (Map.Entry 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, 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, 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.networkSpecifier.pmk, nnri.networkSpecifier.passphrase); 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); } WifiAwareNetworkSpecifier networkSpecifier = null; AwareNetworkRequestInformation nnri = null; for (Map.Entry 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 WifiAwareNetworkSpecifier 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 nnriE = getNetworkRequestByNdpId(ndpId); if (nnriE == null) { Log.w(TAG, "onDataPathConfirm: network request not found for ndpId=" + ndpId); if (accept) { mMgr.endDataPath(ndpId); } return null; } WifiAwareNetworkSpecifier networkSpecifier = nnriE.getKey(); AwareNetworkRequestInformation nnri = nnriE.getValue(); // validate state if (nnri.networkSpecifier.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.networkSpecifier.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.networkSpecifier.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, linkProperties, NETWORK_FACTORY_SCORE_AVAIL, networkSpecifier, ndpId); nnri.networkAgent.sendNetworkInfo(networkInfo); } 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 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(NetworkSpecifier 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; } NetworkSpecifier networkSpecifierBase = request.networkCapabilities.getNetworkSpecifier(); if (!(networkSpecifierBase instanceof WifiAwareNetworkSpecifier)) { Log.w(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request + " - not a WifiAwareNetworkSpecifier"); return false; } WifiAwareNetworkSpecifier networkSpecifier = (WifiAwareNetworkSpecifier) networkSpecifierBase; // 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; } // TODO: validate that the client ID actually comes from the correct process and is // not faked? nnri = AwareNetworkRequestInformation.processNetworkSpecifier(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); } NetworkSpecifier networkSpecifierObj = networkRequest.networkCapabilities.getNetworkSpecifier(); WifiAwareNetworkSpecifier networkSpecifier = null; if (networkSpecifierObj instanceof WifiAwareNetworkSpecifier) { networkSpecifier = (WifiAwareNetworkSpecifier) networkSpecifierObj; } AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier); if (nnri == null) { Log.e(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest=" + networkRequest + " not in cache!?"); return; } if (nnri.networkSpecifier.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.networkSpecifier.peerId, NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP, selectChannelForRequest(nnri), nnri.peerDiscoveryMac, nnri.interfaceName, nnri.networkSpecifier.pmk, nnri.networkSpecifier.passphrase); 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); } NetworkSpecifier networkSpecifierObj = networkRequest.networkCapabilities.getNetworkSpecifier(); WifiAwareNetworkSpecifier networkSpecifier = null; if (networkSpecifierObj instanceof WifiAwareNetworkSpecifier) { networkSpecifier = (WifiAwareNetworkSpecifier) networkSpecifierObj; } 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.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && nnri.state > AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) { mMgr.endDataPath(nnri.ndpId); } if (nnri.networkSpecifier.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 WifiAwareNetworkSpecifier mNetworkSpecifier; private int mNdpId; WifiAwareNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, WifiAwareNetworkSpecifier 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 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 uid; public String interfaceName; public int pubSubId = 0; public byte[] peerDiscoveryMac = null; public int ndpId; public byte[] peerDataMac; public WifiAwareNetworkSpecifier networkSpecifier; public WifiAwareNetworkAgent networkAgent; static AwareNetworkRequestInformation processNetworkSpecifier(WifiAwareNetworkSpecifier ns, WifiAwareStateManager mgr) { int uid, pubSubId = 0; byte[] peerMac = ns.peerMac; if (VDBG) { Log.v(TAG, "processNetworkSpecifier: networkSpecifier=" + ns); } // type: always valid if (ns.type < 0 || ns.type > WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_MAX_VALID) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + ", invalid 'type' value"); return null; } // role: always valid if (ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- invalid 'role' value"); return null; } if (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB && ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- invalid 'type' value for INITIATOR (only IB and OOB are " + "permitted)"); return null; } // look up network specifier information in Aware state manager WifiAwareClientState client = mgr.getClient(ns.clientId); if (client == null) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- not client with this id -- clientId=" + ns.clientId); return null; } uid = client.getUid(); // validate the role (if session ID provided: i.e. session 1xx) if (ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB || ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER) { WifiAwareDiscoverySessionState session = client.getSession(ns.sessionId); if (session == null) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- no session with this id -- sessionId=" + ns.sessionId); return null; } if ((session.isPublishSession() && ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) || ( !session.isPublishSession() && ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- invalid role for session type"); return null; } if (ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB) { pubSubId = session.getPubSubId(); String peerMacStr = session.getMac(ns.peerId, null); if (peerMacStr == null) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- no MAC address associated with this peer id -- peerId=" + ns.peerId); return null; } try { peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false); if (peerMac == null || peerMac.length != 6) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- invalid peer MAC address"); return null; } } catch (IllegalArgumentException e) { Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns + " -- invalid peer MAC address -- e=" + e); return null; } } } // create container and populate AwareNetworkRequestInformation nnri = new AwareNetworkRequestInformation(); nnri.state = (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) ? AwareNetworkRequestInformation.STATE_INITIATOR_IDLE : AwareNetworkRequestInformation.STATE_RESPONDER_IDLE; nnri.uid = uid; nnri.pubSubId = pubSubId; nnri.peerDiscoveryMac = peerMac; nnri.networkSpecifier = ns; return nnri; } @Override public String toString() { StringBuilder sb = new StringBuilder("AwareNetworkRequestInformation: "); sb.append("state=").append(state).append(", ns=").append(networkSpecifier).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, WifiAwareNetworkSpecifier 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 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); } }