• 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.SystemApi;
23 import android.net.wifi.WifiNetworkSpecifier;
24 import android.net.wifi.aware.TlvBufferUtils;
25 import android.net.wifi.aware.WifiAwareUtils;
26 import android.net.wifi.flags.Flags;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 
30 import java.nio.charset.StandardCharsets;
31 import java.util.List;
32 import java.util.Objects;
33 
34 /**
35  * Defines the configuration of USD publish session
36  *
37  * @hide
38  */
39 @SystemApi
40 @FlaggedApi(Flags.FLAG_USD)
41 public final class PublishConfig extends Config implements Parcelable {
42 
43     @TransmissionType
44     private final int mSolicitedTransmissionType;
45     private final int mAnnouncementPeriodMillis;
46     private final boolean mEnableEvents;
47 
PublishConfig(Parcel in)48     private PublishConfig(Parcel in) {
49         super(in.createByteArray(), in.readInt(), in.readInt(), in.createByteArray(),
50                 in.createByteArray(), in.createByteArray(), in.createIntArray());
51         mSolicitedTransmissionType = in.readInt();
52         mAnnouncementPeriodMillis = in.readInt();
53         mEnableEvents = in.readBoolean();
54     }
55 
56     @NonNull
57     public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
58 
59         @Override
60         public PublishConfig createFromParcel(Parcel in) {
61             return new PublishConfig(in);
62         }
63 
64         @Override
65         public PublishConfig[] newArray(int size) {
66             return new PublishConfig[size];
67         }
68     };
69 
PublishConfig(Builder builder)70     private PublishConfig(Builder builder) {
71         super(builder.mServiceName, builder.mTtlSeconds, builder.mServiceProtoType,
72                 builder.mTxMatchFilterTlv, builder.mRxMatchFilterTlv, builder.mServiceSpecificInfo,
73                 builder.mOperatingFrequencies);
74         mSolicitedTransmissionType = builder.mSolicitedTransmissionType;
75         mAnnouncementPeriodMillis = builder.mAnnouncementPeriodMillis;
76         mEnableEvents = builder.mEnableEvents;
77     }
78 
79     @Override
describeContents()80     public int describeContents() {
81         return 0;
82     }
83 
84     @Override
writeToParcel(@onNull Parcel dest, int flags)85     public void writeToParcel(@NonNull Parcel dest, int flags) {
86         dest.writeByteArray(getServiceName());
87         dest.writeInt(getTtlSeconds());
88         dest.writeInt(getServiceProtoType());
89         dest.writeByteArray(getTxMatchFilterTlv());
90         dest.writeByteArray(getRxMatchFilterTlv());
91         dest.writeByteArray(getServiceSpecificInfo());
92         dest.writeIntArray(getOperatingFrequenciesMhz());
93         dest.writeInt(mSolicitedTransmissionType);
94         dest.writeInt(mAnnouncementPeriodMillis);
95         dest.writeBoolean(mEnableEvents);
96     }
97 
98     /**
99      * @return whether a solicited transmission is an unicast or a multicast transmission
100      */
101     @TransmissionType
getSolicitedTransmissionType()102     public int getSolicitedTransmissionType() {
103         return mSolicitedTransmissionType;
104     }
105 
106     /**
107      * @return announcement period in milliseconds, which is the Recommended periodicity of
108      * unsolicited transmissions
109      */
110     @IntRange(from = 0)
getAnnouncementPeriodMillis()111     public int getAnnouncementPeriodMillis() {
112         return mAnnouncementPeriodMillis;
113     }
114 
115     /**
116      * @return whether publish events are enabled or not. See
117      * {@link Builder#setEventsEnabled(boolean)}.
118      */
isEventsEnabled()119     public boolean isEventsEnabled() {
120         return mEnableEvents;
121     }
122 
123     @Override
toString()124     public String toString() {
125         return super.toString() + " PublishConfig{" + "mSolicitedTransmissionType="
126                 + mSolicitedTransmissionType + ", mAnnouncementPeriodMillis="
127                 + mAnnouncementPeriodMillis + ", mEnableEvents=" + mEnableEvents + '}';
128     }
129 
130     @Override
equals(Object o)131     public boolean equals(Object o) {
132         if (this == o) return true;
133         if (!(o instanceof PublishConfig that)) return false;
134         if (!super.equals(o)) return false;
135         return mSolicitedTransmissionType == that.mSolicitedTransmissionType
136                 && mAnnouncementPeriodMillis == that.mAnnouncementPeriodMillis
137                 && mEnableEvents == that.mEnableEvents;
138     }
139 
140     @Override
hashCode()141     public int hashCode() {
142         return Objects.hash(super.hashCode(), mSolicitedTransmissionType, mAnnouncementPeriodMillis,
143                 mEnableEvents);
144     }
145 
146     /**
147      * {@code PublishConfig} builder static inner class.
148      */
149     public static final class Builder {
150         @TransmissionType
151         private int mSolicitedTransmissionType = TRANSMISSION_TYPE_UNICAST;
152         private int mAnnouncementPeriodMillis = 100;
153         private boolean mEnableEvents = false;
154         private final byte[] mServiceName;
155         private int mTtlSeconds = 3000;
156         @ServiceProtoType
157         private int mServiceProtoType = SERVICE_PROTO_TYPE_GENERIC;
158         private byte[] mTxMatchFilterTlv = null;
159         private byte[] mRxMatchFilterTlv = null;
160         private byte[] mServiceSpecificInfo = null;
161         private int[] mOperatingFrequencies = null;
162 
163         /**
164          * Builder for {@link PublishConfig}
165          *
166          * @param serviceName Specify the service name of the USD session. The Service Name is a
167          *                    UTF-8 encoded string from 1 to
168          *                    {@link Characteristics#getMaxServiceNameLength()} bytes in length.
169          *                    The only acceptable single-byte UTF-8 symbols for a Service Name are
170          *                    alphanumeric values (A-Z, a-z, 0-9), the hyphen ('-'), the period
171          *                    ('.') and the underscore ('_'). All valid multi-byte UTF-8
172          *                    characters are acceptable in a Service Name.
173          */
Builder(@onNull String serviceName)174         public Builder(@NonNull String serviceName) {
175             Objects.requireNonNull(serviceName, "serviceName must not be null");
176             mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
177             WifiAwareUtils.validateServiceName(mServiceName);
178         }
179 
180         /**
181          * Sets the time to live for the USD session and returns a reference to this Builder
182          * enabling method chaining. Default value is 3000 seconds.
183          *
184          * @param ttlSeconds Time to live in seconds. Value 0 indicating the session does not
185          *                   terminate on its own.
186          * @return a reference to this Builder
187          */
188         @NonNull
setTtlSeconds(@ntRangefrom = 0) int ttlSeconds)189         public Builder setTtlSeconds(@IntRange(from = 0) int ttlSeconds) {
190             if (ttlSeconds < 0) {
191                 throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
192             }
193             mTtlSeconds = ttlSeconds;
194             return this;
195         }
196 
197         /**
198          * Sets the {@code serviceProtoType} and returns a reference to this Builder enabling method
199          * chaining. Supported service protocol is defined as {@code SERVICE_PROTO_TYPE_*}. Default
200          * value is {@link #SERVICE_PROTO_TYPE_GENERIC}.
201          *
202          * @param serviceProtoType the {@code serviceProtoType} to set
203          * @return a reference to this Builder
204          */
205         @NonNull
setServiceProtoType(@erviceProtoType int serviceProtoType)206         public Builder setServiceProtoType(@ServiceProtoType int serviceProtoType) {
207             if (serviceProtoType < SERVICE_PROTO_TYPE_GENERIC
208                     || serviceProtoType > SERVICE_PROTO_TYPE_CSA_MATTER) {
209                 throw new IllegalArgumentException("Invalid serviceProtoType - "
210                         + serviceProtoType);
211             }
212             mServiceProtoType = serviceProtoType;
213             return this;
214         }
215 
216         /**
217          * Sets the {@code txMatchFilter} and returns a reference to this Builder enabling method
218          * chaining. The {@code txMatchFilter} is the ordered sequence of (length, value) pairs to
219          * be included in the subscribe frame. If not set, empty by default.
220          *
221          * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching
222          * filter examples.
223          *
224          * @param txMatchFilter the {@code txMatchFilter} to set
225          * @return a reference to this Builder
226          */
227         @NonNull
setTxMatchFilter(@onNull List<byte[]> txMatchFilter)228         public Builder setTxMatchFilter(@NonNull List<byte[]> txMatchFilter) {
229             Objects.requireNonNull(txMatchFilter, "txMatchFilter must not be null");
230             this.mTxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
231                     txMatchFilter).getArray();
232             if (!TlvBufferUtils.isValid(mTxMatchFilterTlv, 0, 1)) {
233                 throw new IllegalArgumentException(
234                         "Invalid txMatchFilter configuration - LV fields do not match up to "
235                                 + "length");
236             }
237             return this;
238         }
239 
240         /**
241          * Sets the {@code rxMatchFilter} and returns a reference to this Builder enabling method
242          * chaining. The {@code rxMatchFilter} is the ordered sequence of (length, value) pairs
243          * that specify further the matching conditions beyond the service name used to filter
244          * the USD discovery messages. When a subscriber receives a publish message, it matches the
245          * matching filter field in the publish message against its own matching_filter_rx. If not
246          * set, empty by default.
247          *
248          * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching
249          * filter examples.
250          *
251          * @param rxMatchFilter the {@code rxMatchFilter} to set
252          * @return a reference to this Builder
253          */
254         @NonNull
setRxMatchFilter(@onNull List<byte[]> rxMatchFilter)255         public Builder setRxMatchFilter(@NonNull List<byte[]> rxMatchFilter) {
256             Objects.requireNonNull(rxMatchFilter, "rxMatchFilter must not be null");
257             this.mRxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
258                     rxMatchFilter).getArray();
259             if (!TlvBufferUtils.isValid(mRxMatchFilterTlv, 0, 1)) {
260                 throw new IllegalArgumentException(
261                         "Invalid rxMatchFilter configuration - LV fields do not match up to "
262                                 + "length");
263             }
264             return this;
265         }
266 
267         /**
268          * Sets the solicited transmission type and returns a reference to this Builder enabling
269          * method chaining. The type determines whether the transmission is unicast or multicast.
270          * Default is unicast.
271          *
272          * @param solicitedTransmissionType the transmission type {@code TRANSMISSION_TYPE_*} to
273          *                                  set
274          * @return a reference to this Builder
275          */
276         @NonNull
setSolicitedTransmissionType( @ransmissionType int solicitedTransmissionType)277         public Builder setSolicitedTransmissionType(
278                 @TransmissionType int solicitedTransmissionType) {
279             if (solicitedTransmissionType < TRANSMISSION_TYPE_UNICAST
280                     || solicitedTransmissionType > TRANSMISSION_TYPE_MULTICAST) {
281                 throw new IllegalArgumentException("Invalid solicitedTransmissionType - "
282                         + solicitedTransmissionType);
283             }
284             this.mSolicitedTransmissionType = solicitedTransmissionType;
285             return this;
286         }
287 
288         /**
289          * Sets announcement period and returns a reference to this Builder enabling method
290          * chaining. Announcement period is the recommended periodicity of unsolicited
291          * transmissions. Default value is 100 ms.
292          *
293          * @param announcementPeriodMillis announcement period in milliseconds to set
294          * @return a reference to this Builder
295          */
296         @NonNull
setAnnouncementPeriodMillis( @ntRangefrom = 0) int announcementPeriodMillis)297         public Builder setAnnouncementPeriodMillis(
298                 @IntRange(from = 0) int announcementPeriodMillis) {
299             if (announcementPeriodMillis < 0) {
300                 throw new IllegalArgumentException(
301                         "Invalid announcementPeriodMillis - must be non-negative");
302             }
303             this.mAnnouncementPeriodMillis = announcementPeriodMillis;
304             return this;
305         }
306 
307         /**
308          * Enable or disable publish related events and returns a reference to this Builder
309          * enabling method chaining. If enabled, publish replied events are generated on each
310          * solicited transmission. By default, publish replied events are disabled.
311          * See {@link PublishSessionCallback#onPublishReplied(DiscoveryResult)}.
312          *
313          * @param enableEvents the publish related events are enabled
314          * @return a reference to this Builder
315          */
316         @NonNull
setEventsEnabled(boolean enableEvents)317         public Builder setEventsEnabled(boolean enableEvents) {
318             this.mEnableEvents = enableEvents;
319             return this;
320         }
321 
322         /**
323          * Specify service specific information for the publish session. This is a free-form byte
324          * array available to the application to send additional information as part of the
325          * discovery operation - it will not be used to determine whether a publish/subscribe
326          * match occurs. Default value is null;
327          *
328          * Note: Maximum length is limited by
329          * {@link Characteristics#getMaxServiceSpecificInfoLength()}
330          *
331          * @param serviceSpecificInfo A byte-array for the service-specific
332          *                            information field.
333          * @return a reference to this Builder
334          */
335         @NonNull
setServiceSpecificInfo(@onNull byte[] serviceSpecificInfo)336         public Builder setServiceSpecificInfo(@NonNull byte[] serviceSpecificInfo) {
337             Objects.requireNonNull(serviceSpecificInfo, "serviceSpecificInfo must not be null");
338             mServiceSpecificInfo = serviceSpecificInfo.clone();
339             return this;
340         }
341 
342         /**
343          * Sets the operating frequencies used for publish operation. This overrides the default
344          * channel selection for publish. All frequencies have to be 20 Mhz channel in 2.4 Ghz or
345          * 5 Ghz band per regulation in the geographical location. In {@code operatingFrequencies},
346          * <ul>
347          * <li>The first frequency is the channel used for single channel publish.
348          * <li>Any additional frequencies enable multiple channel publish.
349          * </ul>
350          *
351          * <p>If not set or an empty array is provided, the system defaults to 2437 MHz (channel 6
352          * in the 2.4 GHz band) for single channel publish and a list of allowed channels in the 2.4
353          * GHz and 5 GHz bands for multichannel publishing.
354          *
355          * <p>Note: the dwell time for the single and multi publish channels are defined in the
356          * Wifi Aware Specification Version 4, section 4.5.1 Publisher behavior in USD.
357          *
358          * @param operatingFrequencies frequencies used for publish operation
359          * @return a reference to this Builder
360          * @throws IllegalArgumentException if frequencies are invalid or the number frequencies
361          * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory.
362          */
363         @NonNull
setOperatingFrequenciesMhz(@onNull int[] operatingFrequencies)364         public Builder setOperatingFrequenciesMhz(@NonNull int[] operatingFrequencies) {
365             Objects.requireNonNull(operatingFrequencies, "operatingFrequencies must not be null");
366             if ((operatingFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES)
367                     || !WifiNetworkSpecifier.validateChannelFrequencyInMhz(operatingFrequencies)) {
368                 throw new IllegalArgumentException("Invalid operatingFrequencies");
369             }
370             mOperatingFrequencies = operatingFrequencies.clone();
371             return this;
372         }
373 
374         /**
375          * Returns a {@code PublishConfig} built from the parameters previously set.
376          *
377          * @return a {@code PublishConfig} built with parameters of this {@code PublishConfig
378          * .Builder}
379          */
380         @NonNull
build()381         public PublishConfig build() {
382             return new PublishConfig(this);
383         }
384     }
385 }
386