Skip to content

Improve limitless packet parsing + sync improvements#3776

Merged
mdmohsin7 merged 4 commits into
mainfrom
improve-limitless-parsing
Dec 16, 2025
Merged

Improve limitless packet parsing + sync improvements#3776
mdmohsin7 merged 4 commits into
mainfrom
improve-limitless-parsing

Conversation

@mdmohsin7
Copy link
Copy Markdown
Member

No description provided.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces significant changes to the Limitless device connection, primarily focusing on improving packet parsing and synchronization for both real-time and batch modes. New data structures (_fragmentBuffer, _completedFlashPages) are added to handle fragmented BLE packets and reassemble flash pages. The _handleNotification method has been refactored to incorporate a new _parseBlePacket function, which extracts fragmentation information. New helper methods like _handlePendantMessage, _handleStorageBuffer, _parseFlashPageInfo, _extractOpusFramesFromFlashPage, and _extractOpusRecursive are introduced to process the fragmented data and extract Opus frames and session information. The enableBatchMode, disableBatchMode, and clearBuffer methods are updated to manage the new buffers correctly. Finally, extractFramesWithSessionInfo is enhanced to aggregate data from completed flash pages when in batch mode.

debugPrint(
'Limitless: Batch mode - packet parse failed, data=${data.length}b, first bytes: ${data.take(10).toList()}');
}
_rawDataBuffer.addAll(data);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If _parseBlePacket returns null in batch mode, the raw data is added to _rawDataBuffer. This seems to contradict the new fragmentation logic, which uses _fragmentBuffer for batch mode. If _parseBlePacket fails, it implies the data is not a fragmented packet, but _rawDataBuffer is typically used for real-time mode's frame extraction. This could lead to _rawDataBuffer accumulating unparsed data indefinitely in batch mode, potentially causing memory issues or incorrect data processing if this data was intended to be handled differently.

Comment on lines +118 to +119
final fragment = _fragmentBuffer[index]![i];
if (fragment != null) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The logic if (fragment != null) silently skips fragments if they are missing from _fragmentBuffer[index]!. This can lead to incomplete or corrupted completePayload being passed to _handlePendantMessage without any explicit error or re-request mechanism. Missing fragments in a reassembly process should ideally trigger an error, a re-request, or at least a warning, rather than silently omitting data.

if (opusFrames.isNotEmpty) {
final flashPage = {
'opus_frames': opusFrames,
'timestamp_ms': pageInfo['timestamp_ms'] ?? DateTime.now().millisecondsSinceEpoch,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using DateTime.now().millisecondsSinceEpoch as a fallback timestamp when pageInfo['timestamp_ms'] is null can lead to inconsistencies. If the device's original timestamp is missing or invalid, substituting it with the current app time can misrepresent the actual timing of historical data, especially if device and app clocks are not synchronized. Consider explicitly marking the timestamp as missing or invalid instead of providing a potentially misleading current time.

Comment on lines +244 to +246
} catch (e) {
debugPrint('Limitless: Error handling storage buffer: $e');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Silently ignoring parsing errors in _handleStorageBuffer can hide critical issues related to data corruption or unexpected packet formats. While debugPrint logs the error, the application continues as if no issue occurred, which might lead to incorrect state or data processing. Consider a more robust error handling strategy, such as returning null or throwing a specific exception, to ensure that upstream logic is aware of parsing failures.

Comment on lines +336 to +338
} catch (e) {
// Silently ignore parsing errors
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Silently ignoring parsing errors in _parseFlashPageInfo can obscure problems with the flash page data structure. This can make debugging difficult and lead to incorrect or incomplete session information being processed without any clear indication of failure. It's recommended to at least log these errors with debugPrint to aid in diagnosing issues.

Comment on lines +421 to +423
} catch (e) {
debugPrint('Limitless: Error extracting Opus frames from flash page: $e');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Silently ignoring parsing errors in _extractOpusFramesFromFlashPage can lead to missing or corrupted Opus frames without any clear indication of the problem. This can severely impact audio quality or lead to incomplete recordings. It's recommended to at least log these errors with debugPrint to aid in diagnosing issues.

Comment on lines +457 to +459
// Unknown wire type
break;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _extractOpusRecursive function uses a break statement when an "Unknown wire type" is encountered. This effectively stops parsing without logging or indicating that an unexpected data format was found. This silent failure can lead to incomplete extraction of Opus frames, potentially causing data loss or corruption that is difficult to diagnose. Consider logging this event or handling it more explicitly.

try {
int pos = 0;
int? index;
int seq = 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The seq variable is initialized to 0 and is not checked for nullability in the final return condition (line 599), unlike index, numFrags, and payload. If fieldNum == 2 (which corresponds to seq) is never encountered in the packet, seq will remain 0. If seq is a mandatory part of the packet structure for reassembly, a packet missing this field would be processed with an incorrect sequence number, potentially leading to incorrect fragment reassembly. Consider explicitly checking for its presence or ensuring 0 is a valid default for a missing sequence number.


return {
'opus_frames': allFrames,
'timestamp_ms': timestampMs ?? _firstFlashPageTimestampMs ?? DateTime.now().millisecondsSinceEpoch,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the issue in _handleStorageBuffer, using DateTime.now().millisecondsSinceEpoch as a fallback timestamp in extractFramesWithSessionInfo when timestampMs and _firstFlashPageTimestampMs are null can lead to incorrect or misleading timestamps for aggregated flash pages. This can negatively impact the accurate sequencing and interpretation of recorded data. It's advisable to provide a more explicit indication of a missing or invalid timestamp.

'did_stop_session': didStopSession,
'did_start_recording': didStartRecording,
'did_stop_recording': didStopRecording,
'max_index': maxIndex,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The max_index is calculated and included in the returned map, but there is no subsequent logic to acknowledge this index. If max_index is intended to be used for acknowledging processed data, the acknowledgment mechanism needs to be triggered after extractFramesWithSessionInfo is called. Without proper acknowledgment, the device might resend the same data, leading to redundant processing or potential buffer overflows.

@mdmohsin7 mdmohsin7 marked this pull request as ready for review December 16, 2025 09:41
@mdmohsin7 mdmohsin7 merged commit fa07e89 into main Dec 16, 2025
1 check passed
@mdmohsin7 mdmohsin7 deleted the improve-limitless-parsing branch December 16, 2025 09:50
Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
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 this pull request may close these issues.

1 participant