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.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_128;
20 import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
21 
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.net.ConnectivityManager;
26 import android.net.Network;
27 import android.net.NetworkRequest;
28 import android.net.NetworkSpecifier;
29 import android.os.Build;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 
33 import androidx.annotation.RequiresApi;
34 
35 import com.android.modules.utils.build.SdkLevel;
36 
37 import java.util.Arrays;
38 import java.util.Objects;
39 
40 /**
41  * Network specifier object used to request a Wi-Fi Aware network. Apps should use the
42  * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance.
43  */
44 public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
45     /**
46      * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
47      * @hide
48      */
49     public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
50 
51     /**
52      * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
53      * [only permitted for RESPONDER]
54      * @hide
55      */
56     public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
57 
58     /**
59      * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
60      * @hide
61      */
62     public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
63 
64     /**
65      * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
66      * [only permitted for RESPONDER]
67      * @hide
68      */
69     public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
70 
71     /** @hide */
72     public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
73 
74     /**
75      * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object.
76      * @hide
77      */
78     public final int type;
79 
80     /**
81      * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or
82      * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER.
83      * @hide
84      */
85     public final int role;
86 
87     /**
88      * The client ID of the device.
89      * @hide
90      */
91     public final int clientId;
92 
93     /**
94      * The session ID in which context to request a data-path. Only relevant for IB requests.
95      * @hide
96      */
97     public final int sessionId;
98 
99     /**
100      * The peer ID of the device which the data-path should be connected to. Only relevant for
101      * IB requests (i.e. not IB_ANY_PEER or OOB*).
102      * @hide
103      */
104     public final int peerId;
105 
106     /**
107      * The peer MAC address of the device which the data-path should be connected to. Only relevant
108      * for OB requests (i.e. not OOB_ANY_PEER or IB*).
109      * @hide
110      */
111     public final byte[] peerMac;
112 
113     /**
114      * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should
115      * be specified.
116      * @hide
117      */
118     public final byte[] pmk;
119 
120     /**
121      * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or
122      * passphrase should be specified.
123      * @hide
124      */
125     public final String passphrase;
126 
127     /**
128      * The port information to be used for this link. This information will be communicated to the
129      * peer as part of the layer 2 link setup.
130      *
131      * Information only allowed on secure links since a single layer-2 link is set up for all
132      * requestors. Therefore if multiple apps on a single device request links to the same peer
133      * device they all get the same link. However, the link is only set up on the first request -
134      * hence only the first can transmit the port information. But we don't want to expose that
135      * information to other apps. Limiting to secure links would (usually) imply single app usage.
136      *
137      * @hide
138      */
139     public final int port;
140 
141     /**
142      * The transport protocol information to be used for this link. This information will be
143      * communicated to the peer as part of the layer 2 link setup.
144      *
145      * Information only allowed on secure links since a single layer-2 link is set up for all
146      * requestors. Therefore if multiple apps on a single device request links to the same peer
147      * device they all get the same link. However, the link is only set up on the first request -
148      * hence only the first can transmit the port information. But we don't want to expose that
149      * information to other apps. Limiting to secure links would (usually) imply single app usage.
150      *
151      * @hide
152      */
153     public final int transportProtocol;
154 
155     /**
156      * Channel frequency in MHz for setup data-path on.
157      */
158     private final int mChannelInMhz;
159 
160     /**
161      * Force to use the specified channel or not. If true, Channel request is specified and must be
162      * respected. If the firmware cannot honor the request then the data-path request is rejected.
163      * Otherwise, requested channel can be overridden by firmware.
164      */
165     private final boolean mForcedChannel;
166 
167     private final WifiAwareDataPathSecurityConfig mSecurityConfig;
168 
169     /**
170      * Get the specified channel in MHZ for this Wi-Fi Aware network specifier.
171      * @see Builder#setChannelFrequencyMhz(int, boolean)
172      * @return Channel frequency in Mhz. A value of 0 indicates that no channel was specified.
173      */
174     @IntRange(from = 0)
getChannelFrequencyMhz()175     public int getChannelFrequencyMhz() {
176         return mChannelInMhz;
177     }
178 
179     /**
180      * Check if the specified channel is required to honor or not.
181      * @see Builder#setChannelFrequencyMhz(int, boolean)
182      * @return true if forced to honer, false for recommend to use.
183      */
isChannelRequired()184     public boolean isChannelRequired() {
185         return mForcedChannel;
186     }
187 
188     /**
189      * Get the security config specified in this Network Specifier to encrypt Wi-Fi Aware data-path
190      * @return {@link WifiAwareDataPathSecurityConfig} used to encrypt the data-path
191      */
getWifiAwareDataPathSecurityConfig()192     public @Nullable WifiAwareDataPathSecurityConfig getWifiAwareDataPathSecurityConfig() {
193         return mSecurityConfig;
194     }
195 
196     /** @hide */
WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, byte[] peerMac, int port, int transportProtocol, int channel, boolean forcedChannel, WifiAwareDataPathSecurityConfig securityConfig)197     public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
198             byte[] peerMac, int port, int transportProtocol,
199             int channel, boolean forcedChannel, WifiAwareDataPathSecurityConfig securityConfig) {
200         this.type = type;
201         this.role = role;
202         this.clientId = clientId;
203         this.sessionId = sessionId;
204         this.peerId = peerId;
205         this.peerMac = peerMac;
206         this.port = port;
207         this.transportProtocol = transportProtocol;
208         this.mChannelInMhz = channel;
209         this.mForcedChannel = forcedChannel;
210         this.mSecurityConfig = securityConfig;
211         this.passphrase = securityConfig == null ? null : securityConfig.getPskPassphrase();
212         this.pmk = securityConfig == null ? null : securityConfig.getPmk();
213     }
214 
215     /**
216      * TODO(b/214311843): remove this when make SL4A using the public APIs
217      * @hide
218      */
WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol)219     public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
220             byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) {
221         this.type = type;
222         this.role = role;
223         this.clientId = clientId;
224         this.sessionId = sessionId;
225         this.peerId = peerId;
226         this.peerMac = peerMac;
227         this.port = port;
228         this.transportProtocol = transportProtocol;
229         this.mChannelInMhz = 0;
230         this.mForcedChannel = false;
231         this.pmk = pmk;
232         this.passphrase = passphrase;
233         if (pmk != null || passphrase != null) {
234             this.mSecurityConfig = new WifiAwareDataPathSecurityConfig(
235                     WIFI_AWARE_CIPHER_SUITE_NCS_SK_128, pmk, null, passphrase
236             );
237         } else {
238             mSecurityConfig = null;
239         }
240     }
241 
242     public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR =
243             new Creator<WifiAwareNetworkSpecifier>() {
244                 @Override
245                 public WifiAwareNetworkSpecifier createFromParcel(Parcel in) {
246                     return new WifiAwareNetworkSpecifier(
247                             in.readInt(), // type
248                             in.readInt(), // role
249                             in.readInt(), // clientId
250                             in.readInt(), // sessionId
251                             in.readInt(), // peerId
252                             in.createByteArray(), // peerMac
253                             in.readInt(), // port
254                             in.readInt(), // transportProtocol
255                             in.readInt(), // channel
256                             in.readBoolean(), // forceChannel
257                             in.readParcelable(WifiAwareDataPathSecurityConfig
258                                     .class.getClassLoader())); // securityConfig
259                 }
260 
261                 @Override
262                 public WifiAwareNetworkSpecifier[] newArray(int size) {
263                     return new WifiAwareNetworkSpecifier[size];
264                 }
265             };
266 
267     /**
268      * Indicates whether the network specifier specifies an OOB (out-of-band) data-path - i.e. a
269      * data-path created without a corresponding Aware discovery session.
270      *
271      * @hide
272      */
isOutOfBand()273     public boolean isOutOfBand() {
274         return type == NETWORK_SPECIFIER_TYPE_OOB || type == NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
275     }
276 
277     @Override
describeContents()278     public int describeContents() {
279         return 0;
280     }
281 
282     @Override
writeToParcel(Parcel dest, int flags)283     public void writeToParcel(Parcel dest, int flags) {
284         dest.writeInt(type);
285         dest.writeInt(role);
286         dest.writeInt(clientId);
287         dest.writeInt(sessionId);
288         dest.writeInt(peerId);
289         dest.writeByteArray(peerMac);
290         dest.writeInt(port);
291         dest.writeInt(transportProtocol);
292         dest.writeInt(mChannelInMhz);
293         dest.writeBoolean(mForcedChannel);
294         dest.writeParcelable(mSecurityConfig, flags);
295     }
296 
297     /** @hide */
298     @Override
canBeSatisfiedBy(NetworkSpecifier other)299     public boolean canBeSatisfiedBy(NetworkSpecifier other) {
300         // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier.
301         if (other instanceof WifiAwareAgentNetworkSpecifier) {
302             return ((WifiAwareAgentNetworkSpecifier) other).satisfiesAwareNetworkSpecifier(this);
303         }
304         return equals(other);
305     }
306 
307     /** @hide */
308     @Override
hashCode()309     public int hashCode() {
310         return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac),
311                 port, transportProtocol, mChannelInMhz, mForcedChannel, mSecurityConfig);
312     }
313 
314     /** @hide */
315     @Override
equals(Object obj)316     public boolean equals(Object obj) {
317         if (this == obj) {
318             return true;
319         }
320 
321         if (!(obj instanceof WifiAwareNetworkSpecifier)) {
322             return false;
323         }
324 
325         WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj;
326 
327         return type == lhs.type
328                 && role == lhs.role
329                 && clientId == lhs.clientId
330                 && sessionId == lhs.sessionId
331                 && peerId == lhs.peerId
332                 && Arrays.equals(peerMac, lhs.peerMac)
333                 && port == lhs.port
334                 && transportProtocol == lhs.transportProtocol
335                 && mChannelInMhz == lhs.mChannelInMhz
336                 && mForcedChannel == lhs.mForcedChannel
337                 && Objects.equals(mSecurityConfig, lhs.mSecurityConfig);
338     }
339 
340     /** @hide */
341     @Override
toString()342     public String toString() {
343         StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier [");
344         sb.append("type=").append(type)
345                 .append(", role=").append(role)
346                 .append(", clientId=").append(clientId)
347                 .append(", sessionId=").append(sessionId)
348                 .append(", peerId=").append(peerId)
349                 // masking potential PII (although low impact information)
350                 .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>")
351                 // masking PII
352                 .append(", securityConfig=").append(mSecurityConfig)
353                 .append(", port=").append(port)
354                 .append(", transportProtocol=").append(transportProtocol)
355                 .append(", channel=").append(mChannelInMhz)
356                 .append(", forceChannel=").append(mForcedChannel)
357                 .append("]");
358         return sb.toString();
359     }
360 
361     /**
362      * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a
363      * peer.
364      */
365     public static final class Builder {
366         private DiscoverySession mDiscoverySession;
367         private PeerHandle mPeerHandle;
368         private String mPskPassphrase;
369         private byte[] mPmk;
370         private int mPort = 0; // invalid value
371         private int mTransportProtocol = -1; // invalid value
372         private int mChannel = 0;
373         private boolean mIsRequired = false;
374         private WifiAwareDataPathSecurityConfig mSecurityConfig;
375 
376         /**
377          * Create a builder for {@link WifiAwareNetworkSpecifier} used in requests to set up a
378          * Wi-Fi Aware connection with a specific peer.
379          * <p>
380          * To set up a connection to any peer or to multiple peers use
381          * {@link #Builder(PublishDiscoverySession)}.
382          *
383          * @param discoverySession A Wi-Fi Aware discovery session in whose context the connection
384          *                         is created.
385          * @param peerHandle The handle of the peer to which the Wi-Fi Aware connection is
386          *                   requested. The peer is discovered through Wi-Fi Aware discovery. The
387          *                   handle can be obtained through
388          * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
389          *                   or
390          *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
391          */
Builder(@onNull DiscoverySession discoverySession, @NonNull PeerHandle peerHandle)392         public Builder(@NonNull DiscoverySession discoverySession, @NonNull PeerHandle peerHandle) {
393             if (discoverySession == null) {
394                 throw new IllegalArgumentException("Non-null discoverySession required");
395             }
396             if (peerHandle == null) {
397                 throw new IllegalArgumentException("Non-null peerHandle required");
398             }
399             mDiscoverySession = discoverySession;
400             mPeerHandle = peerHandle;
401         }
402 
403         /**
404          * Create a builder for {@link WifiAwareNetworkSpecifier} used in requests to set up a
405          * Wi-Fi Aware connection. This configuration allows connections to any peers or to
406          * multiple peers (as opposed to only a specific peer with
407          * {@link #Builder(DiscoverySession, PeerHandle)}).
408          * <p>
409          * Multiple connections can be triggered by this configuration and using a single request
410          * via {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
411          * and similar methods. Each successful connection will be signaled via the standard
412          * Connectivity Manager mechanisms -
413          * {@link ConnectivityManager.NetworkCallback#onAvailable(Network)}.
414          * Calling {@link ConnectivityManager#unregisterNetworkCallback(ConnectivityManager.NetworkCallback)}
415          * will terminate all connections.
416          */
417         @RequiresApi(Build.VERSION_CODES.S)
Builder(@onNull PublishDiscoverySession publishDiscoverySession)418         public Builder(@NonNull PublishDiscoverySession publishDiscoverySession) {
419             if (!SdkLevel.isAtLeastS()) {
420                 throw new UnsupportedOperationException();
421             }
422             if (publishDiscoverySession == null) {
423                 throw new IllegalArgumentException("Non-null publishDiscoverySession required");
424             }
425             mDiscoverySession = publishDiscoverySession;
426             mPeerHandle = null;
427         }
428 
429         /**
430          * Configure the PSK Passphrase for the Wi-Fi Aware connection being requested. This method
431          * is optional - if not called, then an Open (unencrypted) connection will be created.
432          * Note: Use
433          * {@link #setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} to avoid
434          * interoperability issues when devices support different cipher suites by explicitly
435          * specifying a cipher suite as opposed to relying on a default cipher suite.
436          * {@link WifiAwareDataPathSecurityConfig.Builder#Builder(int)}
437          *
438          * @param pskPassphrase The (optional) passphrase to be used to encrypt the link. Use the
439          *                      {@link #setPmk(byte[])} to specify a PMK.
440          * @return the current {@link Builder} builder, enabling chaining of builder
441          *         methods.
442          */
setPskPassphrase(@onNull String pskPassphrase)443         public @NonNull Builder setPskPassphrase(@NonNull String pskPassphrase) {
444             if (!WifiAwareUtils.validatePassphrase(pskPassphrase)) {
445                 throw new IllegalArgumentException("Passphrase must meet length requirements");
446             }
447             mPskPassphrase = pskPassphrase;
448             return this;
449         }
450 
451         /**
452          * Configure the PMK for the Wi-Fi Aware connection being requested. This method
453          * is optional - if not called, then an Open (unencrypted) connection will be created.
454          * Note: Use
455          * {@link #setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)} to avoid
456          * interoperability issues when devices support different cipher suites by explicitly
457          * specifying a cipher suite as opposed to relying on a default cipher suite.
458          *
459          * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
460          *            encrypting the data-path. Use the {@link #setPskPassphrase(String)} to
461          *            specify a Passphrase.
462          * @return the current {@link Builder} builder, enabling chaining of builder
463          *         methods.
464          */
setPmk(@onNull byte[] pmk)465         public @NonNull Builder setPmk(@NonNull byte[] pmk) {
466             if (!WifiAwareUtils.validatePmk(pmk)) {
467                 throw new IllegalArgumentException("PMK must 32 bytes");
468             }
469             mPmk = pmk;
470             return this;
471         }
472 
473         /**
474          * Configure the port number which will be used to create a connection over this link. This
475          * configuration should only be done on the server device, e.g. the device creating the
476          * {@link java.net.ServerSocket}.
477          * <p>Notes:
478          * <ul>
479          *     <li>The server device must be the Publisher device!
480          *     <li>The port information can only be specified on secure links, specified using
481          *     {@link #setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)}
482          * </ul>
483          *
484          * @param port A positive integer indicating the port to be used for communication.
485          * @return the current {@link Builder} builder, enabling chaining of builder
486          *         methods.
487          */
setPort(@ntRangefrom = 0, to = 65535) int port)488         public @NonNull Builder setPort(@IntRange(from = 0, to = 65535) int port) {
489             if (port <= 0 || port > 65535) {
490                 throw new IllegalArgumentException("The port must be a positive value (0, 65535]");
491             }
492             mPort = port;
493             return this;
494         }
495 
496         /**
497          * Configure the transport protocol which will be used to create a connection over this
498          * link. This configuration should only be done on the server device, e.g. the device
499          * creating the {@link java.net.ServerSocket} for TCP.
500          * <p>Notes:
501          * <ul>
502          *     <li>The server device must be the Publisher device!
503          *     <li>The transport protocol information can only be specified on secure links,
504          *     specified using
505          *     {@link #setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)}.
506          * </ul>
507          * The transport protocol number is assigned by the Internet Assigned Numbers Authority
508          * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
509          *
510          * @param transportProtocol The transport protocol to be used for communication.
511          * @return the current {@link Builder} builder, enabling chaining of builder
512          *         methods.
513          */
514         public @NonNull
setTransportProtocol(@ntRangefrom = 0, to = 255) int transportProtocol)515                 Builder setTransportProtocol(@IntRange(from = 0, to = 255) int transportProtocol) {
516             if (transportProtocol < 0 || transportProtocol > 255) {
517                 throw new IllegalArgumentException(
518                         "The transport protocol must be in range [0, 255]");
519             }
520             mTransportProtocol = transportProtocol;
521             return this;
522         }
523 
524         /**
525          * Configure the Channel frequency for the Wi-Fi Aware connection being requested. This
526          * method is optional - if not called, then channelInMhz to use will be decided by firmware.
527          * Only use this when {@link WifiAwareManager#isSetChannelOnDataPathSupported()} is true,
528          * otherwise the set channelInMhz will be ignored.
529          * @param channelInMhz Channel frequency in Mhz.
530          * @param required If set to true, Channel request is specified and must be respected.
531          *               If the firmware cannot honor the request then the data-path request
532          *               is rejected. Otherwise, requested channelInMhz is a recommendation and
533          *               may be overridden by the firmware.
534          * @return the current {@link Builder} builder, enabling chaining of builder methods.
535          */
setChannelFrequencyMhz(@ntRangefrom = 0) int channelInMhz, boolean required)536         public @NonNull Builder setChannelFrequencyMhz(@IntRange(from = 0) int channelInMhz,
537                 boolean required) {
538             mChannel = channelInMhz;
539             mIsRequired = required;
540             return this;
541         }
542 
543         /**
544          * Configure security config for the Wi-Fi Aware connection being requested. This method
545          * is optional - if not called, then an Open (unencrypted) connection will be created.
546          * Note: this method is the superset of the {@link #setPmk(byte[])} and
547          * {@link #setPskPassphrase(String)}.
548          *
549          * @param securityConfig The (optional) security config to be used to encrypt the link.
550          * @return the current {@link Builder} builder, enabling chaining of builder
551          *         methods.
552          */
setDataPathSecurityConfig( @onNull WifiAwareDataPathSecurityConfig securityConfig)553         public @NonNull Builder setDataPathSecurityConfig(
554                 @NonNull WifiAwareDataPathSecurityConfig securityConfig) {
555             if (securityConfig == null) {
556                 throw new IllegalArgumentException("The WifiAwareDataPathSecurityConfig "
557                         + "should be non-null");
558             }
559 
560             if (!securityConfig.isValid()) {
561                 throw new IllegalArgumentException("The WifiAwareDataPathSecurityConfig "
562                         + "is invalid");
563             }
564             mSecurityConfig = securityConfig;
565             return this;
566         }
567 
568         /**
569          * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)}
570          * for a WiFi Aware connection (link) to the specified peer. The
571          * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
572          * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
573          * <p> The default builder constructor will initialize a NetworkSpecifier which requests an
574          * open (non-encrypted) link. To request an encrypted link use the
575          * {@link #setPskPassphrase(String)} or {@link #setPmk(byte[])} builder methods.
576          *
577          * @return A {@link NetworkSpecifier} to be used to construct
578          * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass
579          * to {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
580          * android.net.ConnectivityManager.NetworkCallback)}
581          * [or other varieties of that API].
582          */
build()583         public @NonNull WifiAwareNetworkSpecifier build() {
584             if (mDiscoverySession == null) {
585                 throw new IllegalStateException("Null discovery session!?");
586             }
587             if (mPskPassphrase != null && mPmk != null) {
588                 throw new IllegalStateException(
589                         "Can only specify a Passphrase or a PMK - not both!");
590             }
591             WifiAwareDataPathSecurityConfig securityConfig = mSecurityConfig;
592             if (mPskPassphrase != null || mPmk != null) {
593                 if (securityConfig != null) {
594                     throw new IllegalStateException(
595                             "Can only specify a SecurityConfig or a PMK(Passphrase) - not both!");
596                 }
597                 securityConfig = new WifiAwareDataPathSecurityConfig(
598                         WIFI_AWARE_CIPHER_SUITE_NCS_SK_128, mPmk, null,
599                         mPskPassphrase);
600             }
601 
602             int role = mDiscoverySession instanceof SubscribeDiscoverySession
603                     ? WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
604                     : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
605 
606             if (mPort != 0 || mTransportProtocol != -1) {
607                 if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
608                     throw new IllegalStateException(
609                             "Port and transport protocol information can only "
610                                     + "be specified on the Publisher device (which is the server");
611                 }
612                 if (securityConfig == null) {
613                     throw new IllegalStateException("Port and transport protocol information can "
614                             + "only be specified on a secure link");
615                 }
616             }
617             int type = mPeerHandle == null
618                     ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER :
619                     WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
620 
621             return new WifiAwareNetworkSpecifier(type, role, mDiscoverySession.mClientId,
622                     mDiscoverySession.mSessionId, mPeerHandle != null ? mPeerHandle.peerId : 0,
623                     null, mPort, mTransportProtocol, mChannel, mIsRequired, securityConfig);
624         }
625     }
626 }
627