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