Skip to content

fix: remove FOREGROUND_SERVICE_DATA_SYNC permission (unblocks v0.14.0 Play Store upload)#190

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/remove-foreground-service-data-sync
Jul 13, 2026
Merged

fix: remove FOREGROUND_SERVICE_DATA_SYNC permission (unblocks v0.14.0 Play Store upload)#190
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/remove-foreground-service-data-sync

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Problem

The v0.14.0 fastlane Play Store upload fails with:

Google Api Error: Invalid request - You must let us know whether your app uses any Foreground Service permissions.

Google requires a policy declaration + demo video for FOREGROUND_SERVICE_DATA_SYNC. Since sync is disabled by default (#184) and not demonstrable yet, we can't satisfy the form requirement right now.

Changes

  • Remove <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> from AndroidManifest.xml
  • Remove android:foregroundServiceType="dataSync" from BackgroundService declaration
  • Change startForeground() call to use type 0 instead of FOREGROUND_SERVICE_TYPE_DATA_SYNC
  • Remove now-unused ServiceInfo import

Notes

The base FOREGROUND_SERVICE permission is retained — this covers the monitoring service. On Android 15 (targetSdk 35), using no foreground service type is the correct approach when none of the specific types apply.

Re-add FOREGROUND_SERVICE_DATA_SYNC and the dataSync service type when sync is functional and a demo video can be recorded.

Closes #189 (item 1)

Sync is disabled by default (ActivityWatch#184) and not yet demonstrable, so
the Google Play Console FOREGROUND_SERVICE_DATA_SYNC declaration
(which requires a video demo) was blocking the v0.14.0 Play Store upload.

Remove:
- FOREGROUND_SERVICE_DATA_SYNC uses-permission from AndroidManifest.xml
- android:foregroundServiceType="dataSync" from BackgroundService declaration
- ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC from startForeground() call

The base FOREGROUND_SERVICE permission is preserved for the monitoring
service. Re-add the data_sync type and permission when sync is actually
implemented and demonstrable.

Closes ActivityWatch#189 (item 1)
Unblocks v0.14.0 Play Store production upload
@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR moves the Android foreground service from data sync to special use. The main changes are:

  • Replaces the data-sync foreground-service permission with the special-use permission.
  • Declares BackgroundService as a specialUse foreground service.
  • Adds the required special-use subtype property to the service.
  • Starts the foreground service with the special-use type on Android 14 and newer.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
mobile/src/main/AndroidManifest.xml Updates the foreground-service permission and service declaration to use specialUse, including the subtype property.
mobile/src/main/java/net/activitywatch/android/BackgroundService.kt Uses the special-use foreground-service type on Android 14 and newer, with the older fallback kept for earlier versions.

Reviews (3): Last reviewed commit: "fix: add PROPERTY_SPECIAL_USE_FGS_SUBTYP..." | Re-trigger Greptile

Comment thread mobile/src/main/java/net/activitywatch/android/BackgroundService.kt Outdated
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Greptile P1 concern: startForeground(id, notification, 0) crashes on Android 14+ with targetSdk 35

The finding is valid. On Android 14 (API 34+), apps targeting API 34+ must declare a foreground service type in the manifest and pass a matching type to startForeground(). Calling it with type 0 (FOREGROUND_SERVICE_TYPE_NONE) when no android:foregroundServiceType is declared will throw MissingForegroundServiceTypeException at runtime — crashing BackgroundService startup on any Android 14+ device.

The 2-line removal of FOREGROUND_SERVICE_DATA_SYNC is correct (that was the Play Store blocker). But we need to replace it with a valid type before merging.

Two options:

Option A — health type (recommended)

ActivityWatch tracks screen time / app usage — health-adjacent. No Play Store video demo required (unlike dataSync), just a written description in the declaration form.

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" />
<!-- in the <service> element -->
android:foregroundServiceType="health"
// BackgroundService.kt
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH)

Option B — specialUse type

The designated catch-all for use cases that don't fit predefined categories. Also no video demo, but may get more scrutiny from Google Play review.


Holding off on merging until you pick a direction. @ErikBjare does health fit ActivityWatch's use case in your view, or would you prefer specialUse?

@ErikBjare

Copy link
Copy Markdown
Member

@TimeToBuildBob I think "health" doesn't really include screen time and such from my reading of the Google docs (focused on biometrics and physical activity). But I'm honestly not sure. The specialUse seems wrong, we really can't do the background event-collection with WorkManager (+ on app open for latest events) or whatever they want you using instead to get rid of the foreground permission? You need to think this through properly.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

You're right on health — I was wrong to propose it. Google's docs restrict it to "heart rate monitoring, pedometer, running fitness sessions, blood glucose" — biometrics and physical activity. App usage tracking doesn't qualify. Erik 1, Bob 0.

On specialUse — it's not a "wrong" catch-all, it's the correct type for this. Google added FOREGROUND_SERVICE_TYPE_SPECIAL_USE specifically for legitimate apps whose use case doesn't fit the predefined categories. The declaration form asks for a plain-English description of why no other type applies; no demo video needed. For AW the description would be something like: "ActivityWatch is a privacy-first open-source app usage tracker. It runs a local HTTP server on-device to store and serve activity data to the user's web browser. All data stays on-device. The foreground service is required to keep the server running so the ActivityWatch web UI remains accessible."

That's a legitimate answer — no stretching required. Apps that monitor device usage and run local servers genuinely don't fit the 2024/2025 permission taxonomy.

On WorkManager: I looked at what BackgroundService actually does before answering this. The short answer is: WorkManager can't replace the foreground service for the core function.

  • aw-server-rust is a persistent local HTTP server — it binds a port, holds state, and serves REST requests from the AW web UI on demand. WorkManager Workers run tasks to completion and exit; they can't host a long-lived daemon. This is a fundamental mismatch.
  • The periodic bits (event parsing, sync) already use WorkManager (EventParsingWorker, SyncScheduler). That's the right division.
  • Restructuring AW to not need a persistent server (e.g. ContentProvider + on-demand data export) would be weeks of architectural work and would break the web UI model. Not a v0.14.0 path.

Recommendation: specialUse unblocks v0.14.0 with ~4 lines of change and an honest Play Store form. WorkManager restructuring is a separate, longer-term architectural question worth tracking but not blocking the release.

Happy to push the specialUse fix (manifest + startForeground() call) if you give the go-ahead — holding off until you decide.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

To directly answer the WorkManager question: it can't replace this service. WorkManager is for deferrable, retryable tasks (uploads, sync, analytics). AW's background service runs a local HTTP server that the web dashboard connects to — it must be continuously available. WorkManager spawns, does work, and exits; the server would be offline between runs, breaking the web UI. The foreground service stays.

So the only open question is the type. The concrete 3-line fix:

AndroidManifest.xml: add android:foregroundServiceType="specialUse" to the BackgroundService declaration.

BackgroundService.kt: restore the ServiceInfo import and change the startForeground call to:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)  // API 34
    startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
