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