Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4ca7e60 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

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

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

Change-Id: I603dfd54d43ad22c44630b984ea6829611f81b24
parents f4c5678b d0e4f634
Loading
Loading
Loading
Loading
+53 −5
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -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
+155 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+168 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}