Skip to content

Make filtered-but-hidden apps reachable via search (#420, #483)#611

Merged
kasnder merged 2 commits into
masterfrom
claude/fix-483-missing-apps
Jul 12, 2026
Merged

Make filtered-but-hidden apps reachable via search (#420, #483)#611
kasnder merged 2 commits into
masterfrom
claude/fix-483-missing-apps

Conversation

@kasnder

@kasnder kasnder commented Jul 11, 2026

Copy link
Copy Markdown
Member

Problem

Some installed apps are filtered/blocked by TC but never appear in TC's app list, so the user cannot open them to add a per-tracker exception or exclude the app. The only workaround is disabling TC entirely — the worst kind of (unescapable) breakage.

Root cause

Blocking is keyed on UID in the packet path (ServiceSinkhole.isAddressAllowed / blockKnownTracker / shouldTrackApp), and VPN routing is decided from Rule.getRules(true, ...) (the full app set, gated only by !apply || (!includeSystem && system)).

The visible list, however, is built by Rule.getRules(false, ...), which applies the show_system / show_frozen / show_nointernet display filters. So an app hidden by one of those filters — e.g. an OEM-preinstalled package that TC routes because "include system apps in VPN" is enabled, a frozen package, or a shared-UID sibling (#229) — is still routed and blocked by UID while being absent from the list.

AdapterRule's search only iterated the already-filtered visible subset (listAll), so the hidden-but-filtered app could not be found by search either. That matches #420's exact repro (search "Instagram" → missing).

Fix (search-only; default view unchanged)

  • Rule.getSearchStubs(context) — a lightweight, side-effect-free list of rules for every installed app (skips tracker-count queries, default seeding, sorting, snackbars). Enough to be matched by search and to open the per-app details screen.
  • AdapterRule — search now runs over the visible list plus every other installed app (pool built lazily, cached, invalidated on set()). A hidden-but-filtered app can now be found and opened to configure an exception or exclude it. Visible apps keep their fully-populated rules (tracker counts intact). An empty query still shows exactly the visible list, so there is no clutter regression in the default view.

This keeps the recovery path (search → open → configure/exclude) working for any app TC can filter, without adding user-facing toggles (per the project's simplicity-over-configurability philosophy).

Deferred (documented, not fixed here)

The underlying display-filter behaviour and shared-UID runtime attribution (#229 — the packet path resolves a UID to getPackagesForUid(uid)[0], so per-package settings on a non-representative sibling may not take effect at runtime) are left intact. This change guarantees reachability/recoverability, which is the user-facing breakage; a fuller shared-UID attribution redesign is out of scope here.

Testing

  • ./gradlew :app:compileGithubDebugJavaWithJavacBUILD SUCCESSFUL (Rust wgbridgeBuild via preBuild ran; cargo present).
  • Not verified on-device: I cannot install Instagram/Feeder to reproduce the exact hidden-app state. The change is behaviour-preserving for the default (empty-query) view and only widens the search pool. On-device confirmation that searching a hidden app now surfaces it, and that opening it lets the user exclude/except it, is still recommended.

🤖 Generated with Claude Code

Some installed apps are routed through the tun and filtered/blocked by
TC (blocking is keyed on UID in ServiceSinkhole.isAddressAllowed /
blockKnownTracker), yet never appear in the app list, so the user cannot
open them to add an exception or exclude them. The only workaround is
disabling TC entirely.

Root cause: Rule.getRules(false, ...) applies the show_system /
show_frozen / show_nointernet display filters when building the visible
list, but the packet path filters by UID regardless of those display
filters. An app hidden by a display filter (e.g. an OEM-preinstalled
package that TC routes because "include system apps" is enabled, a
frozen package, or a shared-UID sibling — #229) is therefore still
blocked while being absent from the list. AdapterRule's search only
searched the already-filtered visible subset (listAll), so searching for
the hidden app could never find it either — matching the exact failing
repro in #420 (search "Instagram" -> missing).

Fix (search-only, keeps the default view uncluttered):
- Rule.getSearchStubs(): build a lightweight, side-effect-free list of
  rules for every installed app (skips tracker counts, default seeding,
  sorting, snackbars).
- AdapterRule: search now runs over the visible list plus every other
  installed app (lazily built, cached, invalidated on set()), so a
  hidden-but-filtered app can be found and opened to configure an
  exception. Visible apps keep their fully-populated rules. Empty query
  still shows exactly the visible list, so no clutter regression.

Deferred (documented, not fixed here): the underlying display-filter /
shared-UID (#229) attribution behaviour is left intact; this change only
guarantees recoverability, which is the user-facing breakage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kasnder kasnder closed this Jul 11, 2026
@kasnder kasnder reopened this Jul 12, 2026

@kasnder kasnder left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review finding: the new case-insensitive search path used the device default locale for toLowerCase(). In locales with special case mappings (notably Turkish), ASCII package names and user queries can normalize differently, causing hidden apps to remain undiscoverable. I fixed all three normalizations to use Locale.ROOT and verified :app:compileGithubDebugJavaWithJavac.

@kasnder kasnder marked this pull request as ready for review July 12, 2026 10:46
@kasnder kasnder merged commit cba81ac into master Jul 12, 2026
1 check 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