• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.bluetooth.le;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.ParcelUuid;
23 import android.os.Parcelable;
24 import android.util.ArrayMap;
25 import android.util.SparseArray;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 
33 /**
34  * Advertise data packet container for Bluetooth LE advertising. This represents the data to be
35  * advertised as well as the scan response data for active scans.
36  * <p>
37  * Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be
38  * advertised.
39  *
40  * @see BluetoothLeAdvertiser
41  * @see ScanRecord
42  */
43 public final class AdvertiseData implements Parcelable {
44 
45     @Nullable
46     private final List<ParcelUuid> mServiceUuids;
47 
48     @NonNull
49     private final List<ParcelUuid> mServiceSolicitationUuids;
50 
51     @Nullable
52     private final List<TransportDiscoveryData> mTransportDiscoveryData;
53 
54     private final SparseArray<byte[]> mManufacturerSpecificData;
55     private final Map<ParcelUuid, byte[]> mServiceData;
56     private final boolean mIncludeTxPowerLevel;
57     private final boolean mIncludeDeviceName;
58 
AdvertiseData(List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceSolicitationUuids, List<TransportDiscoveryData> transportDiscoveryData, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, boolean includeTxPowerLevel, boolean includeDeviceName)59     private AdvertiseData(List<ParcelUuid> serviceUuids,
60             List<ParcelUuid> serviceSolicitationUuids,
61             List<TransportDiscoveryData> transportDiscoveryData,
62             SparseArray<byte[]> manufacturerData,
63             Map<ParcelUuid, byte[]> serviceData,
64             boolean includeTxPowerLevel,
65             boolean includeDeviceName) {
66         mServiceUuids = serviceUuids;
67         mServiceSolicitationUuids = serviceSolicitationUuids;
68         mTransportDiscoveryData = transportDiscoveryData;
69         mManufacturerSpecificData = manufacturerData;
70         mServiceData = serviceData;
71         mIncludeTxPowerLevel = includeTxPowerLevel;
72         mIncludeDeviceName = includeDeviceName;
73     }
74 
75     /**
76      * Returns a list of service UUIDs within the advertisement that are used to identify the
77      * Bluetooth GATT services.
78      */
getServiceUuids()79     public List<ParcelUuid> getServiceUuids() {
80         return mServiceUuids;
81     }
82 
83     /**
84      * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
85      */
86     @NonNull
getServiceSolicitationUuids()87     public List<ParcelUuid> getServiceSolicitationUuids() {
88         return mServiceSolicitationUuids;
89     }
90 
91     /**
92      * Returns a list of {@link TransportDiscoveryData} within the advertisement.
93      */
94     @NonNull
getTransportDiscoveryData()95     public List<TransportDiscoveryData> getTransportDiscoveryData() {
96         if (mTransportDiscoveryData == null) {
97             return Collections.emptyList();
98         }
99         return mTransportDiscoveryData;
100     }
101 
102     /**
103      * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The
104      * manufacturer id is a non-negative number assigned by Bluetooth SIG.
105      */
getManufacturerSpecificData()106     public SparseArray<byte[]> getManufacturerSpecificData() {
107         return mManufacturerSpecificData;
108     }
109 
110     /**
111      * Returns a map of 16-bit UUID and its corresponding service data.
112      */
getServiceData()113     public Map<ParcelUuid, byte[]> getServiceData() {
114         return mServiceData;
115     }
116 
117     /**
118      * Whether the transmission power level will be included in the advertisement packet.
119      */
getIncludeTxPowerLevel()120     public boolean getIncludeTxPowerLevel() {
121         return mIncludeTxPowerLevel;
122     }
123 
124     /**
125      * Whether the device name will be included in the advertisement packet.
126      */
getIncludeDeviceName()127     public boolean getIncludeDeviceName() {
128         return mIncludeDeviceName;
129     }
130 
131     /**
132      * @hide
133      */
134     @Override
hashCode()135     public int hashCode() {
136         return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData,
137                 mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
138     }
139 
140     /**
141      * @hide
142      */
143     @Override
equals(@ullable Object obj)144     public boolean equals(@Nullable Object obj) {
145         if (this == obj) {
146             return true;
147         }
148         if (obj == null || getClass() != obj.getClass()) {
149             return false;
150         }
151         AdvertiseData other = (AdvertiseData) obj;
152         return Objects.equals(mServiceUuids, other.mServiceUuids)
153                 && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids)
154                 && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData)
155                 && BluetoothLeUtils.equals(mManufacturerSpecificData,
156                     other.mManufacturerSpecificData)
157                 && BluetoothLeUtils.equals(mServiceData, other.mServiceData)
158                 && mIncludeDeviceName == other.mIncludeDeviceName
159                 && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel;
160     }
161 
162     @Override
toString()163     public String toString() {
164         return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids="
165                 + mServiceSolicitationUuids + ", mTransportDiscoveryData="
166                 + mTransportDiscoveryData + ", mManufacturerSpecificData="
167                 + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData="
168                 + BluetoothLeUtils.toString(mServiceData)
169                 + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
170                 + mIncludeDeviceName + "]";
171     }
172 
173     @Override
describeContents()174     public int describeContents() {
175         return 0;
176     }
177 
178     @Override
writeToParcel(Parcel dest, int flags)179     public void writeToParcel(Parcel dest, int flags) {
180         dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags);
181         dest.writeTypedArray(mServiceSolicitationUuids.toArray(
182                 new ParcelUuid[mServiceSolicitationUuids.size()]), flags);
183 
184         dest.writeTypedList(mTransportDiscoveryData);
185 
186         // mManufacturerSpecificData could not be null.
187         dest.writeInt(mManufacturerSpecificData.size());
188         for (int i = 0; i < mManufacturerSpecificData.size(); ++i) {
189             dest.writeInt(mManufacturerSpecificData.keyAt(i));
190             dest.writeByteArray(mManufacturerSpecificData.valueAt(i));
191         }
192         dest.writeInt(mServiceData.size());
193         for (ParcelUuid uuid : mServiceData.keySet()) {
194             dest.writeTypedObject(uuid, flags);
195             dest.writeByteArray(mServiceData.get(uuid));
196         }
197         dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
198         dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
199     }
200 
201     public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseData> CREATOR =
202             new Creator<AdvertiseData>() {
203                 @Override
204                 public AdvertiseData[] newArray(int size) {
205                     return new AdvertiseData[size];
206                 }
207 
208                 @Override
209                 public AdvertiseData createFromParcel(Parcel in) {
210                     Builder builder = new Builder();
211                     ArrayList<ParcelUuid> uuids = in.createTypedArrayList(ParcelUuid.CREATOR);
212                     for (ParcelUuid uuid : uuids) {
213                         builder.addServiceUuid(uuid);
214                     }
215 
216                     ArrayList<ParcelUuid> solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR);
217                     for (ParcelUuid uuid : solicitationUuids) {
218                         builder.addServiceSolicitationUuid(uuid);
219                     }
220 
221                     List<TransportDiscoveryData> transportDiscoveryData =
222                             in.createTypedArrayList(TransportDiscoveryData.CREATOR);
223                     for (TransportDiscoveryData tdd : transportDiscoveryData) {
224                         builder.addTransportDiscoveryData(tdd);
225                     }
226 
227                     int manufacturerSize = in.readInt();
228                     for (int i = 0; i < manufacturerSize; ++i) {
229                         int manufacturerId = in.readInt();
230                         byte[] manufacturerData = in.createByteArray();
231                         builder.addManufacturerData(manufacturerId, manufacturerData);
232                     }
233                     int serviceDataSize = in.readInt();
234                     for (int i = 0; i < serviceDataSize; ++i) {
235                         ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR);
236                         byte[] serviceData = in.createByteArray();
237                         builder.addServiceData(serviceDataUuid, serviceData);
238                     }
239                     builder.setIncludeTxPowerLevel(in.readByte() == 1);
240                     builder.setIncludeDeviceName(in.readByte() == 1);
241                     return builder.build();
242                 }
243             };
244 
245     /**
246      * Builder for {@link AdvertiseData}.
247      */
248     public static final class Builder {
249         @Nullable
250         private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
251         @NonNull
252         private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
253         @Nullable
254         private List<TransportDiscoveryData> mTransportDiscoveryData =
255                 new ArrayList<TransportDiscoveryData>();
256         private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
257         private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
258         private boolean mIncludeTxPowerLevel;
259         private boolean mIncludeDeviceName;
260 
261         /**
262          * Add a service UUID to advertise data.
263          *
264          * @param serviceUuid A service UUID to be advertised.
265          * @throws IllegalArgumentException If the {@code serviceUuid} is null.
266          */
addServiceUuid(ParcelUuid serviceUuid)267         public Builder addServiceUuid(ParcelUuid serviceUuid) {
268             if (serviceUuid == null) {
269                 throw new IllegalArgumentException("serviceUuid is null");
270             }
271             mServiceUuids.add(serviceUuid);
272             return this;
273         }
274 
275         /**
276          * Add a service solicitation UUID to advertise data.
277          *
278          * @param serviceSolicitationUuid A service solicitation UUID to be advertised.
279          * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null.
280          */
281         @NonNull
addServiceSolicitationUuid(@onNull ParcelUuid serviceSolicitationUuid)282         public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) {
283             if (serviceSolicitationUuid == null) {
284                 throw new IllegalArgumentException("serviceSolicitationUuid is null");
285             }
286             mServiceSolicitationUuids.add(serviceSolicitationUuid);
287             return this;
288         }
289 
290         /**
291          * Add service data to advertise data.
292          *
293          * @param serviceDataUuid 16-bit UUID of the service the data is associated with
294          * @param serviceData Service data
295          * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
296          * empty.
297          */
addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData)298         public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
299             if (serviceDataUuid == null || serviceData == null) {
300                 throw new IllegalArgumentException(
301                         "serviceDataUuid or serviceDataUuid is null");
302             }
303             mServiceData.put(serviceDataUuid, serviceData);
304             return this;
305         }
306 
307         /**
308          * Add Transport Discovery Data to advertise data.
309          *
310          * @param transportDiscoveryData Transport Discovery Data, consisting of one or more
311          * Transport Blocks. Transport Discovery Data AD Type Code is already included.
312          * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty
313          */
314         @NonNull
addTransportDiscoveryData( @onNull TransportDiscoveryData transportDiscoveryData)315         public Builder addTransportDiscoveryData(
316                 @NonNull TransportDiscoveryData transportDiscoveryData) {
317             if (transportDiscoveryData == null) {
318                 throw new IllegalArgumentException("transportDiscoveryData is null");
319             }
320             mTransportDiscoveryData.add(transportDiscoveryData);
321             return this;
322         }
323 
324         /**
325          * Add manufacturer specific data.
326          * <p>
327          * Please refer to the Bluetooth Assigned Numbers document provided by the <a
328          * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company
329          * identifiers.
330          *
331          * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG.
332          * @param manufacturerSpecificData Manufacturer specific data
333          * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code
334          * manufacturerSpecificData} is null.
335          */
addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData)336         public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
337             if (manufacturerId < 0) {
338                 throw new IllegalArgumentException(
339                         "invalid manufacturerId - " + manufacturerId);
340             }
341             if (manufacturerSpecificData == null) {
342                 throw new IllegalArgumentException("manufacturerSpecificData is null");
343             }
344             mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData);
345             return this;
346         }
347 
348         /**
349          * Whether the transmission power level should be included in the advertise packet. Tx power
350          * level field takes 3 bytes in advertise packet.
351          */
setIncludeTxPowerLevel(boolean includeTxPowerLevel)352         public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) {
353             mIncludeTxPowerLevel = includeTxPowerLevel;
354             return this;
355         }
356 
357         /**
358          * Set whether the device name should be included in advertise packet.
359          */
setIncludeDeviceName(boolean includeDeviceName)360         public Builder setIncludeDeviceName(boolean includeDeviceName) {
361             mIncludeDeviceName = includeDeviceName;
362             return this;
363         }
364 
365         /**
366          * Build the {@link AdvertiseData}.
367          */
build()368         public AdvertiseData build() {
369             return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids,
370                     mTransportDiscoveryData, mManufacturerSpecificData, mServiceData,
371                     mIncludeTxPowerLevel, mIncludeDeviceName);
372         }
373     }
374 }
375