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.rtt; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.net.MacAddress; 24 import android.net.wifi.OuiKeyedData; 25 import android.net.wifi.ParcelUtil; 26 import android.net.wifi.ScanResult; 27 import android.net.wifi.aware.AttachCallback; 28 import android.net.wifi.aware.DiscoverySessionCallback; 29 import android.net.wifi.aware.IdentityChangedListener; 30 import android.net.wifi.aware.PeerHandle; 31 import android.net.wifi.aware.WifiAwareManager; 32 import android.os.Build; 33 import android.os.Handler; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 37 import androidx.annotation.RequiresApi; 38 39 import com.android.modules.utils.build.SdkLevel; 40 import com.android.wifi.flags.Flags; 41 42 import java.util.ArrayList; 43 import java.util.Collections; 44 import java.util.List; 45 import java.util.Objects; 46 import java.util.StringJoiner; 47 48 /** 49 * Defines the ranging request to other devices. The ranging request is built using 50 * {@link RangingRequest.Builder}. 51 * A ranging request is executed using 52 * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}. 53 * <p> 54 * The ranging request is a batch request - specifying a set of devices (specified using 55 * {@link RangingRequest.Builder#addAccessPoint(ScanResult)} and 56 * {@link RangingRequest.Builder#addAccessPoints(List)}). 57 */ 58 public final class RangingRequest implements Parcelable { 59 private static final int MAX_PEERS = 10; 60 private static final int DEFAULT_RTT_BURST_SIZE = 8; 61 private static final int MIN_RTT_BURST_SIZE = 2; 62 private static final int MAX_RTT_BURST_SIZE = 31; 63 64 /** 65 * Returns the maximum number of peers to range which can be specified in a single {@code 66 * RangingRequest}. The limit applies no matter how the peers are added to the request, e.g. 67 * through {@link RangingRequest.Builder#addAccessPoint(ScanResult)} or 68 * {@link RangingRequest.Builder#addAccessPoints(List)}. 69 * 70 * @return Maximum number of peers. 71 */ getMaxPeers()72 public static int getMaxPeers() { 73 return MAX_PEERS; 74 } 75 76 /** 77 * Returns the default RTT burst size used to determine the average range. 78 * 79 * @return the RTT burst size used by default 80 */ getDefaultRttBurstSize()81 public static int getDefaultRttBurstSize() { 82 return DEFAULT_RTT_BURST_SIZE; 83 } 84 85 /** 86 * Returns the minimum RTT burst size that can be used to determine a average range. 87 * 88 * @return the minimum RTT burst size that can be used 89 */ getMinRttBurstSize()90 public static int getMinRttBurstSize() { 91 return MIN_RTT_BURST_SIZE; 92 } 93 94 /** 95 * Returns the minimum RTT burst size that can be used to determine a average range. 96 * 97 * @return the maximum RTT burst size that can be used 98 */ getMaxRttBurstSize()99 public static int getMaxRttBurstSize() { 100 return MAX_RTT_BURST_SIZE; 101 } 102 103 /** @hide */ 104 public final List<ResponderConfig> mRttPeers; 105 106 /** @hide */ 107 public final int mRttBurstSize; 108 109 /** 110 * List of {@link OuiKeyedData} providing vendor-specific configuration data. 111 */ 112 private @NonNull List<OuiKeyedData> mVendorData; 113 114 /** @hide */ RangingRequest(List<ResponderConfig> rttPeers, int rttBurstSize, @NonNull List<OuiKeyedData> vendorData)115 private RangingRequest(List<ResponderConfig> rttPeers, int rttBurstSize, 116 @NonNull List<OuiKeyedData> vendorData) { 117 mRttPeers = rttPeers; 118 mRttBurstSize = rttBurstSize; 119 mVendorData = new ArrayList<>(vendorData); 120 } 121 122 /** 123 * Returns the list of RTT capable responding peers. 124 * 125 * @return the list of RTT capable responding peers in a common system representation 126 * 127 * @hide 128 */ 129 @SystemApi 130 @NonNull getRttResponders()131 public List<ResponderConfig> getRttResponders() { 132 return mRttPeers; 133 } 134 135 /** 136 * Returns the RTT burst size used to determine the average range. 137 * 138 * @return the RTT burst size used 139 */ getRttBurstSize()140 public int getRttBurstSize() { 141 return mRttBurstSize; 142 } 143 144 /** 145 * Return the vendor-provided configuration data, if it exists. See also {@link 146 * Builder#setVendorData(List)} 147 * 148 * @return Vendor configuration data, or empty list if it does not exist. 149 * @hide 150 */ 151 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 152 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 153 @NonNull 154 @SystemApi getVendorData()155 public List<OuiKeyedData> getVendorData() { 156 if (!SdkLevel.isAtLeastV()) { 157 throw new UnsupportedOperationException(); 158 } 159 return mVendorData; 160 } 161 162 @Override describeContents()163 public int describeContents() { 164 return 0; 165 } 166 167 @Override writeToParcel(Parcel dest, int flags)168 public void writeToParcel(Parcel dest, int flags) { 169 dest.writeList(mRttPeers); 170 dest.writeInt(mRttBurstSize); 171 dest.writeList(mVendorData); 172 } 173 174 public static final @android.annotation.NonNull Creator<RangingRequest> CREATOR = new Creator<RangingRequest>() { 175 @Override 176 public RangingRequest[] newArray(int size) { 177 return new RangingRequest[size]; 178 } 179 180 @Override 181 public RangingRequest createFromParcel(Parcel in) { 182 return new RangingRequest(in.readArrayList(null), in.readInt(), 183 ParcelUtil.readOuiKeyedDataList(in)); 184 } 185 }; 186 187 /** @hide */ 188 @Override toString()189 public String toString() { 190 StringJoiner sj = new StringJoiner(", ", "RangingRequest: mRttPeers=[", "]"); 191 for (ResponderConfig rc : mRttPeers) { 192 sj.add(rc.toString()); 193 } 194 sj.add("mRttBurstSize=" + mRttBurstSize); 195 sj.add("mVendorData=" + mVendorData); 196 return sj.toString(); 197 } 198 199 /** @hide */ enforceValidity(boolean awareSupported)200 public void enforceValidity(boolean awareSupported) { 201 if (mRttPeers.size() > MAX_PEERS) { 202 throw new IllegalArgumentException( 203 "Ranging to too many peers requested. Use getMaxPeers() API to get limit."); 204 } 205 for (ResponderConfig peer: mRttPeers) { 206 if (!peer.isValid(awareSupported)) { 207 throw new IllegalArgumentException("Invalid Responder specification"); 208 } 209 } 210 if (mRttBurstSize < getMinRttBurstSize() || mRttBurstSize > getMaxRttBurstSize()) { 211 throw new IllegalArgumentException("RTT burst size is out of range"); 212 } 213 if (mVendorData == null) { 214 throw new IllegalArgumentException("Vendor data must be non-null"); 215 } 216 } 217 218 /** 219 * Builder class used to construct {@link RangingRequest} objects. 220 */ 221 public static final class Builder { 222 private List<ResponderConfig> mRttPeers = new ArrayList<>(); 223 private int mRttBurstSize = DEFAULT_RTT_BURST_SIZE; 224 private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); 225 226 /** 227 * Set the RTT Burst size for the ranging request. 228 * <p> 229 * If not set, the default RTT burst size given by 230 * {@link #getDefaultRttBurstSize()} is used to determine the default value. 231 * If set, the value must be in the range {@link #getMinRttBurstSize()} and 232 * {@link #getMaxRttBurstSize()} inclusively, or a 233 * {@link java.lang.IllegalArgumentException} will be thrown. 234 * 235 * Note: RTT burst size is applicable to IEEE 802.11mc, and for one special case it is 236 * also applicable to IEEE 802.11az to generate multiple NTB ranging requests per 237 * measurement. It is applicable for IEEE 802.11az based ranging requests when MIMO is 238 * not available, with the transmit and receive spatial streams between the initiator and 239 * responder station is equal to 1. See 240 * {@link RangingResult#get80211azNumberOfRxSpatialStreams()} and 241 * {@link RangingResult#get80211azNumberOfTxSpatialStreams()}. 242 * 243 * @param rttBurstSize The number of FTM packets used to estimate a range. 244 * @return The builder to facilitate chaining 245 * {@code builder.setXXX(..).setXXX(..)}. 246 */ 247 @NonNull setRttBurstSize(int rttBurstSize)248 public Builder setRttBurstSize(int rttBurstSize) { 249 if (rttBurstSize < MIN_RTT_BURST_SIZE || rttBurstSize > MAX_RTT_BURST_SIZE) { 250 throw new IllegalArgumentException("RTT burst size out of range."); 251 } 252 mRttBurstSize = rttBurstSize; 253 return this; 254 } 255 256 /** 257 * Add the device specified by the {@link ScanResult} to the list of devices with 258 * which to measure range. The total number of peers added to a request cannot exceed the 259 * limit specified by {@link #getMaxPeers()}. 260 * <p> 261 * Two-sided Ranging will be performed if the local device and the AP support IEEE 802.11az 262 * (non-trigger based ranging) or IEEE 802.11mc. AP capability is determined by the method 263 * {@link ScanResult#is80211azNtbResponder()} or {@link ScanResult#is80211mcResponder()}. 264 * 265 * If both 11az and 11mc are supported by the local device and the AP, 11az non-trigger 266 * based ranging will be performed. 267 * 268 * If two-sided ranging is not supported, one-sided RTT will be performed with no 269 * correction for the AP packet turnaround time. 270 * 271 * @param apInfo Information about an Access Point (AP) obtained in a Scan Result. 272 * @return The builder to facilitate chaining 273 * {@code builder.setXXX(..).setXXX(..)}. 274 */ 275 @NonNull addAccessPoint(@onNull ScanResult apInfo)276 public Builder addAccessPoint(@NonNull ScanResult apInfo) { 277 if (apInfo == null) { 278 throw new IllegalArgumentException("Null ScanResult!"); 279 } 280 return addResponder(ResponderConfig.fromScanResult(apInfo)); 281 } 282 283 /** 284 * Add the devices specified by the {@link ScanResult}s to the list of devices with 285 * which to measure range. The total number of peers added to a request cannot exceed the 286 * limit specified by {@link #getMaxPeers()}. 287 * <p> 288 * Two-sided Ranging will be performed if the local device and the AP support IEEE 802.11az 289 * (non-trigger based ranging) or IEEE 802.11mc. AP capability is determined by the method 290 * {@link ScanResult#is80211azNtbResponder()} or {@link ScanResult#is80211mcResponder()}. 291 * 292 * If both 11az and 11mc are supported by the local device and the AP, 11az non-trigger 293 * based ranging will be performed. 294 * 295 * If two-sided ranging is not supported, one-sided RTT will be performed with no 296 * correction for the AP packet turnaround time. 297 * 298 * @param apInfos Information about Access Points (APs) obtained in a Scan Result. 299 * @return The builder to facilitate chaining 300 * {@code builder.setXXX(..).setXXX(..)}. 301 */ 302 @NonNull addAccessPoints(@onNull List<ScanResult> apInfos)303 public Builder addAccessPoints(@NonNull List<ScanResult> apInfos) { 304 if (apInfos == null) { 305 throw new IllegalArgumentException("Null list of ScanResults!"); 306 } 307 for (ScanResult scanResult : apInfos) { 308 addAccessPoint(scanResult); 309 } 310 return this; 311 } 312 313 /** 314 * Add the Responder device specified by the {@link ResponderConfig} to the list of devices 315 * with which to measure range. The total number of peers added to the request cannot exceed 316 * the limit specified by {@link #getMaxPeers()}. 317 * <p> 318 * Two-sided Ranging will be performed if the local device and the AP support IEEE 802.11az 319 * (non-trigger based ranging) or IEEE 802.11mc. AP capability is determined by the method 320 * {@link ScanResult#is80211azNtbResponder()} or {@link ScanResult#is80211mcResponder()}. 321 * 322 * If both 11az and 11mc are supported by the local device and the AP, 11az non-trigger 323 * based ranging will be performed. 324 * 325 * If two-sided ranging is not supported, one-sided RTT will be performed with no 326 * correction for the AP packet turnaround time. 327 * 328 * @param responder Information on the RTT Responder. 329 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 330 */ 331 @NonNull addResponder(@onNull ResponderConfig responder)332 public Builder addResponder(@NonNull ResponderConfig responder) { 333 if (responder == null) { 334 throw new IllegalArgumentException("Null Responder!"); 335 } 336 337 mRttPeers.add(responder); 338 return this; 339 } 340 341 /** 342 * Add the devices specified by the {@link ResponderConfig}s to the list of devices with 343 * which to measure range. The total number of peers added to a request cannot exceed the 344 * limit specified by {@link #getMaxPeers()}. 345 * <p> 346 * Two-sided Ranging will be performed if the local device and the AP support IEEE 802.11az 347 * (non-trigger based ranging) or IEEE 802.11mc. AP capability is determined by the method 348 * {@link ScanResult#is80211azNtbResponder()} or {@link ScanResult#is80211mcResponder()}. 349 * 350 * If both 11az and 11mc are supported by the local device and the AP, 11az non-trigger 351 * based ranging will be performed. 352 * 353 * If two-sided ranging is not supported, one-sided RTT will be performed with no 354 * correction for the AP packet turnaround time. 355 * 356 * @param responders Information representing the set of access points to be ranged 357 * @return The builder to facilitate chaining 358 * {@code builder.setXXX(..).setXXX(..)}. 359 */ 360 @NonNull addResponders(@onNull List<ResponderConfig> responders)361 public Builder addResponders(@NonNull List<ResponderConfig> responders) { 362 if (responders == null) { 363 throw new IllegalArgumentException("Null list of Responders"); 364 } 365 for (ResponderConfig responder : responders) { 366 addResponder(responder); 367 } 368 return this; 369 } 370 371 /** 372 * Add the non-802.11mc and non-802.11az capable device specified by the {@link ScanResult} 373 * to the list of devices with which to measure range. The total number of peers added to a 374 * request cannot exceed the limit specified by {@link #getMaxPeers()}. 375 * <p> 376 * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc 377 * and IEEE 802.11az, and instead an alternate protocol called one-sided RTT will be used 378 * with lower accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access 379 * Point(s) are not 802.11mc capable. Use {@link ScanResult#is80211azNtbResponder()} ()} to 380 * verify the Access Point)s) are not 802.11az capable. 381 * <p> 382 * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can 383 * add hundreds of meters to the estimate. With experimentation, it is possible to use this 384 * information to make a statistical estimate of the range by taking multiple measurements 385 * to several Access Points and normalizing the result. For some applications this can be 386 * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but 387 * will not be as accurate as IEEE 802.11mc (two-sided RTT). 388 * <p> 389 * Note: one-sided RTT should only be used if you are very familiar with statistical 390 * estimation techniques. 391 * 392 * @param apInfo Information about an Access Point (AP) obtained in a Scan Result 393 * @return The builder to facilitate chaining 394 * {@code builder.setXXX(..).setXXX(..)}. 395 */ 396 @NonNull addNon80211mcCapableAccessPoint(@onNull ScanResult apInfo)397 public Builder addNon80211mcCapableAccessPoint(@NonNull ScanResult apInfo) { 398 if (apInfo == null) { 399 throw new IllegalArgumentException("Null ScanResult!"); 400 } 401 if (apInfo.is80211mcResponder() || apInfo.is80211azNtbResponder()) { 402 throw new IllegalArgumentException( 403 "AP supports the 802.11mc or 8022.11az protocol."); 404 } 405 return addResponder(ResponderConfig.fromScanResult(apInfo)); 406 } 407 408 /** 409 * Add the non-802.11mc and non-802.11az capable devices specified by the {@link ScanResult} 410 * to the list of devices with which to measure range. The total number of peers added to a 411 * request cannot exceed the limit specified by {@link #getMaxPeers()}. 412 * <p> 413 * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc 414 * and IEEE 802.11az, and instead an alternate protocol called one-sided RTT will be used 415 * with lower accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access 416 * Point(s) are not 802.11mc capable. Use {@link ScanResult#is80211azNtbResponder()} ()} to 417 * verify the Access Point(s) are not 802.11az capable. 418 * <p> 419 * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can 420 * add hundreds of meters to the estimate. With experimentation, it is possible to use this 421 * information to make a statistical estimate of the range by taking multiple measurements 422 * to several Access Points and normalizing the result. For some applications this can be 423 * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but 424 * will not be as accurate as IEEE 802.11mc (two-sided RTT). 425 * <p> 426 * Note: one-sided RTT should only be used if you are very familiar with statistical 427 * estimation techniques. 428 * 429 * @param apInfos Information about Access Points (APs) obtained in a Scan Result. 430 * @return The builder to facilitate chaining 431 * {@code builder.setXXX(..).setXXX(..)}. 432 */ 433 @NonNull addNon80211mcCapableAccessPoints(@onNull List<ScanResult> apInfos)434 public Builder addNon80211mcCapableAccessPoints(@NonNull List<ScanResult> apInfos) { 435 if (apInfos == null) { 436 throw new IllegalArgumentException("Null list of ScanResults!"); 437 } 438 for (ScanResult scanResult : apInfos) { 439 if (scanResult.is80211mcResponder() || scanResult.is80211azNtbResponder()) { 440 throw new IllegalArgumentException( 441 "At least one AP supports the 802.11mc or 802.11az protocol."); 442 } 443 addAccessPoint(scanResult); 444 } 445 return this; 446 } 447 448 /** 449 * Add the device specified by the {@code peerMacAddress} to the list of devices with 450 * which to measure range. 451 * <p> 452 * The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi 453 * Aware device may obtain its MAC address using the {@link IdentityChangedListener} 454 * provided to 455 * {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}. 456 * <p> 457 * Note: in order to use this API the device must support Wi-Fi Aware 458 * {@link android.net.wifi.aware}. The peer device which is being ranged to must be 459 * configured to publish a service (with any name) with: 460 * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}. 461 * <li>Ranging enabled 462 * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}. 463 * 464 * @param peerMacAddress The MAC address of the Wi-Fi Aware peer. 465 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 466 */ addWifiAwarePeer(@onNull MacAddress peerMacAddress)467 public Builder addWifiAwarePeer(@NonNull MacAddress peerMacAddress) { 468 if (peerMacAddress == null) { 469 throw new IllegalArgumentException("Null peer MAC address"); 470 } 471 return addResponder( 472 ResponderConfig.fromWifiAwarePeerMacAddressWithDefaults(peerMacAddress)); 473 } 474 475 /** 476 * Add a device specified by a {@link PeerHandle} to the list of devices with which to 477 * measure range. 478 * <p> 479 * The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g. 480 * using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}. 481 * <p> 482 * Note: in order to use this API the device must support Wi-Fi Aware 483 * {@link android.net.wifi.aware}. The requesting device can be either publisher or 484 * subscriber in a discovery session. For both requesting device and peer device ranging 485 * must be enabled on the discovery session: 486 * <li>{@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)} for 487 * publisher.</li> 488 * <li>Either {@link android.net.wifi.aware.SubscribeConfig.Builder#setMinDistanceMm(int)} 489 * or {@link android.net.wifi.aware.SubscribeConfig.Builder#setMaxDistanceMm(int)} must be 490 * set to enable ranging on subscriber </li> 491 * 492 * @param peerHandle The peer handler of the peer Wi-Fi Aware device. 493 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 494 */ addWifiAwarePeer(@onNull PeerHandle peerHandle)495 public Builder addWifiAwarePeer(@NonNull PeerHandle peerHandle) { 496 if (peerHandle == null) { 497 throw new IllegalArgumentException("Null peer handler (identifier)"); 498 } 499 500 return addResponder(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle)); 501 } 502 503 /** 504 * Set additional vendor-provided configuration data. 505 * 506 * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided 507 * configuration data. Note that multiple elements with the same OUI are allowed. 508 * @return Builder for chaining. 509 * @hide 510 */ 511 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 512 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 513 @NonNull 514 @SystemApi setVendorData(@onNull List<OuiKeyedData> vendorData)515 public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) { 516 if (!SdkLevel.isAtLeastV()) { 517 throw new UnsupportedOperationException(); 518 } 519 if (vendorData == null) { 520 throw new IllegalArgumentException("setVendorData received a null value"); 521 } 522 mVendorData = vendorData; 523 return this; 524 } 525 526 /** 527 * Build {@link RangingRequest} given the current configurations made on the 528 * builder. 529 */ build()530 public RangingRequest build() { 531 return new RangingRequest(mRttPeers, mRttBurstSize, mVendorData); 532 } 533 } 534 535 @Override equals(@ullable Object o)536 public boolean equals(@Nullable Object o) { 537 if (this == o) { 538 return true; 539 } 540 541 if (!(o instanceof RangingRequest)) { 542 return false; 543 } 544 545 RangingRequest lhs = (RangingRequest) o; 546 547 return mRttPeers.size() == lhs.mRttPeers.size() 548 && mRttPeers.containsAll(lhs.mRttPeers) 549 && mRttBurstSize == lhs.mRttBurstSize 550 && Objects.equals(mVendorData, lhs.mVendorData); 551 } 552 553 @Override hashCode()554 public int hashCode() { 555 return Objects.hash(mRttPeers, mRttBurstSize, mVendorData); 556 } 557 } 558