Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android 14 breaking change #843

Closed
angelix opened this issue Nov 6, 2023 · 15 comments · Fixed by #847
Closed

Android 14 breaking change #843

angelix opened this issue Nov 6, 2023 · 15 comments · Fixed by #847

Comments

@angelix
Copy link

angelix commented Nov 6, 2023

Android 14 introduced a breaking change related to MTU negotiation.

This breaking change it may even require a firmware upgrade on the device to properly support the BLE spec in a more strict way.

On the library side, if you try to write more than 512 bytes using the LongWriteOperationBuilder, the write fails silently.

By default the following method is used to calculate the batch size.

//  MtuBasedPayloadSizeLimit.java
...
@Override
    public int getPayloadSizeLimit() {
        return rxBleConnection.getMtu() - gattWriteMtuOverhead;
    }
....

On Android 14 the call to rxBleConnection.getMtu() returns 517.
517 - 3 (GATT_WRITE_MTU_OVERHEAD) = 514 which is larger than the accepted 512.

Based on this very informative issue ticket MTU Overhead should be 5.

As a work around you can use setMaxBatchSize(512) when creating the LongWriteOperationBuilder to use the ConstantPayloadSizeLimit implementation instead of the default MtuBasedPayloadSizeLimit.

@e-Joe
Copy link

e-Joe commented Nov 7, 2023

