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