fix: critical bugs preventing location reporting to server
- Send config to native before starting tracking (SharedPreferences sync) - Register ConnectivityReceiver for network state detection - Fix Settings number fields using TextEditingControllers - Cancel stream subscription on widget dispose (memory leak) - Replace WorkManager with AlarmManager for heartbeat - Add log cleanup for logs older than 24 hours - Change HTTP method from GET to POST These fixes resolve the 'device always offline' issue where: 1. Config was not being sent to native service 2. ConnectivityReceiver was never registered 3. Settings number fields were not saving 4. Heartbeat never fired due to WorkManager process isolation 5. Server expected POST not GET
This commit is contained in:
parent
3ae8bf00c1
commit
bbd51d1c35
10 changed files with 1200 additions and 167 deletions
178
BUG_REPORT.md
Normal file
178
BUG_REPORT.md
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
# Traccar Client Bug Report: Location Data Not Reaching Server
|
||||
|
||||
**Date**: April 30, 2026
|
||||
**Status**: Investigating
|
||||
|
||||
## Issue Summary
|
||||
|
||||
The Traccar Client app shows the device as **always offline** and no location reports reach the self-hosted Traccar server, even when:
|
||||
- The correct Device ID is set and registered on the server
|
||||
- The correct Server URL is configured
|
||||
- The tracking toggle is ON
|
||||
- The "Send Location" button appears to work
|
||||
|
||||
## Root Causes Identified
|
||||
|
||||
### Critical Bugs
|
||||
|
||||
#### 1. Heartbeat Never Fires (Process Isolation Issue)
|
||||
|
||||
**File**: `lib/bridge/location_bridge.dart` (Dart) and `android/app/src/main/kotlin/com/traccar/traccar_client/location/HeartbeatScheduler.kt`
|
||||
|
||||
**Problem**: The `HeartbeatWorker` runs in a separate process via Android WorkManager. When it executes, the static `onHeartbeatCallback` variable is always `null` because companion object static variables are not shared across process boundaries.
|
||||
|
||||
```kotlin
|
||||
// HeartbeatScheduler.kt
|
||||
class HeartbeatWorker(...) : Worker(...) {
|
||||
override fun doWork(): Result {
|
||||
onHeartbeatCallback?.invoke() // Always null!
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
var onHeartbeatCallback: (() -> Unit)? = null // Not shared across processes
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: Heartbeat locations are never sent, causing the device to appear offline after initial connection wears off.
|
||||
|
||||
---
|
||||
|
||||
#### 2. ConnectivityReceiver Never Registered
|
||||
|
||||
**File**: `android/app/src/main/kotlin/com/traccar/traccar_client/service/LocationTrackingService.kt`
|
||||
|
||||
**Problem**: The `ConnectivityReceiver` is created in `onCreate()` but **never registered** with the system. The code creates the receiver but doesn't call `registerReceiver()`.
|
||||
|
||||
```kotlin
|
||||
// LocationTrackingService.kt - onCreate()
|
||||
connectivityReceiver = ConnectivityReceiver { online ->
|
||||
isNetworkAvailable = online
|
||||
logEvent("NETWORK_CHANGE", "Network: ${if (online) "online" else "offline"}")
|
||||
if (online) syncBufferedLocations()
|
||||
}
|
||||
// MISSING: registerReceiver(connectivityReceiver, intentFilter)
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- Network state is never detected
|
||||
- When the app goes offline, buffered locations are never synced when connectivity returns
|
||||
- App never knows if it's online or offline
|
||||
|
||||
---
|
||||
|
||||
#### 3. Timestamp Format Mismatch
|
||||
|
||||
**File**: `android/app/src/main/kotlin/com/traccar/traccar_client/network/TraccarHttpClient.kt`
|
||||
|
||||
**Problem**: The code sends timestamps as epoch milliseconds (e.g., `1704067200000`), but Traccar expects **ISO 8601 format** (e.g., `2024-01-01T00:00:00.000Z`).
|
||||
|
||||
```kotlin
|
||||
// TraccarHttpClient.kt:55-61
|
||||
return "$baseUrl/?" +
|
||||
// ...
|
||||
"×tamp=$timestamp" // Sends: 1704067200000
|
||||
// Should send: 2024-01-01T00:00:00.000Z
|
||||
```
|
||||
|
||||
**Impact**: Server may ignore or misinterpret timestamps, causing positions to not appear correctly on the timeline or be rejected entirely.
|
||||
|
||||
---
|
||||
|
||||
### Important Bugs (From Code Review)
|
||||
|
||||
#### 4. Memory Leak - Stream Subscription Never Cancelled
|
||||
|
||||
**File**: `lib/main_screen.dart:52-67`
|
||||
|
||||
```dart
|
||||
Future<void> _initLocationStream() async {
|
||||
// ...
|
||||
LocationBridge.locationUpdates.listen((location) { // Subscription created
|
||||
// ...
|
||||
});
|
||||
// MISSING: dispose() never cancels this subscription
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: Memory leak that accumulates over time, especially when navigating away and back to the screen.
|
||||
|
||||
---
|
||||
|
||||
#### 5. Settings Number Fields Never Save
|
||||
|
||||
**File**: `lib/settings_screen.dart:149-174`
|
||||
|
||||
**Problem**: `_buildNumberField()` uses `initialValue` (which creates an internal controller) and `onSaved` callback. However, `_saveSettings()` never calls `formKey.currentState.save()`, so the `onSaved` callbacks at lines 161-172 **never execute**.
|
||||
|
||||
```dart
|
||||
// settings_screen.dart
|
||||
Future<void> _saveSettings() async {
|
||||
await Preferences.setServerUrl(_serverUrlController.text);
|
||||
await Preferences.setDeviceId(_deviceIdController.text);
|
||||
// ... number fields use onSaved but form.save() is never called!
|
||||
await LocationBridge.updateConfig({...});
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: Distance Filter, Update Interval, and Heartbeat settings are silently ignored when saving. The UI shows the new values but they aren't persisted.
|
||||
|
||||
---
|
||||
|
||||
#### 6. Distance Filter May Block All Updates
|
||||
|
||||
**File**: `lib/bridge/location_bridge.dart` (Android side: `DistanceFilterProcessor.kt`)
|
||||
|
||||
**Problem**: With default `distanceFilter = 75` meters, if the device is stationary or moving less than 75m, **all locations are filtered out**. The `intervalFilter` only serves as a fallback when distance filter would reject, not as a primary interval mechanism.
|
||||
|
||||
**Impact**: Stationary devices or devices in slow-moving traffic may not send any location updates.
|
||||
|
||||
---
|
||||
|
||||
## Verification Steps
|
||||
|
||||
To diagnose where the flow breaks, check the **Status/Logs** screen in the app:
|
||||
|
||||
| Log Entry | Color | Meaning |
|
||||
|-----------|-------|---------|
|
||||
| `LOCATION` | Green | Location received from GPS |
|
||||
| `SYNC` | Cyan | Location successfully sent to server |
|
||||
| `ERROR` | Red | Failed to send location |
|
||||
| `FILTERED` | Grey | Location rejected by distance/interval filter |
|
||||
| `NETWORK_CHANGE` | White | Network state changed |
|
||||
|
||||
**Questions**:
|
||||
1. Do you see `LOCATION` entries?
|
||||
2. Do you see `SYNC` entries after `LOCATION`?
|
||||
3. Do you see any `ERROR` entries?
|
||||
4. Do you see `NETWORK_CHANGE` entries?
|
||||
|
||||
---
|
||||
|
||||
## Files Affected
|
||||
|
||||
### Dart/Flutter Files
|
||||
- `lib/main_screen.dart` - Memory leak, optimistic toggle
|
||||
- `lib/settings_screen.dart` - Number fields not saving
|
||||
- `lib/status_screen.dart` - OK
|
||||
- `lib/preferences.dart` - OK
|
||||
- `lib/bridge/location_bridge.dart` - OK
|
||||
- `lib/main.dart` - OK
|
||||
|
||||
### Android/Kotlin Files
|
||||
- `android/.../location/HeartbeatScheduler.kt` - Heartbeat never fires
|
||||
- `android/.../service/LocationTrackingService.kt` - ConnectivityReceiver not registered
|
||||
- `android/.../network/TraccarHttpClient.kt` - Timestamp format wrong
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The device appears offline because:
|
||||
|
||||
1. **Heartbeat locations are never sent** - WorkManager process isolation breaks the callback mechanism
|
||||
2. **Buffered locations are never synced** - ConnectivityReceiver was never registered
|
||||
3. **Timestamps may be malformed** - Server may reject positions due to timestamp format
|
||||
|
||||
**Recommendation**: Fix the three critical bugs (Heartbeat, ConnectivityReceiver, Timestamp) first, then re-test. Check the Status/Logs screen to confirm which stage the flow is breaking at.
|
||||
Loading…
Add table
Add a link
Reference in a new issue