1 /*
2  * Copyright (C) 2016 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.aware;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresApi;
22 import android.annotation.SystemApi;
23 import android.net.wifi.OuiKeyedData;
24 import android.net.wifi.ParcelUtil;
25 import android.os.Build;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 
29 import com.android.modules.utils.build.SdkLevel;
30 import com.android.wifi.flags.Flags;
31 
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Objects;
36 
37 /**
38  * Defines a request object to configure a Wi-Fi Aware network. Built using
39  * {@link ConfigRequest.Builder}. Configuration is requested using
40  * {@link WifiAwareManager#attach(AttachCallback, android.os.Handler)}.
41  * Note that the actual achieved configuration may be different from the
42  * requested configuration - since different applications may request different
43  * configurations.
44  *
45  * @hide
46  */
47 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
48 @SystemApi
49 public final class ConfigRequest implements Parcelable {
50     /**
51      * Lower range of possible cluster ID.
52      * @hide
53      */
54     public static final int CLUSTER_ID_MIN = 0;
55 
56     /**
57      * Upper range of possible cluster ID.
58      * @hide
59      */
60     public static final int CLUSTER_ID_MAX = 0xFFFF;
61 
62     /**
63      * Indices for configuration variables which are specified per band.
64      * @hide
65      */
66     public static final int NAN_BAND_24GHZ = 0;
67 
68     /** @hide */
69     public static final int NAN_BAND_5GHZ = 1;
70 
71     /** @hide */
72     public static final int NAN_BAND_6GHZ = 2;
73 
74     /**
75      * Magic values for Discovery Window (DW) interval configuration
76      * @hide
77      */
78     public static final int DW_INTERVAL_NOT_INIT = -1;
79 
80     /** @hide */
81     public static final int DW_DISABLE = 0; // only valid for 5GHz
82 
83     /**
84      * Indicates whether 5G band support is requested.
85      * @hide
86      */
87     public final boolean mSupport5gBand;
88 
89     /**
90      * Indicates whether 6G band support is requested.
91      * @hide
92      */
93     public final boolean mSupport6gBand;
94 
95     /**
96      * Specifies the desired master preference.
97      * @hide
98      */
99     public int mMasterPreference;
100 
101     /**
102      * Specifies the desired lower range of the cluster ID. Must be lower than
103      * {@link ConfigRequest#mClusterHigh}.
104      * @hide
105      */
106     public final int mClusterLow;
107 
108     /**
109      * Specifies the desired higher range of the cluster ID. Must be higher than
110      * {@link ConfigRequest#mClusterLow}.
111      * @hide
112      */
113     public final int mClusterHigh;
114 
115     /**
116      * Specifies the discovery window interval for the device on NAN_BAND_*.
117      * @hide
118      */
119     public final int mDiscoveryWindowInterval[];
120 
121     /**
122      * List of {@link OuiKeyedData} providing vendor-specific configuration data.
123      */
124     private final List<OuiKeyedData> mVendorData;
125 
ConfigRequest(boolean support5gBand, boolean support6gBand, int masterPreference, int clusterLow, int clusterHigh, int[] discoveryWindowInterval, @NonNull List<OuiKeyedData> vendorData)126     private ConfigRequest(boolean support5gBand, boolean support6gBand, int masterPreference,
127             int clusterLow, int clusterHigh, int[] discoveryWindowInterval,
128             @NonNull List<OuiKeyedData> vendorData) {
129         mSupport5gBand = support5gBand;
130         mSupport6gBand = support6gBand;
131         mMasterPreference = masterPreference;
132         mClusterLow = clusterLow;
133         mClusterHigh = clusterHigh;
134         mDiscoveryWindowInterval = discoveryWindowInterval;
135         mVendorData = vendorData;
136     }
137 
138     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
139     @Override
toString()140     public String toString() {
141         return "ConfigRequest [mSupport5gBand=" + mSupport5gBand
142                 + ", mSupport6gBand=" + mSupport6gBand
143                 + ", mMasterPreference=" + mMasterPreference
144                 + ", mClusterLow=" + mClusterLow
145                 + ", mClusterHigh=" + mClusterHigh
146                 + ", mDiscoveryWindowInterval=" + Arrays.toString(mDiscoveryWindowInterval)
147                 + ", mVendorData=" + mVendorData
148                 + "]";
149     }
150 
151     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
152     @Override
describeContents()153     public int describeContents() {
154         return 0;
155     }
156 
157     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
158     @Override
writeToParcel(@onNull Parcel dest, int flags)159     public void writeToParcel(@NonNull Parcel dest, int flags) {
160         dest.writeInt(mSupport5gBand ? 1 : 0);
161         dest.writeInt(mSupport6gBand ? 1 : 0);
162         dest.writeInt(mMasterPreference);
163         dest.writeInt(mClusterLow);
164         dest.writeInt(mClusterHigh);
165         dest.writeIntArray(mDiscoveryWindowInterval);
166         dest.writeList(mVendorData);
167     }
168 
169     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
170     public static final @android.annotation.NonNull Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
171         @Override
172         public ConfigRequest[] newArray(int size) {
173             return new ConfigRequest[size];
174         }
175 
176         @Override
177         public ConfigRequest createFromParcel(Parcel in) {
178             boolean support5gBand = in.readInt() != 0;
179             boolean support6gBand = in.readInt() != 0;
180             int masterPreference = in.readInt();
181             int clusterLow = in.readInt();
182             int clusterHigh = in.readInt();
183             int discoveryWindowInterval[] = in.createIntArray();
184             List<OuiKeyedData> vendorData = ParcelUtil.readOuiKeyedDataList(in);
185 
186             return new ConfigRequest(support5gBand, support6gBand, masterPreference, clusterLow,
187                     clusterHigh, discoveryWindowInterval, vendorData);
188         }
189     };
190 
191     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
192     @Override
equals(Object o)193     public boolean equals(Object o) {
194         if (this == o) {
195             return true;
196         }
197 
198         if (!(o instanceof ConfigRequest)) {
199             return false;
200         }
201 
202         ConfigRequest lhs = (ConfigRequest) o;
203 
204         return mSupport5gBand == lhs.mSupport5gBand
205                 && mSupport6gBand == lhs.mSupport6gBand
206                 && mMasterPreference == lhs.mMasterPreference
207                 && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh
208                 && Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval)
209                 && Objects.equals(mVendorData, lhs.mVendorData);
210     }
211 
212     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
213     @Override
hashCode()214     public int hashCode() {
215         int result = 17;
216 
217         result = 31 * result + (mSupport5gBand ? 1 : 0);
218         result = 31 * result + (mSupport6gBand ? 1 : 0);
219         result = 31 * result + mMasterPreference;
220         result = 31 * result + mClusterLow;
221         result = 31 * result + mClusterHigh;
222         result = 31 * result + Arrays.hashCode(mDiscoveryWindowInterval);
223         result = 31 * result + mVendorData.hashCode();
224 
225         return result;
226     }
227 
228     /**
229      * Verifies that the contents of the ConfigRequest are valid. Otherwise
230      * throws an IllegalArgumentException.
231      * @hide
232      */
validate()233     public void validate() throws IllegalArgumentException {
234         if (mMasterPreference < 0) {
235             throw new IllegalArgumentException(
236                     "Master Preference specification must be non-negative");
237         }
238         if (mMasterPreference == 1 || mMasterPreference == 255 || mMasterPreference > 255) {
239             throw new IllegalArgumentException("Master Preference specification must not "
240                     + "exceed 255 or use 1 or 255 (reserved values)");
241         }
242         if (mClusterLow < CLUSTER_ID_MIN) {
243             throw new IllegalArgumentException("Cluster specification must be non-negative");
244         }
245         if (mClusterLow > CLUSTER_ID_MAX) {
246             throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
247         }
248         if (mClusterHigh < CLUSTER_ID_MIN) {
249             throw new IllegalArgumentException("Cluster specification must be non-negative");
250         }
251         if (mClusterHigh > CLUSTER_ID_MAX) {
252             throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
253         }
254         if (mClusterLow > mClusterHigh) {
255             throw new IllegalArgumentException(
256                     "Invalid argument combination - must have Cluster Low <= Cluster High");
257         }
258         if (mDiscoveryWindowInterval.length != 3) {
259             throw new IllegalArgumentException(
260                     "Invalid discovery window interval: must have 3 elements (2.4 & 5 & 6");
261         }
262         if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT &&
263                 (mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5]
264                 || mDiscoveryWindowInterval[NAN_BAND_24GHZ] > 5)) {
265             throw new IllegalArgumentException(
266                     "Invalid discovery window interval for 2.4GHz: valid is UNSET or [1,5]");
267         }
268         if (mDiscoveryWindowInterval[NAN_BAND_5GHZ] != DW_INTERVAL_NOT_INIT &&
269                 (mDiscoveryWindowInterval[NAN_BAND_5GHZ] < 0 // valid for 5GHz: [0-5]
270                 || mDiscoveryWindowInterval[NAN_BAND_5GHZ] > 5)) {
271             throw new IllegalArgumentException(
272                 "Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]");
273         }
274         if (mDiscoveryWindowInterval[NAN_BAND_6GHZ] != DW_INTERVAL_NOT_INIT
275                 && (mDiscoveryWindowInterval[NAN_BAND_6GHZ] < 0 // valid for 6GHz: [0-5]
276                 || mDiscoveryWindowInterval[NAN_BAND_6GHZ] > 5)) {
277             throw new IllegalArgumentException(
278                 "Invalid discovery window interval for 6GHz: valid is UNSET or [0,5]");
279         }
280         if (mVendorData == null) {
281             throw new IllegalArgumentException("Vendor data list must be non-null");
282         }
283     }
284 
285     /**
286      * Get the vendor-provided configuration data, if it exists. See {@link
287      * Builder#setVendorData(List)}
288      *
289      * @return Vendor configuration data, or empty list if it does not exist.
290      * @hide
291      */
292     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
293     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
294     @SystemApi
295     @NonNull
getVendorData()296     public List<OuiKeyedData> getVendorData() {
297         if (!SdkLevel.isAtLeastV()) {
298             throw new UnsupportedOperationException();
299         }
300         return mVendorData != null ? mVendorData : Collections.emptyList();
301     }
302 
303     /**
304      * Builder used to build {@link ConfigRequest} objects.
305      */
306     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
307     public static final class Builder {
308         private boolean mSupport5gBand = true;
309         private boolean mSupport6gBand = false;
310         private int mMasterPreference = 0;
311         private int mClusterLow = CLUSTER_ID_MIN;
312         private int mClusterHigh = CLUSTER_ID_MAX;
313         private int[] mDiscoveryWindowInterval = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT,
314                 DW_INTERVAL_NOT_INIT};
315         private List<OuiKeyedData> mVendorData = Collections.emptyList();
316 
317         /**
318          * Specify whether 5G band support is required in this request. Disabled by default.
319          *
320          * @param support5gBand Support for 5G band is required.
321          *
322          * @return The builder to facilitate chaining
323          *         {@code builder.setXXX(..).setXXX(..)}.
324          * @hide
325          */
setSupport5gBand(boolean support5gBand)326         public Builder setSupport5gBand(boolean support5gBand) {
327             mSupport5gBand = support5gBand;
328             return this;
329         }
330 
331         /**
332          * Specify whether 6G band support is required in this request. Disabled by default.
333          *
334          * @param support6gBand Support for 6G band is required.
335          *
336          * @return The builder to facilitate chaining
337          *         {@code builder.setXXX(..).setXXX(..)}.
338          * @hide
339          */
setSupport6gBand(boolean support6gBand)340         public Builder setSupport6gBand(boolean support6gBand) {
341             mSupport6gBand = support6gBand;
342             return this;
343         }
344 
345         /**
346          * Specify the Master Preference requested. The permitted range is 0 (the default) to
347          * 255 with 1 and 255 excluded (reserved).
348          *
349          * @param masterPreference The requested master preference
350          *
351          * @return The builder to facilitate chaining
352          *         {@code builder.setXXX(..).setXXX(..)}.
353          * @hide
354          */
setMasterPreference(int masterPreference)355         public Builder setMasterPreference(int masterPreference) {
356             if (masterPreference < 0) {
357                 throw new IllegalArgumentException(
358                         "Master Preference specification must be non-negative");
359             }
360             if (masterPreference == 1 || masterPreference == 255 || masterPreference > 255) {
361                 throw new IllegalArgumentException("Master Preference specification must not "
362                         + "exceed 255 or use 1 or 255 (reserved values)");
363             }
364 
365             mMasterPreference = masterPreference;
366             return this;
367         }
368 
369         /**
370          * The Cluster ID is generated randomly for new Aware networks. Specify
371          * the lower range of the cluster ID. The upper range is specified using
372          * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
373          * range is 0 (the default) to the value specified by
374          * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality of Low and High is
375          * permitted which restricts the Cluster ID to the specified value.
376          *
377          * @param clusterLow The lower range of the generated cluster ID.
378          *
379          * @return The builder to facilitate chaining
380          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
381          * @hide
382          */
setClusterLow(int clusterLow)383         public Builder setClusterLow(int clusterLow) {
384             if (clusterLow < CLUSTER_ID_MIN) {
385                 throw new IllegalArgumentException("Cluster specification must be non-negative");
386             }
387             if (clusterLow > CLUSTER_ID_MAX) {
388                 throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
389             }
390 
391             mClusterLow = clusterLow;
392             return this;
393         }
394 
395         /**
396          * The Cluster ID is generated randomly for new Aware networks. Specify
397          * the lower upper of the cluster ID. The lower range is specified using
398          * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
399          * range is the value specified by
400          * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF (the default). Equality of
401          * Low and High is permitted which restricts the Cluster ID to the specified value.
402          *
403          * @param clusterHigh The upper range of the generated cluster ID.
404          *
405          * @return The builder to facilitate chaining
406          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
407          * @hide
408          */
setClusterHigh(int clusterHigh)409         public Builder setClusterHigh(int clusterHigh) {
410             if (clusterHigh < CLUSTER_ID_MIN) {
411                 throw new IllegalArgumentException("Cluster specification must be non-negative");
412             }
413             if (clusterHigh > CLUSTER_ID_MAX) {
414                 throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
415             }
416 
417             mClusterHigh = clusterHigh;
418             return this;
419         }
420 
421         /**
422          * The discovery window interval specifies the discovery windows in which the device will be
423          * awake. The configuration enables trading off latency vs. power (higher interval means
424          * higher discovery latency but lower power).
425          *
426          * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ} or
427          *        {@link #NAN_BAND_6GHZ}.
428          * @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For
429          *                 the 5GHz band a value of 0 indicates that the device will not be awake
430          *                 for any discovery windows.
431          *
432          * @return The builder itself to facilitate chaining operations
433          *         {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}.
434          * @hide
435          */
setDiscoveryWindowInterval(int band, int interval)436         public Builder setDiscoveryWindowInterval(int band, int interval) {
437             if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ && band != NAN_BAND_6GHZ) {
438                 throw new IllegalArgumentException("Invalid band value");
439             }
440             if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5))
441                     || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))
442                     || (band == NAN_BAND_6GHZ && (interval < 0 || interval > 5))) {
443                 throw new IllegalArgumentException(
444                         "Invalid interval value: 2.4 GHz [1,5] or 5GHz/6GHz [0,5]");
445             }
446 
447             mDiscoveryWindowInterval[band] = interval;
448             return this;
449         }
450 
451         /**
452          * Set additional vendor-provided configuration data.
453          *
454          * @param vendorData List of {@link android.net.wifi.OuiKeyedData} containing the
455          *                   vendor-provided configuration data. Note that multiple elements with
456          *                   the same OUI are allowed.
457          * @return Builder for chaining.
458          * @hide
459          */
460         @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
461         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
462         @SystemApi
463         @NonNull
setVendorData(@onNull List<OuiKeyedData> vendorData)464         public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) {
465             if (!SdkLevel.isAtLeastV()) {
466                 throw new UnsupportedOperationException();
467             }
468             if (vendorData == null) {
469                 throw new IllegalArgumentException("setVendorData received a null value");
470             }
471             mVendorData = vendorData;
472             return this;
473         }
474 
475         /**
476          * Build {@link ConfigRequest} given the current requests made on the
477          * builder.
478          */
479         @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
480         @NonNull
build()481         public ConfigRequest build() {
482             if (mClusterLow > mClusterHigh) {
483                 throw new IllegalArgumentException(
484                         "Invalid argument combination - must have Cluster Low <= Cluster High");
485             }
486 
487             return new ConfigRequest(mSupport5gBand, mSupport6gBand, mMasterPreference, mClusterLow,
488                     mClusterHigh, mDiscoveryWindowInterval, mVendorData);
489         }
490     }
491 }
492