Do you have some example for setMaxBatchSize(512 ?

@angelix
Copy link
Author

angelix commented Nov 12, 2023

@e-Joe

Example from green_android

@Override
    public int write(final byte[] bytes) {
        this.disposable.add(this.connection
                .flatMap(rxConn -> rxConn.createNewLongWriteBuilder()
                        .setMaxBatchSize(512)
                        .setCharacteristicUuid(IO_TX_CHAR_UUID)
                        .setBytes(bytes)
                        .build())
                .subscribe(this::onBytesSent,
                           this::onSendFailure)
        );
        return bytes.length;
    }

@sravan-wellnesys
Copy link

The work-around doesn't work in Android 14, using this .setMaxBatchSize(512) shows no effect for me, any solution for this yet?

@angelix
Copy link
Author

angelix commented Dec 5, 2023

@sravan-wellnesys keep in mind that this change is for writing to BLE device. Be sure that the BLE device also respect the spec.

@sravan-wellnesys
Copy link

sravan-wellnesys commented Dec 6, 2023

@sravan-wellnesys keep in mind that this change is for writing to BLE device. Be sure that the BLE device also respect the spec.

Can you please explain what exactly needs to be done, as I check this https://issuetracker.google.com/issues/307234027, Android 14 is by default requesting MTU 517, So should we update our peripheral device to respond to MTU 517 or By doing what you said .setMaxBatchSize(512) and configure our peripheral device to respond to MTU 512 even if Android 14 requests MTU 517?

Btw, this is what I am getting in logs:

packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc:768 GATTC_TryMtuRequest: **:**:**:**:49:fe conn_id=0x000d 2023-12-05 17:54:21.669 15046-15098 bluetooth com.google.android.bluetooth I packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc:728 GATTC_ConfigureMTU: Configuring ATT mtu size conn_id:13 mtu:517 user mtu 512 2023-12-05 17:54:21.684 15046-15098 bluetooth com.google.android.bluetooth I packages/modules/Bluetooth/system/stack/gatt/gatt_cl.cc:1113 gatt_process_mtu_rsp: Local pending MTU 512, Remote (**:**:**:**:49:fe) MTU 517 2023-12-05 17:54:21.684 15046-15098 bluetooth com.google.android.bluetooth I packages/modules/Bluetooth/system/stack/gatt/gatt_cl.cc:1133 gatt_process_mtu_rsp: MTU Exchange resulted in: 517 2023-12-05 17:54:21.684 15046-15098 bt_btm_ble com.google.android.bluetooth I packages/modules/Bluetooth/system/stack/btm/btm_ble.cc:619 BTM_SetBleDataLength: xx:xx:xx:xx:49:fe, 516

[ERROR:gatt_cl.cc(693)] value.len larger than GATT_MAX_ATTR_LEN, discard

Thanks for your time.

@angelix
Copy link
Author

angelix commented Dec 6, 2023

Can you please explain what exactly needs to be done, as I check this https://issuetracker.google.com/issues/307234027, Android 14 is by default requesting MTU 517, So should we update our peripheral device to respond to MTU 517 or By doing what you said .setMaxBatchSize(512) and configure our peripheral device to respond to MTU 512 even if Android 14 requests MTU 517?

Your device should respond to MTU request with the value 517, but the actual payload you send must be 512 bytes at maximum.

If the device sends more that 512 bytes, from my experience, Android will silently drop the packet.

On my case i had to firmware update my BLE device to send max 512 bytes, and my Android app to send max 512 bytes.

Explanation:
MTU size includes the header size. The header can be from 3-5 bytes, but you should not really care about that. The crucial part is not to send more that 512 byte.

From the issue:

A deeper reason is that the Bluetooth Specification allows the maximum size of an ATT attribute to be 512 bytes and the largest command ATT_PREPARE_WRITE_REQ has 5 bytes of header. Hence 512 + 5 = 517.
The packet data itself should never exceed 512 as per the Bluetooth spec.

@sravan-wellnesys
Copy link

Can you please explain what exactly needs to be done, as I check this https://issuetracker.google.com/issues/307234027, Android 14 is by default requesting MTU 517, So should we update our peripheral device to respond to MTU 517 or By doing what you said .setMaxBatchSize(512) and configure our peripheral device to respond to MTU 512 even if Android 14 requests MTU 517?

Your device should respond to MTU request with the value 517, but the actual payload you send must be 512 bytes at maximum.

If the device sends more that 512 bytes, from my experience, Android will silently drop the packet.

On my case i had to firmware update my BLE device to send max 512 bytes, and my Android app to send max 512 bytes.

Explanation: MTU size includes the header size. The header can be from 3-5 bytes, but you should not really care about that. The crucial part is not to send more that 512 byte.

From the issue:

A deeper reason is that the Bluetooth Specification allows the maximum size of an ATT attribute to be 512 bytes and the largest command ATT_PREPARE_WRITE_REQ has 5 bytes of header. Hence 512 + 5 = 517.
The packet data itself should never exceed 512 as per the Bluetooth spec.

Thanks a lot for your explanation, Is there a way that I don't send a value in requestMTU?, Do Android 14 still request 517 even if I don't specify a size in request instead of the default 23 bytes.

@angelix
Copy link
Author

angelix commented Dec 7, 2023

Thanks a lot for your explanation, Is there a way that I don't send a value in requestMTU?, Do Android 14 still request 517 even if I don't specify a size in request instead of the default 23 bytes.

Just don't call requestMTU at all.

From the issue:

requestMTU(23) has the same effect as not calling requestMTU() at all, in that case the default MTU of 23 bytes will be used.

@sravan-wellnesys
Copy link

Thanks a lot for your explanation, Is there a way that I don't send a value in requestMTU?, Do Android 14 still request 517 even if I don't specify a size in request instead of the default 23 bytes.

Just don't call requestMTU at all.

From the issue:

requestMTU(23) has the same effect as not calling requestMTU() at all, in that case the default MTU of 23 bytes will be used.

Yep, I am not receiving any bytes, not using requestMTU at all like you said. No data transfer is happening. So firmware upgrade is the only solution at this point? Btw my peripheral device is using Bluetooth 4.2.

@angelix
Copy link
Author

angelix commented Dec 8, 2023

Thanks a lot for your explanation, Is there a way that I don't send a value in requestMTU?, Do Android 14 still request 517 even if I don't specify a size in request instead of the default 23 bytes.

Just don't call requestMTU at all.
From the issue:

requestMTU(23) has the same effect as not calling requestMTU() at all, in that case the default MTU of 23 bytes will be used.

Yep, I am not receiving any bytes, not using requestMTU at all like you said. No data transfer is happening. So firmware upgrade is the only solution at this point? Btw my peripheral device is using Bluetooth 4.2.

I think so.
Try using a different android version and see the size of data the device is sending. If it's more than 512, you can be sure that's your issue.

@sravan-wellnesys
Copy link

sravan-wellnesys commented Dec 11, 2023

Thanks a lot for your explanation, Is there a way that I don't send a value in requestMTU?, Do Android 14 still request 517 even if I don't specify a size in request instead of the default 23 bytes.

Just don't call requestMTU at all.
From the issue:

requestMTU(23) has the same effect as not calling requestMTU() at all, in that case the default MTU of 23 bytes will be used.

Yep, I am not receiving any bytes, not using requestMTU at all like you said. No data transfer is happening. So firmware upgrade is the only solution at this point? Btw my peripheral device is using Bluetooth 4.2.

I think so. Try using a different android version and see the size of data the device is sending. If it's more than 512, you can be sure that's your issue.

I tried with Android 12, When Mtu - 512, I get 509 bytes, when Mtu size - 517, I receive 514 bytes, I thought the max was 512. So app issue or device issue?

@angelix
Copy link
Author

angelix commented Dec 12, 2023

I think is device issue that needs a fix with firmware update, the device should not send more than 512 bytes.

@sravan-wellnesys
Copy link

I think is device issue that needs a fix with firmware update, the device should not send more than 512 bytes.

So anything more than 512 does not work for Android 14, but works for the previous versions?

@angelix
Copy link
Author

angelix commented Dec 12, 2023

So anything more than 512 does not work for Android 14, but works for the previous versions?

Correct, Android 14 is more strict following more strictly the version 5.2 of the Bluetooth Core.

@dariuszseweryn
Copy link
Owner

This is a problem that the library can fix on its side — that is fix the LongWriteOperation (which btw. does not use Prepared Write but chunks data on the application side) to send maximum of 512 bytes of data on Android 13 and above. That way people will not get caught by surprise when using LongWriteOperationBuilder's default chunk size. WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants