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