Skip to content

Consolidate system-app settings into one "Monitor system apps" toggle - #656

Merged
kasnder merged 2 commits into
masterfrom
agent/653-system-apps-toggle
Jul 12, 2026
Merged

Consolidate system-app settings into one "Monitor system apps" toggle#656
kasnder merged 2 commits into
masterfrom
agent/653-system-apps-toggle

Conversation

@kasnder

@kasnder kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member

Part of #653

UX simplification

System-app handling was spread across three interacting preferences:

Pref Meaning Read at
include_system_vpn Route system apps through the VPN tun ServiceSinkhole.getBuilder / getBlockingBuilder (addDisallowedApplication when !includeSystem && rule.system)
manage_system Apply tracker detection/blocking to system apps; gate the app-list filter menu + hint ServiceSinkhole.shouldTrackApp (via cachedManageSystem), ActivityMain (menu/hint), ActivitySettings
show_system Show system apps in the app list and Insights Rule.getRules, ActivityMain filter menu, InsightsDataProvider

The user saw two switches in Settings (include_system_vpn + its dependent manage_system) plus a separate overflow-menu filter that flipped show_system. The three were already partly cascaded, but the model was hard to reason about.

This PR presents one user-facing toggle, "Monitor system apps", backed by include_system_vpn. When it changes, ActivitySettings writes manage_system and show_system to match, so routing, detection and visibility always move together. All existing read sites are untouched — they keep reading the derived prefs, which minimizes risk. The manage_system switch is removed from preferences.xml. The Apps overflow menu's separate "system apps" filter item is also removed (in ActivityMain), since visibility now follows the single toggle and a second control there could desynchronise show_system; the "user apps" filter item remains.

The toggle summary preserves and documents the trade-off: off is battery-friendly (system-app background traffic no longer wakes the VPN packet threads), on conflicts with Android's "Block connections without VPN", which must be disabled.

Migration rule

ReceiverAutostart.upgrade gains a block for oldVersion < 2026071202 that collapses the three prefs into the single toggle without silently enabling blocking:

  • The toggle turns on only when the user previously both routed and managed system apps: monitorSystem = include_system_vpn && manage_system. A user who routed system apps but had deliberately left manage_system off is not opted into tracker detection/blocking.
  • All three prefs (include_system_vpn, manage_system, show_system) are then written to monitorSystem, so routing, detection and visibility stay coherent; show_user is forced back on when the toggle ends up off.
  • Very old installs (oldVersion < 2026010401) are handled before the batched editor is committed: the earlier manage_system → include_system_vpn migration only writes to the pending editor, so this block re-derives include_system_vpn from manage_system directly to avoid reading a stale committed value.

Net effect: installs that both routed and managed system apps keep monitoring them; every other install ends up with the toggle off. A user who routed-without-managing loses system-app routing — that is the intended coherent semantics of a single toggle (routing without detection is no longer expressible), and is the only behavior change, documented here rather than applied silently.

Base values/strings.xml only (setting_monitor_system / summary_monitor_system); translation dirs left to Crowdin. No per-flavour preferences.xml overrides exist (github/fdroid/play share the main one). Orphaned setting_system/summary_system strings are left in place to avoid churn.

Measurement plan (wakeups, not throughput)

The battery cost of including system apps is wakeup frequency, not tun MB/s: background traffic from Play Services/Store/carrier apps traverses the tun and wakes the packet threads (with WG egress up, TC absorbs radio-wakeup blame roughly every ~2.5 min). Measure it as follows, holding everything else fixed (same device, same WG/DoH state, screen off, same duration):

  1. Wakeup attribution via batterystats. Reset, then run each arm for a fixed window with the screen off:
    adb shell dumpsys batterystats --reset
    # ... fixed idle window, screen off ...
    adb shell dumpsys batterystats --history | grep -c "wakeupap=.*net.kollnig"   # TC uid wake-up-by count
    adb shell dumpsys batterystats <TC-package>                                    # per-uid wakelock/CPU summary
    
    Compare Monitor system apps ON vs OFF. Primary metric = wakeupap events attributed to the TC uid per hour, plus wakelock time; MB/s is explicitly not the metric.
  2. Time-in-packet-loop (optional native counter). A cheap, low-risk counter in the native engine's per-thread receive loop — count wakeups (returns from the blocking read() on the tun fd) and accumulate loop time, logged through the existing stats/log path — gives a device-independent signal that isolates system-app traffic. Not added in this PR to keep it Java-only and low-risk; specified here so it can land separately if the batterystats signal is noisy.
  3. Confounders to hold constant: WireGuard egress on/off, DoH on/off, blocking mode, and the set of installed system apps. Run each arm at least twice.

Expected result: ON shows materially more TC-attributed wakeups/wakelock time per hour than OFF, confirming routing frequency (not tunnel volume) as the cost — which is exactly why the toggle defaults off and the summary flags the battery/"Block connections without VPN" trade-off.

Verification

  • ./gradlew :app:compileGithubDebugJavaWithJavac -q — passes
  • ./gradlew :app:testGithubDebugUnitTest — BUILD SUCCESSFUL

🤖 Generated with Claude Code

kasnder and others added 2 commits July 12, 2026 15:43
Route+monitor+visibility for system apps were controlled by three
interacting prefs: include_system_vpn (VPN routing), manage_system
(tracker detection/blocking), and show_system (list/insights
visibility). The user saw two switches in settings plus a filter menu.

Present a single user-facing "Monitor system apps" toggle backed by
include_system_vpn; it keeps manage_system and show_system in lock-step
so routing, detection and visibility move together. The summary keeps
the documented trade-off: off is battery-friendly, on conflicts with
Android's "Block connections without VPN".

Migration (ReceiverAutostart, version 2026071202): preserve the
existing VPN-routing choice exactly and derive the other two internal
prefs from include_system_vpn, so an install that routed system apps
now also monitors them; installs that excluded them are unchanged.

Part of #653

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kasnder

kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member Author

Review findings fixed in 16b3531 (pushed to this PR).

Two cases contradicted the promised single, safe toggle:

  1. The migration enabled monitoring/blocking for users who had routed system apps but had explicitly left manage_system off. A settings consolidation must not silently enable blocking. It now enables the new toggle only when both prior routing and management were enabled (while preserving the pre-202601 migration case).
  2. The Apps overflow menu could still alter show_system independently, desynchronising visibility from the new toggle. That menu item is now removed, so routing, monitoring, and visibility stay together.

:app:compileGithubDebugJavaWithJavac -q passes.

@kasnder
kasnder merged commit 44fd6c9 into master Jul 12, 2026
2 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.

1 participant