/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import android.util.ArrayMap; import android.util.SparseArray; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; /** * Advertise data packet container for Bluetooth LE advertising. This represents the data to be * advertised as well as the scan response data for active scans. *

* Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be * advertised. * * @see BluetoothLeAdvertiser * @see ScanRecord */ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; private final SparseArray mManufacturerSpecificData; private final Map mServiceData; private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, SparseArray manufacturerData, Map serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; mIncludeDeviceName = includeDeviceName; } /** * Returns a list of service UUIDs within the advertisement that are used to identify the * Bluetooth GATT services. */ public List getServiceUuids() { return mServiceUuids; } /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ public SparseArray getManufacturerSpecificData() { return mManufacturerSpecificData; } /** * Returns a map of 16-bit UUID and its corresponding service data. */ public Map getServiceData() { return mServiceData; } /** * Whether the transmission power level will be included in the advertisement packet. */ public boolean getIncludeTxPowerLevel() { return mIncludeTxPowerLevel; } /** * Whether the device name will be included in the advertisement packet. */ public boolean getIncludeDeviceName() { return mIncludeDeviceName; } /** * @hide */ @Override public int hashCode() { return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** * @hide */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + mIncludeDeviceName + "]"; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeList(mServiceUuids); // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { dest.writeInt(mManufacturerSpecificData.keyAt(i)); byte[] data = mManufacturerSpecificData.valueAt(i); if (data == null) { dest.writeInt(0); } else { dest.writeInt(1); dest.writeInt(data.length); dest.writeByteArray(data); } } dest.writeInt(mServiceData.size()); for (ParcelUuid uuid : mServiceData.keySet()) { dest.writeParcelable(uuid, flags); byte[] data = mServiceData.get(uuid); if (data == null) { dest.writeInt(0); } else { dest.writeInt(1); dest.writeInt(data.length); dest.writeByteArray(data); } } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); } public static final Parcelable.Creator CREATOR = new Creator() { @Override public AdvertiseData[] newArray(int size) { return new AdvertiseData[size]; } @Override public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); @SuppressWarnings("unchecked") List uuids = in.readArrayList(ParcelUuid.class.getClassLoader()); if (uuids != null) { for (ParcelUuid uuid : uuids) { builder.addServiceUuid(uuid); } } int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); if (in.readInt() == 1) { int manufacturerDataLength = in.readInt(); byte[] manufacturerData = new byte[manufacturerDataLength]; in.readByteArray(manufacturerData); builder.addManufacturerData(manufacturerId, manufacturerData); } } int serviceDataSize = in.readInt(); for (int i = 0; i < serviceDataSize; ++i) { ParcelUuid serviceDataUuid = in.readParcelable( ParcelUuid.class.getClassLoader()); if (in.readInt() == 1) { int serviceDataLength = in.readInt(); byte[] serviceData = new byte[serviceDataLength]; in.readByteArray(serviceData); builder.addServiceData(serviceDataUuid, serviceData); } } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); return builder.build(); } }; /** * Builder for {@link AdvertiseData}. */ public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); private SparseArray mManufacturerSpecificData = new SparseArray(); private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; private boolean mIncludeDeviceName; /** * Add a service UUID to advertise data. * * @param serviceUuid A service UUID to be advertised. * @throws IllegalArgumentException If the {@code serviceUuids} are null. */ public Builder addServiceUuid(ParcelUuid serviceUuid) { if (serviceUuid == null) { throw new IllegalArgumentException("serivceUuids are null"); } mServiceUuids.add(serviceUuid); return this; } /** * Add service data to advertise data. * * @param serviceDataUuid 16-bit UUID of the service the data is associated with * @param serviceData Service data * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is * empty. */ public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null || serviceData == null) { throw new IllegalArgumentException( "serviceDataUuid or serviceDataUuid is null"); } mServiceData.put(serviceDataUuid, serviceData); return this; } /** * Add manufacturer specific data. *

* Please refer to the Bluetooth Assigned Numbers document provided by the Bluetooth SIG for a list of existing company * identifiers. * * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. * @param manufacturerSpecificData Manufacturer specific data * @throws IllegalArgumentException If the {@code manufacturerId} is negative or * {@code manufacturerSpecificData} is null. */ public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { if (manufacturerId < 0) { throw new IllegalArgumentException( "invalid manufacturerId - " + manufacturerId); } if (manufacturerSpecificData == null) { throw new IllegalArgumentException("manufacturerSpecificData is null"); } mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); return this; } /** * Whether the transmission power level should be included in the advertise packet. Tx power * level field takes 3 bytes in advertise packet. */ public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) { mIncludeTxPowerLevel = includeTxPowerLevel; return this; } /** * Set whether the device name should be included in advertise packet. */ public Builder setIncludeDeviceName(boolean includeDeviceName) { mIncludeDeviceName = includeDeviceName; return this; } /** * Build the {@link AdvertiseData}. */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, mIncludeDeviceName); } } }