diff options
author | 2021-11-19 08:42:55 +0000 | |
---|---|---|
committer | 2021-11-19 08:42:55 +0000 | |
commit | e7adf14ea3ca07166e8bf328560c9f43be04ffaf (patch) | |
tree | 356985b3f266a5acf044110b65e25e1ef8a97ab7 | |
parent | 9b6a363c8ca6d721b078df8fa2cb139df0c85ac6 (diff) | |
parent | 3327af5d7b1bd96494bf243cde8fc72e1293bca7 (diff) |
Merge "Bluetooth: make it possible to advertise Transport Discovery Data"
-rw-r--r-- | core/api/current.txt | 27 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/AdvertiseData.java | 58 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 3 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/TransportBlock.java | 155 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/TransportDiscoveryData.java | 168 |
5 files changed, 406 insertions, 5 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 087b54ee86f2..b519cbeffe24 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9546,6 +9546,7 @@ package android.bluetooth.le { method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData(); method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids(); method public java.util.List<android.os.ParcelUuid> getServiceUuids(); + method @NonNull public java.util.List<android.bluetooth.le.TransportDiscoveryData> getTransportDiscoveryData(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR; } @@ -9556,6 +9557,7 @@ package android.bluetooth.le { method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]); method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid); method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid); + method @NonNull public android.bluetooth.le.AdvertiseData.Builder addTransportDiscoveryData(@NonNull android.bluetooth.le.TransportDiscoveryData); method public android.bluetooth.le.AdvertiseData build(); method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean); method public android.bluetooth.le.AdvertiseData.Builder setIncludeTxPowerLevel(boolean); @@ -9815,6 +9817,31 @@ package android.bluetooth.le { method public android.bluetooth.le.ScanSettings.Builder setScanMode(int); } + public final class TransportBlock implements android.os.Parcelable { + ctor public TransportBlock(int, int, int, @Nullable byte[]); + method public int describeContents(); + method public int getOrgId(); + method public int getTdsFlags(); + method @Nullable public byte[] getTransportData(); + method public int getTransportDataLength(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportBlock> CREATOR; + } + + public final class TransportDiscoveryData implements android.os.Parcelable { + ctor public TransportDiscoveryData(int, @NonNull java.util.List<android.bluetooth.le.TransportBlock>); + ctor public TransportDiscoveryData(@NonNull byte[]); + method public int describeContents(); + method @NonNull public java.util.List<android.bluetooth.le.TransportBlock> getTransportBlocks(); + method public int getTransportDataType(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportDiscoveryData> CREATOR; + } + } package android.companion { diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index cec658049ca5..fdf62ec3a647 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.util.ArrayMap; import android.util.SparseArray; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,6 +48,9 @@ public final class AdvertiseData implements Parcelable { @NonNull private final List<ParcelUuid> mServiceSolicitationUuids; + @Nullable + private final List<TransportDiscoveryData> mTransportDiscoveryData; + private final SparseArray<byte[]> mManufacturerSpecificData; private final Map<ParcelUuid, byte[]> mServiceData; private final boolean mIncludeTxPowerLevel; @@ -54,12 +58,14 @@ public final class AdvertiseData implements Parcelable { private AdvertiseData(List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceSolicitationUuids, + List<TransportDiscoveryData> transportDiscoveryData, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; mServiceSolicitationUuids = serviceSolicitationUuids; + mTransportDiscoveryData = transportDiscoveryData; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; @@ -83,6 +89,17 @@ public final class AdvertiseData implements Parcelable { } /** + * Returns a list of {@link TransportDiscoveryData} within the advertisement. + */ + @NonNull + public List<TransportDiscoveryData> getTransportDiscoveryData() { + if (mTransportDiscoveryData == null) { + return Collections.emptyList(); + } + return mTransportDiscoveryData; + } + + /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ @@ -116,8 +133,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, - mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, + mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -134,6 +151,7 @@ public final class AdvertiseData implements Parcelable { AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) + && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -144,7 +162,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" - + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + + mServiceSolicitationUuids + ", mTransportDiscoveryData=" + + mTransportDiscoveryData + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -162,6 +181,8 @@ public final class AdvertiseData implements Parcelable { dest.writeTypedArray(mServiceSolicitationUuids.toArray( new ParcelUuid[mServiceSolicitationUuids.size()]), flags); + dest.writeTypedList(mTransportDiscoveryData); + // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { @@ -197,6 +218,12 @@ public final class AdvertiseData implements Parcelable { builder.addServiceSolicitationUuid(uuid); } + List<TransportDiscoveryData> transportDiscoveryData = + in.createTypedArrayList(TransportDiscoveryData.CREATOR); + for (TransportDiscoveryData tdd : transportDiscoveryData) { + builder.addTransportDiscoveryData(tdd); + } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); @@ -223,6 +250,9 @@ public final class AdvertiseData implements Parcelable { private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>(); @NonNull private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>(); + @Nullable + private List<TransportDiscoveryData> mTransportDiscoveryData = + new ArrayList<TransportDiscoveryData>(); private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>(); private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>(); private boolean mIncludeTxPowerLevel; @@ -256,6 +286,7 @@ public final class AdvertiseData implements Parcelable { mServiceSolicitationUuids.add(serviceSolicitationUuid); return this; } + /** * Add service data to advertise data. * @@ -274,6 +305,23 @@ public final class AdvertiseData implements Parcelable { } /** + * Add Transport Discovery Data to advertise data. + * + * @param transportDiscoveryData Transport Discovery Data, consisting of one or more + * Transport Blocks. Transport Discovery Data AD Type Code is already included. + * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty + */ + @NonNull + public Builder addTransportDiscoveryData( + @NonNull TransportDiscoveryData transportDiscoveryData) { + if (transportDiscoveryData == null) { + throw new IllegalArgumentException("transportDiscoveryData is null"); + } + mTransportDiscoveryData.add(transportDiscoveryData); + return this; + } + + /** * Add manufacturer specific data. * <p> * Please refer to the Bluetooth Assigned Numbers document provided by the <a @@ -319,8 +367,8 @@ public final class AdvertiseData implements Parcelable { */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, - mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, - mIncludeDeviceName); + mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, + mIncludeTxPowerLevel, mIncludeDeviceName); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 58029745ab9c..b9f8a57a75ea 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -567,6 +567,9 @@ public final class BluetoothLeAdvertiser { + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } + for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { + size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); + } for (ParcelUuid uuid : data.getServiceData().keySet()) { int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; size += OVERHEAD_BYTES_PER_FIELD + uuidLen diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java new file mode 100644 index 000000000000..b388beda6b3b --- /dev/null +++ b/core/java/android/bluetooth/le/TransportBlock.java @@ -0,0 +1,155 @@ +/* + * 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.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * Wrapper for Transport Discovery Data Transport Blocks. + * This class represents a Transport Block from a Transport Discovery Data. + * + * @see TransportDiscoveryData + * @see AdvertiseData + */ +public final class TransportBlock implements Parcelable { + private static final String TAG = "TransportBlock"; + private final int mOrgId; + private final int mTdsFlags; + private final int mTransportDataLength; + private final byte[] mTransportData; + + /** + * Creates an instance of TransportBlock from raw data. + * + * @param orgId the Organization ID + * @param tdsFlags the TDS flags + * @param transportDataLength the total length of the Transport Data + * @param transportData the Transport Data + */ + public TransportBlock(int orgId, int tdsFlags, int transportDataLength, + @Nullable byte[] transportData) { + mOrgId = orgId; + mTdsFlags = tdsFlags; + mTransportDataLength = transportDataLength; + mTransportData = transportData; + } + + private TransportBlock(Parcel in) { + mOrgId = in.readInt(); + mTdsFlags = in.readInt(); + mTransportDataLength = in.readInt(); + mTransportData = new byte[mTransportDataLength]; + in.readByteArray(mTransportData); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mOrgId); + dest.writeInt(mTdsFlags); + dest.writeInt(mTransportDataLength); + dest.writeByteArray(mTransportData); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() { + @Override + public TransportBlock createFromParcel(Parcel in) { + return new TransportBlock(in); + } + + @Override + public TransportBlock[] newArray(int size) { + return new TransportBlock[size]; + } + }; + + /** + * Gets the Organization ID of the Transport Block which corresponds to one of the + * the Bluetooth SIG Assigned Numbers. + */ + public int getOrgId() { + return mOrgId; + } + + /** + * Gets the TDS flags of the Transport Block which represents the role of the device and + * information about its state and supported features. + */ + public int getTdsFlags() { + return mTdsFlags; + } + + /** + * Gets the total number of octets in the Transport Data field in this Transport Block. + */ + public int getTransportDataLength() { + return mTransportDataLength; + } + + /** + * Gets the Transport Data of the Transport Block which contains organization-specific data. + */ + @Nullable + public byte[] getTransportData() { + return mTransportData; + } + + /** + * Converts this TransportBlock to byte array + * + * @return byte array representation of this Transport Block or null if the conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mOrgId); + buffer.put((byte) mTdsFlags); + buffer.put((byte) mTransportDataLength); + if (mTransportData != null) { + buffer.put(mTransportData); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportBlock + */ + public int totalBytes() { + // 3 uint8 + byte[] length + int size = 3 + mTransportDataLength; + return size; + } +} diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java new file mode 100644 index 000000000000..c8e97f9a823a --- /dev/null +++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java @@ -0,0 +1,168 @@ +/* + * 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.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper for Transport Discovery Data AD Type. + * This class contains the Transport Discovery Data AD Type Code as well as + * a list of potential Transport Blocks. + * + * @see AdvertiseData + */ +public final class TransportDiscoveryData implements Parcelable { + private static final String TAG = "TransportDiscoveryData"; + private final int mTransportDataType; + private final List<TransportBlock> mTransportBlocks; + + /** + * Creates a TransportDiscoveryData instance. + * + * @param transportDataType the Transport Discovery Data AD Type + * @param transportBlocks the list of Transport Blocks + */ + public TransportDiscoveryData(int transportDataType, + @NonNull List<TransportBlock> transportBlocks) { + mTransportDataType = transportDataType; + mTransportBlocks = transportBlocks; + } + + /** + * Creates a TransportDiscoveryData instance from byte arrays. + * + * Uses the transport discovery data bytes and parses them into an usable class. + * + * @param transportDiscoveryData the raw discovery data + */ + public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { + ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); + mTransportBlocks = new ArrayList(); + if (byteBuffer.remaining() > 0) { + mTransportDataType = byteBuffer.get(); + } else { + mTransportDataType = -1; + } + try { + while (byteBuffer.remaining() > 0) { + int orgId = byteBuffer.get(); + int tdsFlags = byteBuffer.get(); + int transportDataLength = byteBuffer.get(); + byte[] transportData = new byte[transportDataLength]; + byteBuffer.get(transportData, 0, transportDataLength); + mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, + transportDataLength, transportData)); + } + } catch (BufferUnderflowException e) { + Log.e(TAG, "Error while parsing data: " + e.toString()); + } + } + + private TransportDiscoveryData(Parcel in) { + mTransportDataType = in.readInt(); + mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mTransportDataType); + dest.writeTypedList(mTransportBlocks); + } + + public static final @NonNull Creator<TransportDiscoveryData> CREATOR = + new Creator<TransportDiscoveryData>() { + @Override + public TransportDiscoveryData createFromParcel(Parcel in) { + return new TransportDiscoveryData(in); + } + + @Override + public TransportDiscoveryData[] newArray(int size) { + return new TransportDiscoveryData[size]; + } + }; + + /** + * Gets the transport data type. + */ + public int getTransportDataType() { + return mTransportDataType; + } + + /** + * @return the list of {@link TransportBlock} in this TransportDiscoveryData + * or an empty list if there are no Transport Blocks + */ + @NonNull + public List<TransportBlock> getTransportBlocks() { + if (mTransportBlocks == null) { + return Collections.emptyList(); + } + return mTransportBlocks; + } + + /** + * Converts this TransportDiscoveryData to byte array + * + * @return byte array representation of this Transport Discovery Data or null if the + * conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mTransportDataType); + for (TransportBlock transportBlock : getTransportBlocks()) { + buffer.put(transportBlock.toByteArray()); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportDataDiscovery + */ + public int totalBytes() { + int size = 1; // Counting Transport Data Type here. + for (TransportBlock transportBlock : getTransportBlocks()) { + size += transportBlock.totalBytes(); + } + return size; + } +} |