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

Commit 5335419d authored by Jakub Pawlowski's avatar Jakub Pawlowski
Browse files

Change how Advertise Data is passed to BTIF (3/3)

The way that AD data is passed right now put some additional
limitations, i.e. only one manufacturer specific data can be set, or
only one service UUID. By moving AD generation to upper layers, aother
set of limitations is removed.

Test: Covered by BleAdvertiseApiTest sl4a test
Bug: 30622771
Change-Id: I5444455a89ee085dcccfdc98846955374d58c374
parent f4c104e3
Loading
Loading
Loading
Loading
+15 −41
Original line number Diff line number Diff line
@@ -1097,30 +1097,16 @@ static void gattAdvertiseNative(JNIEnv *env, jobject object,
    sGattIf->client->listen(client_if, start);
}

static void gattSetAdvDataNative(JNIEnv *env, jobject object, jint client_if,
        jboolean setScanRsp, jboolean inclName, jboolean inclTxPower, jint minInterval,
        jint maxInterval, jint appearance, jbyteArray manufacturerData, jbyteArray serviceData,
        jbyteArray serviceUuid)
static void gattSetAdvDataNative(JNIEnv *env, jobject object,
                                 jboolean setScanRsp, jbyteArray data)
{
    if (!sGattIf) return;
    jbyte* arr_data = env->GetByteArrayElements(manufacturerData, NULL);
    uint16_t arr_len = (uint16_t) env->GetArrayLength(manufacturerData);
    vector<uint8_t> data(arr_data, arr_data + arr_len);
    env->ReleaseByteArrayElements(manufacturerData, arr_data, JNI_ABORT);
    jbyte* data_data = env->GetByteArrayElements(data, NULL);
    uint16_t data_len = (uint16_t) env->GetArrayLength(data);
    vector<uint8_t> data_vec(data_data, data_data + data_len);
    env->ReleaseByteArrayElements(data, data_data, JNI_ABORT);

    jbyte* arr_service_data = env->GetByteArrayElements(serviceData, NULL);
    uint16_t arr_service_data_len = (uint16_t) env->GetArrayLength(serviceData);
    vector<uint8_t> service_data(arr_service_data, arr_service_data + arr_service_data_len);
    env->ReleaseByteArrayElements(serviceData, arr_service_data, JNI_ABORT);

    jbyte* arr_service_uuid = env->GetByteArrayElements(serviceUuid, NULL);
    uint16_t arr_service_uuid_len = (uint16_t) env->GetArrayLength(serviceUuid);
    vector<uint8_t> service_uuid(arr_service_uuid, arr_service_uuid + arr_service_uuid_len);
    env->ReleaseByteArrayElements(serviceUuid, arr_service_uuid, JNI_ABORT);

    sGattIf->advertiser->SetData(client_if, setScanRsp, inclName, inclTxPower,
        minInterval, maxInterval, appearance, std::move(data),
        std::move(service_data), std::move(service_uuid));
    sGattIf->advertiser->SetData(setScanRsp, std::move(data_vec));
}

static void gattSetScanParametersNative(JNIEnv* env, jobject object,
@@ -1387,28 +1373,16 @@ static void gattClientSetAdvParamsNative(JNIEnv* env, jobject object, jint adver
}

static void gattClientSetAdvDataNative(JNIEnv* env, jobject object, jint advertiser_id,
        jboolean set_scan_rsp, jboolean incl_name, jboolean incl_txpower, jint appearance,
        jbyteArray manufacturer_data,jbyteArray service_data, jbyteArray service_uuid)
        jboolean set_scan_rsp, jbyteArray data)
{
    if (!sGattIf) return;
    jbyte* manu_data = env->GetByteArrayElements(manufacturer_data, NULL);
    uint16_t manu_len = (uint16_t) env->GetArrayLength(manufacturer_data);
    vector<uint8_t> manu_vec(manu_data, manu_data + manu_len);
    env->ReleaseByteArrayElements(manufacturer_data, manu_data, JNI_ABORT);

    jbyte* serv_data = env->GetByteArrayElements(service_data, NULL);
    uint16_t serv_data_len = (uint16_t) env->GetArrayLength(service_data);
    vector<uint8_t> serv_data_vec(serv_data, serv_data + serv_data_len);
    env->ReleaseByteArrayElements(service_data, serv_data, JNI_ABORT);

    jbyte* serv_uuid = env->GetByteArrayElements(service_uuid, NULL);
    uint16_t serv_uuid_len = (uint16_t) env->GetArrayLength(service_uuid);
    vector<uint8_t> serv_uuid_vec(serv_uuid, serv_uuid + serv_uuid_len);
    env->ReleaseByteArrayElements(service_uuid, serv_uuid, JNI_ABORT);
    jbyte* data_data = env->GetByteArrayElements(data, NULL);
    uint16_t data_len = (uint16_t) env->GetArrayLength(data);
    vector<uint8_t> data_vec(data_data, data_data + data_len);
    env->ReleaseByteArrayElements(data, data_data, JNI_ABORT);

    sGattIf->advertiser->MultiAdvSetInstData(
        advertiser_id, set_scan_rsp, incl_name, incl_txpower, appearance,
        std::move(manu_vec), std::move(serv_data_vec), std::move(serv_uuid_vec),
        advertiser_id, set_scan_rsp, std::move(data_vec),
        base::Bind(&ble_advertiser_setadv_data_cb, advertiser_id));
}

