startAdvertising失败,errorCode=1(AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE)
errorCode解释:Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.
原因:创建AdvertiseData对象时,addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData)中的serviceData过长。
下面是自定义函数:
private void startAdvertising() {
BluetoothLeAdvertiser mBluetoothLeAdvertiser= mBluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser == null) {
Log.i(TAG, "startAdvertising failed");
//创建失败
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(false)//注释1
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build();
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(false)//注释2
.setIncludeTxPowerLevel(false)//注释3
//.addManufacturerData(0, null)//注释4
//.addServiceUuid(new ParcelUuid(switcherUuid))//绑定服务uuid //注释5
.addServiceData(new ParcelUuid(switcherUuid), switcherData)//注释6
.build();
mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}
byte[] switcherData = {0x02, 0x01, 0x02, 0x0B, 0x09, 0x48, 0x55, 0x41, 0x57, 0x45, 0x49, 0x20, 0x50, 0x32, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00/*, 0x00, 0x00, 0x00, 0x00*/};
UUID switcherUuid = UUID.fromString("0000ffff-0000-1000-8000-00805f9b34fb");
下面是mBluetoothLeAdvertiser.startAdvertising原生函数:
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, final AdvertiseCallback callback) {
startAdvertising(settings, advertiseData, null, callback);
}
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, AdvertiseData scanResponse,
final AdvertiseCallback callback) {
synchronized (mLegacyAdvertisers) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
boolean isConnectable = settings.isConnectable();
if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES
|| totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);//这里会提示数据过长,Advertising启动失败
return;
}
if (mLegacyAdvertisers.containsKey(callback)) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
return;
}
AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder();
parameters.setLegacyMode(true);
parameters.setConnectable(isConnectable);
parameters.setScannable(true); // legacy advertisements we support are always scannable
if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
parameters.setInterval(1600); // 1s
} else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
parameters.setInterval(400); // 250ms
} else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) {
parameters.setInterval(160); // 100ms
}
if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) {
parameters.setTxPowerLevel(-21);
} else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) {
parameters.setTxPowerLevel(-15);
} else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) {
parameters.setTxPowerLevel(-7);
} else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
parameters.setTxPowerLevel(1);
}
int duration = 0;
int timeoutMillis = settings.getTimeout();
if (timeoutMillis > 0) {
duration = (timeoutMillis < 10) ? 1 : timeoutMillis / 10;
}
AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings);
mLegacyAdvertisers.put(callback, wrapped);
startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null,
duration, 0, wrapped);
}
}
源码中可以看到Advertising启动失败是因为totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES (=31)
下面是totalBytes源码:
private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
if (data == null) return 0;
// Flags field is omitted if the advertising is not connectable.
int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0; //从上面的源码可以知道这里的isFlagsIncluded就是settings.isConnectable()
if (data.getServiceUuids() != null) {
int num16BitUuids = 0;
int num32BitUuids = 0;
int num128BitUuids = 0;
for (ParcelUuid uuid : data.getServiceUuids()) {
if (BluetoothUuid.is16BitUuid(uuid)) {
++num16BitUuids;
} else if (BluetoothUuid.is32BitUuid(uuid)) {
++num32BitUuids;
} else {
++num128BitUuids;
}
}
// 16 bit service uuids are grouped into one field when doing advertising.
if (num16BitUuids != 0) {
size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
}
// 32 bit service uuids are grouped into one field when doing advertising.
if (num32BitUuids != 0) {
size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
}
// 128 bit service uuids are grouped into one field when doing advertising.
if (num128BitUuids != 0) {
size += OVERHEAD_BYTES_PER_FIELD
+ num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
}
}
for (ParcelUuid uuid : data.getServiceData().keySet()) {
int uuidLen = BluetoothUuid.uuidToBytes(uuid).length;
size += OVERHEAD_BYTES_PER_FIELD + uuidLen
+ byteLength(data.getServiceData().get(uuid));
}
for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) {
size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH
+ byteLength(data.getManufacturerSpecificData().valueAt(i));
}
if (data.getIncludeTxPowerLevel()) {
size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
}
if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) {
size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length();
}
return size;
}
可以看到size有6处赋值,有6个影响点:
1、isFlagsIncluded,从上面的源码可以知道isFlagsIncluded就是settings.isConnectable(),对应的是自定义函数中注释1处的改动。如果注释1处设置为false,那size就等于0;
2、data.getServiceUuids(),对应的是自定义函数中注释5处的改动。如果不设置ServiceUuid,那size仍然等于0;
3、data.getServiceData(),对应的是自定义函数中注释6处的改动。我的主要目的就是设置ServiceData,可以看到此处size += OVERHEAD_BYTES_PER_FIELD + uuidLen + byteLength(data.getServiceData().get(uuid));
从源码中可以获知到OVERHEAD_BYTES_PER_FIELD=2,从BluetoothUuid.uuidToBytes可以获知到uuidLen=2,按照size不能大于MAX_LEGACY_ADVERTISING_DATA_BYTES (31)的限制可以知道ServiceData的长度不能大于27。
4、data.getManufacturerSpecificData(),对应的是自定义函数中注释4处的改动。如果不设置ManufacturerSpecificData,那size不会增加。
5、data.getIncludeTxPowerLevel(),对应的是自定义函数中注释3处的改动。如果不设置IncludeTxPowerLevel,那size不会增加。
6、data.getIncludeDeviceName(),对应的是自定义函数中注释2处的改动。如果不设置IncludeDeviceName,那size不会增加。
由上可知,可供设置的ServiceData的最大长度是27字节。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)