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