@@ -1648,9 +1622,9 @@ static JNINativeMethod sAdvertiseMethods[] = {
    {"registerAdvertiserNative", "(JJ)V", (void *) registerAdvertiserNative},
    {"unregisterAdvertiserNative", "(I)V", (void *) unregisterAdvertiserNative},
    {"gattClientSetParamsNative", "(IIIIII)V", (void *) gattClientSetAdvParamsNative},
    {"gattClientSetAdvDataNative", "(IZZZI[B[B[B)V", (void *) gattClientSetAdvDataNative},
    {"gattClientSetAdvDataNative", "(IZ[B)V", (void *) gattClientSetAdvDataNative},
    {"gattClientEnableAdvNative", "(IZI)V", (void *) gattClientEnableAdvNative},
    {"gattSetAdvDataNative", "(IZZZIII[B[B[B)V", (void *) gattSetAdvDataNative},
    {"gattSetAdvDataNative", "(Z[B)V", (void *) gattSetAdvDataNative},
    {"gattAdvertiseNative", "(IZ)V", (void *) gattAdvertiseNative},
};

+1 −1
Original line number Diff line number Diff line
@@ -1507,7 +1507,7 @@ public class AdapterService extends Service {
        return mAdapterProperties.getUuids();
    }

     String getName() {
    public String getName() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                       "Need BLUETOOTH permission");

