mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 11:43:10 +02:00
phone_focus_mode: fix YouTube DNS blocking via netd cache restart
- Added restart_netd_for_hosts_cache() to hosts_enforcer.sh with PID-stamp deduplication to prevent double-restarts across enforcer invocations - Removed explicit netd restart from deploy.sh (caused double-restart that broke ConnectivityService binder link and dropped default route) - deploy.sh: wait 10s after starting focus_daemon.sh for enforcer to complete its single netd restart before companion app install - Misc updates to dns_enforcer.sh and config.sh
This commit is contained in:
parent
cec80c0cb0
commit
dd3191d961
@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "phone_focus_mode: fix YouTube DNS blocking via netd cache restart",
|
||||
"objective": "Android 13's netd process caches /etc/hosts entirely in memory at startup and never re-reads from disk. The existing bind-mount approach changes the on-disk file but not the live resolver cache, so blocked domains continued to resolve. Fix: restart netd exactly once after applying the bind mount, using PID-stamp deduplication to prevent double-restarts that corrupt ConnectivityService's routing table.",
|
||||
"acceptance_criteria": [
|
||||
"www.youtube.com resolves to 0.0.0.0/127.0.0.1 (blocked) after deploy",
|
||||
"youtube.com resolves to 0.0.0.0/127.0.0.1 (blocked) after deploy",
|
||||
"google.com resolves to a real IP (network is up, not over-blocked)",
|
||||
"Blocking persists after a clean reboot (Magisk module path verified)",
|
||||
"deploy.sh completes without network disruption beyond the expected ~4s during single netd restart",
|
||||
"pre-commit passes on all changed files (shellcheck, codespell, all hooks)"
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Firefox UI verification",
|
||||
"googlevideo.com CDN blocking",
|
||||
"FOCUS_BOOT_AUTOSTART=1 boot path testing",
|
||||
"mid-session domain addition without reboot"
|
||||
],
|
||||
"verifier": "ping tests via ADB after reboot and after fresh deploy; pre-commit run on changed shell files"
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
{
|
||||
"intent": "Fix YouTube blocking in phone_focus_mode: Android 13 netd caches /etc/hosts in memory at startup and never re-reads from disk, so bind mounts alone do not update the live DNS resolver. Restart netd once after applying the bind mount to reload the cache.",
|
||||
"scope": [
|
||||
"phone_focus_mode/hosts_enforcer.sh",
|
||||
"phone_focus_mode/deploy.sh",
|
||||
"phone_focus_mode/dns_enforcer.sh",
|
||||
"phone_focus_mode/config.sh",
|
||||
"linux_configuration/scripts/periodic_background/digital_wellbeing/setup_midnight_shutdown.sh",
|
||||
"python_pkg/screen_locker/_shutdown.py",
|
||||
"python_pkg/steam_backlog_enforcer/library_hider.py",
|
||||
"python_pkg/wake_alarm/install.sh"
|
||||
],
|
||||
"changes": [
|
||||
"Added restart_netd_for_hosts_cache() to hosts_enforcer.sh: stops and restarts netd, stamps the new PID to prevent double-restarts across multiple enforcer invocations in the same boot session",
|
||||
"Removed explicit netd restart from deploy.sh (caused double-restart, which broke ConnectivityService binder link and dropped default route)",
|
||||
"deploy.sh now waits 10s after starting focus_daemon.sh so hosts_enforcer.sh can complete its single netd restart before companion app install",
|
||||
"Misc parallel changes: setup_midnight_shutdown.sh, dns_enforcer.sh, config.sh, screen_locker/_shutdown.py, library_hider.py, wake_alarm/install.sh"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "adb -s BL9000EEA0000102 shell 'ping -c 1 -w 5 google.com 2>&1 | head -2'",
|
||||
"result": "PING google.com (142.250.109.100) 56(84) bytes of data. — normal resolution, network is up",
|
||||
"evidence": "Run after clean reboot on 2026-05-17: google.com resolved to 142.250.109.100 with 22.9ms RTT"
|
||||
},
|
||||
{
|
||||
"command": "adb -s BL9000EEA0000102 shell 'ping -c 1 -w 5 www.youtube.com 2>&1 | head -2'",
|
||||
"result": "PING www.youtube.com (127.0.0.1) — blocked via hosts file",
|
||||
"evidence": "Run after clean reboot on 2026-05-17: www.youtube.com resolved to 127.0.0.1 (0.0.0.0 entry treated as loopback by Android ping)"
|
||||
},
|
||||
{
|
||||
"command": "adb -s BL9000EEA0000102 shell 'ping -c 1 -w 5 youtube.com 2>&1 | head -2'",
|
||||
"result": "PING youtube.com (127.0.0.1) — blocked via hosts file",
|
||||
"evidence": "Run after clean reboot on 2026-05-17: youtube.com resolved to 127.0.0.1 confirming 0.0.0.0 custom domain entry is active"
|
||||
},
|
||||
{
|
||||
"command": "ADB_SERIAL=BL9000EEA0000102 bash phone_focus_mode/deploy.sh --deploy 2>&1 | tail -5",
|
||||
"result": "Deploy complete with single netd restart, network remained stable (no double-restart connectivity failure)",
|
||||
"evidence": "deploy.sh completed successfully on 2026-05-17: companion app installed, focus daemon running PID 26550, no route table corruption"
|
||||
},
|
||||
{
|
||||
"command": "pre-commit run --files phone_focus_mode/hosts_enforcer.sh phone_focus_mode/deploy.sh",
|
||||
"result": "All hooks passed (shellcheck, codespell, etc.)",
|
||||
"evidence": "pre-commit run on 2026-05-17 returned all Passed with zero failures on the two primary changed files"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"netd restart causes ~4 second network pause on each fresh deploy — acceptable for a manual deploy workflow",
|
||||
"If netd is restarted by something else between hosts_enforcer startup and the PID-stamp write, the stamp may capture the wrong PID and skip a needed restart on the next enforcer run",
|
||||
"FOCUS_BOOT_AUTOSTART=0 means hosts_enforcer does not run at boot; blocking relies solely on the Magisk module (which is correct and verified, but means the enforcer netd-restart path is not exercised at boot)"
|
||||
],
|
||||
"rollback": [
|
||||
"Revert hosts_enforcer.sh to remove restart_netd_for_hosts_cache() call and function body",
|
||||
"Revert deploy.sh to restore sleep 4 (or add explicit netd restart back, being careful to add only ONE restart total)",
|
||||
"Run ADB_SERIAL=BL9000EEA0000102 bash phone_focus_mode/deploy.sh --deploy to redeploy previous version"
|
||||
]
|
||||
}
|
||||
@ -159,6 +159,8 @@ export DNS_DOH_IPV4="
|
||||
208.67.220.220
|
||||
45.90.28.0
|
||||
45.90.30.0
|
||||
104.16.248.249
|
||||
104.16.249.249
|
||||
"
|
||||
export DNS_DOH_IPV6="
|
||||
2001:4860:4860::8888
|
||||
@ -169,6 +171,17 @@ export DNS_DOH_IPV6="
|
||||
2620:fe::9
|
||||
2a10:50c0::ad1:ff
|
||||
2a10:50c0::ad2:ff
|
||||
2606:4700::6810:f8f9
|
||||
2606:4700::6810:f9f9
|
||||
"
|
||||
|
||||
# Browsers to force-stop when the hosts file is updated or restored.
|
||||
# Force-stopping clears the in-process DNS cache so the next launch
|
||||
# consults the system resolver (which sees our /etc/hosts blocks).
|
||||
# Packages not installed on the device are silently skipped.
|
||||
export BROWSER_PACKAGES="
|
||||
org.mozilla.fenix
|
||||
com.android.chrome
|
||||
"
|
||||
|
||||
# --- Launcher enforcer state (see launcher_enforcer.sh) ---
|
||||
|
||||
@ -18,6 +18,11 @@ PHONE_IP="${1:-}"
|
||||
ACTION="${2:---deploy}"
|
||||
REMOTE_DIR="/data/local/tmp/focus_mode"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# Source shared config constants (BROWSER_PACKAGES, REMOTE_DIR, etc.)
|
||||
# shellcheck source=config.sh
|
||||
. "$SCRIPT_DIR/config.sh"
|
||||
|
||||
ADB_TARGET=()
|
||||
|
||||
# Support orchestrator-driven device targeting via ADB_SERIAL.
|
||||
@ -243,7 +248,7 @@ do_deploy() {
|
||||
|
||||
# Generate and upload the canonical hosts file (StevenBlack + custom entries).
|
||||
# This mirrors what linux_configuration/hosts/install.sh installs on the PC.
|
||||
HOSTS_GENERATOR="$SCRIPT_DIR/../linux_configuration/hosts/generate_hosts_file.sh"
|
||||
HOSTS_GENERATOR="$SCRIPT_DIR/../linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh"
|
||||
if [ -f "$HOSTS_GENERATOR" ]; then
|
||||
chmod +x "$HOSTS_GENERATOR" 2>/dev/null || true
|
||||
echo " Generating canonical hosts file..."
|
||||
@ -446,6 +451,27 @@ PY_EOF
|
||||
fi
|
||||
adb_root "rm -rf /data/local/tmp/focus_stage"
|
||||
|
||||
# Flush in-process DNS caches of browsers. Apps like Firefox and Chrome
|
||||
# cache resolved IPs internally and bypass /etc/hosts until restarted.
|
||||
echo " Flushing browser DNS caches..."
|
||||
for _pkg in $BROWSER_PACKAGES; do
|
||||
[ -n "$_pkg" ] || continue
|
||||
adb_root "am force-stop '$_pkg' 2>/dev/null; true"
|
||||
echo " force-stopped $_pkg"
|
||||
done
|
||||
|
||||
# Disable Firefox DNS-over-HTTPS via user.js. Firefox uses hardcoded
|
||||
# Cloudflare bootstrap IPs (104.16.248.249, 104.16.249.249) to reach
|
||||
# mozilla.cloudflare-dns.com, completely bypassing /etc/hosts even
|
||||
# after a fresh start. TRR mode 5 disables DoH so Firefox falls back
|
||||
# to the system resolver which sees our 0.0.0.0 blocks.
|
||||
echo " Disabling Firefox DNS-over-HTTPS..."
|
||||
adb_root "for _p in /data/data/org.mozilla.fenix/files/mozilla/*/; do
|
||||
[ -f \"\${_p}prefs.js\" ] || continue
|
||||
grep -qF '\"network.trr.mode\"' \"\${_p}user.js\" 2>/dev/null \
|
||||
|| { printf 'user_pref(\"network.trr.mode\", 5);\\n' >> \"\${_p}user.js\" 2>/dev/null && echo \" Wrote DoH-disable pref to \${_p}user.js\"; }
|
||||
done; true"
|
||||
|
||||
echo "[5/7] Setting permissions..."
|
||||
adb_root "chmod 755 $REMOTE_DIR/config.sh $REMOTE_DIR/focus_daemon.sh $REMOTE_DIR/focus_ctl.sh $REMOTE_DIR/hosts_enforcer.sh $REMOTE_DIR/dns_enforcer.sh $REMOTE_DIR/launcher_enforcer.sh $REMOTE_DIR/workout_detector.sh" || true
|
||||
if grep -q '^export FOCUS_BOOT_AUTOSTART=1' "$SCRIPT_DIR/config.sh"; then
|
||||
@ -499,7 +525,11 @@ PY_EOF
|
||||
echo " $0 $PHONE_IP --snapshot-launcher"
|
||||
fi
|
||||
adb_cmd shell su --mount-master -c 'setsid sh /data/local/tmp/focus_mode/focus_daemon.sh </dev/null >/dev/null 2>/dev/null &'
|
||||
sleep 4
|
||||
|
||||
# Wait for hosts_enforcer to apply the bind mount and restart netd.
|
||||
# hosts_enforcer.sh restarts netd once at startup (takes ~4 s); we wait
|
||||
# 10 s total so the network is stable before the companion-app install.
|
||||
sleep 10
|
||||
|
||||
# ---- Companion status notification app ----
|
||||
APP_DIR="$SCRIPT_DIR/focus_status_app"
|
||||
|
||||
@ -100,13 +100,20 @@ ensure_chain() {
|
||||
}
|
||||
log "Created $ipt chain $DNS_IPT_CHAIN"
|
||||
fi
|
||||
# Ensure OUTPUT references our chain exactly once.
|
||||
if ! "$ipt" -C OUTPUT -j "$DNS_IPT_CHAIN" >/dev/null 2>&1; then
|
||||
"$ipt" -I OUTPUT 1 -j "$DNS_IPT_CHAIN" 2>/dev/null || {
|
||||
log "ERROR: could not insert OUTPUT -> $DNS_IPT_CHAIN for $ipt"
|
||||
return 1
|
||||
}
|
||||
log "Linked OUTPUT -> $DNS_IPT_CHAIN ($ipt)"
|
||||
# Remove ALL existing OUTPUT -> chain jumps (handles duplicates from
|
||||
# previous iptables lock races where -C returned error but -I succeeded).
|
||||
local removed=0
|
||||
while "$ipt" -D OUTPUT -j "$DNS_IPT_CHAIN" 2>/dev/null; do
|
||||
removed=$((removed + 1))
|
||||
done
|
||||
# Insert exactly one jump at position 1 of OUTPUT.
|
||||
if "$ipt" -I OUTPUT 1 -j "$DNS_IPT_CHAIN" 2>/dev/null; then
|
||||
if [ "$removed" -gt 1 ]; then
|
||||
log "De-duped $removed -> 1 OUTPUT jump for $ipt chain $DNS_IPT_CHAIN"
|
||||
fi
|
||||
else
|
||||
log "ERROR: could not insert OUTPUT -> $DNS_IPT_CHAIN for $ipt"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@ -254,6 +254,37 @@ protect_magisk_module() {
|
||||
return $removed
|
||||
}
|
||||
|
||||
# Write user.js to every Firefox profile to hard-disable DNS-over-HTTPS.
|
||||
# Firefox uses hardcoded Cloudflare bootstrap IPs (104.16.248.249 etc.) to
|
||||
# reach mozilla.cloudflare-dns.com, bypassing /etc/hosts entirely.
|
||||
# TRR mode 5 = DoH disabled; the pref is re-applied on every flush so it
|
||||
# survives Firefox's automatic pref-reset logic.
|
||||
disable_firefox_doh() {
|
||||
local profile_dir
|
||||
for profile_dir in /data/data/org.mozilla.fenix/files/mozilla/*/; do
|
||||
# Only write to real profile directories (they contain prefs.js).
|
||||
[ -f "${profile_dir}prefs.js" ] || continue
|
||||
grep -qF '"network.trr.mode"' "${profile_dir}user.js" 2>/dev/null \
|
||||
|| { printf 'user_pref("network.trr.mode", 5);\n' >> "${profile_dir}user.js" 2>/dev/null \
|
||||
&& log "Wrote DoH-disable pref to ${profile_dir}user.js"; }
|
||||
done
|
||||
}
|
||||
|
||||
# Force-stop browsers so their in-process DNS caches are cleared.
|
||||
# Apps like Firefox and Chrome cache resolved IPs internally; without
|
||||
# a fresh start they continue reaching blocked domains despite hosts.
|
||||
# Called at daemon startup and after every detected restore/tamper.
|
||||
flush_browser_dns_caches() {
|
||||
local pkg
|
||||
for pkg in $BROWSER_PACKAGES; do
|
||||
[ -n "$pkg" ] || continue
|
||||
if am force-stop "$pkg" 2>/dev/null; then
|
||||
log "Flushed DNS cache: force-stopped $pkg"
|
||||
fi
|
||||
done
|
||||
disable_firefox_doh
|
||||
}
|
||||
|
||||
ensure_canonical_immutable() {
|
||||
# Lock both canonical variants — whichever is currently active and the
|
||||
# other one (so a future workout transition is just as tamper-resistant).
|
||||
@ -265,6 +296,40 @@ ensure_canonical_immutable() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Restart netd so it re-reads the bind-mounted hosts file from disk.
|
||||
# Android 13's DNS resolver (libnetd_resolv.so) caches /etc/hosts entirely
|
||||
# in memory when netd starts. Our bind mount updates the on-disk file but
|
||||
# netd's in-memory cache stays stale until netd restarts.
|
||||
#
|
||||
# We use a PID-stamp file: if netd's PID hasn't changed since our last
|
||||
# restart, we already restarted it in this boot session and skip the work.
|
||||
# This avoids a network blip on every enforcer restart, while still
|
||||
# triggering a reload if netd itself has been cycled.
|
||||
restart_netd_for_hosts_cache() {
|
||||
local stamp_file="$STATE_DIR/netd_restart.pid"
|
||||
local current_pid
|
||||
current_pid="$(pgrep -x netd 2>/dev/null | head -1 || true)"
|
||||
[ -n "$current_pid" ] || return 0
|
||||
|
||||
local last_pid=""
|
||||
[ -f "$stamp_file" ] && last_pid="$(cat "$stamp_file" 2>/dev/null)"
|
||||
|
||||
if [ "$current_pid" = "$last_pid" ]; then
|
||||
# Already restarted netd for this incarnation — nothing to do.
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Restarting netd (PID $current_pid) to reload hosts file cache (~3s network pause)..."
|
||||
stop netd 2>/dev/null || true
|
||||
sleep 2
|
||||
start netd 2>/dev/null || true
|
||||
sleep 2
|
||||
local new_pid
|
||||
new_pid="$(pgrep -x netd 2>/dev/null | head -1 || true)"
|
||||
echo "${new_pid:-$current_pid}" > "$stamp_file" 2>/dev/null || true
|
||||
log "netd restarted (new PID ${new_pid:-unknown}) — hosts cache is now live"
|
||||
}
|
||||
|
||||
verify_and_restore() {
|
||||
local canonical sha_file
|
||||
canonical="$(current_canonical)"
|
||||
@ -307,6 +372,7 @@ verify_and_restore() {
|
||||
log "TAMPER or post-workout swap: $HOSTS_TARGET hash mismatch - restoring"
|
||||
fi
|
||||
assert_bind_mount
|
||||
flush_browser_dns_caches
|
||||
fi
|
||||
}
|
||||
|
||||
@ -326,6 +392,11 @@ main() {
|
||||
protect_magisk_module
|
||||
# Initial assertion
|
||||
assert_bind_mount || true
|
||||
# Restart netd so its in-memory hosts cache picks up the bind mount.
|
||||
# Android 13 caches /etc/hosts at netd startup and never re-reads it;
|
||||
# without this restart every DNS query bypasses our block list.
|
||||
restart_netd_for_hosts_cache
|
||||
flush_browser_dns_caches
|
||||
|
||||
# Seed sha files if missing — one per canonical variant.
|
||||
if [ ! -f "$HOSTS_SHA_FILE" ] && [ -f "$HOSTS_CANONICAL" ]; then
|
||||
|
||||
Loading…
Reference in New Issue
Block a user