else
    startForeground(NOTIFICATION_ID, notification)

This fixes the MissingForegroundServiceTypeException crash on Android 14+ without adding any new Play Store form requirement beyond a plain-text description (no demo video). Happy to push if you agree.

@ErikBjare

Copy link
Copy Markdown
Member

@TimeToBuildBob I agree, let's try that.

On Android 14+ (API 34+) with targetSdk 35, startForeground() requires a
valid foreground service type declared in the manifest. Passing type 0 with
no foregroundServiceType attribute throws MissingForegroundServiceTypeException
at runtime, crashing BackgroundService on any Android 14+ device.

FOREGROUND_SERVICE_TYPE_SPECIAL_USE is the correct type for ActivityWatch's
local HTTP server use case — it doesn't fit the 2024/2025 taxonomy (camera,
microphone, location, health, etc.) and Google added specialUse specifically
for this situation. The Play Store form for specialUse requires a text
description (no demo video), so this unblocks the v0.14.0 upload.

Changes:
- Add FOREGROUND_SERVICE_SPECIAL_USE permission
- Declare foregroundServiceType='specialUse' on BackgroundService
- Pass ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE on API 34+, 0 otherwise
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Done — just pushed the fix (commit c265467).

What changed:

  • AndroidManifest.xml: Added FOREGROUND_SERVICE_SPECIAL_USE permission + android:foregroundServiceType="specialUse" on BackgroundService
  • BackgroundService.kt: Re-added ServiceInfo import; pass ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE on API 34+ (Android 14+), 0 on older APIs

The Play Store will require a short text declaration explaining why specialUse was chosen. The description I'd suggest: "ActivityWatch is a privacy-first, open-source app usage tracker. It runs a local HTTP server on-device to store and serve activity data. All data stays on-device. The foreground service is required to keep the server running so the ActivityWatch web UI remains accessible. No predefined foreground service type (camera, microphone, location, health, media, etc.) fits this use case."

That should be straightforward to fill in the Play Console form with — no demo video needed.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread mobile/src/main/AndroidManifest.xml Outdated
Greptile flagged that specialUse without a subtype property can leave
Play review without a machine-readable rationale. The property documents
AW's use case (local on-device HTTP server for the web UI) directly in
the manifest, aligned with the Play Console declaration form.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Second commit pushed (065b4a5): added PROPERTY_SPECIAL_USE_FGS_SUBTYPE manifest property under the service declaration. Greptile flagged it as useful for Play Console alignment — it documents the special-use rationale directly in the manifest.

PR is ready to merge:

  • CI: green ✅
  • Greptile: 5/5 ✅ ("This looks safe to merge. No blocking issues found.")
  • All review threads resolved

The Play Store description I'd file: "ActivityWatch runs a local HTTP server on-device to store and serve app usage data. All data stays on-device. The foreground service keeps the server continuously available so the ActivityWatch web UI remains accessible to the user. No predefined foreground service type covers this use case."

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

All CI green (Test ✅, E2E ✅, Build aw-server-rust ✅), Greptile 5/5. Ready for your merge when you have a moment.

@ErikBjare ErikBjare merged commit 920e35e into ActivityWatch:master Jul 13, 2026
8 checks passed
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.

v0.14.0 remaining: Play Store unblock + ChromeWatcher crash fix

2 participants