/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.wifi.aware;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

import libcore.util.HexEncoding;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

Defines the configuration of a Aware publish session. Built using Builder. A publish session is created using WifiAwareSession.publish(PublishConfig, DiscoverySessionCallback, Handler) or updated using PublishDiscoverySession.updatePublish(PublishConfig).
/** * Defines the configuration of a Aware publish session. Built using * {@link PublishConfig.Builder}. A publish session is created using * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, * android.os.Handler)} or updated using * {@link PublishDiscoverySession#updatePublish(PublishConfig)}. */
public final class PublishConfig implements Parcelable {
@hide
/** @hide */
@IntDef({ PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED }) @Retention(RetentionPolicy.SOURCE) public @interface PublishTypes { }
Defines an unsolicited publish session - a publish session where the publisher is advertising itself by broadcasting on-the-air. An unsolicited publish session is paired with an passive subscribe session SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE. Configuration is done using Builder.setPublishType(int).
/** * Defines an unsolicited publish session - a publish session where the publisher is * advertising itself by broadcasting on-the-air. An unsolicited publish session is paired * with an passive subscribe session {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE}. * Configuration is done using {@link PublishConfig.Builder#setPublishType(int)}. */
public static final int PUBLISH_TYPE_UNSOLICITED = 0;
Defines a solicited publish session - a publish session which is silent, waiting for a matching active subscribe session - and responding to it in unicast. A solicited publish session is paired with an active subscribe session SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE. Configuration is done using Builder.setPublishType(int).
/** * Defines a solicited publish session - a publish session which is silent, waiting for a * matching active subscribe session - and responding to it in unicast. A * solicited publish session is paired with an active subscribe session * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE}. Configuration is done using * {@link PublishConfig.Builder#setPublishType(int)}. */
public static final int PUBLISH_TYPE_SOLICITED = 1;
@hide
/** @hide */
public final byte[] mServiceName;
@hide
/** @hide */
public final byte[] mServiceSpecificInfo;
@hide
/** @hide */
public final byte[] mMatchFilter;
@hide
/** @hide */
public final int mPublishType;
@hide
/** @hide */
public final int mTtlSec;
@hide
/** @hide */
public final boolean mEnableTerminateNotification;
@hide
/** @hide */
public final boolean mEnableRanging;
@hide
/** @hide */
public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int publishType, int ttlSec, boolean enableTerminateNotification, boolean enableRanging) { mServiceName = serviceName; mServiceSpecificInfo = serviceSpecificInfo; mMatchFilter = matchFilter; mPublishType = publishType; mTtlSec = ttlSec; mEnableTerminateNotification = enableTerminateNotification; mEnableRanging = enableRanging; } @Override public String toString() { return "PublishConfig [mServiceName='" + (mServiceName == null ? "<null>" : String.valueOf( HexEncoding.encode(mServiceName))) + ", mServiceName.length=" + ( mServiceName == null ? 0 : mServiceName.length) + ", mServiceSpecificInfo='" + ( (mServiceSpecificInfo == null) ? "<null>" : String.valueOf( HexEncoding.encode(mServiceSpecificInfo))) + ", mServiceSpecificInfo.length=" + (mServiceSpecificInfo == null ? 0 : mServiceSpecificInfo.length) + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString() + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length) + ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification=" + mEnableTerminateNotification + ", mEnableRanging=" + mEnableRanging + "]"; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(mServiceName); dest.writeByteArray(mServiceSpecificInfo); dest.writeByteArray(mMatchFilter); dest.writeInt(mPublishType); dest.writeInt(mTtlSec); dest.writeInt(mEnableTerminateNotification ? 1 : 0); dest.writeInt(mEnableRanging ? 1 : 0); } public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() { @Override public PublishConfig[] newArray(int size) { return new PublishConfig[size]; } @Override public PublishConfig createFromParcel(Parcel in) { byte[] serviceName = in.createByteArray(); byte[] ssi = in.createByteArray(); byte[] matchFilter = in.createByteArray(); int publishType = in.readInt(); int ttlSec = in.readInt(); boolean enableTerminateNotification = in.readInt() != 0; boolean enableRanging = in.readInt() != 0; return new PublishConfig(serviceName, ssi, matchFilter, publishType, ttlSec, enableTerminateNotification, enableRanging); } }; @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof PublishConfig)) { return false; } PublishConfig lhs = (PublishConfig) o; return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo, lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter) && mPublishType == lhs.mPublishType && mTtlSec == lhs.mTtlSec && mEnableTerminateNotification == lhs.mEnableTerminateNotification && mEnableRanging == lhs.mEnableRanging; } @Override public int hashCode() { return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec, mEnableTerminateNotification, mEnableRanging); }
Verifies that the contents of the PublishConfig are valid. Otherwise throws an IllegalArgumentException.
@hide
/** * Verifies that the contents of the PublishConfig are valid. Otherwise * throws an IllegalArgumentException. * * @hide */
public void assertValid(Characteristics characteristics, boolean rttSupported) throws IllegalArgumentException { WifiAwareUtils.validateServiceName(mServiceName); if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) { throw new IllegalArgumentException( "Invalid txFilter configuration - LV fields do not match up to length"); } if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) { throw new IllegalArgumentException("Invalid publishType - " + mPublishType); } if (mTtlSec < 0) { throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); } if (characteristics != null) { int maxServiceNameLength = characteristics.getMaxServiceNameLength(); if (maxServiceNameLength != 0 && mServiceName.length > maxServiceNameLength) { throw new IllegalArgumentException( "Service name longer than supported by device characteristics"); } int maxServiceSpecificInfoLength = characteristics.getMaxServiceSpecificInfoLength(); if (maxServiceSpecificInfoLength != 0 && mServiceSpecificInfo != null && mServiceSpecificInfo.length > maxServiceSpecificInfoLength) { throw new IllegalArgumentException( "Service specific info longer than supported by device characteristics"); } int maxMatchFilterLength = characteristics.getMaxMatchFilterLength(); if (maxMatchFilterLength != 0 && mMatchFilter != null && mMatchFilter.length > maxMatchFilterLength) { throw new IllegalArgumentException( "Match filter longer than supported by device characteristics"); } } if (!rttSupported && mEnableRanging) { throw new IllegalArgumentException("Ranging is not supported"); } }
Builder used to build PublishConfig objects.
/** * Builder used to build {@link PublishConfig} objects. */
public static final class Builder { private byte[] mServiceName; private byte[] mServiceSpecificInfo; private byte[] mMatchFilter; private int mPublishType = PUBLISH_TYPE_UNSOLICITED; private int mTtlSec = 0; private boolean mEnableTerminateNotification = true; private boolean mEnableRanging = false;
Specify the service name of the publish session. The actual on-air value is a 6 byte hashed representation of this string.

The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric values (A-Z, a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte UTF-8 characters are acceptable in a Service Name.

Must be called - an empty ServiceName is not valid.

Params:
  • serviceName – The service name for the publish session.
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Specify the service name of the publish session. The actual on-air * value is a 6 byte hashed representation of this string. * <p> * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric * values (A-Z, a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte * UTF-8 characters are acceptable in a Service Name. * <p> * Must be called - an empty ServiceName is not valid. * * @param serviceName The service name for the publish session. * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setServiceName(@NonNull String serviceName) { if (serviceName == null) { throw new IllegalArgumentException("Invalid service name - must be non-null"); } mServiceName = serviceName.getBytes(StandardCharsets.UTF_8); return this; }
Specify service specific information for the publish session. This is a free-form byte array available to the application to send additional information as part of the discovery operation - it will not be used to determine whether a publish/subscribe match occurs.

Optional. Empty by default.

Params:
  • serviceSpecificInfo – A byte-array for the service-specific information field.
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Specify service specific information for the publish session. This is * a free-form byte array available to the application to send * additional information as part of the discovery operation - it * will not be used to determine whether a publish/subscribe match * occurs. * <p> * Optional. Empty by default. * * @param serviceSpecificInfo A byte-array for the service-specific * information field. * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) { mServiceSpecificInfo = serviceSpecificInfo; return this; }
The match filter for a publish session. Used to determine whether a service discovery occurred - in addition to relying on the service name.

Optional. Empty by default.

Params:
  • matchFilter – A list of match filter entries (each of which is an arbitrary byte array).
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * The match filter for a publish session. Used to determine whether a service * discovery occurred - in addition to relying on the service name. * <p> * Optional. Empty by default. * * @param matchFilter A list of match filter entries (each of which is an arbitrary byte * array). * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) { mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( matchFilter).getArray(); return this; }
Specify the type of the publish session: solicited (aka active - publish packets are transmitted over-the-air), or unsolicited (aka passive - no publish packets are transmitted, a match is made against an active subscribe session whose packets are transmitted over-the-air).
Params:
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Specify the type of the publish session: solicited (aka active - publish * packets are transmitted over-the-air), or unsolicited (aka passive - * no publish packets are transmitted, a match is made against an active * subscribe session whose packets are transmitted over-the-air). * * @param publishType Publish session type: * {@link PublishConfig#PUBLISH_TYPE_SOLICITED} or * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED} (the default). * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setPublishType(@PublishTypes int publishType) { if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) { throw new IllegalArgumentException("Invalid publishType - " + publishType); } mPublishType = publishType; return this; }
Sets the time interval (in seconds) an unsolicited ( setPublishType(int)) publish session will be alive - broadcasting a packet. When the TTL is reached an event will be generated for DiscoverySessionCallback.onSessionTerminated() [unless setTerminateNotificationEnabled(boolean) disables the callback].

Optional. 0 by default - indicating the session doesn't terminate on its own. Session will be terminated when DiscoverySession.close() is called.

Params:
  • ttlSec – Lifetime of a publish session in seconds.
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Sets the time interval (in seconds) an unsolicited ( * {@link PublishConfig.Builder#setPublishType(int)}) publish session * will be alive - broadcasting a packet. When the TTL is reached * an event will be generated for * {@link DiscoverySessionCallback#onSessionTerminated()} [unless * {@link #setTerminateNotificationEnabled(boolean)} disables the callback]. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. * Session will be terminated when {@link DiscoverySession#close()} is * called. * * @param ttlSec Lifetime of a publish session in seconds. * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setTtlSec(int ttlSec) { if (ttlSec < 0) { throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); } mTtlSec = ttlSec; return this; }
Configure whether a publish terminate notification DiscoverySessionCallback.onSessionTerminated() is reported back to the callback.
Params:
  • enable – If true the terminate callback will be called when the publish is terminated. Otherwise it will not be called.
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Configure whether a publish terminate notification * {@link DiscoverySessionCallback#onSessionTerminated()} is reported * back to the callback. * * @param enable If true the terminate callback will be called when the * publish is terminated. Otherwise it will not be called. * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setTerminateNotificationEnabled(boolean enable) { mEnableTerminateNotification = enable; return this; }
Configure whether the publish discovery session supports ranging and allows peers to measure distance to it. This API is used in conjunction with Builder.setMinDistanceMm(int) and Builder.setMaxDistanceMm(int) to specify a minimum and/or maximum distance at which discovery will be triggered.

Optional. Disabled by default - i.e. any peer attempt to measure distance to this device will be refused and discovery will proceed without ranging constraints.

The device must support Wi-Fi RTT for this feature to be used. Feature support is checked as described in rtt.

Params:
  • enable – If true, ranging is supported on request of the peer.
Returns:The builder to facilitate chaining builder.setXXX(..).setXXX(..).
/** * Configure whether the publish discovery session supports ranging and allows peers to * measure distance to it. This API is used in conjunction with * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or * maximum distance at which discovery will be triggered. * <p> * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device * will be refused and discovery will proceed without ranging constraints. * <p> * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked * as described in {@link android.net.wifi.rtt}. * * @param enable If true, ranging is supported on request of the peer. * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */
public Builder setRangingEnabled(boolean enable) { mEnableRanging = enable; return this; }
Build PublishConfig given the current requests made on the builder.
/** * Build {@link PublishConfig} given the current requests made on the * builder. */
public PublishConfig build() { return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec, mEnableTerminateNotification, mEnableRanging); } } }