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 17package android.bluetooth.le; 18 19import android.annotation.Nullable; 20import android.os.Parcel; 21import android.os.ParcelUuid; 22import android.os.Parcelable; 23import android.util.ArrayMap; 24import android.util.SparseArray; 25 26import java.util.ArrayList; 27import java.util.List; 28import java.util.Map; 29import 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 */ 41public 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 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 */ 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 */ 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 */ 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 */ 89 public boolean getIncludeTxPowerLevel() { 90 return mIncludeTxPowerLevel; 91 } 92 93 /** 94 * Whether the device name will be included in the advertisement packet. 95 */ 96 public boolean getIncludeDeviceName() { 97 return mIncludeDeviceName; 98 } 99 100 /** 101 * @hide 102 */ 103 @Override 104 public int hashCode() { 105 return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, 106 mIncludeDeviceName, mIncludeTxPowerLevel); 107 } 108 109 /** 110 * @hide 111 */ 112 @Override 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, 123 other.mManufacturerSpecificData) 124 && BluetoothLeUtils.equals(mServiceData, other.mServiceData) 125 && mIncludeDeviceName == other.mIncludeDeviceName 126 && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; 127 } 128 129 @Override 130 public String toString() { 131 return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" 132 + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" 133 + BluetoothLeUtils.toString(mServiceData) 134 + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" 135 + mIncludeDeviceName + "]"; 136 } 137 138 @Override 139 public int describeContents() { 140 return 0; 141 } 142 143 @Override 144 public void writeToParcel(Parcel dest, int flags) { 145 dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags); 146 147 // mManufacturerSpecificData could not be null. 148 dest.writeInt(mManufacturerSpecificData.size()); 149 for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { 150 dest.writeInt(mManufacturerSpecificData.keyAt(i)); 151 dest.writeByteArray(mManufacturerSpecificData.valueAt(i)); 152 } 153 dest.writeInt(mServiceData.size()); 154 for (ParcelUuid uuid : mServiceData.keySet()) { 155 dest.writeTypedObject(uuid, flags); 156 dest.writeByteArray(mServiceData.get(uuid)); 157 } 158 dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); 159 dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); 160 } 161 162 public static final Parcelable.Creator<AdvertiseData> CREATOR = 163 new Creator<AdvertiseData>() { 164 @Override 165 public AdvertiseData[] newArray(int size) { 166 return new AdvertiseData[size]; 167 } 168 169 @Override 170 public AdvertiseData createFromParcel(Parcel in) { 171 Builder builder = new Builder(); 172 ArrayList<ParcelUuid> uuids = in.createTypedArrayList(ParcelUuid.CREATOR); 173 for (ParcelUuid uuid : uuids) { 174 builder.addServiceUuid(uuid); 175 } 176 177 int manufacturerSize = in.readInt(); 178 for (int i = 0; i < manufacturerSize; ++i) { 179 int manufacturerId = in.readInt(); 180 byte[] manufacturerData = in.createByteArray(); 181 builder.addManufacturerData(manufacturerId, manufacturerData); 182 } 183 int serviceDataSize = in.readInt(); 184 for (int i = 0; i < serviceDataSize; ++i) { 185 ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR); 186 byte[] serviceData = in.createByteArray(); 187 builder.addServiceData(serviceDataUuid, serviceData); 188 } 189 builder.setIncludeTxPowerLevel(in.readByte() == 1); 190 builder.setIncludeDeviceName(in.readByte() == 1); 191 return builder.build(); 192 } 193 }; 194 195 /** 196 * Builder for {@link AdvertiseData}. 197 */ 198 public static final class Builder { 199 @Nullable 200 private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>(); 201 private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>(); 202 private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>(); 203 private boolean mIncludeTxPowerLevel; 204 private boolean mIncludeDeviceName; 205 206 /** 207 * Add a service UUID to advertise data. 208 * 209 * @param serviceUuid A service UUID to be advertised. 210 * @throws IllegalArgumentException If the {@code serviceUuids} are null. 211 */ 212 public Builder addServiceUuid(ParcelUuid serviceUuid) { 213 if (serviceUuid == null) { 214 throw new IllegalArgumentException("serivceUuids are null"); 215 } 216 mServiceUuids.add(serviceUuid); 217 return this; 218 } 219 220 /** 221 * Add service data to advertise data. 222 * 223 * @param serviceDataUuid 16-bit UUID of the service the data is associated with 224 * @param serviceData Service data 225 * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is 226 * empty. 227 */ 228 public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { 229 if (serviceDataUuid == null || serviceData == null) { 230 throw new IllegalArgumentException( 231 "serviceDataUuid or serviceDataUuid is null"); 232 } 233 mServiceData.put(serviceDataUuid, serviceData); 234 return this; 235 } 236 237 /** 238 * Add manufacturer specific data. 239 * <p> 240 * Please refer to the Bluetooth Assigned Numbers document provided by the <a 241 * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company 242 * identifiers. 243 * 244 * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. 245 * @param manufacturerSpecificData Manufacturer specific data 246 * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code 247 * manufacturerSpecificData} is null. 248 */ 249 public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { 250 if (manufacturerId < 0) { 251 throw new IllegalArgumentException( 252 "invalid manufacturerId - " + manufacturerId); 253 } 254 if (manufacturerSpecificData == null) { 255 throw new IllegalArgumentException("manufacturerSpecificData is null"); 256 } 257 mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); 258 return this; 259 } 260 261 /** 262 * Whether the transmission power level should be included in the advertise packet. Tx power 263 * level field takes 3 bytes in advertise packet. 264 */ 265 public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) { 266 mIncludeTxPowerLevel = includeTxPowerLevel; 267 return this; 268 } 269 270 /** 271 * Set whether the device name should be included in advertise packet. 272 */ 273 public Builder setIncludeDeviceName(boolean includeDeviceName) { 274 mIncludeDeviceName = includeDeviceName; 275 return this; 276 } 277 278 /** 279 * Build the {@link AdvertiseData}. 280 */ 281 public AdvertiseData build() { 282 return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, 283 mIncludeTxPowerLevel, mIncludeDeviceName); 284 } 285 } 286} 287