Privacy tab: offline guest ad/telemetry hosts blocker#60
Conversation
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 |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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().
| _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)) |
There was a problem hiding this comment.
💡 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".
| def _instance_paths(instance_dir: str) -> tuple[str, str]: | ||
| return (os.path.join(instance_dir, _BACKUP_NAME), | ||
| os.path.join(instance_dir, _STATE_NAME)) |
There was a problem hiding this comment.
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 👍 / 👎.
| for d in BLOCKLIST: | ||
| lines.append("0.0.0.0 %s" % d) | ||
| lines.append("0.0.0.0 www.%s" % d) |
There was a problem hiding this comment.
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 👍 / 👎.
| cmds = ["cd %s" % etcdir, "rm hosts", | ||
| "write %s hosts" % _ms._dq(_ms._cygpath(tmp)), |
There was a problem hiding this comment.
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 👍 / 👎.
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: writes0.0.0.0entries for a conservative, independently-sourced list of ad/tracker/attribution domains (plusrtbhouse— the ad exchange caught in a live capture of the player) into the guest/system/etc/hosts, offline via the same Root.vhd +debugfspath the Magisk install uses. Backs up the original once, idempotent (marked block), e2fsck-verified;remove()restores it exactly.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.