diff options
author | Etan Cohen <etancohen@google.com> | 2017-08-14 17:42:37 -0700 |
---|---|---|
committer | Etan Cohen <etancohen@google.com> | 2017-09-05 12:04:08 -0700 |
commit | a25953fc4fd640cebf57f1e33490a507af3c1db5 (patch) | |
tree | 65c5317b0c961f7c85eef089c1576eb081f4555f | |
parent | 083b290298faaed46e3a175eb3f707a9c453d7f6 (diff) |
[AWARE] Support multiple NAN data interfaces (NDI)
Support all the NAN data-interfaces which the firmware supports -
based on the returned capability.
- Create the data-interfaces
- Assign data-paths to data-interfaces based on constraints of
unique [(src mac, dst mac), security config] tuples. I.e. a
single data-interface can only have a single security configuration
to a specific destination interface.
Bug: 63635857
Test: unit tests + integrated test suite
Change-Id: I4a21f3b025e8164673366e22da062d536d588148
4 files changed, 137 insertions, 33 deletions
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java index 16888c81e..efeca655b 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java @@ -60,10 +60,11 @@ 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.Objects; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; /** * Manages Aware data-path lifetime: interface creation/deletion, data-path setup and tear-down. @@ -323,6 +324,8 @@ public class WifiAwareDataPathStateManager { * - 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. + * - The request must be pending (i.e. we could have completed requests for the same + * parameters) */ if (entry.getValue().pubSubId != 0 && entry.getValue().pubSubId != pubSubId) { continue; @@ -333,6 +336,11 @@ public class WifiAwareDataPathStateManager { continue; } + if (entry.getValue().state + != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) { + continue; + } + networkSpecifier = entry.getKey(); nnri = entry.getValue(); break; @@ -884,18 +892,43 @@ public class WifiAwareDataPathStateManager { } /** - * Select one of the existing interfaces for the new network request. + * Select one of the existing interfaces for the new network request. A request is canonical + * (otherwise it wouldn't be executed). * - * TODO: for now there is only a single interface - simply pick it. + * Construct a list of all interfaces currently used to communicate to the peer. The remaining + * interfaces are available for use for this request - if none are left then the request should + * fail (signaled to the caller by returning a null). */ private String selectInterfaceForRequest(AwareNetworkRequestInformation req) { - Iterator<String> it = mInterfaces.iterator(); - if (it.hasNext()) { - return it.next(); + SortedSet<String> potential = new TreeSet<>(mInterfaces); + Set<String> used = new HashSet<>(); + + if (VDBG) { + Log.v(TAG, "selectInterfaceForRequest: req=" + req + ", mNetworkRequestsCache=" + + mNetworkRequestsCache); } - Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!"); + for (AwareNetworkRequestInformation nnri : mNetworkRequestsCache.values()) { + if (nnri == req) { + continue; + } + + if (Arrays.equals(req.peerDiscoveryMac, nnri.peerDiscoveryMac)) { + used.add(nnri.interfaceName); + } + } + + if (VDBG) { + Log.v(TAG, "selectInterfaceForRequest: potential=" + potential + ", used=" + used); + } + + for (String ifName: potential) { + if (!used.contains(ifName)) { + return ifName; + } + } + Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - no interfaces available!"); return null; } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java index 2f424db4c..2121160bb 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java @@ -167,10 +167,6 @@ public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub imp capabilities.maxSubscribeInterfaceAddresses; frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites; - // TODO (b/63635857): enable framework support of >1 NDI - // Until then: force corresponding capability to 1. - frameworkCapabilities.maxNdiInterfaces = 1; - mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities); } else { Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " (" diff --git a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java index 41eef6960..a2f604686 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java @@ -55,6 +55,7 @@ public class WifiAwareShellCommand extends ShellCommand { for (DelegatedShellCommand dsc: mDelegatedCommands.values()) { dsc.onReset(); } + return 0; } else { DelegatedShellCommand delegatedCmd = null; if (!TextUtils.isEmpty(cmd)) { diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java index 227a196a8..61143d9bb 100644 --- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi.aware; import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.CHANNEL_NOT_REQUESTED; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -84,6 +85,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Unit test harness for WifiAwareDataPathStateManager class. @@ -359,8 +362,7 @@ public class WifiAwareDataPathStateManagerTest { final byte pubSubId = 58; final int requestorId = 1341234; final int ndpId = 2; - final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); - final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + final int[] startOrder = {0, 1, 2}; final int[] endOrder = {1, 0, 2}; int networkRequestId = 0; @@ -378,8 +380,10 @@ public class WifiAwareDataPathStateManagerTest { boolean first = true; for (int i : startOrder) { networkRequestId += 1; - peerDiscoveryMac[5] = (byte) (peerDiscoveryMac[5] + 1); - peerDataPathMac[5] = (byte) (peerDataPathMac[5] + 1); + byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); + byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + peerDiscoveryMac[5] = (byte) (peerDiscoveryMac[5] + i); + peerDataPathMac[5] = (byte) (peerDataPathMac[5] + i); // (0) initialize ress[i] = initDataPathEndPoint(first, clientId, (byte) (pubSubId + i), @@ -474,16 +478,16 @@ public class WifiAwareDataPathStateManagerTest { InOrder inOrderM = inOrder(mAwareMetricsMock); // (1) initialize all clients - Messenger messenger = initOobDataPathEndPoint(true, clientId, inOrder, inOrderM); + Messenger messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM); for (int i = 1; i < numRequestsPre + numRequestsPost; ++i) { - initOobDataPathEndPoint(false, clientId + i, inOrder, inOrderM); + initOobDataPathEndPoint(false, 1, clientId + i, inOrder, inOrderM); } // (2) make 3 network requests (all identical under the hood) for (int i = 0; i < numRequestsPre; ++i) { nrs[i] = getDirectNetworkRequest(clientId + i, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null, - null); + null, i); Message reqNetworkMsg = Message.obtain(); reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; @@ -532,7 +536,7 @@ public class WifiAwareDataPathStateManagerTest { for (int i = numRequestsPre; i < numRequestsPre + numRequestsPost; ++i) { nrs[i] = getDirectNetworkRequest(clientId + i, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null, - null); + null, i); Message reqNetworkMsg = Message.obtain(); reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; @@ -570,6 +574,74 @@ public class WifiAwareDataPathStateManagerTest { mAwareMetricsMock, mMockNwMgt); } + /** + * Validate that multiple NDP requests to the same peer target different NDIs. + */ + @Test + public void testMultipleNdi() throws Exception { + final int numNdis = 5; + final int clientId = 123; + final int ndpId = 5; + final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false); + final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false); + + ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); + ArgumentCaptor<String> ifNameCaptor = ArgumentCaptor.forClass(String.class); + + InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mMockNwMgt); + InOrder inOrderM = inOrder(mAwareMetricsMock); + + // (1) initialize all clients + Messenger messenger = initOobDataPathEndPoint(true, numNdis, clientId, inOrder, inOrderM); + for (int i = 1; i < numNdis + 3; ++i) { + initOobDataPathEndPoint(false, numNdis, clientId + i, inOrder, inOrderM); + } + + // (2) make N network requests: each unique + Set<String> interfaces = new HashSet<>(); + for (int i = 0; i < numNdis + 1; ++i) { + byte[] pmk = new byte[32]; + pmk[0] = (byte) i; + + NetworkRequest nr = getDirectNetworkRequest(clientId + i, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, pmk, + null, i); + + Message reqNetworkMsg = Message.obtain(); + reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK; + reqNetworkMsg.obj = nr; + reqNetworkMsg.arg1 = 0; + messenger.send(reqNetworkMsg); + mMockLooper.dispatchAll(); + + if (i < numNdis) { + inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(), eq(0), + eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac), + ifNameCaptor.capture(), eq(pmk), eq(null), eq(true), any()); + interfaces.add(ifNameCaptor.getValue()); + + mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId + i); + mDut.onDataPathConfirmNotification(ndpId + i, peerDataPathMac, true, 0, null); + mMockLooper.dispatchAll(); + + inOrder.verify(mMockNwMgt).setInterfaceUp(anyString()); + inOrder.verify(mMockNwMgt).enableIpv6(anyString()); + inOrder.verify(mMockCm).registerNetworkAgent(any(), any(), any(), any(), anyInt(), + any()); + inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS), + eq(true), anyLong()); + inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any()); + } + } + + // verify that each interface name is unique + assertEquals("Number of unique interface names", numNdis, interfaces.size()); + + verifyNoMoreInteractions(mMockNative, mMockCm, mMockCallback, mMockSessionCallback, + mAwareMetricsMock, mMockNwMgt); + } + /* * Initiator tests */ @@ -929,7 +1001,7 @@ public class WifiAwareDataPathStateManagerTest { nr = getDirectNetworkRequest(clientId, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null); + providePassphrase ? passphrase : null, 0); } else { nr = getSessionNetworkRequest(clientId, res.mSessionId, provideMac ? res.mPeerHandle : null, providePmk ? pmk : null, @@ -1038,7 +1110,7 @@ public class WifiAwareDataPathStateManagerTest { nr = getDirectNetworkRequest(clientId, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null, - providePassphrase ? passphrase : null); + providePassphrase ? passphrase : null, 0); } else { nr = getSessionNetworkRequest(clientId, res.mSessionId, provideMac ? res.mPeerHandle : null, providePmk ? pmk : null, @@ -1183,7 +1255,7 @@ public class WifiAwareDataPathStateManagerTest { } private NetworkRequest getDirectNetworkRequest(int clientId, int role, byte[] peer, - byte[] pmk, String passphrase) throws Exception { + byte[] pmk, String passphrase, int requestId) throws Exception { final IWifiAwareManager mockAwareService = mock(IWifiAwareManager.class); final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mockAwareService); @@ -1220,7 +1292,7 @@ public class WifiAwareDataPathStateManagerTest { nc.setLinkDownstreamBandwidthKbps(1); nc.setSignalStrength(1); - return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.REQUEST); + return new NetworkRequest(nc, 0, requestId, NetworkRequest.Type.REQUEST); } private DataPathEndPointInfo initDataPathEndPoint(boolean isFirstIteration, int clientId, @@ -1237,7 +1309,7 @@ public class WifiAwareDataPathStateManagerTest { Messenger messenger = null; if (isFirstIteration) { - messenger = initOobDataPathEndPoint(true, clientId, inOrder, inOrderM); + messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM); } if (doPublish) { @@ -1271,8 +1343,8 @@ public class WifiAwareDataPathStateManagerTest { isFirstIteration ? messenger : null); } - private Messenger initOobDataPathEndPoint(boolean startUpSequence, int clientId, - InOrder inOrder, InOrder inOrderM) throws Exception { + private Messenger initOobDataPathEndPoint(boolean startUpSequence, int maxNdiInterfaces, + int clientId, InOrder inOrder, InOrder inOrderM) throws Exception { final int pid = 2000; final String callingPackage = "com.android.somePackage"; final ConfigRequest configRequest = new ConfigRequest.Builder().build(); @@ -1282,7 +1354,7 @@ public class WifiAwareDataPathStateManagerTest { ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class); Capabilities capabilities = new Capabilities(); - capabilities.maxNdiInterfaces = 1; + capabilities.maxNdiInterfaces = maxNdiInterfaces; if (startUpSequence) { // (0) start/registrations @@ -1322,12 +1394,14 @@ public class WifiAwareDataPathStateManagerTest { any()); if (startUpSequence) { - inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(), - strCaptor.capture()); - collector.checkThat("interface created -- 0", sAwareInterfacePrefix + 0, - equalTo(strCaptor.getValue())); - mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0); - mMockLooper.dispatchAll(); + for (int i = 0; i < maxNdiInterfaces; ++i) { + inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(), + strCaptor.capture()); + collector.checkThat("interface created -- " + i, sAwareInterfacePrefix + i, + equalTo(strCaptor.getValue())); + mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0); + mMockLooper.dispatchAll(); + } return messengerCaptor.getValue(); } |