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