From 7faa4facf9d90ee8ba8968f9ca8e6faee91821b5 Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Wed, 17 Oct 2018 15:48:39 -0700 Subject: [PATCH] [AWARE] Add Wi-Fi Aware-specific Network info: port, transport protocol Add port and transport protocol to the network specification and network capabilities. Bug: 117605977 Test: atest android.net.wifi.aware Change-Id: Ia78e5f31ce55f4112feb8209110930a6503e69ad --- api/current.txt | 4 + .../net/wifi/aware/WifiAwareManager.java | 70 ++++- .../net/wifi/aware/WifiAwareNetworkInfo.java | 58 +++- .../wifi/aware/WifiAwareNetworkSpecifier.java | 56 ++-- .../WifiAwareAgentNetworkSpecifierTest.java | 3 +- .../net/wifi/aware/WifiAwareManagerTest.java | 263 ++++++++++++++++-- 6 files changed, 415 insertions(+), 39 deletions(-) diff --git a/api/current.txt b/api/current.txt index f81eb54743ca6..9554924636fd5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29824,12 +29824,16 @@ package android.net.wifi.aware { method public android.net.NetworkSpecifier build(); method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setDiscoverySession(android.net.wifi.aware.DiscoverySession); method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPeerHandle(android.net.wifi.aware.PeerHandle); + method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPort(int); method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPskPassphrase(java.lang.String); + method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setTransportProtocol(int); } public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo { method public int describeContents(); method public java.net.Inet6Address getPeerIpv6Addr(); + method public int getPort(); + method public int getTransportProtocol(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 26a6c08bee290..1fa1fd521a8e6 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -35,6 +35,7 @@ import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; import libcore.util.HexEncoding; @@ -434,6 +435,8 @@ public class WifiAwareManager { null, // peerMac (not used in this method) pmk, passphrase, + 0, // no port info for deprecated IB APIs + -1, // no transport info for deprecated IB APIs Process.myUid()); } @@ -473,6 +476,8 @@ public class WifiAwareManager { peer, pmk, passphrase, + 0, // no port info for OOB APIs + -1, // no transport protocol info for OOB APIs Process.myUid()); } @@ -824,6 +829,8 @@ public class WifiAwareManager { private PeerHandle mPeerHandle; private String mPskPassphrase; private byte[] mPmk; + private int mPort = 0; // invalid value + private int mTransportProtocol = -1; // invalid value /** * Configure the {@link PublishDiscoverySession} or {@link SubscribeDiscoverySession} @@ -901,6 +908,55 @@ public class WifiAwareManager { return this; } + /** + * Configure the port number which will be used to create a connection over this link. This + * configuration should only be done on the server device, e.g. the device creating the + * {@link java.net.ServerSocket}. + *

Notes: + *

+ * + * @param port A positive integer indicating the port to be used for communication. + * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder + * methods. + */ + public @NonNull NetworkSpecifierBuilder setPort(int port) { + if (port <= 0 || port > 65535) { + throw new IllegalArgumentException("The port must be a positive value (0, 65535]"); + } + mPort = port; + return this; + } + + /** + * Configure the transport protocol which will be used to create a connection over this + * link. This configuration should only be done on the server device, e.g. the device + * creating the {@link java.net.ServerSocket} for TCP. + *

Notes: + *

+ * The transport protocol number is assigned by the Internet Assigned Numbers Authority + * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml. + * + * @param transportProtocol The transport protocol to be used for communication. + * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder + * methods. + */ + public @NonNull NetworkSpecifierBuilder setTransportProtocol(int transportProtocol) { + if (transportProtocol < 0 || transportProtocol > 255) { + throw new IllegalArgumentException( + "The transport protocol must be in range [0, 255]"); + } + mTransportProtocol = transportProtocol; + return this; + } + /** * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} * for a WiFi Aware connection (link) to the specified peer. The @@ -929,6 +985,18 @@ public class WifiAwareManager { ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; + if (mPort != 0 || mTransportProtocol != -1) { + if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { + throw new IllegalStateException( + "Port and transport protocol information can only " + + "be specified on the Publisher device (which is the server"); + } + if (TextUtils.isEmpty(mPskPassphrase) && mPmk == null) { + throw new IllegalStateException("Port and transport protocol information can " + + "only be specified on a secure link"); + } + } + if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && mPeerHandle == null) { throw new IllegalStateException("Null peerHandle!?"); } @@ -936,7 +1004,7 @@ public class WifiAwareManager { return new WifiAwareNetworkSpecifier( WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role, mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId, - null, mPmk, mPskPassphrase, Process.myUid()); + null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid()); } } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java index 0f29e081e2a0e..b258906df5229 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java @@ -38,17 +38,30 @@ import java.util.Objects; * android.net.NetworkCapabilities)} callback. *

* The Wi-Fi Aware-specific network information include the peer's scoped link-local IPv6 address - * for the Wi-Fi Aware link. The scoped link-local IPv6 can then be used to create a + * for the Wi-Fi Aware link, as well as (optionally) the port and transport protocol specified by + * the peer. + * The scoped link-local IPv6, port, and transport protocol can then be used to create a * {@link java.net.Socket} connection to the peer. + *

+ * Note: these are the peer's IPv6 and port information - not the local device's! */ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { private Inet6Address mIpv6Addr; + private int mPort = 0; // a value of 0 is considered invalid + private int mTransportProtocol = -1; // a value of -1 is considered invalid /** @hide */ public WifiAwareNetworkInfo(Inet6Address ipv6Addr) { mIpv6Addr = ipv6Addr; } + /** @hide */ + public WifiAwareNetworkInfo(Inet6Address ipv6Addr, int port, int transportProtocol) { + mIpv6Addr = ipv6Addr; + mPort = port; + mTransportProtocol = transportProtocol; + } + /** * Get the scoped link-local IPv6 address of the Wi-Fi Aware peer (not of the local device!). * @@ -59,6 +72,34 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { return mIpv6Addr; } + /** + * Get the port number to be used to create a network connection to the Wi-Fi Aware peer. + * The port information is provided by the app running on the peer which requested the + * connection, using the {@link WifiAwareManager.NetworkSpecifierBuilder#setPort(int)}. + * + * @return A port number on the peer. A value of 0 indicates that no port was specified by the + * peer. + */ + public int getPort() { + return mPort; + } + + /** + * Get the transport protocol to be used to communicate over a network connection to the Wi-Fi + * Aware peer. The transport protocol is provided by the app running on the peer which requested + * the connection, using the + * {@link WifiAwareManager.NetworkSpecifierBuilder#setTransportProtocol(int)}. + *

+ * The transport protocol number is assigned by the Internet Assigned Numbers Authority + * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml. + * + * @return A transport protocol id. A value of -1 indicates that no transport protocol was + * specified by the peer. + */ + public int getTransportProtocol() { + return mTransportProtocol; + } + // parcelable methods @Override @@ -71,6 +112,8 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { dest.writeByteArray(mIpv6Addr.getAddress()); NetworkInterface ni = mIpv6Addr.getScopedInterface(); dest.writeString(ni == null ? null : ni.getName()); + dest.writeInt(mPort); + dest.writeInt(mTransportProtocol); } public static final Creator CREATOR = @@ -94,8 +137,10 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { e.printStackTrace(); return null; } + int port = in.readInt(); + int transportProtocol = in.readInt(); - return new WifiAwareNetworkInfo(ipv6Addr); + return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol); } @Override @@ -109,7 +154,9 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { @Override public String toString() { - return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).toString(); + return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).append( + ", port=").append(mPort).append(", transportProtocol=").append( + mTransportProtocol).toString(); } /** @hide */ @@ -124,12 +171,13 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { } WifiAwareNetworkInfo lhs = (WifiAwareNetworkInfo) obj; - return Objects.equals(mIpv6Addr, lhs.mIpv6Addr); + return Objects.equals(mIpv6Addr, lhs.mIpv6Addr) && mPort == lhs.mPort + && mTransportProtocol == lhs.mTransportProtocol; } /** @hide */ @Override public int hashCode() { - return Objects.hash(mIpv6Addr); + return Objects.hash(mIpv6Addr, mPort, mTransportProtocol); } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index 6e37fcf4d338c..a93a6d58a3ec7 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -19,7 +19,6 @@ package android.net.wifi.aware; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; import java.util.Arrays; import java.util.Objects; @@ -116,6 +115,32 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements */ public final String passphrase; + /** + * The port information to be used for this link. This information will be communicated to the + * peer as part of the layer 2 link setup. + * + * Information only allowed on secure links since a single layer-2 link is set up for all + * requestors. Therefore if multiple apps on a single device request links to the same peer + * device they all get the same link. However, the link is only set up on the first request - + * hence only the first can transmit the port information. But we don't want to expose that + * information to other apps. Limiting to secure links would (usually) imply single app usage. + * + * @hide + */ + public final int port; + + /** + * The transport protocol information to be used for this link. This information will be + * communicated to the peer as part of the layer 2 link setup. + * + * Information only allowed on secure links since a single layer-2 link is set up for all + * requestors. Therefore if multiple apps on a single device request links to the same peer + * device they all get the same link. However, the link is only set up on the first request - + * hence only the first can transmit the port information. But we don't want to expose that + * information to other apps. Limiting to secure links would (usually) imply single app usage. + */ + public final int transportProtocol; + /** * The UID of the process initializing this network specifier. Validated by receiver using * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the @@ -127,7 +152,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements /** @hide */ public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, - byte[] peerMac, byte[] pmk, String passphrase, int requestorUid) { + byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol, + int requestorUid) { this.type = type; this.role = role; this.clientId = clientId; @@ -136,6 +162,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements this.peerMac = peerMac; this.pmk = pmk; this.passphrase = passphrase; + this.port = port; + this.transportProtocol = transportProtocol; this.requestorUid = requestorUid; } @@ -152,6 +180,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements in.createByteArray(), // peerMac in.createByteArray(), // pmk in.readString(), // passphrase + in.readInt(), // port + in.readInt(), // transportProtocol in.readInt()); // requestorUid } @@ -186,6 +216,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements dest.writeByteArray(peerMac); dest.writeByteArray(pmk); dest.writeString(passphrase); + dest.writeInt(port); + dest.writeInt(transportProtocol); dest.writeInt(requestorUid); } @@ -202,19 +234,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements /** @hide */ @Override public int hashCode() { - int result = 17; - - result = 31 * result + type; - result = 31 * result + role; - result = 31 * result + clientId; - result = 31 * result + sessionId; - result = 31 * result + peerId; - result = 31 * result + Arrays.hashCode(peerMac); - result = 31 * result + Arrays.hashCode(pmk); - result = 31 * result + Objects.hashCode(passphrase); - result = 31 * result + requestorUid; - - return result; + return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac), + Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid); } /** @hide */ @@ -238,6 +259,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements && Arrays.equals(peerMac, lhs.peerMac) && Arrays.equals(pmk, lhs.pmk) && Objects.equals(passphrase, lhs.passphrase) + && port == lhs.port + && transportProtocol == lhs.transportProtocol && requestorUid == lhs.requestorUid; } @@ -256,7 +279,8 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements .append(", pmk=").append((pmk == null) ? "" : "") // masking PII .append(", passphrase=").append((passphrase == null) ? "" : "") - .append(", requestorUid=").append(requestorUid) + .append(", port=").append(port).append(", transportProtocol=") + .append(transportProtocol).append(", requestorUid=").append(requestorUid) .append("]"); return sb.toString(); } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java index 4189e40718d82..c3b62854f12cc 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -62,6 +62,7 @@ public class WifiAwareAgentNetworkSpecifierTest { WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR); assertEquals(dut, rereadDut); + assertEquals(dut.hashCode(), rereadDut.hashCode()); // Ensure that individual network specifiers are satisfied by both the original & marshaled // |WifiAwareNetworkAgentSpecifier instances. @@ -181,6 +182,6 @@ public class WifiAwareAgentNetworkSpecifierTest { WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) { return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6], - null, null, 0); + null, null, 10, 5, 0); } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index ed38c7613a627..6da6d4adeb628 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -16,8 +16,11 @@ package android.net.wifi.aware; +import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB; + 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.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -913,9 +916,10 @@ public class WifiAwareManagerTest { final int clientId = 4565; final int sessionId = 123; final PeerHandle peerHandle = new PeerHandle(123412); - final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; final byte[] pmk = PMK_VALID; final String passphrase = PASSPHRASE_VALID; + final int port = 5; + final int transportProtocol = 10; final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); @@ -959,56 +963,70 @@ public class WifiAwareManagerTest { .setPeerHandle(peerHandle).build(); // validate format - collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(ns.role)); collector.checkThat("client_id", clientId, equalTo(ns.clientId)); collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); - collector.checkThat("role", role, equalTo(nsb.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(nsb.role)); collector.checkThat("client_id", clientId, equalTo(nsb.clientId)); collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId)); + collector.checkThat("port", 0, equalTo(nsb.port)); + collector.checkThat("transportProtocol", -1, equalTo(nsb.transportProtocol)); // (4) request an encrypted (PMK) network specifier from the session ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk( peerHandle, pmk); - nsb = - (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() - .setDiscoverySession( - publishSession.getValue()).setPeerHandle(peerHandle).setPmk(pmk).build(); + nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle) + .setPmk(pmk).setPort(port).setTransportProtocol(transportProtocol).build(); // validate format - collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(ns.role)); collector.checkThat("client_id", clientId, equalTo(ns.clientId)); collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); collector.checkThat("pmk", pmk , equalTo(ns.pmk)); - collector.checkThat("role", role, equalTo(nsb.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(nsb.role)); collector.checkThat("client_id", clientId, equalTo(nsb.clientId)); collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId)); collector.checkThat("pmk", pmk , equalTo(nsb.pmk)); + collector.checkThat("port", port, equalTo(nsb.port)); + collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol)); // (5) request an encrypted (Passphrase) network specifier from the session - ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase( - peerHandle, passphrase); + ns = + (WifiAwareNetworkSpecifier) publishSession.getValue() + .createNetworkSpecifierPassphrase( + peerHandle, passphrase); nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle) - .setPskPassphrase(passphrase).build(); + .setPskPassphrase(passphrase).setPort(port).setTransportProtocol(transportProtocol) + .build(); // validate format - collector.checkThat("role", role, equalTo(ns.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(ns.role)); collector.checkThat("client_id", clientId, equalTo(ns.clientId)); collector.checkThat("session_id", sessionId, equalTo(ns.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId)); collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase)); - collector.checkThat("role", role, equalTo(nsb.role)); + collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, + equalTo(nsb.role)); collector.checkThat("client_id", clientId, equalTo(nsb.clientId)); collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId)); collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId)); collector.checkThat("passphrase", passphrase, equalTo(nsb.passphrase)); + collector.checkThat("port", port, equalTo(nsb.port)); + collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); @@ -1325,6 +1343,140 @@ public class WifiAwareManagerTest { executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false); } + /** + * Validate that get an exception when creating a network specifier with an invalid port number + * (<=0). + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierBuilderInvalidPortNumber() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final byte[] pmk = PMK_VALID; + final int port = 0; + + DiscoverySession publishSession = executeSessionStartup(true); + + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setPmk(pmk).setPort(port).build(); + } + + /** + * Validate that get an exception when creating a network specifier with port information + * without also requesting a secure link. + */ + @Test(expected = IllegalStateException.class) + public void testNetworkSpecifierBuilderInvalidPortOnInsecure() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final int port = 5; + + DiscoverySession publishSession = executeSessionStartup(true); + + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setPort(port).build(); + } + + /** + * Validate that get an exception when creating a network specifier with port information on + * a responder. + */ + @Test(expected = IllegalStateException.class) + public void testNetworkSpecifierBuilderInvalidPortOnResponder() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final int port = 5; + + DiscoverySession subscribeSession = executeSessionStartup(false); + + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle) + .setPort(port).build(); + } + + /** + * Validate that get an exception when creating a network specifier with an invalid transport + * protocol number (not in [0, 255]). + */ + @Test + public void testNetworkSpecifierBuilderInvalidTransportProtocolNumber() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final byte[] pmk = PMK_VALID; + final int tpNegative = -1; + final int tpTooLarge = 256; + final int tpSmallest = 0; + final int tpLargest = 255; + + DiscoverySession publishSession = executeSessionStartup(true); + + try { + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setPmk(pmk).setTransportProtocol(tpNegative).build(); + assertTrue("No exception on negative transport protocol!", false); + } catch (IllegalArgumentException e) { + // nop - exception is correct! + } + try { + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setPmk(pmk).setTransportProtocol(tpTooLarge).build(); + assertTrue("No exception on >255 transport protocol!", false); + } catch (IllegalArgumentException e) { + // nop - exception is correct! + } + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setPmk(pmk).setTransportProtocol(tpSmallest).build(); + nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession( + publishSession).setPeerHandle(peerHandle).setPmk( + pmk).setTransportProtocol(tpLargest).build(); + } + + /** + * Validate that get an exception when creating a network specifier with transport protocol + * information without also requesting a secure link. + */ + @Test(expected = IllegalStateException.class) + public void testNetworkSpecifierBuilderInvalidTransportProtocolOnInsecure() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final int transportProtocol = 5; + + DiscoverySession publishSession = executeSessionStartup(true); + + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(publishSession).setPeerHandle(peerHandle) + .setTransportProtocol(transportProtocol).build(); + } + + /** + * Validate that get an exception when creating a network specifier with transport protocol + * information on a responder. + */ + @Test(expected = IllegalStateException.class) + public void testNetworkSpecifierBuilderInvalidTransportProtocolOnResponder() throws Exception { + final PeerHandle peerHandle = new PeerHandle(123412); + final int transportProtocol = 5; + + DiscoverySession subscribeSession = executeSessionStartup(false); + + WifiAwareNetworkSpecifier nsb = + (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder() + .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle) + .setTransportProtocol(transportProtocol).build(); + } + + /* + * Utilities + */ + private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk, String passphrase, boolean doInitiator) throws Exception { final int clientId = 134; @@ -1356,7 +1508,83 @@ public class WifiAwareManagerTest { } } - // WifiAwareNetworkInfo tests + private DiscoverySession executeSessionStartup(boolean isPublish) throws Exception { + final int clientId = 4565; + final int sessionId = 123; + final PeerHandle peerHandle = new PeerHandle(123412); + final int port = 5; + final ConfigRequest configRequest = new ConfigRequest.Builder().build(); + final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); + final PublishConfig publishConfig = new PublishConfig.Builder().build(); + + ArgumentCaptor sessionCaptor = ArgumentCaptor.forClass( + WifiAwareSession.class); + ArgumentCaptor clientProxyCallback = ArgumentCaptor + .forClass(IWifiAwareEventCallback.class); + ArgumentCaptor sessionProxyCallback = ArgumentCaptor + .forClass(IWifiAwareDiscoverySessionCallback.class); + ArgumentCaptor publishSession = ArgumentCaptor + .forClass(PublishDiscoverySession.class); + ArgumentCaptor subscribeSession = ArgumentCaptor + .forClass(SubscribeDiscoverySession.class); + + + InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService, + mockPublishSession, mockRttListener); + + // (1) connect successfully + mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); + clientProxyCallback.getValue().onConnectSuccess(clientId); + mMockLooper.dispatchAll(); + inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); + WifiAwareSession session = sessionCaptor.getValue(); + + if (isPublish) { + // (2) publish successfully + session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); + inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig), + sessionProxyCallback.capture()); + sessionProxyCallback.getValue().onSessionStarted(sessionId); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture()); + return publishSession.getValue(); + } else { + // (2) subscribe successfully + session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); + inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig), + sessionProxyCallback.capture()); + sessionProxyCallback.getValue().onSessionStarted(sessionId); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture()); + return subscribeSession.getValue(); + } + } + + // WifiAwareNetworkSpecifier && WifiAwareNetworkInfo tests + + @Test + public void testWifiAwareNetworkSpecifierParcel() { + WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB, + WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334, + HexEncoding.decode("000102030405".toCharArray(), false), + "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001); + + Parcel parcelW = Parcel.obtain(); + ns.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + WifiAwareNetworkSpecifier rereadNs = + WifiAwareNetworkSpecifier.CREATOR.createFromParcel(parcelR); + + assertEquals(ns, rereadNs); + assertEquals(ns.hashCode(), rereadNs.hashCode()); + } @Test public void testWifiAwareNetworkCapabilitiesParcel() throws UnknownHostException { @@ -1364,9 +1592,11 @@ public class WifiAwareManagerTest { "11:22:33:44:55:66").getLinkLocalIpv6FromEui48Mac(); // note: dummy scope = 5 final Inet6Address inet6Scoped = Inet6Address.getByAddress(null, inet6.getAddress(), 5); + final int port = 5; + final int transportProtocol = 6; assertEquals(inet6Scoped.toString(), "/fe80::1322:33ff:fe44:5566%5"); - WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped); + WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped, port, transportProtocol); Parcel parcelW = Parcel.obtain(); cap.writeToParcel(parcelW, 0); @@ -1380,6 +1610,7 @@ public class WifiAwareManagerTest { WifiAwareNetworkInfo.CREATOR.createFromParcel(parcelR); assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5"); + assertEquals(cap, rereadCap); assertEquals(cap.hashCode(), rereadCap.hashCode()); }