-
-
Notifications
You must be signed in to change notification settings - Fork 799
Description
Before open an issue
- I have checked the closed issues and haven’t found a similar one.
- I understand that hardware issues cannot be debugged here.
- I have verified my code, installation, and phone permissions. The library is working but there is an inconsistent behavior between iOS and Android.
Describe the bug
I’m seeing a discrepancy in how manufacturer data is reported by react-native-ble-manager on iOS vs. Android when the peripheral includes two separate manufacturer data fields (one in the advertising packet and one in the scan response), both using the same company ID.
- On iOS, the manufacturer data is merged (concatenated) from both advertising and scan response.
- On Android, only the scan response portion is exposed (the advertising portion gets overwritten/ignored).
This makes it difficult to build consistent cross-platform logic in our app that relies on manufacturer data bytes.
To Reproduce
-
Set up a BLE peripheral that advertises two manufacturer data fields under the same company ID
0xAABB:- Advertising Packet (in my case):
FF BB AA 01 02 03 04 (FF for manufacturerData, '01 02 03 04' is the payload) - Scan Response (in my case):
FF BB AA 05 06 07 08 (FF for manufacturerData, '05 06 07 08' is the payload)
- Advertising Packet (in my case):
-
Use react-native-ble-manager (for example, the library’s example app).
-
Log the manufacturer data from the discovered peripheral on both iOS and Android. In my case, I simply changed the
renderItemfunction (of the example app in this repo) to log the platform, manufacturerRawData and rawData:
const renderItem = ({ item }: { item: Peripheral }) => {
const bytesToHexString = (bytes: number[] | undefined) => {
if (!bytes) return '';
return bytes.map(byte => byte.toString(16).padStart(2, '0')).join('');
};
console.log(`
${Platform.OS} - Manufacturer Data: ${bytesToHexString(item.advertising.manufacturerRawData?.bytes)}
${Platform.OS} - Raw Data: ${bytesToHexString(item.advertising.rawData?.bytes)}`);
const backgroundColor = item.connected ? '#069400' : Colors.white;
return (
<TouchableHighlight
underlayColor="#0082FC"
onPress={() => togglePeripheralConnection(item)}
>
<View style={[styles.row, { backgroundColor }]}>
<Text style={styles.peripheralName}>
{/* completeLocalName (item.name) & shortAdvertisingName (advertising.localName) may not always be the same */}
{item.name} - {item?.advertising?.localName}
{item.connecting && ' - Connecting...'}
</Text>
<Text style={styles.rssi}>RSSI: {item.rssi}</Text>
<Text style={styles.peripheralId}>{item.id}</Text>
</View>
</TouchableHighlight>
);
};
-
Observe:
- iOS merges the adv + scan response manufacturer data into one long chunk.
- Android only shows the scan response portion (the adv data is replaced).
Expected behavior
I would expect either:
- Both platforms to present the same final manufacturer data (whether merged or overwritten),
- OR an explicit way to differentiate “advertising” vs. “scan response” data so I can handle them consistently in my app.
Currently, the difference causes confusion and complicates cross-platform development.
Screenshots
The entire raw data was logged on Android (not available on iOS) with item.advertising.rawData?.bytes and converted to hex:
0201060f0941415f393039383736353433323307ffbbaa0102030407ffbbaa05060708000000000000000000000000000000000000000000000000000000
As you can see, the raw data on Android has both the initial adv payload:
ffbbaa01020304
and the scan response
ffbbaa05060708
iOS (Logged in the example app)
Used item.advertising.manufacturerRawData?.bytes and converted to hex.
bbaa0102030405060708
Splitting it out:
bb aa– Company ID (0xAABB, reversed on-air)01 02 03 04– Advertising packet data05 06 07 08– Scan response data
iOS concatenates the advertising and scan response manufacturer data for the same Company ID.
Android (Logged in the example app)
Used item.advertising.manufacturerRawData?.bytes and converted to hex.
0000aabb05060708
Splitting it out:
00 00 aa bb– Company ID (not sure why zero-padded)05 06 07 08– Scan response data
The advertising data chunk (05 06 07 08) related to the manufacturer data is missing on Android because it’s overwritten by the scan response for the same Company ID.
Smartphone
- Device: iPhone 14 Pro, Xiaomi Mi11i
- OS: iOS 18.1.1, Android 13
- react-native-ble-manager version: 12.1.2
- react-native version: example app of this library
Additional context
- The usecase of having multiple manufacturerData with the same Company ID is useful when the custom data that needs to be sent from the peripheral is larger than what fits into the advertising packet. In this case, the scan response can be used to send longer payloads.
- This issue arises from differences in how the iOS BLE stack merges adv + scan response data and how Android overwrites it.
- Ideally, react-native-ble-manager could unify or provide an API to handle these data collisions consistently.
- Thank you for this library and for any guidance on handling this scenario!
P.S. I have a patch locally that addresses this issue, but I am not sure it's the best way yet. Will keep working on it and create a PR soon, any help from the maintainers is welcome, as I have not worked on native Android code before.