ActivityWatch / ActivityWatch/aw-android

Sync aborts the app process (SIGABRT in libaw_sync.so) on Android 16 — syncBoth and syncPush alike

Đang mở
#220 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Kotlin
Star
265
Fork
57
Merge trung bình
2 ngày 21 giờ
Pull request đã merge (30 ngày)
19

Mô tả

## Summary

With sync enabled, the app is killed by `SIGABRT` roughly half a second into every sync cycle. The abort happens inside `libaw_sync.so`, so `performSyncAsync` never returns from `syncFn()` and the SAF mirror added in #209 is never reached — the sync directory is populated internally but nothing is ever copied to the user-chosen SAF directory.

This affects **both** JNI entry points: `syncBoth` and `syncPush` abort identically, so it is not specific to the pull path.

## Environment

- Device: OnePlus Nord CE 3 5G (`CPH2493` / `OP556FL1`), arm64
- Android 16 (`BP2A.250605.015`)
- Built from `master` @ `53a2fb6` (includes #204 and #209), debug variant
- `libaw_sync.so` / `libaw_server.so` taken unmodified from the CI `Build` workflow artifacts

## Steps to reproduce

1. Install a build containing #204 + #209.
2. Open Sync Settings, enable the toggle, choose an SAF directory in shared storage (e.g. `/sdcard/ActivityWatchSync`).
3. Wait ~1 minute for `SyncScheduler`'s first sync.

## Observed

```
08:04:29.565 I SyncInterface: Starting sync operation: Full Sync
08:04:30.277 F DEBUG : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
08:04:30.277 F DEBUG : pid: 25108, tid: 25696, name: pool-6-thread-1
08:04:30.277 F DEBUG : #00 pc 000000000008b70c libc.so (abort+156)
08:04:30.277 F DEBUG : #01 pc 00000000012eb124 base.apk (offset 0x2f30000)
08:04:30.277 F DEBUG : #02 pc 00000000012e7d5c base.apk (offset 0x2f30000)
08:04:30.277 F DEBUG : #03 pc 00000000012d8c80 base.apk (offset 0x2f30000)
08:04:30.277 F DEBUG : #04 pc 00000000012e81f8 base.apk (offset 0x2f30000)
08:04:30.277 F DEBUG : #05 pc 00000000012d7fa4 base.apk (offset 0x2f30000)
08:04:30.277 F DEBUG : #15 pc 00000000006d4c28 base.apk (Java_net_activitywatch_android_SyncInterface_syncBoth+260)
08:04:30.277 F DEBUG : #25 ... SyncInterface$syncBothAsync$2.invoke
08:04:30.277 F DEBUG : #33 ... SyncInterface.performSyncAsync$lambda$4
```

Patching `SyncScheduler.performSync()` to call `syncPushAsync` instead produces the same abort, twice in one capture:

```
08:14:10.059 I SyncInterface: Starting sync operation: Push
08:14:10.579 F libc : Fatal signal 6 (SIGABRT) in tid 8024 (pool-6-thread-1)
#15 ... (Java_net_activitywatch_android_SyncInterface_syncPush+260)
08:15:23.946 I SyncInterface: Starting sync operation: Push
08:15:24.024 F libc : Fatal signal 6 (SIGABRT) in tid 9839 (pool-5-thread-1)
#15 ... (Java_net_activitywatch_android_SyncInterface_syncPush+260)
08:15:24.528 I Zygote : Process 8448 exited due to signal 6 (Aborted)
```

Note both entry points abort at the same `+260` offset.

## What this narrows it down to

The staging tree **is** created before the crash:

```
files/sync///test.db 45056 bytes
```

In `aw-sync/src/sync.rs`, `sync_run` fetches `info` from the local server, takes `info.device_id`, and only then calls `setup_local_remote(path, device_id)`. Since the directory is named with the correct device id, both the `get_info()` call against the in-app `aw-server-rust` and the datastore creation must have succeeded. That places the abort **downstream of both**, in the bucket-sync step itself rather than in startup, the HTTP client, or directory setup.

`SIGABRT` with no fault address is consistent with a Rust panic, but the panic message never reaches `logcat` (Rust writes panics to stderr, which Android discards), and the shipped `.so` has no `.debug_info`, so `addr2line` only resolves to the nearest exported symbol. A debug build, or routing the Rust panic hook to `android_log`, would identify the frame immediately.

## Suggested follow-ups

1. Install a panic hook that logs via `android_log` so panics are diagnosable from `logcat` at all — useful well beyond this bug.
2. Publish a symbol-bearing build (or upload symbols) so tombstones can be symbolicated.

## Secondary observations

Two smaller things found while investigating, happy to open separate issues or PRs if useful:

**`SyncSettingsActivity` is unreachable from the UI.** It is registered only in `res/menu/main.xml` (the options menu), but `MainActivity` never calls `setSupportActionBar()`, the theme is `AppTheme.NoActionBar`, and the `Toolbar` in `res/layout/app_bar_main.xml` has been commented out since `4a02a0b6` (2018). So `onCreateOptionsMenu` inflates a menu with no host and no overflow button is rendered, while `activity_main_drawer.xml` has no sync entry. Adding a `nav_sync_settings` item to the drawer (mirroring `nav_auth_settings`) makes it reachable.

**The SAF mirror in #209 copies nothing even when sync succeeds.** `copySyncFilesToSafDir()` does a flat `File(syncDir).listFiles()?.filter { it.isFile }`, but `aw-sync` never writes a regular file at the root of the sync directory — `setup_local_remote` writes `//test.db`, and on Android the observed tree is `///test.db`. So the filter matches zero entries. The receiving side also requires the nesting: `find_remotes` in `aw-sync/src/util.rs` keeps only directories and looks for `*.db` one level inside them, so a flattened copy would be ignored even if it were made. A recursive, structure-preserving mirror fixes both halves.

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.