Skip to content

Privacy tab: offline guest ad/telemetry hosts blocker#60

Merged
RobThePCGuy merged 1 commit into
masterfrom
feature/privacy
Jul 20, 2026
Merged

Privacy tab: offline guest ad/telemetry hosts blocker#60
RobThePCGuy merged 1 commit into
masterfrom
feature/privacy

Conversation

@RobThePCGuy

Copy link
Copy Markdown
Owner

Adds a Privacy tab that null-routes ad/tracker/analytics domains in an instance's guest hosts file — a surgical, emulator-only, reversible telemetry block. Built clean-room (my own live capture + public ad-network sources, nothing from any third-party debloat repo).

What it does

  • telemetry_block.py: writes 0.0.0.0 entries for a conservative, independently-sourced list of ad/tracker/attribution domains (plus rtbhouse — the ad exchange caught in a live capture of the player) into the guest /system/etc/hosts, offline via the same Root.vhd + debugfs path the Magisk install uses. Backs up the original once, idempotent (marked block), e2fsck-verified; remove() restores it exactly.
  • Emulator-only: it never touches the user's Windows hosts, and it doesn't block Google Play, GMS, or an app's own servers.
  • New Privacy nav-rail tab: pick an instance → Block / Remove, with live status, run through the elevated worker (closes BlueStacks first).

Not yet live-verified

168 tests cover the pure logic + state + page. The offline hosts write and the exact domain list still want a pass on a clean instance — the list is a starter, to be finalized from a live capture of the target build.

BlueStacks and the apps inside it phone home to ad/analytics endpoints, and
the in-app "disable ads" toggle covers only a fraction (a live capture still
shows the player reaching an ad exchange, rtbhouse, with it off). Add a
surgical, emulator-only, reversible block.

- telemetry_block.py: null-routes a conservative, independently-sourced list
  of ad/tracker/attribution domains (+ rtbhouse from the live capture) in the
  guest /system/etc/hosts, written OFFLINE via the same Root.vhd + debugfs
  path the Magisk install uses. Backs up the original once, idempotent via
  marked block, e2fsck-verified, and remove() restores it exactly. Never
  touches the user's Windows hosts. Does not block Google Play / GMS / an
  app's own servers.
- New "Privacy" nav-rail tab: pick an instance -> Block ads & telemetry /
  Remove block, with live status; runs through the elevated _run_async worker
  (closes BlueStacks first, like the Magisk install).

168 tests green (pure logic + state + page). The offline write and the exact
domain list still want a live pass on a clean instance -- the list is a
starter, finalized from a capture of the target build.
for d in tb.BLOCKLIST:
assert d == d.lower() and " " not in d and "/" not in d
# the ad exchange caught in the live capture is in the list
assert "rtbhouse.net" in tb.BLOCKLIST
@RobThePCGuy
RobThePCGuy merged commit 57787d0 into master Jul 20, 2026
3 of 4 checks passed
@RobThePCGuy
RobThePCGuy deleted the feature/privacy branch July 20, 2026 20:02

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces offline ad and telemetry blocking for BlueStacks instances by modifying the guest's hosts file inside Root.vhd. It adds a new Privacy page to the GUI, integrates it into the main window and navigation rail, and includes comprehensive unit tests. The review feedback highlights a critical issue in the remove() function of telemetry_block.py, where the restoration of the hosts file is not verified before deleting the backup. Implementing the suggested verification step will prevent potential file corruption or loss on write failures.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread telemetry_block.py
base = _strip_block(_dump_hosts(dev, sysroot, env))
if not base.strip():
base = "127.0.0.1\tlocalhost\n::1\t\tlocalhost\n" # stock fallback
_write_hosts(dev, sysroot, base, env)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In remove(), the return value of _write_hosts is not checked, and there is no verification that the hosts file was successfully restored. If _write_hosts fails (for example, due to a write error or debugfs failure), the guest's hosts file might be left deleted or corrupted, and the backup file will still be deleted.

We should verify that the hosts file was successfully restored by dumping it back and comparing it with base before deleting the backup file, similar to how it is verified in apply().

Suggested change
_write_hosts(dev, sysroot, base, env)
out = _write_hosts(dev, sysroot, base, env)
written = _dump_hosts(dev, sysroot, env)
if written != base:
raise RuntimeError("hosts file not restored (debugfs: %s)" % _ms._errtail(out))

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7f60de9be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread telemetry_block.py
Comment on lines +86 to +88
def _instance_paths(instance_dir: str) -> tuple[str, str]:
return (os.path.join(instance_dir, _BACKUP_NAME),
os.path.join(instance_dir, _STATE_NAME))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope sidecars to the resolved Root.vhd

When two instances share a system image, _resolve_root_vhd() points both at the same master Root.vhd, but these paths store status and backups under each selected instance. Applying through instance A therefore changes both guests while B still appears unblocked; applying through both and then removing through either restores the shared image while the other remains marked blocked. Key the state and backup by the resolved VHD so every instance sharing it reports and controls one consistent state.

Useful? React with 👍 / 👎.

Comment thread telemetry_block.py
Comment on lines +95 to +97
for d in BLOCKLIST:
lines.append("0.0.0.0 %s" % d)
lines.append("0.0.0.0 www.%s" % d)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the actual tracker hostnames to the hosts block

Hosts-file entries match exact hostnames, not entire domain suffixes, so adding only doubleclick.net and www.doubleclick.net, for example, does not block common endpoints such as googleads.g.doubleclick.net; the same applies to arbitrary subdomains of every listed network. In the normal case where ads and telemetry use service-specific subdomains, this feature reports the domain as blocked while allowing the traffic, so the list needs the concrete captured hostnames or another mechanism that supports suffix matching.

Useful? React with 👍 / 👎.

Comment thread telemetry_block.py
Comment on lines +158 to +159
cmds = ["cd %s" % etcdir, "rm hosts",
"write %s hosts" % _ms._dq(_ms._cygpath(tmp)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore hosts when the replacement write fails

If debugfs successfully executes rm hosts but the following write fails—for example because the image is full or the temporary source cannot be read—the original hosts file is left deleted. apply() subsequently raises when its marker verification fails and never writes the applied-state sidecar, so the Privacy page continues to show only the Block action rather than offering restoration from the backup. Make this replacement transactional or roll the backup back before propagating a write failure.

Useful? React with 👍 / 👎.

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.

2 participants