1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.wifi.aware; 18 19 import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR; 20 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.net.ConnectivityManager; 24 import android.net.Network; 25 import android.net.NetworkRequest; 26 import android.net.NetworkSpecifier; 27 import android.os.Build; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.text.TextUtils; 31 32 import androidx.annotation.RequiresApi; 33 34 import com.android.modules.utils.build.SdkLevel; 35 36 import java.util.Arrays; 37 import java.util.Objects; 38 39 /** 40 * Network specifier object used to request a Wi-Fi Aware network. Apps should use the 41 * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance. 42 */ 43 public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { 44 /** 45 * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional 46 * @hide 47 */ 48 public static final int NETWORK_SPECIFIER_TYPE_IB = 0; 49 50 /** 51 * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional 52 * [only permitted for RESPONDER] 53 * @hide 54 */ 55 public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; 56 57 /** 58 * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional 59 * @hide 60 */ 61 public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; 62 63 /** 64 * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional 65 * [only permitted for RESPONDER] 66 * @hide 67 */ 68 public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; 69 70 /** @hide */ 71 public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; 72 73 /** 74 * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object. 75 * @hide 76 */ 77 public final int type; 78 79 /** 80 * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or 81 * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER. 82 * @hide 83 */ 84 public final int role; 85 86 /** 87 * The client ID of the device. 88 * @hide 89 */ 90 public final int clientId; 91 92 /** 93 * The session ID in which context to request a data-path. Only relevant for IB requests. 94 * @hide 95 */ 96 public final int sessionId; 97 98 /** 99 * The peer ID of the device which the data-path should be connected to. Only relevant for 100 * IB requests (i.e. not IB_ANY_PEER or OOB*). 101 * @hide 102 */ 103 public final int peerId; 104 105 /** 106 * The peer MAC address of the device which the data-path should be connected to. Only relevant 107 * for OB requests (i.e. not OOB_ANY_PEER or IB*). 108 * @hide 109 */ 110 public final byte[] peerMac; 111 112 /** 113 * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should 114 * be specified. 115 * @hide 116 */ 117 public final byte[] pmk; 118 119 /** 120 * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or 121 * passphrase should be specified. 122 * @hide 123 */ 124 public final String passphrase; 125 126 /** 127 * The port information to be used for this link. This information will be communicated to the 128 * peer as part of the layer 2 link setup. 129 * 130 * Information only allowed on secure links since a single layer-2 link is set up for all 131 * requestors. Therefore if multiple apps on a single device request links to the same peer 132 * device they all get the same link. However, the link is only set up on the first request - 133 * hence only the first can transmit the port information. But we don't want to expose that 134 * information to other apps. Limiting to secure links would (usually) imply single app usage. 135 * 136 * @hide 137 */ 138 public final int port; 139 140 /** 141 * The transport protocol information to be used for this link. This information will be 142 * communicated to the peer as part of the layer 2 link setup. 143 * 144 * Information only allowed on secure links since a single layer-2 link is set up for all 145 * requestors. Therefore if multiple apps on a single device request links to the same peer 146 * device they all get the same link. However, the link is only set up on the first request - 147 * hence only the first can transmit the port information. But we don't want to expose that 148 * information to other apps. Limiting to secure links would (usually) imply single app usage. 149 * 150 * @hide 151 */ 152 public final int transportProtocol; 153 154 /** @hide */ WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol)155 public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, 156 byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) { 157 this.type = type; 158 this.role = role; 159 this.clientId = clientId; 160 this.sessionId = sessionId; 161 this.peerId = peerId; 162 this.peerMac = peerMac; 163 this.pmk = pmk; 164 this.passphrase = passphrase; 165 this.port = port; 166 this.transportProtocol = transportProtocol; 167 } 168 169 public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR = 170 new Creator<WifiAwareNetworkSpecifier>() { 171 @Override 172 public WifiAwareNetworkSpecifier createFromParcel(Parcel in) { 173 return new WifiAwareNetworkSpecifier( 174 in.readInt(), // type 175 in.readInt(), // role 176 in.readInt(), // clientId 177 in.readInt(), // sessionId 178 in.readInt(), // peerId 179 in.createByteArray(), // peerMac 180 in.createByteArray(), // pmk 181 in.readString(), // passphrase 182 in.readInt(), // port 183 in.readInt()); // transportProtocol 184 } 185 186 @Override 187 public WifiAwareNetworkSpecifier[] newArray(int size) { 188 return new WifiAwareNetworkSpecifier[size]; 189 } 190 }; 191 192 /** 193 * Indicates whether the network specifier specifies an OOB (out-of-band) data-path - i.e. a 194 * data-path created without a corresponding Aware discovery session. 195 * 196 * @hide 197 */ isOutOfBand()198 public boolean isOutOfBand() { 199 return type == NETWORK_SPECIFIER_TYPE_OOB || type == NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; 200 } 201 202 @Override describeContents()203 public int describeContents() { 204 return 0; 205 } 206 207 @Override writeToParcel(Parcel dest, int flags)208 public void writeToParcel(Parcel dest, int flags) { 209 dest.writeInt(type); 210 dest.writeInt(role); 211 dest.writeInt(clientId); 212 dest.writeInt(sessionId); 213 dest.writeInt(peerId); 214 dest.writeByteArray(peerMac); 215 dest.writeByteArray(pmk); 216 dest.writeString(passphrase); 217 dest.writeInt(port); 218 dest.writeInt(transportProtocol); 219 } 220 221 /** @hide */ 222 @Override canBeSatisfiedBy(NetworkSpecifier other)223 public boolean canBeSatisfiedBy(NetworkSpecifier other) { 224 // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier. 225 if (other instanceof WifiAwareAgentNetworkSpecifier) { 226 return ((WifiAwareAgentNetworkSpecifier) other).satisfiesAwareNetworkSpecifier(this); 227 } 228 return equals(other); 229 } 230 231 /** @hide */ 232 @Override hashCode()233 public int hashCode() { 234 return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac), 235 Arrays.hashCode(pmk), passphrase, port, transportProtocol); 236 } 237 238 /** @hide */ 239 @Override equals(Object obj)240 public boolean equals(Object obj) { 241 if (this == obj) { 242 return true; 243 } 244 245 if (!(obj instanceof WifiAwareNetworkSpecifier)) { 246 return false; 247 } 248 249 WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj; 250 251 return type == lhs.type 252 && role == lhs.role 253 && clientId == lhs.clientId 254 && sessionId == lhs.sessionId 255 && peerId == lhs.peerId 256 && Arrays.equals(peerMac, lhs.peerMac) 257 && Arrays.equals(pmk, lhs.pmk) 258 && Objects.equals(passphrase, lhs.passphrase) 259 && port == lhs.port 260 && transportProtocol == lhs.transportProtocol; 261 } 262 263 /** @hide */ 264 @Override toString()265 public String toString() { 266 StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier ["); 267 sb.append("type=").append(type) 268 .append(", role=").append(role) 269 .append(", clientId=").append(clientId) 270 .append(", sessionId=").append(sessionId) 271 .append(", peerId=").append(peerId) 272 // masking potential PII (although low impact information) 273 .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>") 274 // masking PII 275 .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>") 276 // masking PII 277 .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") 278 .append(", port=").append(port).append(", transportProtocol=") 279 .append(transportProtocol) 280 .append("]"); 281 return sb.toString(); 282 } 283 284 /** 285 * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a 286 * peer. 287 */ 288 public static final class Builder { 289 private DiscoverySession mDiscoverySession; 290 private PeerHandle mPeerHandle; 291 private String mPskPassphrase; 292 private byte[] mPmk; 293 private int mPort = 0; // invalid value 294 private int mTransportProtocol = -1; // invalid value 295 296 /** 297 * Create a builder for {@link WifiAwareNetworkSpecifier} used in requests to set up a 298 * Wi-Fi Aware connection with a specific peer. 299 * <p> 300 * To set up a connection to any peer or to multiple peers use 301 * {@link #Builder(PublishDiscoverySession)}. 302 * 303 * @param discoverySession A Wi-Fi Aware discovery session in whose context the connection 304 * is created. 305 * @param peerHandle The handle of the peer to which the Wi-Fi Aware connection is 306 * requested. The peer is discovered through Wi-Fi Aware discovery. The 307 * handle can be obtained through 308 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} 309 * or 310 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}. 311 */ Builder(@onNull DiscoverySession discoverySession, @NonNull PeerHandle peerHandle)312 public Builder(@NonNull DiscoverySession discoverySession, @NonNull PeerHandle peerHandle) { 313 if (discoverySession == null) { 314 throw new IllegalArgumentException("Non-null discoverySession required"); 315 } 316 if (peerHandle == null) { 317 throw new IllegalArgumentException("Non-null peerHandle required"); 318 } 319 mDiscoverySession = discoverySession; 320 mPeerHandle = peerHandle; 321 } 322 323 /** 324 * Create a builder for {@link WifiAwareNetworkSpecifier} used in requests to set up a 325 * Wi-Fi Aware connection. This configuration allows connections to any peers or to 326 * multiple peers (as opposed to only a specific peer with 327 * {@link #Builder(DiscoverySession, PeerHandle)}). 328 * <p> 329 * Multiple connections can be triggered by this configuration and using a single request 330 * via {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} 331 * and similar methods. Each successful connection will be signaled via the standard 332 * Connectivity Manager mechanisms - 333 * {@link ConnectivityManager.NetworkCallback#onAvailable(Network)}. 334 * Calling {@link ConnectivityManager#unregisterNetworkCallback(ConnectivityManager.NetworkCallback)} 335 * will terminate all connections. 336 */ 337 @RequiresApi(Build.VERSION_CODES.S) Builder(@onNull PublishDiscoverySession publishDiscoverySession)338 public Builder(@NonNull PublishDiscoverySession publishDiscoverySession) { 339 if (!SdkLevel.isAtLeastS()) { 340 throw new UnsupportedOperationException(); 341 } 342 if (publishDiscoverySession == null) { 343 throw new IllegalArgumentException("Non-null publishDiscoverySession required"); 344 } 345 mDiscoverySession = publishDiscoverySession; 346 mPeerHandle = null; 347 } 348 349 /** 350 * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method 351 * is optional - if not called, then an Open (unencrypted) connection will be created. 352 * 353 * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. Use the 354 * {@link #setPmk(byte[])} to specify a PMK. 355 * @return the current {@link Builder} builder, enabling chaining of builder 356 * methods. 357 */ setPskPassphrase(@onNull String pskPassphrase)358 public @NonNull Builder setPskPassphrase(@NonNull String pskPassphrase) { 359 if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) { 360 throw new IllegalArgumentException("Passphrase must meet length requirements"); 361 } 362 mPskPassphrase = pskPassphrase; 363 return this; 364 } 365 366 /** 367 * Configure the PMK for the Wi-Fi Aware connection being requested. This method 368 * is optional - if not called, then an Open (unencrypted) connection will be created. 369 * 370 * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for 371 * encrypting the data-path. Use the {@link #setPskPassphrase(String)} to 372 * specify a Passphrase. 373 * @return the current {@link Builder} builder, enabling chaining of builder 374 * methods. 375 */ setPmk(@onNull byte[] pmk)376 public @NonNull Builder setPmk(@NonNull byte[] pmk) { 377 if (!WifiAwareUtils.validatePmk(pmk)) { 378 throw new IllegalArgumentException("PMK must 32 bytes"); 379 } 380 mPmk = pmk; 381 return this; 382 } 383 384 /** 385 * Configure the port number which will be used to create a connection over this link. This 386 * configuration should only be done on the server device, e.g. the device creating the 387 * {@link java.net.ServerSocket}. 388 * <p>Notes: 389 * <ul> 390 * <li>The server device must be the Publisher device! 391 * <li>The port information can only be specified on secure links, specified using 392 * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. 393 * </ul> 394 * 395 * @param port A positive integer indicating the port to be used for communication. 396 * @return the current {@link Builder} builder, enabling chaining of builder 397 * methods. 398 */ setPort(@ntRangefrom = 0, to = 65535) int port)399 public @NonNull Builder setPort(@IntRange(from = 0, to = 65535) int port) { 400 if (port <= 0 || port > 65535) { 401 throw new IllegalArgumentException("The port must be a positive value (0, 65535]"); 402 } 403 mPort = port; 404 return this; 405 } 406 407 /** 408 * Configure the transport protocol which will be used to create a connection over this 409 * link. This configuration should only be done on the server device, e.g. the device 410 * creating the {@link java.net.ServerSocket} for TCP. 411 * <p>Notes: 412 * <ul> 413 * <li>The server device must be the Publisher device! 414 * <li>The transport protocol information can only be specified on secure links, 415 * specified using {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. 416 * </ul> 417 * The transport protocol number is assigned by the Internet Assigned Numbers Authority 418 * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml. 419 * 420 * @param transportProtocol The transport protocol to be used for communication. 421 * @return the current {@link Builder} builder, enabling chaining of builder 422 * methods. 423 */ 424 public @NonNull setTransportProtocol(@ntRangefrom = 0, to = 255) int transportProtocol)425 Builder setTransportProtocol(@IntRange(from = 0, to = 255) int transportProtocol) { 426 if (transportProtocol < 0 || transportProtocol > 255) { 427 throw new IllegalArgumentException( 428 "The transport protocol must be in range [0, 255]"); 429 } 430 mTransportProtocol = transportProtocol; 431 return this; 432 } 433 434 /** 435 * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} 436 * for a WiFi Aware connection (link) to the specified peer. The 437 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to 438 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 439 * <p> The default builder constructor will initialize a NetworkSpecifier which requests an 440 * open (non-encrypted) link. To request an encrypted link use the 441 * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])} builder methods. 442 * 443 * @return A {@link NetworkSpecifier} to be used to construct 444 * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass 445 * to {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, 446 * android.net.ConnectivityManager.NetworkCallback)} 447 * [or other varieties of that API]. 448 */ build()449 public @NonNull WifiAwareNetworkSpecifier build() { 450 if (mDiscoverySession == null) { 451 throw new IllegalStateException("Null discovery session!?"); 452 } 453 if (mPskPassphrase != null & mPmk != null) { 454 throw new IllegalStateException( 455 "Can only specify a Passphrase or a PMK - not both!"); 456 } 457 458 int role = mDiscoverySession instanceof SubscribeDiscoverySession 459 ? WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 460 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; 461 462 if (mPort != 0 || mTransportProtocol != -1) { 463 if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 464 throw new IllegalStateException( 465 "Port and transport protocol information can only " 466 + "be specified on the Publisher device (which is the server"); 467 } 468 if (TextUtils.isEmpty(mPskPassphrase) && mPmk == null) { 469 throw new IllegalStateException("Port and transport protocol information can " 470 + "only be specified on a secure link"); 471 } 472 } 473 int type = mPeerHandle == null 474 ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER : 475 WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB; 476 477 return new WifiAwareNetworkSpecifier(type, role, mDiscoverySession.mClientId, 478 mDiscoverySession.mSessionId, mPeerHandle != null ? mPeerHandle.peerId : 0, 479 null, mPmk, mPskPassphrase, mPort, mTransportProtocol); 480 } 481 } 482 } 483