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.StringJoiner;
36 
37 /**
38  * Defines the ranging request to other devices. The ranging request is built using
39  * {@link RangingRequest.Builder}.
40  * A ranging request is executed using
41  * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}.
42  * <p>
43  * The ranging request is a batch request - specifying a set of devices (specified using
44  * {@link RangingRequest.Builder#addAccessPoint(ScanResult)} and
45  * {@link RangingRequest.Builder#addAccessPoints(List)}).
46  */
47 public final class RangingRequest implements Parcelable {
48     private static final int MAX_PEERS = 10;
49 
50     /**
51      * Returns the maximum number of peers to range which can be specified in a single {@code
52      * RangingRequest}. The limit applies no matter how the peers are added to the request, e.g.
53      * through {@link RangingRequest.Builder#addAccessPoint(ScanResult)} or
54      * {@link RangingRequest.Builder#addAccessPoints(List)}.
55      *
56      * @return Maximum number of peers.
57      */
getMaxPeers()58     public static int getMaxPeers() {
59         return MAX_PEERS;
60     }
61 
62     /** @hide */
63     public final List<ResponderConfig> mRttPeers;
64 
65     /** @hide */
RangingRequest(List<ResponderConfig> rttPeers)66     private RangingRequest(List<ResponderConfig> rttPeers) {
67         mRttPeers = rttPeers;
68     }
69 
70     @Override
describeContents()71     public int describeContents() {
72         return 0;
73     }
74 
75     @Override
writeToParcel(Parcel dest, int flags)76     public void writeToParcel(Parcel dest, int flags) {
77         dest.writeList(mRttPeers);
78     }
79 
80     public static final @android.annotation.NonNull Creator<RangingRequest> CREATOR = new Creator<RangingRequest>() {
81         @Override
82         public RangingRequest[] newArray(int size) {
83             return new RangingRequest[size];
84         }
85 
86         @Override
87         public RangingRequest createFromParcel(Parcel in) {
88             return new RangingRequest(in.readArrayList(null));
89         }
90     };
91 
92     /** @hide */
93     @Override
toString()94     public String toString() {
95         StringJoiner sj = new StringJoiner(", ", "RangingRequest: mRttPeers=[", "]");
96         for (ResponderConfig rc : mRttPeers) {
97             sj.add(rc.toString());
98         }
99         return sj.toString();
100     }
101 
102     /** @hide */
enforceValidity(boolean awareSupported)103     public void enforceValidity(boolean awareSupported) {
104         if (mRttPeers.size() > MAX_PEERS) {
105             throw new IllegalArgumentException(
106                     "Ranging to too many peers requested. Use getMaxPeers() API to get limit.");
107         }
108 
109         for (ResponderConfig peer: mRttPeers) {
110             if (!peer.isValid(awareSupported)) {
111                 throw new IllegalArgumentException("Invalid Responder specification");
112             }
113         }
114     }
115 
116     /**
117      * Builder class used to construct {@link RangingRequest} objects.
118      */
119     public static final class Builder {
120         private List<ResponderConfig> mRttPeers = new ArrayList<>();
121 
122         /**
123          * Add the device specified by the {@link ScanResult} to the list of devices with
124          * which to measure range. The total number of peers added to a request cannot exceed the
125          * limit specified by {@link #getMaxPeers()}.
126          * <p>
127          * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
128          * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
129          * not supported the result status will be
130          * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
131          *
132          * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
133          * @return The builder to facilitate chaining
134          *         {@code builder.setXXX(..).setXXX(..)}.
135          */
addAccessPoint(@onNull ScanResult apInfo)136         public Builder addAccessPoint(@NonNull ScanResult apInfo) {
137             if (apInfo == null) {
138                 throw new IllegalArgumentException("Null ScanResult!");
139             }
140             return addResponder(ResponderConfig.fromScanResult(apInfo));
141         }
142 
143         /**
144          * Add the devices specified by the {@link ScanResult}s to the list of devices with
145          * which to measure range. The total number of peers added to a request cannot exceed the
146          * limit specified by {@link #getMaxPeers()}.
147          * <p>
148          * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
149          * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
150          * not supported the result status will be
151          * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
152          *
153          * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
154          * @return The builder to facilitate chaining
155          *         {@code builder.setXXX(..).setXXX(..)}.
156          */
addAccessPoints(@onNull List<ScanResult> apInfos)157         public Builder addAccessPoints(@NonNull List<ScanResult> apInfos) {
158             if (apInfos == null) {
159                 throw new IllegalArgumentException("Null list of ScanResults!");
160             }
161             for (ScanResult scanResult : apInfos) {
162                 addAccessPoint(scanResult);
163             }
164             return this;
165         }
166 
167         /**
168          * Add the device specified by the {@code peerMacAddress} to the list of devices with
169          * which to measure range.
170          * <p>
171          * The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi
172          * Aware device may obtain its MAC address using the {@link IdentityChangedListener}
173          * provided to
174          * {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}.
175          * <p>
176          * Note: in order to use this API the device must support Wi-Fi Aware
177          * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
178          * configured to publish a service (with any name) with:
179          * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
180          * <li>Ranging enabled
181          * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
182          *
183          * @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
184          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
185          */
addWifiAwarePeer(@onNull MacAddress peerMacAddress)186         public Builder addWifiAwarePeer(@NonNull MacAddress peerMacAddress) {
187             if (peerMacAddress == null) {
188                 throw new IllegalArgumentException("Null peer MAC address");
189             }
190             return addResponder(
191                     ResponderConfig.fromWifiAwarePeerMacAddressWithDefaults(peerMacAddress));
192         }
193 
194         /**
195          * Add a device specified by a {@link PeerHandle} to the list of devices with which to
196          * measure range.
197          * <p>
198          * The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g.
199          * using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}.
200          * <p>
201          * Note: in order to use this API the device must support Wi-Fi Aware
202          * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
203          * configured to publish a service (with any name) with:
204          * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
205          * <li>Ranging enabled
206          * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
207          *
208          * @param peerHandle The peer handler of the peer Wi-Fi Aware device.
209          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
210          */
addWifiAwarePeer(@onNull PeerHandle peerHandle)211         public Builder addWifiAwarePeer(@NonNull PeerHandle peerHandle) {
212             if (peerHandle == null) {
213                 throw new IllegalArgumentException("Null peer handler (identifier)");
214             }
215 
216             return addResponder(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle));
217         }
218 
219         /**
220          * Add the Responder device specified by the {@link ResponderConfig} to the list of devices
221          * with which to measure range. The total number of peers added to the request cannot exceed
222          * the limit specified by {@link #getMaxPeers()}.
223          *
224          * @param responder Information on the RTT Responder.
225          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
226          *
227          * @hide
228          */
229         @SystemApi
addResponder(@onNull ResponderConfig responder)230         public Builder addResponder(@NonNull ResponderConfig responder) {
231             if (responder == null) {
232                 throw new IllegalArgumentException("Null Responder!");
233             }
234 
235             mRttPeers.add(responder);
236             return this;
237         }
238 
239         /**
240          * Build {@link RangingRequest} given the current configurations made on the
241          * builder.
242          */
build()243         public RangingRequest build() {
244             return new RangingRequest(mRttPeers);
245         }
246     }
247 
248     @Override
equals(@ullable Object o)249     public boolean equals(@Nullable Object o) {
250         if (this == o) {
251             return true;
252         }
253 
254         if (!(o instanceof RangingRequest)) {
255             return false;
256         }
257 
258         RangingRequest lhs = (RangingRequest) o;
259 
260         return mRttPeers.size() == lhs.mRttPeers.size() && mRttPeers.containsAll(lhs.mRttPeers);
261     }
262 
263     @Override
hashCode()264     public int hashCode() {
265         return mRttPeers.hashCode();
266     }
267 }
268