Home | History | Annotate | Download | only in aware
      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.IntDef;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 
     25 import libcore.util.HexEncoding;
     26 
     27 import java.lang.annotation.Retention;
     28 import java.lang.annotation.RetentionPolicy;
     29 import java.nio.charset.StandardCharsets;
     30 import java.util.Arrays;
     31 import java.util.List;
     32 import java.util.Objects;
     33 
     34 /**
     35  * Defines the configuration of a Aware publish session. Built using
     36  * {@link PublishConfig.Builder}. A publish session is created using
     37  * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback,
     38  * android.os.Handler)} or updated using
     39  * {@link PublishDiscoverySession#updatePublish(PublishConfig)}.
     40  */
     41 public final class PublishConfig implements Parcelable {
     42     /** @hide */
     43     @IntDef({
     44             PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED })
     45     @Retention(RetentionPolicy.SOURCE)
     46     public @interface PublishTypes {
     47     }
     48 
     49     /**
     50      * Defines an unsolicited publish session - a publish session where the publisher is
     51      * advertising itself by broadcasting on-the-air. An unsolicited publish session is paired
     52      * with an passive subscribe session {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE}.
     53      * Configuration is done using {@link PublishConfig.Builder#setPublishType(int)}.
     54      */
     55     public static final int PUBLISH_TYPE_UNSOLICITED = 0;
     56 
     57     /**
     58      * Defines a solicited publish session - a publish session which is silent, waiting for a
     59      * matching active subscribe session - and responding to it in unicast. A
     60      * solicited publish session is paired with an active subscribe session
     61      * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE}. Configuration is done using
     62      * {@link PublishConfig.Builder#setPublishType(int)}.
     63      */
     64     public static final int PUBLISH_TYPE_SOLICITED = 1;
     65 
     66     /** @hide */
     67     public final byte[] mServiceName;
     68 
     69     /** @hide */
     70     public final byte[] mServiceSpecificInfo;
     71 
     72     /** @hide */
     73     public final byte[] mMatchFilter;
     74 
     75     /** @hide */
     76     public final int mPublishType;
     77 
     78     /** @hide */
     79     public final int mTtlSec;
     80 
     81     /** @hide */
     82     public final boolean mEnableTerminateNotification;
     83 
     84     /** @hide */
     85     public final boolean mEnableRanging;
     86 
     87     /** @hide */
     88     public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
     89             int publishType, int ttlSec, boolean enableTerminateNotification,
     90             boolean enableRanging) {
     91         mServiceName = serviceName;
     92         mServiceSpecificInfo = serviceSpecificInfo;
     93         mMatchFilter = matchFilter;
     94         mPublishType = publishType;
     95         mTtlSec = ttlSec;
     96         mEnableTerminateNotification = enableTerminateNotification;
     97         mEnableRanging = enableRanging;
     98     }
     99 
    100     @Override
    101     public String toString() {
    102         return "PublishConfig [mServiceName='" + (mServiceName == null ? "<null>" : String.valueOf(
    103                 HexEncoding.encode(mServiceName))) + ", mServiceName.length=" + (
    104                 mServiceName == null ? 0 : mServiceName.length) + ", mServiceSpecificInfo='" + (
    105                 (mServiceSpecificInfo == null) ? "<null>" : String.valueOf(
    106                         HexEncoding.encode(mServiceSpecificInfo)))
    107                 + ", mServiceSpecificInfo.length=" + (mServiceSpecificInfo == null ? 0
    108                 : mServiceSpecificInfo.length) + ", mMatchFilter="
    109                 + (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
    110                 + ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
    111                 + ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
    112                 + ", mEnableTerminateNotification=" + mEnableTerminateNotification
    113                 + ", mEnableRanging=" + mEnableRanging + "]";
    114     }
    115 
    116     @Override
    117     public int describeContents() {
    118         return 0;
    119     }
    120 
    121     @Override
    122     public void writeToParcel(Parcel dest, int flags) {
    123         dest.writeByteArray(mServiceName);
    124         dest.writeByteArray(mServiceSpecificInfo);
    125         dest.writeByteArray(mMatchFilter);
    126         dest.writeInt(mPublishType);
    127         dest.writeInt(mTtlSec);
    128         dest.writeInt(mEnableTerminateNotification ? 1 : 0);
    129         dest.writeInt(mEnableRanging ? 1 : 0);
    130     }
    131 
    132     public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
    133         @Override
    134         public PublishConfig[] newArray(int size) {
    135             return new PublishConfig[size];
    136         }
    137 
    138         @Override
    139         public PublishConfig createFromParcel(Parcel in) {
    140             byte[] serviceName = in.createByteArray();
    141             byte[] ssi = in.createByteArray();
    142             byte[] matchFilter = in.createByteArray();
    143             int publishType = in.readInt();
    144             int ttlSec = in.readInt();
    145             boolean enableTerminateNotification = in.readInt() != 0;
    146             boolean enableRanging = in.readInt() != 0;
    147 
    148             return new PublishConfig(serviceName, ssi, matchFilter, publishType,
    149                     ttlSec, enableTerminateNotification, enableRanging);
    150         }
    151     };
    152 
    153     @Override
    154     public boolean equals(Object o) {
    155         if (this == o) {
    156             return true;
    157         }
    158 
    159         if (!(o instanceof PublishConfig)) {
    160             return false;
    161         }
    162 
    163         PublishConfig lhs = (PublishConfig) o;
    164 
    165         return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
    166                 lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
    167                 && mPublishType == lhs.mPublishType
    168                 && mTtlSec == lhs.mTtlSec
    169                 && mEnableTerminateNotification == lhs.mEnableTerminateNotification
    170                 && mEnableRanging == lhs.mEnableRanging;
    171     }
    172 
    173     @Override
    174     public int hashCode() {
    175         return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec,
    176                 mEnableTerminateNotification, mEnableRanging);
    177     }
    178 
    179     /**
    180      * Verifies that the contents of the PublishConfig are valid. Otherwise
    181      * throws an IllegalArgumentException.
    182      *
    183      * @hide
    184      */
    185     public void assertValid(Characteristics characteristics, boolean rttSupported)
    186             throws IllegalArgumentException {
    187         WifiAwareUtils.validateServiceName(mServiceName);
    188 
    189         if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
    190             throw new IllegalArgumentException(
    191                     "Invalid txFilter configuration - LV fields do not match up to length");
    192         }
    193         if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) {
    194             throw new IllegalArgumentException("Invalid publishType - " + mPublishType);
    195         }
    196         if (mTtlSec < 0) {
    197             throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
    198         }
    199 
    200         if (characteristics != null) {
    201             int maxServiceNameLength = characteristics.getMaxServiceNameLength();
    202             if (maxServiceNameLength != 0 && mServiceName.length > maxServiceNameLength) {
    203                 throw new IllegalArgumentException(
    204                         "Service name longer than supported by device characteristics");
    205             }
    206             int maxServiceSpecificInfoLength = characteristics.getMaxServiceSpecificInfoLength();
    207             if (maxServiceSpecificInfoLength != 0 && mServiceSpecificInfo != null
    208                     && mServiceSpecificInfo.length > maxServiceSpecificInfoLength) {
    209                 throw new IllegalArgumentException(
    210                         "Service specific info longer than supported by device characteristics");
    211             }
    212             int maxMatchFilterLength = characteristics.getMaxMatchFilterLength();
    213             if (maxMatchFilterLength != 0 && mMatchFilter != null
    214                     && mMatchFilter.length > maxMatchFilterLength) {
    215                 throw new IllegalArgumentException(
    216                         "Match filter longer than supported by device characteristics");
    217             }
    218         }
    219 
    220         if (!rttSupported && mEnableRanging) {
    221             throw new IllegalArgumentException("Ranging is not supported");
    222         }
    223     }
    224 
    225     /**
    226      * Builder used to build {@link PublishConfig} objects.
    227      */
    228     public static final class Builder {
    229         private byte[] mServiceName;
    230         private byte[] mServiceSpecificInfo;
    231         private byte[] mMatchFilter;
    232         private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
    233         private int mTtlSec = 0;
    234         private boolean mEnableTerminateNotification = true;
    235         private boolean mEnableRanging = false;
    236 
    237         /**
    238          * Specify the service name of the publish session. The actual on-air
    239          * value is a 6 byte hashed representation of this string.
    240          * <p>
    241          * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length.
    242          * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric
    243          * values (A-Z, a-z, 0-9), the hyphen ('-'), and the period ('.'). All valid multi-byte
    244          * UTF-8 characters are acceptable in a Service Name.
    245          * <p>
    246          * Must be called - an empty ServiceName is not valid.
    247          *
    248          * @param serviceName The service name for the publish session.
    249          *
    250          * @return The builder to facilitate chaining
    251          *         {@code builder.setXXX(..).setXXX(..)}.
    252          */
    253         public Builder setServiceName(@NonNull String serviceName) {
    254             if (serviceName == null) {
    255                 throw new IllegalArgumentException("Invalid service name - must be non-null");
    256             }
    257             mServiceName = serviceName.getBytes(StandardCharsets.UTF_8);
    258             return this;
    259         }
    260 
    261         /**
    262          * Specify service specific information for the publish session. This is
    263          * a free-form byte array available to the application to send
    264          * additional information as part of the discovery operation - it
    265          * will not be used to determine whether a publish/subscribe match
    266          * occurs.
    267          * <p>
    268          *     Optional. Empty by default.
    269          *
    270          * @param serviceSpecificInfo A byte-array for the service-specific
    271          *            information field.
    272          *
    273          * @return The builder to facilitate chaining
    274          *         {@code builder.setXXX(..).setXXX(..)}.
    275          */
    276         public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
    277             mServiceSpecificInfo = serviceSpecificInfo;
    278             return this;
    279         }
    280 
    281         /**
    282          * The match filter for a publish session. Used to determine whether a service
    283          * discovery occurred - in addition to relying on the service name.
    284          * <p>
    285          *     Optional. Empty by default.
    286          *
    287          * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
    288          *                    array).
    289          *
    290          * @return The builder to facilitate chaining
    291          *         {@code builder.setXXX(..).setXXX(..)}.
    292          */
    293         public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
    294             mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
    295                     matchFilter).getArray();
    296             return this;
    297         }
    298 
    299         /**
    300          * Specify the type of the publish session: solicited (aka active - publish
    301          * packets are transmitted over-the-air), or unsolicited (aka passive -
    302          * no publish packets are transmitted, a match is made against an active
    303          * subscribe session whose packets are transmitted over-the-air).
    304          *
    305          * @param publishType Publish session type:
    306          *            {@link PublishConfig#PUBLISH_TYPE_SOLICITED} or
    307          *            {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED} (the default).
    308          *
    309          * @return The builder to facilitate chaining
    310          *         {@code builder.setXXX(..).setXXX(..)}.
    311          */
    312         public Builder setPublishType(@PublishTypes int publishType) {
    313             if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
    314                 throw new IllegalArgumentException("Invalid publishType - " + publishType);
    315             }
    316             mPublishType = publishType;
    317             return this;
    318         }
    319 
    320         /**
    321          * Sets the time interval (in seconds) an unsolicited (
    322          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
    323          * will be alive - broadcasting a packet. When the TTL is reached
    324          * an event will be generated for
    325          * {@link DiscoverySessionCallback#onSessionTerminated()} [unless
    326          * {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
    327          * <p>
    328          *     Optional. 0 by default - indicating the session doesn't terminate on its own.
    329          *     Session will be terminated when {@link DiscoverySession#close()} is
    330          *     called.
    331          *
    332          * @param ttlSec Lifetime of a publish session in seconds.
    333          *
    334          * @return The builder to facilitate chaining
    335          *         {@code builder.setXXX(..).setXXX(..)}.
    336          */
    337         public Builder setTtlSec(int ttlSec) {
    338             if (ttlSec < 0) {
    339                 throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
    340             }
    341             mTtlSec = ttlSec;
    342             return this;
    343         }
    344 
    345         /**
    346          * Configure whether a publish terminate notification
    347          * {@link DiscoverySessionCallback#onSessionTerminated()} is reported
    348          * back to the callback.
    349          *
    350          * @param enable If true the terminate callback will be called when the
    351          *            publish is terminated. Otherwise it will not be called.
    352          *
    353          * @return The builder to facilitate chaining
    354          *         {@code builder.setXXX(..).setXXX(..)}.
    355          */
    356         public Builder setTerminateNotificationEnabled(boolean enable) {
    357             mEnableTerminateNotification = enable;
    358             return this;
    359         }
    360 
    361         /**
    362          * Configure whether the publish discovery session supports ranging and allows peers to
    363          * measure distance to it. This API is used in conjunction with
    364          * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
    365          * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
    366          * maximum distance at which discovery will be triggered.
    367          * <p>
    368          * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device
    369          * will be refused and discovery will proceed without ranging constraints.
    370          * <p>
    371          * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
    372          * as described in {@link android.net.wifi.rtt}.
    373          *
    374          * @param enable If true, ranging is supported on request of the peer.
    375          *
    376          * @return The builder to facilitate chaining
    377          *         {@code builder.setXXX(..).setXXX(..)}.
    378          */
    379         public Builder setRangingEnabled(boolean enable) {
    380             mEnableRanging = enable;
    381             return this;
    382         }
    383 
    384         /**
    385          * Build {@link PublishConfig} given the current requests made on the
    386          * builder.
    387          */
    388         public PublishConfig build() {
    389             return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
    390                     mTtlSec, mEnableTerminateNotification, mEnableRanging);
    391         }
    392     }
    393 }
    394