Android: merge manufacturer data with duplicate company id (don't drop a structure)#258
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces custom parsing of raw BLE advertisement bytes in UniversalBleHelper.kt to extract and merge manufacturer-specific data by company ID, falling back to the standard API if no raw data is parsed. The reviewer recommends adding unit tests to verify this parsing logic, especially for handling duplicate company IDs, and provides a Kotlin test implementation using Mockito.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
@fotiDim could you please look at this? |
|
@ashekochikhin thanks for the PR. I will have a look until Monday. Please hold on. |
There was a problem hiding this comment.
Pull request overview
This PR fixes an Android-specific scan parsing issue where manufacturerDataList could drop one of multiple manufacturer-specific (AD type 0xFF) structures sharing the same company ID by parsing ScanRecord.getBytes() directly and concatenating payloads per company ID.
Changes:
- Update
ScanResult.manufacturerDataListon Android to parse raw scan-record bytes and concatenate all0xFFstructures per company ID (preserving record order), with a fallback to framework parsing when raw bytes are unavailable. - Add Kotlin unit tests covering duplicate-company-ID merging, single MSD parsing, multiple company IDs, and malformed/truncated data behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt | Parse raw scan record bytes to merge duplicate company IDs instead of relying on framework SparseArray behavior. |
| android/src/test/kotlin/com/navideck/universal_ble/UniversalBleHelperTest.kt | Add unit tests validating correct merging and parsing edge cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private fun scanResultWithRawBytes(raw: ByteArray?): ScanResult { | ||
| val scanResult = mock(ScanResult::class.java) | ||
| val scanRecord = mock(ScanRecord::class.java) | ||
| `when`(scanResult.scanRecord).thenReturn(scanRecord) | ||
| `when`(scanRecord.bytes).thenReturn(raw) | ||
| return scanResult |
|
Thanks for the PR! I agree that we shouldn't drop duplicate manufacturer data entries. One thing I'd like to change though is the merging. If we concatenate payloads with the same company ID, we lose the original advertisement structure and end up creating a payload that wasn't actually sent. Since the library API already supports multiple Manufacturer Specific Data entries in a single advertisement, could you instead return a list, one |
Yes, returning one entry per One thing to flag though: it would differ from how the Android framework itself does it. AOSP's So on Android 15+ the platform gives one merged entry, not two. Merging here would keep us consistent with that. What do you think? |
|
In that case I think merging makes more sense. Consistency within a platform is more important than cross-platform consistency. Maybe in the future we merge iOS advertisements as well to be consistent. @rohitsangwan01 what do you think? |
|
@fotiDim yes in that case, i think we can merge as it is |
Fixes #256.
Summary
On Android,
manufacturerDataListis built fromScanRecord.getManufacturerSpecificData()— aSparseArray<byte[]>keyed bycompany id. When one advertisement carries two manufacturer-specific (0xFF) AD
structures with the same company id (e.g. split across ADV_IND and SCAN_RSP),
the framework's
SparseArray.putoverwrites one, so the plugin only ever surfacesa single structure.
This parses the raw
ScanRecord.getBytes()instead and concatenates every 0xFFstructure per company id (record order), so no payload is dropped. Falls back to
the framework
SparseArrayparse when raw bytes are unavailable.Changes
android/.../UniversalBleHelper.kt—ScanResult.manufacturerDataListnow walksthe raw scan-record bytes and merges same-company-id manufacturer data. No public
API change; Android-only.