fix: Android 9 crash - Implement getForegroundInfo() for WorkManager Expedited Work (Issue #15)
This commit fixes the critical crash on Android 9 (API 28) that occurred when using WorkManager Expedited Work for background sync operations. ## Root Cause When setExpedited() is used in WorkManager, the CoroutineWorker must implement getForegroundInfo() to return a ForegroundInfo object with a Foreground Service notification. On Android 9-11, WorkManager calls this method, but the default implementation throws: IllegalStateException: Not implemented ## Solution - Implemented getForegroundInfo() in SyncWorker - Returns ForegroundInfo with sync progress notification - Android 10+: Sets FOREGROUND_SERVICE_TYPE_DATA_SYNC for proper service typing - Added required Foreground Service permissions to AndroidManifest.xml ## Technical Changes - SyncWorker.kt: Added getForegroundInfo() override - NotificationHelper.kt: Added createSyncProgressNotification() factory method - strings.xml: Added sync_in_progress UI strings (EN + DE) - AndroidManifest.xml: Added FOREGROUND_SERVICE permissions - Version updated to 1.7.1 (versionCode 18) ## Previously Fixed (in this release) - Kernel-VPN compatibility (Wireguard interface detection) - HTTP connection lifecycle optimization (SafeSardineWrapper) - Stability improvements for sync sessions ## Testing - Tested on Android 9 (API 28) - No crash on second app start - Tested on Android 15 (API 35) - No regressions - WiFi-connect sync working correctly - Expedited work notifications display properly Fixes #15 Thanks to @roughnecks for detailed bug report and testing!
This commit is contained in:
@@ -8,19 +8,30 @@ Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## [1.7.1] - 2026-01-30
|
## [1.7.1] - 2026-02-02
|
||||||
|
|
||||||
### 🐛 Kritische Fehlerbehebungen
|
### 🐛 Kritische Fehlerbehebungen
|
||||||
|
|
||||||
- **App-Absturz auf Android 9 nach längerer Nutzung behoben** ([ref #15](https://github.com/inventory69/simple-notes-sync/issues/15))
|
#### Android 9 App-Absturz Fix ([#15](https://github.com/inventory69/simple-notes-sync/issues/15))
|
||||||
- Ressourcenerschöpfung durch nicht geschlossene HTTP-Verbindungen behoben
|
|
||||||
- App konnte nach ~30-45 Minuten Nutzung durch angesammelte Connection-Leaks abstürzen
|
|
||||||
- Danke an [@roughnecks] für den detaillierten Fehlerbericht!
|
|
||||||
|
|
||||||
- **VPN-Kompatibilitäts-Regression behoben** ([ref #11](https://github.com/inventory69/simple-notes-sync/issues/11))
|
**Problem:** App stürzte auf Android 9 (API 28) ab wenn WorkManager Expedited Work für Hintergrund-Sync verwendet wurde.
|
||||||
- WiFi Socket-Binding erkennt jetzt korrekt Wireguard VPN-Interfaces (tun*, wg*, *-wg-*)
|
|
||||||
- Traffic wird korrekt durch VPN-Tunnel geleitet statt direkt über WiFi
|
**Root Cause:** Wenn `setExpedited()` in WorkManager verwendet wird, muss die `CoroutineWorker` die Methode `getForegroundInfo()` implementieren um eine Foreground Service Notification zurückzugeben. Auf Android 9-11 ruft WorkManager diese Methode auf, aber die Standard-Implementierung wirft `IllegalStateException: Not implemented`.
|
||||||
- Behebt "Verbindungs-Timeout" beim Sync zu externen Servern über VPN
|
|
||||||
|
**Lösung:** `getForegroundInfo()` in `SyncWorker` implementiert um eine korrekte `ForegroundInfo` mit Sync-Progress-Notification zurückzugeben.
|
||||||
|
|
||||||
|
**Details:**
|
||||||
|
- `ForegroundInfo` mit Sync-Progress-Notification für Android 9-11 hinzugefügt
|
||||||
|
- Android 10+: Setzt `FOREGROUND_SERVICE_TYPE_DATA_SYNC` für korrekte Service-Typisierung
|
||||||
|
- Foreground Service Permissions in AndroidManifest.xml hinzugefügt
|
||||||
|
- Notification zeigt Sync-Progress mit indeterminiertem Progress Bar
|
||||||
|
- Danke an [@roughnecks](https://github.com/roughnecks) für das detaillierte Debugging!
|
||||||
|
|
||||||
|
#### VPN-Kompatibilitäts-Fix ([#11](https://github.com/inventory69/simple-notes-sync/issues/11))
|
||||||
|
|
||||||
|
- WiFi Socket-Binding erkennt jetzt korrekt Wireguard VPN-Interfaces (tun*, wg*, *-wg-*)
|
||||||
|
- Traffic wird korrekt durch VPN-Tunnel geleitet statt direkt über WiFi
|
||||||
|
- Behebt "Verbindungs-Timeout" beim Sync zu externen Servern über VPN
|
||||||
|
|
||||||
### 🔧 Technische Änderungen
|
### 🔧 Technische Änderungen
|
||||||
|
|
||||||
@@ -28,10 +39,12 @@ Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Weniger unnötige 401-Authentifizierungs-Challenges durch preemptive Auth-Header
|
- Weniger unnötige 401-Authentifizierungs-Challenges durch preemptive Auth-Header
|
||||||
- ProGuard-Regel hinzugefügt um harmlose TextInclusionStrategy-Warnungen zu unterdrücken
|
- ProGuard-Regel hinzugefügt um harmlose TextInclusionStrategy-Warnungen zu unterdrücken
|
||||||
- VPN-Interface-Erkennung via `NetworkInterface.getNetworkInterfaces()` Pattern-Matching
|
- VPN-Interface-Erkennung via `NetworkInterface.getNetworkInterfaces()` Pattern-Matching
|
||||||
|
- Foreground Service Erkennung und Notification-System für Hintergrund-Sync-Tasks
|
||||||
|
|
||||||
### 🌍 Lokalisierung
|
### 🌍 Lokalisierung
|
||||||
|
|
||||||
- Hardcodierte deutsche Fehlermeldungen behoben - jetzt String-Resources für korrekte Lokalisierung
|
- Hardcodierte deutsche Fehlermeldungen behoben - jetzt String-Resources für korrekte Lokalisierung
|
||||||
|
- Deutsche und englische Strings für Sync-Progress-Notifications hinzugefügt
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
31
CHANGELOG.md
31
CHANGELOG.md
@@ -8,19 +8,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## [1.7.1] - 2026-01-30
|
## [1.7.1] - 2026-02-02
|
||||||
|
|
||||||
### 🐛 Critical Bug Fixes
|
### 🐛 Critical Bug Fixes
|
||||||
|
|
||||||
- **Fixed app crash on Android 9 after extended use** ([ref #15](https://github.com/inventory69/simple-notes-sync/issues/15))
|
#### Android 9 App Crash Fix ([#15](https://github.com/inventory69/simple-notes-sync/issues/15))
|
||||||
- Fixed resource exhaustion caused by unclosed HTTP connections
|
|
||||||
- App could crash after ~30-45 minutes of use due to accumulated connection leaks
|
|
||||||
- Thanks to [@roughnecks] for the detailed bug report!
|
|
||||||
|
|
||||||
- **Fixed VPN compatibility regression** ([ref #11](https://github.com/inventory69/simple-notes-sync/issues/11))
|
**Problem:** App crashed on Android 9 (API 28) when using WorkManager Expedited Work for background sync.
|
||||||
- WiFi socket binding now correctly detects Wireguard VPN interfaces (tun*, wg*, *-wg-*)
|
|
||||||
- Traffic routes through VPN tunnel instead of bypassing it directly to WiFi
|
**Root Cause:** When `setExpedited()` is used in WorkManager, the `CoroutineWorker` must implement `getForegroundInfo()` to return a Foreground Service notification. On Android 9-11, WorkManager calls this method, but the default implementation throws `IllegalStateException: Not implemented`.
|
||||||
- Fixes "Connection timeout" when syncing to external servers via VPN
|
|
||||||
|
**Solution:** Implemented `getForegroundInfo()` in `SyncWorker` to return a proper `ForegroundInfo` with sync progress notification.
|
||||||
|
|
||||||
|
**Details:**
|
||||||
|
- Added `ForegroundInfo` with sync progress notification for Android 9-11
|
||||||
|
- Android 10+: Sets `FOREGROUND_SERVICE_TYPE_DATA_SYNC` for proper service typing
|
||||||
|
- Added Foreground Service permissions to AndroidManifest.xml
|
||||||
|
- Notification shows sync progress with indeterminate progress bar
|
||||||
|
- Thanks to [@roughnecks](https://github.com/roughnecks) for the detailed debugging!
|
||||||
|
|
||||||
|
#### VPN Compatibility Fix ([#11](https://github.com/inventory69/simple-notes-sync/issues/11))
|
||||||
|
|
||||||
|
- WiFi socket binding now correctly detects Wireguard VPN interfaces (tun*, wg*, *-wg-*)
|
||||||
|
- Traffic routes through VPN tunnel instead of bypassing it directly to WiFi
|
||||||
|
- Fixes "Connection timeout" when syncing to external servers via VPN
|
||||||
|
|
||||||
### 🔧 Technical Changes
|
### 🔧 Technical Changes
|
||||||
|
|
||||||
@@ -28,10 +39,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Reduced unnecessary 401 authentication challenges with preemptive auth headers
|
- Reduced unnecessary 401 authentication challenges with preemptive auth headers
|
||||||
- Added ProGuard rule to suppress harmless TextInclusionStrategy warnings on older Android versions
|
- Added ProGuard rule to suppress harmless TextInclusionStrategy warnings on older Android versions
|
||||||
- VPN interface detection via `NetworkInterface.getNetworkInterfaces()` pattern matching
|
- VPN interface detection via `NetworkInterface.getNetworkInterfaces()` pattern matching
|
||||||
|
- Foreground Service detection and notification system for background sync tasks
|
||||||
|
|
||||||
### 🌍 Localization
|
### 🌍 Localization
|
||||||
|
|
||||||
- Fixed hardcoded German error messages - now uses string resources for proper localization
|
- Fixed hardcoded German error messages - now uses string resources for proper localization
|
||||||
|
- Added German and English strings for sync progress notifications
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId = "dev.dettmer.simplenotes"
|
applicationId = "dev.dettmer.simplenotes"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 18 // 🔧 v1.7.1: Connection Leak Fix (Issue #15)
|
versionCode = 18 // 🔧 v1.7.1: Android 9 getForegroundInfo Fix (Issue #15)
|
||||||
versionName = "1.7.1" // 🔧 v1.7.1: Connection Leak Fix
|
versionName = "1.7.1" // 🔧 v1.7.1: Android 9 getForegroundInfo Fix
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
<!-- Battery Optimization (for WorkManager background sync) -->
|
<!-- Battery Optimization (for WorkManager background sync) -->
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
|
<!-- v1.7.2: Foreground Service for Expedited Work (Android 9-11) -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<!-- v1.7.2: Foreground Service Type for Android 10+ -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
<!-- BOOT Permission - CRITICAL für Auto-Sync nach Neustart! -->
|
<!-- BOOT Permission - CRITICAL für Auto-Sync nach Neustart! -->
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ package dev.dettmer.simplenotes.sync
|
|||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.ServiceInfo
|
||||||
|
import android.os.Build
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import dev.dettmer.simplenotes.BuildConfig
|
import dev.dettmer.simplenotes.BuildConfig
|
||||||
import dev.dettmer.simplenotes.utils.Constants
|
import dev.dettmer.simplenotes.utils.Constants
|
||||||
@@ -26,6 +29,35 @@ class SyncWorker(
|
|||||||
const val ACTION_SYNC_COMPLETED = "dev.dettmer.simplenotes.SYNC_COMPLETED"
|
const val ACTION_SYNC_COMPLETED = "dev.dettmer.simplenotes.SYNC_COMPLETED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 🔧 v1.7.2: Required for expedited work on Android 9-11
|
||||||
|
*
|
||||||
|
* WorkManager ruft diese Methode auf um die Foreground-Notification zu erstellen
|
||||||
|
* wenn der Worker als Expedited Work gestartet wird.
|
||||||
|
*
|
||||||
|
* Ab Android 12+ wird diese Methode NICHT aufgerufen (neue Expedited API).
|
||||||
|
* Auf Android 9-11 MUSS diese Methode implementiert sein!
|
||||||
|
*
|
||||||
|
* @see https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#foregroundinfo
|
||||||
|
*/
|
||||||
|
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||||
|
val notification = NotificationHelper.createSyncProgressNotification(applicationContext)
|
||||||
|
|
||||||
|
// Android 10+ benötigt foregroundServiceType
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
ForegroundInfo(
|
||||||
|
NotificationHelper.SYNC_PROGRESS_NOTIFICATION_ID,
|
||||||
|
notification,
|
||||||
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ForegroundInfo(
|
||||||
|
NotificationHelper.SYNC_PROGRESS_NOTIFICATION_ID,
|
||||||
|
notification
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft ob die App im Vordergrund ist.
|
* Prüft ob die App im Vordergrund ist.
|
||||||
* Wenn ja, brauchen wir keine Benachrichtigung - die UI zeigt die Änderungen direkt.
|
* Wenn ja, brauchen wir keine Benachrichtigung - die UI zeigt die Änderungen direkt.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ object NotificationHelper {
|
|||||||
private const val CHANNEL_ID = "notes_sync_channel"
|
private const val CHANNEL_ID = "notes_sync_channel"
|
||||||
private const val NOTIFICATION_ID = 1001
|
private const val NOTIFICATION_ID = 1001
|
||||||
private const val SYNC_NOTIFICATION_ID = 2
|
private const val SYNC_NOTIFICATION_ID = 2
|
||||||
|
const val SYNC_PROGRESS_NOTIFICATION_ID = 1003 // v1.7.2: For expedited work foreground notification
|
||||||
private const val AUTO_CANCEL_TIMEOUT_MS = 30_000L
|
private const val AUTO_CANCEL_TIMEOUT_MS = 30_000L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,6 +55,26 @@ object NotificationHelper {
|
|||||||
Logger.d(TAG, "🗑️ Cleared old sync notifications")
|
Logger.d(TAG, "🗑️ Cleared old sync notifications")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 🔧 v1.7.2: Erstellt Notification für Sync-Progress (Expedited Work)
|
||||||
|
*
|
||||||
|
* Wird von SyncWorker.getForegroundInfo() aufgerufen auf Android 9-11.
|
||||||
|
* Muss eine gültige, sichtbare Notification zurückgeben.
|
||||||
|
*
|
||||||
|
* @return Notification (nicht anzeigen, nur erstellen)
|
||||||
|
*/
|
||||||
|
fun createSyncProgressNotification(context: Context): android.app.Notification {
|
||||||
|
return NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
|
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||||
|
.setContentTitle(context.getString(R.string.sync_in_progress))
|
||||||
|
.setContentText(context.getString(R.string.sync_in_progress_text))
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setProgress(0, 0, true) // Indeterminate progress
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeigt Erfolgs-Notification nach Sync
|
* Zeigt Erfolgs-Notification nach Sync
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -438,6 +438,8 @@
|
|||||||
<!-- ============================= -->
|
<!-- ============================= -->
|
||||||
<string name="notification_channel_name">Notizen Synchronisierung</string>
|
<string name="notification_channel_name">Notizen Synchronisierung</string>
|
||||||
<string name="notification_channel_desc">Benachrichtigungen über Sync-Status</string>
|
<string name="notification_channel_desc">Benachrichtigungen über Sync-Status</string>
|
||||||
|
<string name="sync_in_progress">Synchronisierung läuft</string>
|
||||||
|
<string name="sync_in_progress_text">Notizen werden synchronisiert…</string>
|
||||||
<string name="notification_sync_success_title">Sync erfolgreich</string>
|
<string name="notification_sync_success_title">Sync erfolgreich</string>
|
||||||
<string name="notification_sync_success_message">%d Notiz(en) synchronisiert</string>
|
<string name="notification_sync_success_message">%d Notiz(en) synchronisiert</string>
|
||||||
<string name="notification_sync_failed_title">Sync fehlgeschlagen</string>
|
<string name="notification_sync_failed_title">Sync fehlgeschlagen</string>
|
||||||
|
|||||||
@@ -438,6 +438,8 @@
|
|||||||
<!-- ============================= -->
|
<!-- ============================= -->
|
||||||
<string name="notification_channel_name">Notes Synchronization</string>
|
<string name="notification_channel_name">Notes Synchronization</string>
|
||||||
<string name="notification_channel_desc">Notifications about sync status</string>
|
<string name="notification_channel_desc">Notifications about sync status</string>
|
||||||
|
<string name="sync_in_progress">Syncing</string>
|
||||||
|
<string name="sync_in_progress_text">Syncing notes…</string>
|
||||||
<string name="notification_sync_success_title">Sync successful</string>
|
<string name="notification_sync_success_title">Sync successful</string>
|
||||||
<string name="notification_sync_success_message">%d note(s) synchronized</string>
|
<string name="notification_sync_success_message">%d note(s) synchronized</string>
|
||||||
<string name="notification_sync_failed_title">Sync failed</string>
|
<string name="notification_sync_failed_title">Sync failed</string>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
• Behoben: App-Absturz auf Android 9 - Danke an @roughnecks
|
• Behoben: App-Absturz auf Android 9 (Issue #15) - Danke an @roughnecks
|
||||||
• Behoben: Kernel-VPN-Kompatibilität (Wireguard)
|
- WorkManager Expedited Work Kompatibilität (getForegroundInfo)
|
||||||
• Verbessert: Stabilität der Sync-Sessions
|
- Kernel-VPN-Kompatibilität (Wireguard tun/wg Interfaces)
|
||||||
• Technisch: Optimierte Verbindungsverwaltung
|
• Verbessert: Stabilität und Verbindungsverwaltung
|
||||||
|
• Technisch: Optimierter HTTP-Connection-Lebenszyklus
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
• Fixed: App crash on Android 9 - Thanks to @roughnecks
|
• Fixed: App crash on Android 9 (Issue #15) - Thanks to @roughnecks
|
||||||
• Fixed: Kernel-VPN compatibility (Wireguard)
|
- WorkManager expedited work compatibility (getForegroundInfo)
|
||||||
• Improved: Stability at sync sessions
|
- Kernel-VPN compatibility (Wireguard tun/wg interfaces)
|
||||||
• Technical: Optimized connection management
|
• Improved: Stability and connection management
|
||||||
|
• Technical: Optimized HTTP connection lifecycle
|
||||||
|
|||||||
Reference in New Issue
Block a user