+148 −73
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.bluetooth.btservice.AdapterService;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.io.ByteArrayOutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@@ -390,82 +391,158 @@ class AdvertiseManager {
            }
        }

        private void setAdvertisingData(AdvertiseClient client, AdvertiseData data,
                boolean isScanResponse) {
            if (data == null) {
                return;
            }
            boolean includeName = data.getIncludeDeviceName();
            boolean includeTxPower = data.getIncludeTxPowerLevel();
            int appearance = 0;
            byte[] manufacturerData = getManufacturerData(data);
        private static final int DEVICE_NAME_MAX = 18;

        private static final int COMPLETE_LIST_16_BIT_SERVICE_UUIDS = 0X03;
        private static final int COMPLETE_LIST_32_BIT_SERVICE_UUIDS = 0X05;
        private static final int COMPLETE_LIST_128_BIT_SERVICE_UUIDS = 0X07;
        private static final int SHORTENED_LOCAL_NAME = 0X08;
        private static final int COMPLETE_LOCAL_NAME = 0X09;
        private static final int TX_POWER_LEVEL = 0x0A;
        private static final int SERVICE_DATA_16_BIT_UUID = 0X16;
        private static final int SERVICE_DATA_32_BIT_UUID = 0X20;
        private static final int SERVICE_DATA_128_BIT_UUID = 0X21;
        private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF;

        private byte[] advertiseDataToBytes(AdvertiseData data) {
            // Flags are added by lower layers of the stack, only if needed;
            // no need to add them here.

            byte[] serviceData = getServiceData(data);
            byte[] serviceUuids;
            if (data.getServiceUuids() == null) {
                serviceUuids = new byte[0];
            ByteArrayOutputStream ret = new ByteArrayOutputStream();

            if (data.getIncludeDeviceName()) {
                String name = mAdapterService.getName();
                try {
                    byte[] nameBytes = name.getBytes("UTF-8");

                    int nameLength = nameBytes.length;
                    byte type;

                    // TODO(jpawlowski) put a better limit on device name!
                    if (nameLength > DEVICE_NAME_MAX) {
                      nameLength = DEVICE_NAME_MAX;
                      type = SHORTENED_LOCAL_NAME;
                    } else {
                ByteBuffer advertisingUuidBytes = ByteBuffer.allocate(
                        data.getServiceUuids().size() * 16)
                        .order(ByteOrder.LITTLE_ENDIAN);
                for (ParcelUuid parcelUuid : data.getServiceUuids()) {
                    UUID uuid = parcelUuid.getUuid();
                    // Least significant bits first as the advertising UUID should be in
                    // little-endian.
                    advertisingUuidBytes.putLong(uuid.getLeastSignificantBits())
                            .putLong(uuid.getMostSignificantBits());
                }
                serviceUuids = advertisingUuidBytes.array();
                      type = COMPLETE_LOCAL_NAME;
                    }
            if (mAdapterService.isMultiAdvertisementSupported()) {
                gattClientSetAdvDataNative(client.advertiserId, isScanResponse, includeName,
                        includeTxPower, appearance,
                        manufacturerData, serviceData, serviceUuids);
            } else {
                gattSetAdvDataNative(client.advertiserId, isScanResponse, includeName,
                        includeTxPower, 0, 0, appearance,
                        manufacturerData, serviceData, serviceUuids);

                    ret.write(nameLength + 1);
                    ret.write(type);
                    ret.write(nameBytes, 0, nameLength);
                } catch (java.io.UnsupportedEncodingException e) {
                    loge("Can't include name - encoding error!", e);
                }
            }

        // Combine manufacturer id and manufacturer data.
        private byte[] getManufacturerData(AdvertiseData advertiseData) {
            if (advertiseData.getManufacturerSpecificData().size() == 0) {
                return new byte[0];
            }
            int manufacturerId = advertiseData.getManufacturerSpecificData().keyAt(0);
            byte[] manufacturerData = advertiseData.getManufacturerSpecificData().get(
            for (int i = 0; i< data.getManufacturerSpecificData().size(); i++) {
                int manufacturerId = data.getManufacturerSpecificData().keyAt(i);

                byte[] manufacturerData = data.getManufacturerSpecificData().get(
                        manufacturerId);
                int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length);
                byte[] concated = new byte[dataLen];
            // / First two bytes are manufacturer id in little-endian.
                // First two bytes are manufacturer id in little-endian.
                concated[0] = (byte) (manufacturerId & 0xFF);
                concated[1] = (byte) ((manufacturerId >> 8) & 0xFF);
                if (manufacturerData != null) {
                    System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length);
                }
            return concated;

                ret.write(concated.length + 1);
                ret.write(MANUFACTURER_SPECIFIC_DATA);
                ret.write(concated, 0, concated.length);
            }

            if (data.getIncludeTxPowerLevel()) {
                ret.write(2 /* Length */);
                ret.write(TX_POWER_LEVEL);
                ret.write(0);  // lower layers will fill this value.
            }

            if (data.getServiceUuids() != null) {
                ByteArrayOutputStream serviceUuids16 = new ByteArrayOutputStream();
                ByteArrayOutputStream serviceUuids32 = new ByteArrayOutputStream();
                ByteArrayOutputStream serviceUuids128 = new ByteArrayOutputStream();

                for (ParcelUuid parcelUuid : data.getServiceUuids()) {
                    byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);

                    if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
                        serviceUuids16.write(uuid, 0, uuid.length);
                    } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
                        serviceUuids32.write(uuid, 0, uuid.length);
                    } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
                        serviceUuids128.write(uuid, 0, uuid.length);
                    }
                }

        // Combine service UUID and service data.
        private byte[] getServiceData(AdvertiseData advertiseData) {
            if (advertiseData.getServiceData().isEmpty()) {
                return new byte[0];
                if (serviceUuids16.size() != 0) {
                    ret.write(serviceUuids16.size() + 1);
                    ret.write(COMPLETE_LIST_16_BIT_SERVICE_UUIDS);
                    ret.write(serviceUuids16.toByteArray(), 0, serviceUuids16.size());
                }
            ParcelUuid uuid = advertiseData.getServiceData().keySet().iterator().next();
            byte[] serviceData = advertiseData.getServiceData().get(uuid);
            int dataLen = 2 + (serviceData == null ? 0 : serviceData.length);

                if (serviceUuids32.size() != 0) {
                    ret.write(serviceUuids32.size() + 1);
                    ret.write(COMPLETE_LIST_32_BIT_SERVICE_UUIDS);
                    ret.write(serviceUuids32.toByteArray(), 0, serviceUuids32.size());
                }

                if (serviceUuids128.size() != 0) {
                    ret.write(serviceUuids128.size() + 1);
                    ret.write(COMPLETE_LIST_128_BIT_SERVICE_UUIDS);
                    ret.write(serviceUuids128.toByteArray(), 0, serviceUuids128.size());
                }
            }

            if (!data.getServiceData().isEmpty()) {
                for (ParcelUuid parcelUuid: data.getServiceData().keySet()) {
                    byte[] serviceData = data.getServiceData().get(parcelUuid);

                    byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
                    int uuidLen = uuid.length;

                    int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length);
                    byte[] concated = new byte[dataLen];
            // Extract 16 bit UUID value.
            int uuidValue = BluetoothUuid.getServiceIdentifierFromParcelUuid(
                    uuid);
            // First two bytes are service data UUID in little-endian.
            concated[0] = (byte) (uuidValue & 0xFF);
            concated[1] = (byte) ((uuidValue >> 8) & 0xFF);

                    System.arraycopy(uuid, 0, concated, 0, uuidLen);

                    if (serviceData != null) {
                System.arraycopy(serviceData, 0, concated, 2, serviceData.length);
                        System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length);
                    }

                    if (uuid.length == BluetoothUuid.UUID_BYTES_16_BIT) {
                        ret.write(concated.length + 1);
                        ret.write(SERVICE_DATA_16_BIT_UUID);
                        ret.write(concated, 0, concated.length);
                    } else if (uuid.length == BluetoothUuid.UUID_BYTES_32_BIT) {
                        ret.write(concated.length + 1);
                        ret.write(SERVICE_DATA_32_BIT_UUID);
                        ret.write(concated, 0, concated.length);
                    } else /*if (uuid.length == BluetoothUuid.UUID_BYTES_128_BIT)*/ {
                        ret.write(concated.length + 1);
                        ret.write(SERVICE_DATA_128_BIT_UUID);
                        ret.write(concated, 0, concated.length);
                    }
                }
            }

            return ret.toByteArray();
        }

        private void setAdvertisingData(AdvertiseClient client, AdvertiseData data,
                boolean isScanResponse) {
            if (data == null) {
                return;
            }

            byte [] data_out = advertiseDataToBytes(data);

            if (mAdapterService.isMultiAdvertisementSupported()) {
                gattClientSetAdvDataNative(client.advertiserId, isScanResponse, data_out);
            } else {
                gattSetAdvDataNative(isScanResponse, data_out);
            }
            return concated;
        }

        // Convert settings tx power level to stack tx power level.
@@ -521,14 +598,12 @@ class AdvertiseManager {
                int min_interval, int max_interval, int adv_type, int chnl_map, int tx_power);

        private native void gattClientSetAdvDataNative(int advertiserId,
                boolean set_scan_rsp, boolean incl_name, boolean incl_txpower, int appearance,
                byte[] manufacturer_data, byte[] service_data, byte[] service_uuid);
                boolean set_scan_rsp, byte[] data);

        private native void gattClientEnableAdvNative(int advertiserId, boolean enable, int timeout_s);
        private native void gattClientEnableAdvNative(int advertiserId,
                boolean enable, int timeout_s);

        private native void gattSetAdvDataNative(int advertiserId, boolean setScanRsp, boolean inclName,
                boolean inclTxPower, int minSlaveConnectionInterval, int maxSlaveConnectionInterval,
                int appearance, byte[] manufacturerData, byte[] serviceData, byte[] serviceUuid);
        private native void gattSetAdvDataNative(boolean setScanRsp, byte[] data);

        private native void gattAdvertiseNative(int advertiserId, boolean start);
    }