fix: survive permanently broken path_provider channel on Android (#192) #194

Merged
guettlibot merged 13 commits from issue-192-fix into main 2026-05-24 01:50:09 +00:00
guettlibot commented 2026-05-24 01:41:39 +00:00 (Migrated from codeberg.org)

Problem

On some Android devices (e.g. Samsung S1RXS32.50-13-25) the app crashes with:

PlatformException(channel-error, path_provider unavailable after 6 attempts — cannot open database.)

Increasing the back-off delays does not help because the path_provider platform channel is permanently unavailable in those contexts — it never becomes ready no matter how long we wait.

Root causes

1. callbackDispatcher missing binding initialization

The WorkManager entry point never called WidgetsFlutterBinding.ensureInitialized(). Without it, Flutter platform channels (path_provider, flutter_secure_storage, …) are not registered in the background isolate. Any call to getApplicationSupportDirectory() in that isolate fails with channel-error regardless of wait time.

2. No fallback when path_provider is permanently broken

_resolveDatabasePath retried with exponential back-off but ultimately gave up and crashed. On some devices path_provider is broken even in the main isolate (device-specific driver/SELinux issue), so retrying indefinitely wouldn't help either.

Fix

  1. callbackDispatcher: add WidgetsFlutterBinding.ensureInitialized() before Workmanager().executeTask(), as required by the workmanager plugin docs.

  2. _resolveDatabasePath Android fallback: after exhausting all back-off retries, read the package name from /proc/self/cmdline (the Android process name equals the package name) and construct the standard app files-dir path (/data/user/0/<pkg>/files/sharedinbox.db) without any platform channel. This matches the path that path_provider_android would have returned.

Tests

  • Existing database_path_test.dart tests continue to pass unchanged.
  • Added _androidFallbackPath returns null when process name is not a package name — asserts the fallback correctly rejects non-Android process names (e.g. the Dart executable path on the host test machine).
  • The "throws after all retries" test is now skipped on Android (where the fallback may succeed instead of throwing).

Closes #192

## Problem On some Android devices (e.g. Samsung S1RXS32.50-13-25) the app crashes with: ``` PlatformException(channel-error, path_provider unavailable after 6 attempts — cannot open database.) ``` Increasing the back-off delays does not help because the `path_provider` platform channel is **permanently unavailable** in those contexts — it never becomes ready no matter how long we wait. ## Root causes ### 1. `callbackDispatcher` missing binding initialization The WorkManager entry point never called `WidgetsFlutterBinding.ensureInitialized()`. Without it, Flutter platform channels (path_provider, flutter_secure_storage, …) are not registered in the background isolate. Any call to `getApplicationSupportDirectory()` in that isolate fails with `channel-error` regardless of wait time. ### 2. No fallback when path_provider is permanently broken `_resolveDatabasePath` retried with exponential back-off but ultimately gave up and crashed. On some devices path_provider is broken even in the main isolate (device-specific driver/SELinux issue), so retrying indefinitely wouldn't help either. ## Fix 1. **`callbackDispatcher`**: add `WidgetsFlutterBinding.ensureInitialized()` before `Workmanager().executeTask()`, as required by the workmanager plugin docs. 2. **`_resolveDatabasePath` Android fallback**: after exhausting all back-off retries, read the package name from `/proc/self/cmdline` (the Android process name equals the package name) and construct the standard app files-dir path (`/data/user/0/<pkg>/files/sharedinbox.db`) without any platform channel. This matches the path that `path_provider_android` would have returned. ## Tests - Existing `database_path_test.dart` tests continue to pass unchanged. - Added `_androidFallbackPath returns null when process name is not a package name` — asserts the fallback correctly rejects non-Android process names (e.g. the Dart executable path on the host test machine). - The "throws after all retries" test is now skipped on Android (where the fallback may succeed instead of throwing). Closes #192
Sign in to join this conversation.