Merge "Bluetooth: make it possible to advertise Transport Discovery Data" am: e7adf14ea3 am: 4e43b68f20

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1454977

Change-Id: I6865279b3684152e762ad6d86dcdea41c8c2e864
This commit is contained in:
Treehugger Robot
2021-11-19 09:10:42 +00:00
committed by Automerger Merge Worker
5 changed files with 406 additions and 5 deletions

View File

@@ -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 {

View File

@@ -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;
@@ -82,6 +88,17 @@ public final class AdvertiseData implements Parcelable {
return mServiceSolicitationUuids;
}
/**
* 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.
*
@@ -273,6 +304,23 @@ public final class AdvertiseData implements Parcelable {
return this;
}
/**
* 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>
@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}
}