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