• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.usd;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.net.wifi.WifiNetworkSpecifier;
25 import android.net.wifi.aware.TlvBufferUtils;
26 import android.net.wifi.aware.WifiAwareUtils;
27 import android.net.wifi.flags.Flags;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 
31 import java.nio.charset.StandardCharsets;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 
36 
37 /**
38  * Defines the configuration of USD subscribe session.
39  *
40  * @hide
41  */
42 @SystemApi
43 @FlaggedApi(Flags.FLAG_USD)
44 public final class SubscribeConfig extends Config implements Parcelable {
45 
SubscribeConfig(Builder builder)46     private SubscribeConfig(Builder builder) {
47         super(builder.mServiceName, builder.mTtlSeconds, builder.mServiceProtoType,
48                 builder.mTxMatchFilterTlv, builder.mRxMatchFilterTlv, builder.mServiceSpecificInfo,
49                 builder.mOperatingFrequencies);
50         mSubscribeType = builder.mSubscribeType;
51         mQueryPeriodMillis = builder.mQueryPeriodMillis;
52         mRecommendedFrequencies = builder.mRecommendedFrequencies;
53     }
54 
55     @SubscribeType
56     private final int mSubscribeType;
57     private final int mQueryPeriodMillis;
58     private final int[] mRecommendedFrequencies;
59 
SubscribeConfig(Parcel in)60     private SubscribeConfig(Parcel in) {
61         super(in.createByteArray(), in.readInt(), in.readInt(), in.createByteArray(),
62                 in.createByteArray(), in.createByteArray(), in.createIntArray());
63         mSubscribeType = in.readInt();
64         mQueryPeriodMillis = in.readInt();
65         mRecommendedFrequencies = in.createIntArray();
66     }
67 
68     @Override
writeToParcel(@onNull Parcel dest, int flags)69     public void writeToParcel(@NonNull Parcel dest, int flags) {
70         dest.writeByteArray(getServiceName());
71         dest.writeInt(getTtlSeconds());
72         dest.writeInt(getServiceProtoType());
73         dest.writeByteArray(getTxMatchFilterTlv());
74         dest.writeByteArray(getRxMatchFilterTlv());
75         dest.writeByteArray(getServiceSpecificInfo());
76         dest.writeIntArray(getOperatingFrequenciesMhz());
77         dest.writeInt(mSubscribeType);
78         dest.writeInt(mQueryPeriodMillis);
79         dest.writeIntArray(mRecommendedFrequencies);
80     }
81 
82     @Override
describeContents()83     public int describeContents() {
84         return 0;
85     }
86 
87     @NonNull
88     public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
89 
90         @Override
91         public SubscribeConfig createFromParcel(Parcel in) {
92             return new SubscribeConfig(in);
93         }
94 
95         @Override
96         public SubscribeConfig[] newArray(int size) {
97             return new SubscribeConfig[size];
98         }
99     };
100 
101     /**
102      * Gets the type of subscribe session. See {@code SUBSCRIBE_TYPE_XXX} for different types of
103      * subscribe.
104      *
105      * @return subscribe type
106      */
107     @SubscribeType
getSubscribeType()108     public int getSubscribeType() {
109         return mSubscribeType;
110     }
111 
112     /**
113      * Gets the recommended periodicity of query transmissions for the subscribe session.
114      *
115      * @return Query period in milliseconds
116      */
117     @IntRange(from = 0)
getQueryPeriodMillis()118     public int getQueryPeriodMillis() {
119         return mQueryPeriodMillis;
120     }
121 
122     /**
123      * Gets the recommended frequency list to be used for subscribe operation. See
124      * {@link Builder#setRecommendedOperatingFrequenciesMhz(int[])}.
125      *
126      * @return frequency list or null if not set
127      */
128     @Nullable
getRecommendedOperatingFrequenciesMhz()129     public int[] getRecommendedOperatingFrequenciesMhz() {
130         return mRecommendedFrequencies;
131     }
132 
133     @Override
toString()134     public String toString() {
135         return super.toString() + " SubscribeConfig{" + "mSubscribeType=" + mSubscribeType
136                 + ", mQueryPeriodMillis=" + mQueryPeriodMillis + ", mRecommendedFrequencies="
137                 + Arrays.toString(mRecommendedFrequencies) + '}';
138     }
139 
140     @Override
equals(Object o)141     public boolean equals(Object o) {
142         if (this == o) return true;
143         if (!(o instanceof SubscribeConfig that)) return false;
144         if (!super.equals(o)) return false;
145         return mSubscribeType == that.mSubscribeType
146                 && mQueryPeriodMillis == that.mQueryPeriodMillis
147                 && Arrays.equals(mRecommendedFrequencies, that.mRecommendedFrequencies);
148     }
149 
150     @Override
hashCode()151     public int hashCode() {
152         int result = Objects.hash(super.hashCode(), mSubscribeType, mQueryPeriodMillis);
153         result = 31 * result + Arrays.hashCode(mRecommendedFrequencies);
154         return result;
155     }
156 
157     /**
158      * {@code SubscribeConfig} builder static inner class.
159      */
160     @FlaggedApi(Flags.FLAG_USD)
161     public static final class Builder {
162         @SubscribeType private int mSubscribeType = SUBSCRIBE_TYPE_ACTIVE;
163         private int mQueryPeriodMillis = 100;
164         private int[] mRecommendedFrequencies = null;
165         private final byte[] mServiceName;
166         private int mTtlSeconds = 3000;
167         @ServiceProtoType private int mServiceProtoType = SERVICE_PROTO_TYPE_GENERIC;
168         private byte[] mTxMatchFilterTlv = null;
169         private byte[] mRxMatchFilterTlv = null;
170         private byte[] mServiceSpecificInfo = null;
171         private int[] mOperatingFrequencies = null;
172 
173         /**
174          * Builder for {@link SubscribeConfig}
175          *
176          * @param serviceName Specify the service name of the USD session. The Service Name is a
177          *                    UTF-8 encoded string from 1 to
178          *                    {@link Characteristics#getMaxServiceNameLength()} bytes in length.
179          *                    The only acceptable single-byte UTF-8 symbols for a Service Name are
180          *                    alphanumeric values (A-Z, a-z, 0-9), the hyphen ('-'), the period
181          *                    ('.') and the underscore ('_'). Allvalid multi-byte UTF-8
182          *                    characters are acceptable in a Service Name.
183          */
Builder(@onNull String serviceName)184         public Builder(@NonNull String serviceName) {
185             Objects.requireNonNull(serviceName, "serviceName must not be null");
186             mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
187             WifiAwareUtils.validateServiceName(mServiceName);
188         }
189 
190         /**
191          * Sets the time to live for the USD session and returns a reference to this Builder
192          * enabling method chaining. Default value is 3000 seconds.
193          *
194          * @param ttlSeconds Time to live in seconds. Value 0 indicating the session does not
195          *                   terminate on its own.
196          * @return a reference to this Builder
197          */
198         @NonNull
setTtlSeconds(@ntRangefrom = 0) int ttlSeconds)199         public Builder setTtlSeconds(@IntRange(from = 0) int ttlSeconds) {
200             if (ttlSeconds < 0) {
201                 throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
202             }
203             mTtlSeconds = ttlSeconds;
204             return this;
205         }
206 
207         /**
208          * Sets the {@code serviceProtoType} and returns a reference to this Builder enabling method
209          * chaining. Supported service protocol is defined as {@code SERVICE_PROTO_TYPE_*}. Default
210          * value is {@link #SERVICE_PROTO_TYPE_GENERIC}.
211          *
212          * @param serviceProtoType the {@code serviceProtoType} to set
213          * @return a reference to this Builder
214          */
215         @NonNull
setServiceProtoType(@erviceProtoType int serviceProtoType)216         public Builder setServiceProtoType(@ServiceProtoType int serviceProtoType) {
217             if (serviceProtoType < SERVICE_PROTO_TYPE_GENERIC
218                     || serviceProtoType > SERVICE_PROTO_TYPE_CSA_MATTER) {
219                 throw new IllegalArgumentException("Invalid serviceProtoType - "
220                         + serviceProtoType);
221             }
222             mServiceProtoType = serviceProtoType;
223             return this;
224         }
225 
226         /**
227          * Sets the {@code txMatchFilter} and returns a reference to this Builder enabling method
228          * chaining. The {@code txMatchFilter} is the ordered sequence of (length, value) pairs to
229          * be included in the subscribe frame. If not set, empty by default.
230          *
231          * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching
232          * filter examples.
233          *
234          * @param txMatchFilter the {@code txMatchFilter} to set
235          * @return a reference to this Builder
236          */
237         @NonNull
setTxMatchFilter(@onNull List<byte[]> txMatchFilter)238         public Builder setTxMatchFilter(@NonNull List<byte[]> txMatchFilter) {
239             Objects.requireNonNull(txMatchFilter, "txMatchFilter must not be null");
240             mTxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
241                     txMatchFilter).getArray();
242             if (!TlvBufferUtils.isValid(mTxMatchFilterTlv, 0, 1)) {
243                 throw new IllegalArgumentException(
244                         "Invalid txMatchFilter configuration - LV fields do not match up to "
245                                 + "length");
246             }
247             return this;
248         }
249 
250         /**
251          * Sets the {@code rxMatchFilter} and returns a reference to this Builder enabling method
252          * chaining. The {@code rxMatchFilter} is the ordered sequence of (length, value) pairs
253          * that specify further the matching conditions beyond the service name used to filter
254          * the USD discovery messages. When a subscriber receives a publish message, it matches the
255          * matching filter field in the publish message against its own matching_filter_rx. If not
256          * set, empty by default.
257          *
258          * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching
259          * filter examples.
260          *
261          * @param rxMatchFilter the {@code rxMatchFilter} to set
262          * @return a reference to this Builder
263          */
264         @NonNull
setRxMatchFilter(@onNull List<byte[]> rxMatchFilter)265         public Builder setRxMatchFilter(@NonNull List<byte[]> rxMatchFilter) {
266             Objects.requireNonNull(rxMatchFilter, "rxMatchFilter must not be null");
267             mRxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
268                     rxMatchFilter).getArray();
269             if (!TlvBufferUtils.isValid(mRxMatchFilterTlv, 0, 1)) {
270                 throw new IllegalArgumentException(
271                         "Invalid rxMatchFilter configuration - LV fields do not match up to "
272                                 + "length");
273             }
274             return this;
275         }
276 
277         /**
278          * Sets the susbcribe type and returns a reference to this Builder enabling method chaining.
279          *
280          * @param subscribeType the {@code subscribeType} to set
281          * @return a reference to this Builder
282          */
283         @NonNull
setSubscribeType(@ubscribeType int subscribeType)284         public Builder setSubscribeType(@SubscribeType int subscribeType) {
285             if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) {
286                 throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType);
287             }
288             mSubscribeType = subscribeType;
289             return this;
290         }
291 
292         /**
293          * Sets the query period and returns a reference to this Builder enabling method chaining.
294          * Default value is 100 ms.
295          *
296          * @param queryPeriodMillis the {@code queryPeriodMillis} to set
297          * @return a reference to this Builder
298          */
299         @NonNull
setQueryPeriodMillis(@ntRangefrom = 0) int queryPeriodMillis)300         public Builder setQueryPeriodMillis(@IntRange(from = 0) int queryPeriodMillis) {
301             if (queryPeriodMillis < 0) {
302                 throw new IllegalArgumentException(
303                         "Invalid queryPeriodMillis - must be non-negative");
304             }
305             mQueryPeriodMillis = queryPeriodMillis;
306             return this;
307         }
308 
309         /**
310          * Sets the recommended frequencies to use in case the framework couldn't pick a default
311          * channel for the subscriber operation. This will be a no-op if
312          * {@link #setOperatingFrequenciesMhz(int[])} is used.
313          *
314          * <p>Here is the default subscriber channel selection preference order with {@code
315          * recommendedFreqList}
316          * <ol>
317          * <li>Channel 6 in 2.4 Ghz if there is no multichannel concurrency.
318          * <li>Station channel if the station connected on non-DFS/Indoor channel.
319          * <li>Pick a channel from {@code recommendedFreqList} if regulatory permits.
320          * <li>Pick any available channel
321          * </ol>
322          *
323          * <p>Note: If multiple channels are available for the subscriber, the channel having AP
324          * with the best RSSI will be picked.
325          *
326          * @param recommendedFrequencies the {@code recommendedFreqList} to set
327          * @return a reference to this Builder
328          * @throws IllegalArgumentException if frequencies are invalid or the number frequencies
329          * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory.
330          */
331         @NonNull
setRecommendedOperatingFrequenciesMhz( @onNull int[] recommendedFrequencies)332         public Builder setRecommendedOperatingFrequenciesMhz(
333                 @NonNull int[] recommendedFrequencies) {
334             Objects.requireNonNull(recommendedFrequencies,
335                     "recommendedFrequencies must not be null");
336             if ((recommendedFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES)
337                     || !WifiNetworkSpecifier.validateChannelFrequencyInMhz(
338                     recommendedFrequencies)) {
339                 throw new IllegalArgumentException("Invalid recommendedFrequencies");
340             }
341             this.mRecommendedFrequencies = recommendedFrequencies.clone();
342             return this;
343         }
344 
345         /**
346          * Specify service specific information for the publish session. This is a free-form byte
347          * array available to the application to send additional information as part of the
348          * discovery operation - it will not be used to determine whether a publish/subscribe
349          * match occurs. Default value is null;
350          *
351          * Note: Maximum length is limited by
352          * {@link Characteristics#getMaxServiceSpecificInfoLength()}
353          *
354          * @param serviceSpecificInfo A byte-array for the service-specific
355          *            information field.
356          * @return a reference to this Builder
357          */
358         @NonNull
setServiceSpecificInfo(@onNull byte[] serviceSpecificInfo)359         public Builder setServiceSpecificInfo(@NonNull byte[] serviceSpecificInfo) {
360             Objects.requireNonNull(serviceSpecificInfo, "serviceSpecificInfo must not be null");
361             mServiceSpecificInfo = serviceSpecificInfo.clone();
362             return this;
363         }
364 
365         /**
366          * Sets the frequencies used for subscribe operation. The subscriber picks one of the
367          * frequencies from this list. This overrides the default channel selection as described
368          * below.
369          *
370          * <p>If null, here is the default subscriber channel selection preference order,
371          * <ol>
372          * <li>Channel 6 in 2.4 Ghz if there is no multichannel concurrency.
373          * <li>Station channel if the station connected on non-DFS/Indoor channel.
374          * <li>Pick a channel from {@link #setRecommendedOperatingFrequenciesMhz(int[])} if
375          * regulatory permits.
376          * <li>Pick any available channel.
377          * </ol>
378          * <p>Note: the dwell time for subscriber operation is calculated internally based on
379          * existing concurrency operation (e.g. Station + USD).
380          *
381          * @param operatingFrequencies frequencies used for subscribe operation
382          * @return a reference to this Builder
383          * @throws IllegalArgumentException if frequencies are invalid or the number frequencies
384          * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory.
385          */
386         @NonNull
setOperatingFrequenciesMhz(@onNull int[] operatingFrequencies)387         public Builder setOperatingFrequenciesMhz(@NonNull int[] operatingFrequencies) {
388             Objects.requireNonNull(operatingFrequencies, "operatingFrequencies must not be null");
389             if ((operatingFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES)
390                     || !WifiNetworkSpecifier.validateChannelFrequencyInMhz(operatingFrequencies)) {
391                 throw new IllegalArgumentException("Invalid operatingFrequencies");
392             }
393             mOperatingFrequencies = operatingFrequencies.clone();
394             return this;
395         }
396 
397         /**
398          * Returns a {@code SubscribeConfig} built from the parameters previously set.
399          *
400          * @return a {@code SubscribeConfig} built with parameters of this {@code SubscribeConfig
401          * .Builder}
402          */
403         @NonNull
build()404         public SubscribeConfig build() {
405             return new SubscribeConfig(this);
406         }
407     }
408 }
409