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 * Ranging will only be supported if the Access Point supports IEEE 802.11mc, also known as 211 * two-sided RTT. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point's 212 * capabilities. If not supported the result status will be 213 * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}. 214 * 215 * @param apInfo Information of an Access Point (AP) obtained in a Scan Result. 216 * @return The builder to facilitate chaining 217 * {@code builder.setXXX(..).setXXX(..)}. 218 */ addAccessPoint(@onNull ScanResult apInfo)219 public Builder addAccessPoint(@NonNull ScanResult apInfo) { 220 if (apInfo == null) { 221 throw new IllegalArgumentException("Null ScanResult!"); 222 } 223 return addResponder(ResponderConfig.fromScanResult(apInfo)); 224 } 225 226 /** 227 * Add the devices specified by the {@link ScanResult}s to the list of devices with 228 * which to measure range. The total number of peers added to a request cannot exceed the 229 * limit specified by {@link #getMaxPeers()}. 230 * <p> 231 * Ranging will only be supported if the Access Point supports IEEE 802.11mc, also known as 232 * two-sided RTT. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point's 233 * capabilities. If not supported, the result status will be 234 * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}. 235 * 236 * @param apInfos Information of an Access Points (APs) obtained in a Scan Result. 237 * @return The builder to facilitate chaining 238 * {@code builder.setXXX(..).setXXX(..)}. 239 */ addAccessPoints(@onNull List<ScanResult> apInfos)240 public Builder addAccessPoints(@NonNull List<ScanResult> apInfos) { 241 if (apInfos == null) { 242 throw new IllegalArgumentException("Null list of ScanResults!"); 243 } 244 for (ScanResult scanResult : apInfos) { 245 addAccessPoint(scanResult); 246 } 247 return this; 248 } 249 250 /** 251 * Add the non-802.11mc capable device specified by the {@link ScanResult} to the list of 252 * devices with which to measure range. The total number of peers added to a request cannot 253 * exceed the limit specified by {@link #getMaxPeers()}. 254 * <p> 255 * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc, 256 * and instead an alternate protocol called one-sided RTT will be used with lower 257 * accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point)s) are 258 * not 802.11mc capable. 259 * <p> 260 * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can 261 * add hundreds of meters to the estimate. With experimentation it is possible to use this 262 * information to make a statistical estimate of the range by taking multiple measurements 263 * to several Access Points and normalizing the result. For some applications this can be 264 * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but 265 * will not be as accurate as IEEE 802.11mc (two-sided RTT). 266 * <p> 267 * Note: one-sided RTT should only be used if you are very familiar with statistical 268 * estimation techniques. 269 * 270 * @param apInfo Information of an Access Point (AP) obtained in a Scan Result. 271 * @return The builder to facilitate chaining 272 * {@code builder.setXXX(..).setXXX(..)}. 273 */ 274 @NonNull addNon80211mcCapableAccessPoint(@onNull ScanResult apInfo)275 public Builder addNon80211mcCapableAccessPoint(@NonNull ScanResult apInfo) { 276 if (apInfo == null) { 277 throw new IllegalArgumentException("Null ScanResult!"); 278 } 279 if (apInfo.is80211mcResponder()) { 280 throw new IllegalArgumentException("AP supports the 802.11mc protocol."); 281 } 282 return addResponder(ResponderConfig.fromScanResult(apInfo)); 283 } 284 285 /** 286 * Add the non-802.11mc capable devices specified by the {@link ScanResult} to the list of 287 * devices with which to measure range. The total number of peers added to a request cannot 288 * exceed the limit specified by {@link #getMaxPeers()}. 289 * <p> 290 * Accurate ranging cannot be supported if the Access Point does not support IEEE 802.11mc, 291 * and instead an alternate protocol called one-sided RTT will be used with lower 292 * accuracy. Use {@link ScanResult#is80211mcResponder()} to verify the Access Point)s) are 293 * not 802.11mc capable. 294 * <p> 295 * One-sided RTT does not subtract the RTT turnaround time at the Access Point, which can 296 * add hundreds of meters to the estimate. With experimentation it is possible to use this 297 * information to make a statistical estimate of the range by taking multiple measurements 298 * to several Access Points and normalizing the result. For some applications this can be 299 * used to improve range estimates based on Receive Signal Strength Indication (RSSI), but 300 * will not be as accurate as IEEE 802.11mc (two-sided RTT). 301 * <p> 302 * Note: one-sided RTT should only be used if you are very familiar with statistical 303 * estimation techniques. 304 * 305 * @param apInfos Information of an Access Points (APs) obtained in a Scan Result. 306 * @return The builder to facilitate chaining 307 * {@code builder.setXXX(..).setXXX(..)}. 308 */ 309 @NonNull addNon80211mcCapableAccessPoints(@onNull List<ScanResult> apInfos)310 public Builder addNon80211mcCapableAccessPoints(@NonNull List<ScanResult> apInfos) { 311 if (apInfos == null) { 312 throw new IllegalArgumentException("Null list of ScanResults!"); 313 } 314 for (ScanResult scanResult : apInfos) { 315 if (scanResult.is80211mcResponder()) { 316 throw new IllegalArgumentException( 317 "At least one AP supports the 802.11mc protocol."); 318 } 319 addAccessPoint(scanResult); 320 } 321 return this; 322 } 323 324 /** 325 * Add the device specified by the {@code peerMacAddress} to the list of devices with 326 * which to measure range. 327 * <p> 328 * The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi 329 * Aware device may obtain its MAC address using the {@link IdentityChangedListener} 330 * provided to 331 * {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}. 332 * <p> 333 * Note: in order to use this API the device must support Wi-Fi Aware 334 * {@link android.net.wifi.aware}. The peer device which is being ranged to must be 335 * configured to publish a service (with any name) with: 336 * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}. 337 * <li>Ranging enabled 338 * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}. 339 * 340 * @param peerMacAddress The MAC address of the Wi-Fi Aware peer. 341 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 342 */ addWifiAwarePeer(@onNull MacAddress peerMacAddress)343 public Builder addWifiAwarePeer(@NonNull MacAddress peerMacAddress) { 344 if (peerMacAddress == null) { 345 throw new IllegalArgumentException("Null peer MAC address"); 346 } 347 return addResponder( 348 ResponderConfig.fromWifiAwarePeerMacAddressWithDefaults(peerMacAddress)); 349 } 350 351 /** 352 * Add a device specified by a {@link PeerHandle} to the list of devices with which to 353 * measure range. 354 * <p> 355 * The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g. 356 * using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}. 357 * <p> 358 * Note: in order to use this API the device must support Wi-Fi Aware 359 * {@link android.net.wifi.aware}. The requesting device can be either publisher or 360 * subscriber in a discovery session. For both requesting device and peer device ranging 361 * must be enabled on the discovery session: 362 * <li>{@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)} for 363 * publisher.</li> 364 * <li>Either {@link android.net.wifi.aware.SubscribeConfig.Builder#setMinDistanceMm(int)} 365 * or {@link android.net.wifi.aware.SubscribeConfig.Builder#setMaxDistanceMm(int)} must be 366 * set to enable ranging on subscriber </li> 367 * 368 * @param peerHandle The peer handler of the peer Wi-Fi Aware device. 369 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 370 */ addWifiAwarePeer(@onNull PeerHandle peerHandle)371 public Builder addWifiAwarePeer(@NonNull PeerHandle peerHandle) { 372 if (peerHandle == null) { 373 throw new IllegalArgumentException("Null peer handler (identifier)"); 374 } 375 376 return addResponder(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle)); 377 } 378 379 /** 380 * Add the Responder device specified by the {@link ResponderConfig} to the list of devices 381 * with which to measure range. The total number of peers added to the request cannot exceed 382 * the limit specified by {@link #getMaxPeers()}. 383 * 384 * @param responder Information on the RTT Responder. 385 * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. 386 * 387 * @hide 388 */ 389 @SystemApi addResponder(@onNull ResponderConfig responder)390 public Builder addResponder(@NonNull ResponderConfig responder) { 391 if (responder == null) { 392 throw new IllegalArgumentException("Null Responder!"); 393 } 394 395 mRttPeers.add(responder); 396 return this; 397 } 398 399 /** 400 * Build {@link RangingRequest} given the current configurations made on the 401 * builder. 402 */ build()403 public RangingRequest build() { 404 return new RangingRequest(mRttPeers, mRttBurstSize); 405 } 406 } 407 408 @Override equals(@ullable Object o)409 public boolean equals(@Nullable Object o) { 410 if (this == o) { 411 return true; 412 } 413 414 if (!(o instanceof RangingRequest)) { 415 return false; 416 } 417 418 RangingRequest lhs = (RangingRequest) o; 419 420 return mRttPeers.size() == lhs.mRttPeers.size() 421 && mRttPeers.containsAll(lhs.mRttPeers) 422 && mRttBurstSize == lhs.mRttBurstSize; 423 } 424 425 @Override hashCode()426 public int hashCode() { 427 return Objects.hash(mRttPeers, mRttBurstSize); 428 } 429 } 430