1 /*
2  * Copyright (C) 2016 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.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION;
20 
21 import android.annotation.FlaggedApi;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.net.wifi.OuiKeyedData;
28 import android.net.wifi.ParcelUtil;
29 import android.net.wifi.ScanResult;
30 import android.net.wifi.WifiScanner;
31 import android.net.wifi.util.HexEncoding;
32 import android.os.Build;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 
36 import androidx.annotation.RequiresApi;
37 
38 import com.android.modules.utils.build.SdkLevel;
39 import com.android.wifi.flags.Flags;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.nio.charset.StandardCharsets;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.Objects;
48 
49 /**
50  * Defines the configuration of an Aware publish session. Built using
51  * {@link PublishConfig.Builder}. A publish session is created using
52  * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
53  * android.os.Handler)} or updated using
54  * {@link PublishDiscoverySession#updatePublish(PublishConfig)}.
55  */
56 public final class PublishConfig implements Parcelable {
57     /** @hide */
58     @IntDef({
59             PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED })
60     @Retention(RetentionPolicy.SOURCE)
61     public @interface PublishTypes {
62     }
63 
64     /**
65      * Defines an unsolicited publish session - a publish session where the publisher is
66      * advertising itself by broadcasting on-the-air. An unsolicited publish session is paired
67      * with a passive subscribe session {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE}.
68      * Configuration is done using {@link PublishConfig.Builder#setPublishType(int)}.
69      */
70     public static final int PUBLISH_TYPE_UNSOLICITED = 0;
71 
72     /**
73      * Defines a solicited publish session - a publish session which is silent, waiting for a
74      * matching active subscribe session - and responding to it in unicast. A
75      * solicited publish session is paired with an active subscribe session
76      * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE}. Configuration is done using
77      * {@link PublishConfig.Builder#setPublishType(int)}.
78      */
79     public static final int PUBLISH_TYPE_SOLICITED = 1;
80 
81     /** @hide */
82     public final byte[] mServiceName;
83 
84     /** @hide */
85     public final byte[] mServiceSpecificInfo;
86 
87     /** @hide */
88     public final byte[] mMatchFilter;
89 
90     /** @hide */
91     public final int mPublishType;
92 
93     /** @hide */
94     public final int mTtlSec;
95 
96     /** @hide */
97     public final boolean mEnableTerminateNotification;
98 
99     /** @hide */
100     public final boolean mEnableRanging;
101 
102     private final boolean mEnableInstantMode;
103 
104     private final int mBand;
105 
106     private final WifiAwareDataPathSecurityConfig mSecurityConfig;
107 
108     private final AwarePairingConfig mPairingConfig;
109 
110     private final boolean mIsSuspendable;
111 
112     private final List<OuiKeyedData> mVendorData;
113 
114     /** @hide */
PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int publishType, int ttlSec, boolean enableTerminateNotification, boolean enableRanging, boolean enableInstantMode, @WifiScanner.WifiBand int band, WifiAwareDataPathSecurityConfig securityConfig, AwarePairingConfig pairingConfig, boolean isSuspendable, @NonNull List<OuiKeyedData> vendorData)115     public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
116             int publishType, int ttlSec, boolean enableTerminateNotification,
117             boolean enableRanging, boolean enableInstantMode, @WifiScanner.WifiBand int
118             band, WifiAwareDataPathSecurityConfig securityConfig,
119             AwarePairingConfig pairingConfig, boolean isSuspendable,
120             @NonNull List<OuiKeyedData> vendorData) {
121         mServiceName = serviceName;
122         mServiceSpecificInfo = serviceSpecificInfo;
123         mMatchFilter = matchFilter;
124         mPublishType = publishType;
125         mTtlSec = ttlSec;
126         mEnableTerminateNotification = enableTerminateNotification;
127         mEnableRanging = enableRanging;
128         mEnableInstantMode = enableInstantMode;
129         mBand = band;
130         mSecurityConfig = securityConfig;
131         mPairingConfig = pairingConfig;
132         mIsSuspendable = isSuspendable;
133         mVendorData = vendorData;
134     }
135 
136     @Override
toString()137     public String toString() {
138         return "PublishConfig [mServiceName='" + (mServiceName == null ? "<null>" : String.valueOf(
139                 HexEncoding.encode(mServiceName))) + ", mServiceName.length=" + (
140                 mServiceName == null ? 0 : mServiceName.length) + ", mServiceSpecificInfo='" + (
141                 (mServiceSpecificInfo == null) ? "<null>" : String.valueOf(
142                         HexEncoding.encode(mServiceSpecificInfo)))
143                 + ", mServiceSpecificInfo.length=" + (mServiceSpecificInfo == null ? 0
144                 : mServiceSpecificInfo.length) + ", mMatchFilter="
145                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
146                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
147                 + ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
148                 + ", mEnableTerminateNotification=" + mEnableTerminateNotification
149                 + ", mEnableRanging=" + mEnableRanging + "]"
150                 + ", mEnableInstantMode=" + mEnableInstantMode
151                 + ", mBand=" + mBand
152                 + ", mSecurityConfig" + mSecurityConfig
153                 + ", mPairingConfig" + mPairingConfig
154                 + ", mIsSuspendable=" + mIsSuspendable
155                 + ", mVendorData=" + mVendorData + "]";
156     }
157 
158     @Override
describeContents()159     public int describeContents() {
160         return 0;
161     }
162 
163     @Override
writeToParcel(Parcel dest, int flags)164     public void writeToParcel(Parcel dest, int flags) {
165         dest.writeByteArray(mServiceName);
166         dest.writeByteArray(mServiceSpecificInfo);
167         dest.writeByteArray(mMatchFilter);
168         dest.writeInt(mPublishType);
169         dest.writeInt(mTtlSec);
170         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
171         dest.writeInt(mEnableRanging ? 1 : 0);
172         dest.writeBoolean(mEnableInstantMode);
173         dest.writeInt(mBand);
174         dest.writeParcelable(mSecurityConfig, flags);
175         dest.writeParcelable(mPairingConfig, flags);
176         dest.writeBoolean(mIsSuspendable);
177         dest.writeList(mVendorData);
178     }
179 
180     @NonNull
181     public static final Creator<PublishConfig> CREATOR = new Creator<>() {
182         @Override
183         public PublishConfig[] newArray(int size) {
184             return new PublishConfig[size];
185         }
186 
187         @Override
188         public PublishConfig createFromParcel(Parcel in) {
189             byte[] serviceName = in.createByteArray();
190             byte[] ssi = in.createByteArray();
191             byte[] matchFilter = in.createByteArray();
192             int publishType = in.readInt();
193             int ttlSec = in.readInt();
194             boolean enableTerminateNotification = in.readInt() != 0;
195             boolean enableRanging = in.readInt() != 0;
196             boolean enableInstantMode = in.readBoolean();
197             int band = in.readInt();
198             WifiAwareDataPathSecurityConfig securityConfig = in
199                     .readParcelable(WifiAwareDataPathSecurityConfig.class.getClassLoader());
200             AwarePairingConfig pairingConfig = in
201                     .readParcelable(AwarePairingConfig.class.getClassLoader());
202             boolean isSuspendable = in.readBoolean();
203             List<OuiKeyedData> vendorData = ParcelUtil.readOuiKeyedDataList(in);
204 
205             return new PublishConfig(serviceName, ssi, matchFilter, publishType, ttlSec,
206                     enableTerminateNotification, enableRanging, enableInstantMode,
207                     band, securityConfig, pairingConfig, isSuspendable, vendorData);
208         }
209     };
210 
211     @Override
equals(Object o)212     public boolean equals(Object o) {
213         if (this == o) {
214             return true;
215         }
216 
217         if (!(o instanceof PublishConfig)) {
218             return false;
219         }
220 
221         PublishConfig lhs = (PublishConfig) o;
222 
223         return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
224                 lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
225                 && mPublishType == lhs.mPublishType
226                 && mTtlSec == lhs.mTtlSec
227                 && mEnableTerminateNotification == lhs.mEnableTerminateNotification
228                 && mEnableRanging == lhs.mEnableRanging
229                 && mEnableInstantMode == lhs.mEnableInstantMode
230                 && mBand == lhs.mBand
231                 && mIsSuspendable == lhs.mIsSuspendable
232                 && Objects.equals(mSecurityConfig, lhs.mSecurityConfig)
233                 && Objects.equals(mPairingConfig, lhs.mPairingConfig)
234                 && Objects.equals(mVendorData, lhs.mVendorData);
235     }
236 
237     @Override
hashCode()238     public int hashCode() {
239         return Objects.hash(Arrays.hashCode(mServiceName), Arrays.hashCode(mServiceSpecificInfo),
240                 Arrays.hashCode(mMatchFilter), mPublishType, mTtlSec, mEnableTerminateNotification,
241                 mEnableRanging, mEnableInstantMode, mBand, mSecurityConfig, mPairingConfig,
242                 mIsSuspendable, mVendorData);
243     }
244 
245     /**
246      * Verifies that the contents of the PublishConfig are valid. Otherwise
247      * throws an IllegalArgumentException.
248      *
249      * @hide
250      */
assertValid(Characteristics characteristics, boolean rttSupported)251     public void assertValid(Characteristics characteristics, boolean rttSupported)
252             throws IllegalArgumentException {
253         WifiAwareUtils.validateServiceName(mServiceName);
254 
255         if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
256             throw new IllegalArgumentException(
257                     "Invalid txFilter configuration - LV fields do not match up to length");
258         }
259         if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) {
260             throw new IllegalArgumentException("Invalid publishType - " + mPublishType);
261         }
262         if (mTtlSec < 0) {
263             throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
264         }
265         if (mSecurityConfig != null && !mSecurityConfig.isValid()) {
266             throw new IllegalArgumentException("WifiAwareDataPathSecurityConfig is invalid");
267         }
268 
269         if (characteristics != null) {
270             int maxServiceNameLength = characteristics.getMaxServiceNameLength();
271             if (maxServiceNameLength != 0 && mServiceName.length > maxServiceNameLength) {
272                 throw new IllegalArgumentException(
273                         "Service name longer than supported by device characteristics");
274             }
275             int maxServiceSpecificInfoLength = characteristics.getMaxServiceSpecificInfoLength();
276             if (maxServiceSpecificInfoLength != 0 && mServiceSpecificInfo != null
277                     && mServiceSpecificInfo.length > maxServiceSpecificInfoLength) {
278                 throw new IllegalArgumentException(
279                         "Service specific info longer than supported by device characteristics");
280             }
281             int maxMatchFilterLength = characteristics.getMaxMatchFilterLength();
282             if (maxMatchFilterLength != 0 && mMatchFilter != null
283                     && mMatchFilter.length > maxMatchFilterLength) {
284                 throw new IllegalArgumentException(
285                         "Match filter longer than supported by device characteristics");
286             }
287             if (mEnableInstantMode) {
288                 if (SdkLevel.isAtLeastT()
289                         && characteristics.isInstantCommunicationModeSupported()) {
290                     // Valid to use instant communication mode
291                 } else {
292                     throw new IllegalArgumentException("instant mode is not supported");
293                 }
294             }
295             if (mIsSuspendable && !characteristics.isSuspensionSupported()) {
296                 throw new IllegalArgumentException("Aware Suspension is not supported");
297             }
298             if (mSecurityConfig != null && (characteristics.getSupportedCipherSuites()
299                     & mSecurityConfig.getCipherSuite()) == 0) {
300                 throw new IllegalArgumentException("Unsupported cipher suite");
301             }
302             if (mPairingConfig != null && !characteristics.isAwarePairingSupported()) {
303                 throw new IllegalArgumentException("Aware Pairing is not supported");
304             }
305         }
306 
307         if (!rttSupported && mEnableRanging) {
308             throw new IllegalArgumentException("Ranging is not supported");
309         }
310     }
311 
312     /**
313      * Check if instant communication mode is enabled for this publish session.
314      * @see Builder#setInstantCommunicationModeEnabled(boolean, int)
315      * @return true for enabled, false otherwise.
316      */
isInstantCommunicationModeEnabled()317     public boolean isInstantCommunicationModeEnabled() {
318         return mEnableInstantMode;
319     }
320 
321     /**
322      * Get the Wi-Fi band for instant communication mode for this publish session
323      *
324      * @see Builder#setInstantCommunicationModeEnabled(boolean, int)
325      * @return The Wi-Fi band. If instant communication mode is not enabled will return {@link
326      *     ScanResult#WIFI_BAND_24_GHZ} as default.
327      */
328     @WifiAwareManager.InstantModeBand
getInstantCommunicationBand()329     public int getInstantCommunicationBand() {
330         return mBand;
331     }
332 
333     /**
334      * Get the data-path security config for this publish session
335      * @see Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)
336      * @return A {@link WifiAwareDataPathSecurityConfig} specified in this config.
337      */
338     @Nullable
getSecurityConfig()339     public WifiAwareDataPathSecurityConfig getSecurityConfig() {
340         return mSecurityConfig;
341     }
342 
343     /**
344      * Get the Aware Pairing config for this publish session
345      * @see Builder#setPairingConfig(AwarePairingConfig)
346      * @return A {@link AwarePairingConfig} specified in this config.
347      */
348     @Nullable
getPairingConfig()349     public AwarePairingConfig getPairingConfig() {
350         return mPairingConfig;
351     }
352 
353     /**
354      * Check if suspension is supported for this publish session.
355      * @see Builder#setSuspendable(boolean)
356      * @return true for supported, false otherwise.
357      * @hide
358      */
359     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
360     @SystemApi
isSuspendable()361     public boolean isSuspendable() {
362         if (!SdkLevel.isAtLeastU()) {
363             throw new UnsupportedOperationException();
364         }
365         return mIsSuspendable;
366     }
367 
368     /**
369      * Return the vendor-provided configuration data, if it exists. See also {@link
370      * Builder#setVendorData(List)}
371      *
372      * @return Vendor configuration data, or empty list if it does not exist.
373      * @hide
374      */
375     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
376     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
377     @NonNull
378     @SystemApi
getVendorData()379     public List<OuiKeyedData> getVendorData() {
380         if (!SdkLevel.isAtLeastV()) {
381             throw new UnsupportedOperationException();
382         }
383         return mVendorData != null ? mVendorData : Collections.emptyList();
384     }
385 
386     /**
387      * Builder used to build {@link PublishConfig} objects.
388      */
389     public static final class Builder {
390         private byte[] mServiceName;
391         private byte[] mServiceSpecificInfo;
392         private byte[] mMatchFilter;
393         private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
394         private int mTtlSec = 0;
395         private boolean mEnableTerminateNotification = true;
396         private boolean mEnableRanging = false;
397         private boolean mEnableInstantMode = false;
398         private int mBand = WifiScanner.WIFI_BAND_24_GHZ;
399         private WifiAwareDataPathSecurityConfig mSecurityConfig = null;
400         private AwarePairingConfig mPairingConfig = null;
401         private boolean mIsSuspendable = false;
402         private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList();
403 
404         /**
405          * Specify the service name of the publish session. The actual on-air
406          * value is a 6 byte hashed representation of this string.
407          * <p>
408          * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length.
409          * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric
410          * values (A-Z, a-z, 0-9), the hyphen ('-'), the period ('.') and the underscore ('_'). All
411          * valid multi-byte UTF-8 characters are acceptable in a Service Name.
412          * <p>
413          * Note: for compatibility with devices running Android 11 or older, avoid using
414          * underscore ('_') symbol as a single-byte UTF-8 service name.
415          * <p>
416          * Must be called - an empty ServiceName is not valid.
417          *
418          * @param serviceName The service name for the publish session.
419          *
420          * @return The builder to facilitate chaining
421          *         {@code builder.setXXX(..).setXXX(..)}.
422          */
setServiceName(@onNull String serviceName)423         public Builder setServiceName(@NonNull String serviceName) {
424             if (serviceName == null) {
425                 throw new IllegalArgumentException("Invalid service name - must be non-null");
426             }
427             mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
428             return this;
429         }
430 
431         /**
432          * Specify service specific information for the publish session. This is
433          * a free-form byte array available to the application to send
434          * additional information as part of the discovery operation - it
435          * will not be used to determine whether a publish/subscribe match
436          * occurs.
437          * <p>
438          *     Optional. Empty by default.
439          *
440          * @param serviceSpecificInfo A byte-array for the service-specific
441          *            information field.
442          *
443          * @return The builder to facilitate chaining
444          *         {@code builder.setXXX(..).setXXX(..)}.
445          */
setServiceSpecificInfo(@ullable byte[] serviceSpecificInfo)446         public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
447             mServiceSpecificInfo = serviceSpecificInfo;
448             return this;
449         }
450 
451         /**
452          * The match filter for a publish session. Used to determine whether a service
453          * discovery occurred - in addition to relying on the service name.
454          * <p>
455          *     Optional. Empty by default.
456          *
457          * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
458          *                    array).
459          *
460          * @return The builder to facilitate chaining
461          *         {@code builder.setXXX(..).setXXX(..)}.
462          */
setMatchFilter(@ullable List<byte[]> matchFilter)463         public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
464             mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
465                     matchFilter).getArray();
466             return this;
467         }
468 
469         /**
470          * Specify the type of the publish session: solicited (aka active - publish
471          * packets are transmitted over-the-air), or unsolicited (aka passive -
472          * no publish packets are transmitted, a match is made against an active
473          * subscribe session whose packets are transmitted over-the-air).
474          *
475          * @param publishType Publish session type:
476          *            {@link PublishConfig#PUBLISH_TYPE_SOLICITED} or
477          *            {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED} (the default).
478          *
479          * @return The builder to facilitate chaining
480          *         {@code builder.setXXX(..).setXXX(..)}.
481          */
setPublishType(@ublishTypes int publishType)482         public Builder setPublishType(@PublishTypes int publishType) {
483             if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
484                 throw new IllegalArgumentException("Invalid publishType - " + publishType);
485             }
486             mPublishType = publishType;
487             return this;
488         }
489 
490         /**
491          * Sets the time interval (in seconds) an unsolicited (
492          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
493          * will be alive - broadcasting a packet. When the TTL is reached
494          * an event will be generated for
495          * {@link DiscoverySessionCallback#onSessionTerminated()} [unless
496          * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
497          * <p>
498          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
499          *     Session will be terminated when {@link DiscoverySession#close()} is
500          *     called.
501          *
502          * @param ttlSec Lifetime of a publish session in seconds.
503          *
504          * @return The builder to facilitate chaining
505          *         {@code builder.setXXX(..).setXXX(..)}.
506          */
setTtlSec(int ttlSec)507         public Builder setTtlSec(int ttlSec) {
508             if (ttlSec < 0) {
509                 throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
510             }
511             mTtlSec = ttlSec;
512             return this;
513         }
514 
515         /**
516          * Configure whether a publish terminate notification
517          * {@link DiscoverySessionCallback#onSessionTerminated()} is reported
518          * back to the callback.
519          *
520          * @param enable If true the terminate callback will be called when the
521          *            publish is terminated. Otherwise it will not be called.
522          *
523          * @return The builder to facilitate chaining
524          *         {@code builder.setXXX(..).setXXX(..)}.
525          */
setTerminateNotificationEnabled(boolean enable)526         public Builder setTerminateNotificationEnabled(boolean enable) {
527             mEnableTerminateNotification = enable;
528             return this;
529         }
530 
531         /**
532          * Configure whether the publish discovery session supports ranging and allows peers to
533          * measure distance to it. This API is used in conjunction with
534          * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
535          * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
536          * maximum distance at which discovery will be triggered.
537          * <p>
538          * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device
539          * will be refused and discovery will proceed without ranging constraints.
540          * <p>
541          * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
542          * as described in {@link android.net.wifi.rtt}.
543          *
544          * @param enable If true, ranging is supported on request of the peer.
545          *
546          * @return The builder to facilitate chaining
547          *         {@code builder.setXXX(..).setXXX(..)}.
548          */
setRangingEnabled(boolean enable)549         public Builder setRangingEnabled(boolean enable) {
550             mEnableRanging = enable;
551             return this;
552         }
553 
554         /**
555          * Configure whether to enable and use instant communication for this publish session.
556          * Instant communication will speed up service discovery and any data-path set up as part of
557          * this session. Use {@link Characteristics#isInstantCommunicationModeSupported()} to check
558          * if the device supports this feature.
559          *
560          * <p>Note: due to increased power requirements of this mode - it will only remain enabled
561          * for 30 seconds from the time the discovery session is started.
562          *
563          * @param enabled true for enable instant communication mode, default is false.
564          * @param band When setting to {@link ScanResult#WIFI_BAND_5_GHZ}, device will try to enable
565          *     instant communication mode on 5Ghz, but may fall back to 2.4Ghz due to regulatory
566          *     requirements.
567          * @return the current {@link Builder} builder, enabling chaining of builder methods.
568          */
569         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
570         @NonNull
setInstantCommunicationModeEnabled( boolean enabled, @WifiAwareManager.InstantModeBand int band)571         public Builder setInstantCommunicationModeEnabled(
572                 boolean enabled, @WifiAwareManager.InstantModeBand int band) {
573             if (!SdkLevel.isAtLeastT()) {
574                 throw new UnsupportedOperationException();
575             }
576             if (band != ScanResult.WIFI_BAND_24_GHZ && band != ScanResult.WIFI_BAND_5_GHZ) {
577                 throw new IllegalArgumentException();
578             }
579             mBand = band;
580             mEnableInstantMode = enabled;
581             return this;
582         }
583 
584         /**
585          * Configure security config for the Wi-Fi Aware publish session. The security config set
586          * here must be the same as the one used to request Wi-Fi Aware data-path connection using
587          * {@link WifiAwareNetworkSpecifier.Builder#setDataPathSecurityConfig(WifiAwareDataPathSecurityConfig)}.
588          * This security config will create a security identifier (SCID) which contains a PMKID and
589          * transmitted in the publish message. The device which subscribe this session can get this
590          * info by {@link ServiceDiscoveryInfo#getScid()}
591          * This method is optional - if not called, then no security context identifier will be
592          * passed in the publish message, then no security context identifier will be provided in
593          * the {@link ServiceDiscoveryInfo} on the subscriber. Security can still be negotiated
594          * using out-of-band (OOB) mechanisms.
595          *
596          * @param securityConfig The (optional) security config to be used to create security
597          *                       context Identifier
598          * @return the current {@link Builder} builder, enabling chaining of builder methods.
599          */
600         @NonNull
setDataPathSecurityConfig( @onNull WifiAwareDataPathSecurityConfig securityConfig)601         public Builder setDataPathSecurityConfig(
602                 @NonNull WifiAwareDataPathSecurityConfig securityConfig) {
603             if (securityConfig == null) {
604                 throw new IllegalArgumentException("The WifiAwareDataPathSecurityConfig "
605                         + "should be non-null");
606             }
607             if (!securityConfig.isValid()) {
608                 throw new IllegalArgumentException("The WifiAwareDataPathSecurityConfig "
609                         + "is invalid");
610             }
611             mSecurityConfig = securityConfig;
612             return this;
613         }
614 
615         /**
616          * Set the {@link AwarePairingConfig} for this publish session, the peer can use this info
617          * to determine the config of the following bootstrapping, pairing setup/verification
618          * request.
619          * @see AwarePairingConfig
620          * @param config The pairing config set to the peer. Only valid when
621          * {@link Characteristics#isAwarePairingSupported()} is true.
622          * @return the current {@link Builder} builder, enabling chaining of builder methods.
623          */
624         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setPairingConfig(@ullable AwarePairingConfig config)625         @NonNull public Builder setPairingConfig(@Nullable AwarePairingConfig config) {
626             if (!SdkLevel.isAtLeastU()) {
627                 throw new UnsupportedOperationException();
628             }
629             mPairingConfig = config;
630             return this;
631         }
632 
633         /**
634          * Specify whether to configure the publish discovery session to be suspendable. This API
635          * doesn't suspend the session, it allows it to be suspended and resumed in the future using
636          * {@link DiscoverySession#suspend()} and {@link DiscoverySession#resume()} respectively.
637          * <p>
638          * Optional. Not suspendable by default.
639          * <p>
640          * The device must support Wi-Fi Aware suspension for a publish session to be
641          * suspendable. Feature support check is determined by
642          * {@link Characteristics#isSuspensionSupported()}.
643          *
644          * @param isSuspendable If true, then this publish session can be suspended.
645          *
646          * @return the current {@link Builder} builder, enabling chaining of builder methods.
647          *
648          * @see DiscoverySession#suspend()
649          * @see DiscoverySession#resume()
650          * @hide
651          */
652         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
653         @RequiresPermission(value = MANAGE_WIFI_NETWORK_SELECTION)
654         @SystemApi
655         @NonNull
setSuspendable(boolean isSuspendable)656         public Builder setSuspendable(boolean isSuspendable) {
657             if (!SdkLevel.isAtLeastU()) {
658                 throw new UnsupportedOperationException();
659             }
660             mIsSuspendable = isSuspendable;
661             return this;
662         }
663 
664         /**
665          * Set additional vendor-provided configuration data.
666          *
667          * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided
668          *     configuration data. Note that multiple elements with the same OUI are allowed.
669          * @return Builder for chaining.
670          * @hide
671          */
672         @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
673         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
674         @NonNull
675         @SystemApi
setVendorData(@onNull List<OuiKeyedData> vendorData)676         public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) {
677             if (!SdkLevel.isAtLeastV()) {
678                 throw new UnsupportedOperationException();
679             }
680             if (vendorData == null) {
681                 throw new IllegalArgumentException("setVendorData received a null value");
682             }
683             mVendorData = vendorData;
684             return this;
685         }
686 
687         /**
688          * Build {@link PublishConfig} given the current requests made on the
689          * builder.
690          */
build()691         public PublishConfig build() {
692             return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
693                     mTtlSec, mEnableTerminateNotification, mEnableRanging, mEnableInstantMode,
694                     mBand, mSecurityConfig, mPairingConfig, mIsSuspendable, mVendorData);
695         }
696     }
697 }
698