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