| /* |
| * Copyright (C) 2017 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 android.net.wifi.aware; |
| |
| import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR; |
| |
| import android.annotation.IntRange; |
| import android.annotation.NonNull; |
| import android.net.NetworkSpecifier; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.TextUtils; |
| |
| import java.util.Arrays; |
| import java.util.Objects; |
| |
| /** |
| * Network specifier object used to request a Wi-Fi Aware network. Apps should use the |
| * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance. |
| */ |
| public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { |
| /** |
| * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_IB = 0; |
| |
| /** |
| * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional |
| * [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1; |
| |
| /** |
| * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_OOB = 2; |
| |
| /** |
| * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional |
| * [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3; |
| |
| /** @hide */ |
| public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; |
| |
| /** |
| * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object. |
| * @hide |
| */ |
| public final int type; |
| |
| /** |
| * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or |
| * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER. |
| * @hide |
| */ |
| public final int role; |
| |
| /** |
| * The client ID of the device. |
| * @hide |
| */ |
| public final int clientId; |
| |
| /** |
| * The session ID in which context to request a data-path. Only relevant for IB requests. |
| * @hide |
| */ |
| public final int sessionId; |
| |
| /** |
| * The peer ID of the device which the data-path should be connected to. Only relevant for |
| * IB requests (i.e. not IB_ANY_PEER or OOB*). |
| * @hide |
| */ |
| public final int peerId; |
| |
| /** |
| * The peer MAC address of the device which the data-path should be connected to. Only relevant |
| * for OB requests (i.e. not OOB_ANY_PEER or IB*). |
| * @hide |
| */ |
| public final byte[] peerMac; |
| |
| /** |
| * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should |
| * be specified. |
| * @hide |
| */ |
| public final byte[] pmk; |
| |
| /** |
| * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or |
| * passphrase should be specified. |
| * @hide |
| */ |
| 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. |
| * |
| * @hide |
| */ |
| public final int transportProtocol; |
| |
| /** @hide */ |
| public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, |
| byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) { |
| this.type = type; |
| this.role = role; |
| this.clientId = clientId; |
| this.sessionId = sessionId; |
| this.peerId = peerId; |
| this.peerMac = peerMac; |
| this.pmk = pmk; |
| this.passphrase = passphrase; |
| this.port = port; |
| this.transportProtocol = transportProtocol; |
| } |
| |
| public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR = |
| new Creator<WifiAwareNetworkSpecifier>() { |
| @Override |
| public WifiAwareNetworkSpecifier createFromParcel(Parcel in) { |
| return new WifiAwareNetworkSpecifier( |
| in.readInt(), // type |
| in.readInt(), // role |
| in.readInt(), // clientId |
| in.readInt(), // sessionId |
| in.readInt(), // peerId |
| in.createByteArray(), // peerMac |
| in.createByteArray(), // pmk |
| in.readString(), // passphrase |
| in.readInt(), // port |
| in.readInt()); // transportProtocol |
| } |
| |
| @Override |
| public WifiAwareNetworkSpecifier[] newArray(int size) { |
| return new WifiAwareNetworkSpecifier[size]; |
| } |
| }; |
| |
| /** |
| * Indicates whether the network specifier specifies an OOB (out-of-band) data-path - i.e. a |
| * data-path created without a corresponding Aware discovery session. |
| * |
| * @hide |
| */ |
| public boolean isOutOfBand() { |
| return type == NETWORK_SPECIFIER_TYPE_OOB || type == NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER; |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(type); |
| dest.writeInt(role); |
| dest.writeInt(clientId); |
| dest.writeInt(sessionId); |
| dest.writeInt(peerId); |
| dest.writeByteArray(peerMac); |
| dest.writeByteArray(pmk); |
| dest.writeString(passphrase); |
| dest.writeInt(port); |
| dest.writeInt(transportProtocol); |
| } |
| |
| /** @hide */ |
| @Override |
| public boolean satisfiedBy(NetworkSpecifier other) { |
| // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier. |
| if (other instanceof WifiAwareAgentNetworkSpecifier) { |
| return ((WifiAwareAgentNetworkSpecifier) other).satisfiesAwareNetworkSpecifier(this); |
| } |
| return equals(other); |
| } |
| |
| /** @hide */ |
| @Override |
| public int hashCode() { |
| return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac), |
| Arrays.hashCode(pmk), passphrase, port, transportProtocol); |
| } |
| |
| /** @hide */ |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| |
| if (!(obj instanceof WifiAwareNetworkSpecifier)) { |
| return false; |
| } |
| |
| WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj; |
| |
| return type == lhs.type |
| && role == lhs.role |
| && clientId == lhs.clientId |
| && sessionId == lhs.sessionId |
| && peerId == lhs.peerId |
| && Arrays.equals(peerMac, lhs.peerMac) |
| && Arrays.equals(pmk, lhs.pmk) |
| && Objects.equals(passphrase, lhs.passphrase) |
| && port == lhs.port |
| && transportProtocol == lhs.transportProtocol; |
| } |
| |
| /** @hide */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier ["); |
| sb.append("type=").append(type) |
| .append(", role=").append(role) |
| .append(", clientId=").append(clientId) |
| .append(", sessionId=").append(sessionId) |
| .append(", peerId=").append(peerId) |
| // masking potential PII (although low impact information) |
| .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>") |
| // masking PII |
| .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>") |
| // masking PII |
| .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") |
| .append(", port=").append(port).append(", transportProtocol=") |
| .append(transportProtocol) |
| .append("]"); |
| return sb.toString(); |
| } |
| |
| /** |
| * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a |
| * peer. |
| */ |
| public static final class Builder { |
| private DiscoverySession mDiscoverySession; |
| private PeerHandle mPeerHandle; |
| private String mPskPassphrase; |
| private byte[] mPmk; |
| private int mPort = 0; // invalid value |
| private int mTransportProtocol = -1; // invalid value |
| |
| /** |
| * Create a builder for {@link WifiAwareNetworkSpecifier} used in requests to set up a |
| * Wi-Fi Aware connection with a peer. |
| * |
| * @param discoverySession A Wi-Fi Aware discovery session in whose context the connection |
| * is created. |
| * @param peerHandle The handle of the peer to which the Wi-Fi Aware connection is |
| * requested. The peer is discovered through Wi-Fi Aware discovery. The |
| * handle can be obtained through |
| * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} |
| * or |
| * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}. |
| */ |
| public Builder(@NonNull DiscoverySession discoverySession, @NonNull PeerHandle peerHandle) { |
| if (discoverySession == null) { |
| throw new IllegalArgumentException("Non-null discoverySession required"); |
| } |
| if (peerHandle == null) { |
| throw new IllegalArgumentException("Non-null peerHandle required"); |
| } |
| mDiscoverySession = discoverySession; |
| mPeerHandle = peerHandle; |
| } |
| |
| /** |
| * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method |
| * is optional - if not called, then an Open (unencrypted) connection will be created. |
| * |
| * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. Use the |
| * {@link #setPmk(byte[])} to specify a PMK. |
| * @return the current {@link Builder} builder, enabling chaining of builder |
| * methods. |
| */ |
| public @NonNull Builder setPskPassphrase(@NonNull String pskPassphrase) { |
| if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) { |
| throw new IllegalArgumentException("Passphrase must meet length requirements"); |
| } |
| mPskPassphrase = pskPassphrase; |
| return this; |
| } |
| |
| /** |
| * Configure the PMK for the Wi-Fi Aware connection being requested. This method |
| * is optional - if not called, then an Open (unencrypted) connection will be created. |
| * |
| * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for |
| * encrypting the data-path. Use the {@link #setPskPassphrase(String)} to |
| * specify a Passphrase. |
| * @return the current {@link Builder} builder, enabling chaining of builder |
| * methods. |
| */ |
| public @NonNull Builder setPmk(@NonNull byte[] pmk) { |
| if (!WifiAwareUtils.validatePmk(pmk)) { |
| throw new IllegalArgumentException("PMK must 32 bytes"); |
| } |
| mPmk = pmk; |
| 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}. |
| * <p>Notes: |
| * <ul> |
| * <li>The server device must be the Publisher device! |
| * <li>The port information can only be specified on secure links, specified using |
| * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. |
| * </ul> |
| * |
| * @param port A positive integer indicating the port to be used for communication. |
| * @return the current {@link Builder} builder, enabling chaining of builder |
| * methods. |
| */ |
| public @NonNull Builder setPort(@IntRange(from = 0, to = 65535) 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. |
| * <p>Notes: |
| * <ul> |
| * <li>The server device must be the Publisher device! |
| * <li>The transport protocol information can only be specified on secure links, |
| * specified using {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])}. |
| * </ul> |
| * 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 Builder} builder, enabling chaining of builder |
| * methods. |
| */ |
| public @NonNull |
| Builder setTransportProtocol(@IntRange(from = 0, to = 255) 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 |
| * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to |
| * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. |
| * <p> The default builder constructor will initialize a NetworkSpecifier which requests an |
| * open (non-encrypted) link. To request an encrypted link use the |
| * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])} builder methods. |
| * |
| * @return A {@link NetworkSpecifier} to be used to construct |
| * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass |
| * to {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, |
| * android.net.ConnectivityManager.NetworkCallback)} |
| * [or other varieties of that API]. |
| */ |
| public @NonNull WifiAwareNetworkSpecifier build() { |
| if (mDiscoverySession == null) { |
| throw new IllegalStateException("Null discovery session!?"); |
| } |
| if (mPeerHandle == null) { |
| throw new IllegalStateException("Null peerHandle!?"); |
| } |
| if (mPskPassphrase != null & mPmk != null) { |
| throw new IllegalStateException( |
| "Can only specify a Passphrase or a PMK - not both!"); |
| } |
| |
| int role = mDiscoverySession instanceof SubscribeDiscoverySession |
| ? 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"); |
| } |
| } |
| |
| return new WifiAwareNetworkSpecifier( |
| WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role, |
| mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId, |
| null, mPmk, mPskPassphrase, mPort, mTransportProtocol); |
| } |
| } |
| } |