diff --git a/docs/superpowers/contracts/phone-focus-mode-fresh-install-2026-05-31.json b/docs/superpowers/contracts/phone-focus-mode-fresh-install-2026-05-31.json new file mode 100644 index 0000000..dd8fe28 --- /dev/null +++ b/docs/superpowers/contracts/phone-focus-mode-fresh-install-2026-05-31.json @@ -0,0 +1,17 @@ +{ + "title": "BL-9000 fresh-phone recovery automation", + "objective": "After a factory reset, deploy.sh runs end-to-end without any manual Magisk UI or coordinate-entry steps: it auto-installs the Magisk Systemless Hosts module (rebooting if needed), captures home GPS coordinates automatically, and deploys all focus mode daemons and hosts blocking. A separate --capture-coords action handles the case where GPS is unavailable at deploy time.", + "acceptance_criteria": [ + "deploy.sh detects missing Magisk Hosts module, creates it, reboots, and continues without user intervention", + "deploy.sh captures current GPS as home coords when config_secrets.sh has placeholder values", + "On GPS failure, deploy completes with stub coords and prints exact --capture-coords command", + "--capture-coords updates phone config_secrets.sh and restarts daemon in one command", + "batch3_bloatware_uninstall.sh removes all 42 BL-9000 bloatware packages with 0 errors", + "com.kuhy.workout_app and com.shazam.android are never disabled by focus mode" + ], + "out_of_scope": [ + "Wireless ADB auto-reconnect after reboot (USB reconnect is handled; wireless requires manual re-pairing)", + "Launcher enforcement setup (still requires manual Minimalist Phone install + --snapshot-launcher)" + ], + "verifier": "ADB_SERIAL=BL9000EEA0000102 ./deploy.sh && ./deploy.sh --capture-coords && ./deploy.sh --status (Distance: 0m, No apps disabled)" +} diff --git a/docs/superpowers/evidence/phone-focus-mode-fresh-install-2026-05-31.json b/docs/superpowers/evidence/phone-focus-mode-fresh-install-2026-05-31.json new file mode 100644 index 0000000..924d714 --- /dev/null +++ b/docs/superpowers/evidence/phone-focus-mode-fresh-install-2026-05-31.json @@ -0,0 +1,55 @@ +{ + "intent": "Full fresh-phone recovery after factory reset: bloatware removal, focus mode deploy with auto Magisk Hosts module install, GPS coordinate auto-capture, and whitelist additions.", + "scope": [ + "phone_focus_mode/batch3_bloatware_uninstall.sh", + "phone_focus_mode/deploy.sh", + "phone_focus_mode/config.sh", + "linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh", + "linux_configuration/scripts/periodic_background/hosts/install.sh" + ], + "changes": [ + "batch3_bloatware_uninstall.sh: rewritten with 39 confirmed BL-9000 Blackview packages + Chrome/YouTube/YouTube Music; no reboot between removals; supports --list dry-run and ADB_SERIAL/PHONE_IP", + "deploy.sh: added ensure_magisk_hosts_module() — auto-creates Magisk Systemless Hosts module dir+module.prop and reboots if absent/disabled, verified working on live device", + "deploy.sh: added fetch_home_coords_from_phone() — enables location, waits for fused/network fix, parses 'last location=Location[' format; non-blocking fallback to stub coords on failure", + "deploy.sh: added --capture-coords action for post-WiFi GPS capture and daemon restart", + "deploy.sh: check_coords() now detects non-numeric placeholder coords and sets NEEDS_GPS_FETCH flag", + "config.sh: added com.kuhy.workout_app and com.shazam.android to WHITELIST", + "hosts scripts: broadened Facebook/Messenger unblocking to cover all subdomains (fbcdn.net, facebook.net, m.facebook.com)" + ], + "verification": [ + { + "command": "ADB_SERIAL=BL9000EEA0000102 ./batch3_bloatware_uninstall.sh --list", + "result": "pass", + "evidence": "42 packages detected on live device, 0 not installed" + }, + { + "command": "ADB_SERIAL=BL9000EEA0000102 ./batch3_bloatware_uninstall.sh", + "result": "pass", + "evidence": "42 removed, 0 skipped, 0 errors" + }, + { + "command": "ADB_SERIAL=BL9000EEA0000102 ./deploy.sh", + "result": "pass", + "evidence": "Magisk Hosts absent -> auto-created + rebooted -> active; 177783-line hosts deployed; daemons running PID 9696/22478/25289" + }, + { + "command": "ADB_SERIAL=BL9000EEA0000102 ./deploy.sh --capture-coords", + "result": "pass", + "evidence": "GPS fix acquired 52.229047,20.951483; config_secrets.sh updated; daemon restarted; status shows Distance: 0m" + }, + { + "command": "pre-commit run --files phone_focus_mode/batch3_bloatware_uninstall.sh phone_focus_mode/config.sh phone_focus_mode/deploy.sh linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh linux_configuration/scripts/periodic_background/hosts/install.sh", + "result": "pass", + "evidence": "All hooks passed including shellcheck" + } + ], + "risks": [ + "ensure_magisk_hosts_module reboots the phone mid-deploy; if the device fails to reconnect (wireless ADB), deploy must be retried manually", + "GPS stub coords (0.000001,0.000001) written on GPS failure mean focus mode stays in normal mode until --capture-coords is run" + ], + "rollback": [ + "Bloatware: pm install-existing --user 0 restores any removed package", + "Focus mode: adb shell su -c 'sh /data/local/tmp/focus_mode/focus_ctl.sh stop' disables all enforcement", + "Magisk Hosts module: remove /data/adb/modules/hosts/ and reboot to restore stock /system/etc/hosts" + ] +} diff --git a/linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh b/linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh index 46e1029..a8279b9 100755 --- a/linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh +++ b/linux_configuration/scripts/periodic_background/hosts/generate_hosts_file.sh @@ -84,8 +84,10 @@ sed -i 's/^0\.0\.0\.0 4chan\.org/#0.0.0.0 4chan.org/' "$TMP" sed -i 's/^0\.0\.0\.0 boards\.4chan\.org/#0.0.0.0 boards.4chan.org/' "$TMP" sed -i 's/^0\.0\.0\.0 sys\.4chan\.org/#0.0.0.0 sys.4chan.org/' "$TMP" sed -i 's/^0\.0\.0\.0 www\.4chan\.org/#0.0.0.0 www.4chan.org/' "$TMP" -sed -i 's/^0\.0\.0\.0 www\.facebook\.com/#0.0.0.0 www.facebook.com/' "$TMP" -sed -i 's/^0\.0\.0\.0 messenger\.com/#0.0.0.0 messenger.com/' "$TMP" +sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?facebook\.com)/#\1/' "$TMP" +sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?messenger\.com)/#\1/' "$TMP" +sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?fbcdn\.net)/#\1/' "$TMP" +sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?facebook\.net)/#\1/' "$TMP" sed -i 's/^0\.0\.0\.0 delio\.com.pl/#0.0.0.0 delio.com.pl/' "$TMP" sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?linkedin\.com)/#\1/' "$TMP" sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?licdn\.com)/#\1/' "$TMP" diff --git a/linux_configuration/scripts/periodic_background/hosts/install.sh b/linux_configuration/scripts/periodic_background/hosts/install.sh index cf0cea9..01802be 100755 --- a/linux_configuration/scripts/periodic_background/hosts/install.sh +++ b/linux_configuration/scripts/periodic_background/hosts/install.sh @@ -180,8 +180,12 @@ fi # boards.4chan.org # sys.4chan.org # www.4chan.org +# facebook.com # www.facebook.com +# m.facebook.com # messenger.com +# fbcdn.net +# facebook.net # delio.com.pl # loverslab.com # linkedin.com @@ -408,8 +412,10 @@ sudo sed -i 's/^0\.0\.0\.0 4chan\.org/#0.0.0.0 4chan.org/' /etc/hosts sudo sed -i 's/^0\.0\.0\.0 boards\.4chan\.org/#0.0.0.0 boards.4chan.org/' /etc/hosts sudo sed -i 's/^0\.0\.0\.0 sys\.4chan\.org/#0.0.0.0 sys.4chan.org/' /etc/hosts sudo sed -i 's/^0\.0\.0\.0 www\.4chan\.org/#0.0.0.0 www.4chan.org/' /etc/hosts -sudo sed -i 's/^0\.0\.0\.0 www\.facebook\.com/#0.0.0.0 www.facebook.com/' /etc/hosts -sudo sed -i 's/^0\.0\.0\.0 messenger\.com/#0.0.0.0 messenger.com/' /etc/hosts +sudo sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?facebook\.com)/#\1/' /etc/hosts +sudo sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?messenger\.com)/#\1/' /etc/hosts +sudo sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?fbcdn\.net)/#\1/' /etc/hosts +sudo sed -i -E 's/^(0\.0\.0\.0[[:space:]]+[a-zA-Z0-9._-]*\.?facebook\.net)/#\1/' /etc/hosts sudo sed -i 's/^0\.0\.0\.0 delio\.com.pl/#0.0.0.0 delio.com.pl/' /etc/hosts sudo sed -i 's/^0\.0\.0\.0 loverslab\.com/#0.0.0.0 loverslab.com/' /etc/hosts diff --git a/phone_focus_mode/batch3_bloatware_uninstall.sh b/phone_focus_mode/batch3_bloatware_uninstall.sh index 73c44b3..d8e9121 100755 --- a/phone_focus_mode/batch3_bloatware_uninstall.sh +++ b/phone_focus_mode/batch3_bloatware_uninstall.sh @@ -1,117 +1,181 @@ #!/bin/bash -DEVICE_SERIAL="BL9000EEA0000102" -BACKUP_BASE="/home/kuhy/testsAndMisc_binaries/phone_focus_mode_backups" -APPS_TO_UNINSTALL=("com.android.settings" "com.android.systemui" "com.google.android.gms" "com.google.android.apps.docs" "com.google.android.apps.maps") -SUBSTITUTE_APPS=("com.android.tv" "com.android.managedprovisioning" "com.google.android.apps.fitness" "com.google.android.apps.books" "com.google.android.apps.wellbeing" "com.google.android.apps.mediashell") +# ============================================================ +# BL-9000 Bloatware Uninstall +# +# Removes Blackview OEM bloatware and specified Google apps +# using `pm uninstall --user 0` (soft-remove, reversible via +# `pm install-existing --user 0 `). No reboots between +# packages — one optional reboot at the end. +# +# Usage: +# ./batch3_bloatware_uninstall.sh [--list] [--reboot] +# +# --list Dry-run: show which packages would be removed. +# --reboot Reboot the phone after all removals. +# +# Device selection (pick one): +# ADB_SERIAL=BL9000EEA0000102 ./batch3_bloatware_uninstall.sh +# PHONE_IP=192.168.1.x ./batch3_bloatware_uninstall.sh +# ============================================================ -function log_msg() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" -} +set -euo pipefail -function verify_device() { - adb -s "$DEVICE_SERIAL" shell echo "Device OK" &>/dev/null - [ $? -ne 0 ] && log_msg "ERROR: Device not accessible" && exit 1 -} +DRY_RUN=0 +DO_REBOOT=0 -function get_app_version() { - adb -s "$DEVICE_SERIAL" shell dumpsys package "$1" 2>/dev/null | grep "versionName=" | head -1 | cut -d'=' -f2 -} - -function app_exists() { - adb -s "$DEVICE_SERIAL" shell pm list packages | grep -q "^package:${1}$" -} - -function get_substitute() { - for sub in "${SUBSTITUTE_APPS[@]}"; do - app_exists "$sub" && echo "$sub" && return 0 - done - return 1 -} - -function execute_checkpoint() { - local pkg=$1 app_num=$2 - log_msg "=========================================" - log_msg "APP #${app_num}: Processing $pkg" - log_msg "=========================================" - - if ! app_exists "$pkg"; then - log_msg "WARNING: $pkg not found. Searching for substitute..." - actual_pkg=$(get_substitute "$pkg") - [ -z "$actual_pkg" ] && log_msg "ERROR: Could not find substitute. Skipping." && return 1 - log_msg "SUBSTITUTING: Using $actual_pkg" - pkg="$actual_pkg" - fi - - TIMESTAMP=$(date +%s) - CHECKPOINT_DIR="${BACKUP_BASE}/checkpoint_${TIMESTAMP}_${pkg}" - mkdir -p "$CHECKPOINT_DIR" - log_msg "Checkpoint: $CHECKPOINT_DIR" - - log_msg "[1/6] Pulling APK..." - adb -s "$DEVICE_SERIAL" shell pm path "$pkg" > "$CHECKPOINT_DIR/package_path.txt" - if grep -q "^package:" "$CHECKPOINT_DIR/package_path.txt"; then - APK_PATH=$(grep "^package:" "$CHECKPOINT_DIR/package_path.txt" | cut -d':' -f2) - pull_log="$CHECKPOINT_DIR/pull_output.txt" - adb -s "$DEVICE_SERIAL" pull "$APK_PATH" "$CHECKPOINT_DIR/app.apk" > "$pull_log" 2>&1 || true - if ! grep -e "Pull" -e "error" "$pull_log"; then - log_msg "APK pulled" - fi - fi - - log_msg "[2/6] Backing up PM state..." - adb -s "$DEVICE_SERIAL" shell dumpsys package "$pkg" > "$CHECKPOINT_DIR/pm_state.txt" - VNAME=$(get_app_version "$pkg") - log_msg "Version: $VNAME" - - log_msg "[3/6] Taking snapshot..." - adb -s "$DEVICE_SERIAL" shell dumpsys activity activities > "$CHECKPOINT_DIR/activities_before.txt" - adb -s "$DEVICE_SERIAL" shell pm list packages > "$CHECKPOINT_DIR/packages_before.txt" - - log_msg "[4/6] Uninstalling: pm uninstall --user 0 $pkg" - adb -s "$DEVICE_SERIAL" shell pm uninstall --user 0 "$pkg" > "$CHECKPOINT_DIR/uninstall_output.txt" 2>&1 - UNINSTALL_RESULT=$(cat "$CHECKPOINT_DIR/uninstall_output.txt") - log_msg "Result: $UNINSTALL_RESULT" - - log_msg "[5/6] Rebooting device..." - adb -s "$DEVICE_SERIAL" reboot - sleep 5 - - REBOOT_TIMEOUT=180 - WAIT_START=$(date +%s) - while true; do - adb -s "$DEVICE_SERIAL" shell echo "up" &>/dev/null && break - [ $(($(date +%s) - WAIT_START)) -ge $REBOOT_TIMEOUT ] && log_msg "ERROR: Timeout" && break - sleep 3 - echo -n "." - done - echo "" - - sleep 5 - adb -s "$DEVICE_SERIAL" shell pm list packages > "$CHECKPOINT_DIR/packages_after.txt" - - if adb -s "$DEVICE_SERIAL" shell pm list packages | grep -q "^package:${pkg}$"; then - log_msg "WARNING: $pkg still present" - else - log_msg "SUCCESS: $pkg uninstalled" - fi - - log_msg "[6/6] Generating report..." - cat > "$CHECKPOINT_DIR/report.txt" <<< "CHECKPOINT REPORT: $pkg (Timestamp: $TIMESTAMP, Device: $DEVICE_SERIAL) - Version: $VNAME - Result: $UNINSTALL_RESULT - Checkpoint: $CHECKPOINT_DIR" - log_msg "✓ Complete" - return 0 -} - -log_msg "=========================================" -log_msg "BATCH 3: BLOATWARE UNINSTALL" -log_msg "=========================================" -verify_device -log_msg "Device verified" - -for i in "${!APPS_TO_UNINSTALL[@]}"; do - execute_checkpoint "${APPS_TO_UNINSTALL[$i]}" $((i + 1)) - [ $((i + 1)) -lt ${#APPS_TO_UNINSTALL[@]} ] && sleep 5 +for arg in "$@"; do + case "$arg" in + --list) DRY_RUN=1 ;; + --reboot) DO_REBOOT=1 ;; + *) echo "Unknown flag: $arg"; exit 1 ;; + esac done -log_msg "=========================================" -log_msg "BATCH 3 COMPLETE" -log_msg "=========================================" +# ---- Device targeting (mirrors deploy.sh) ---- +ADB_TARGET=() +if [[ -n "${ADB_SERIAL:-}" ]]; then + ADB_TARGET=(-s "${ADB_SERIAL}") +elif [[ -n "${PHONE_IP:-}" ]]; then + echo "Connecting to ${PHONE_IP}:5555 ..." + adb connect "${PHONE_IP}:5555" + ADB_TARGET=(-s "${PHONE_IP}:5555") +fi + +adb_cmd() { adb "${ADB_TARGET[@]}" "$@"; } + +# Requires --mount-master so the command runs in the global mount namespace. +adb_root() { + printf '%s\n' "$1" | adb_cmd shell su --mount-master -c "sh -s" +} + +# ============================================================ +# PACKAGES TO REMOVE +# +# All removed with `pm uninstall --user 0` — the APK stays in +# /system so a factory reset or `pm install-existing` restores +# it. Safe to run even if a package is absent (skipped). +# ============================================================ + +# ---- Blackview OEM bloatware (BL-9000 confirmed packages) ---- +BV_BLOATWARE=( + com.blackview.apkupgrade # Blackview OTA updater + com.blackview.bvworkspace # BV desktop workspace + com.blackview.childmode # Child mode + com.blackview.cplog # CPU/hardware logger + com.blackview.darkmode.one # Dark mode theme variant + com.blackview.darkmode.two + com.blackview.darkmode.three + com.blackview.easytrans # BV easy-transfer tool + com.blackview.filetrans # BV file-transfer tool + com.blackview.focusmode # BV own focus mode (replaced by ours) + com.blackview.frozenapp # App freezer + com.blackview.gamemode # Game mode panel + com.blackview.health # BV health tracker + com.blackview.helper # BV AI assistant / helper + com.blackview.launcher # BV launcher (competitor to Minimalist Phone) + com.blackview.launcher.overlay.framework + com.blackview.leftscreen # Left swipe panel + com.blackview.notebook # BV notes app + com.blackview.qrcode # QR scanner (camera does it natively) + com.blackview.reversepay # Reverse wireless charging pay + com.blackview.smscode # SMS code extractor + com.blackview.systemmanager # BV system manager + com.blackview.theme.color.mode0 # Color themes (8 variants) + com.blackview.theme.color.mode1 + com.blackview.theme.color.mode2 + com.blackview.theme.color.mode3 + com.blackview.theme.color.mode4 + com.blackview.theme.color.mode5 + com.blackview.theme.color.mode6 + com.blackview.theme.color.mode7 + com.blackview.theme.config + com.blackview.theme.icon.clearwave # Icon themes + com.blackview.theme.icon.oil + com.blackview.tool # BV diagnostic tool + com.blackview.userfeedback # BV telemetry / feedback + com.blackview.wallpaper # BV wallpaper collection + com.blackview.wallpaperpicker + com.blackview.wallpaperpicker.overlay + com.blackview.weather # BV weather widget +) + +# ---- Google apps explicitly requested for removal ---- +GOOGLE_REMOVE=( + com.android.chrome # Google Chrome (Firefox is whitelisted) + com.google.android.youtube # YouTube app (hosts-blocked anyway) + com.google.android.apps.youtube.music # YouTube Music +) + +ALL_PACKAGES=("${BV_BLOATWARE[@]}" "${GOOGLE_REMOVE[@]}") + +# ============================================================ +# Main +# ============================================================ +echo "============================================================" +if [[ $DRY_RUN -eq 1 ]]; then + echo "DRY RUN — packages that would be removed:" +else + echo "BL-9000 bloatware removal" +fi +echo "============================================================" + +echo "" +echo "[1] Verifying device connection..." +if ! adb_cmd get-state >/dev/null 2>&1; then + echo "ERROR: No ADB device reachable." + echo " Set ADB_SERIAL= or PHONE_IP= and retry." + exit 1 +fi + +echo "[2] Verifying root..." +if ! adb_root "id" 2>/dev/null | grep -q "uid=0"; then + echo "ERROR: Root shell failed. Ensure Magisk is installed." + exit 1 +fi +echo " Root confirmed." +echo "" + +removed=0 +skipped=0 +errors=0 + +for pkg in "${ALL_PACKAGES[@]}"; do + # Check presence + if ! adb_cmd shell pm list packages 2>/dev/null | grep -qx "package:${pkg}"; then + printf ' %-55s [not installed]\n' "$pkg" + (( skipped++ )) || true + continue + fi + + if [[ $DRY_RUN -eq 1 ]]; then + printf ' %-55s [would remove]\n' "$pkg" + (( removed++ )) || true + continue + fi + + printf ' Removing %-48s ... ' "$pkg" + result="$(adb_cmd shell pm uninstall --user 0 "$pkg" 2>&1 || true)" + if echo "$result" | grep -qi "success"; then + echo "OK" + (( removed++ )) || true + else + echo "FAILED (${result})" + (( errors++ )) || true + fi +done + +echo "" +echo "============================================================" +if [[ $DRY_RUN -eq 1 ]]; then + echo "Dry-run complete: ${removed} would be removed, ${skipped} not installed." + echo "Run without --list to apply." +else + echo "Done: ${removed} removed, ${skipped} skipped (not installed), ${errors} errors." + if [[ $DO_REBOOT -eq 1 ]]; then + echo "Rebooting phone..." + adb_cmd reboot + else + echo "Run with --reboot to reboot now, or reboot manually." + fi +fi +echo "============================================================" diff --git a/phone_focus_mode/config.sh b/phone_focus_mode/config.sh index 91c4f07..2b00ff8 100755 --- a/phone_focus_mode/config.sh +++ b/phone_focus_mode/config.sh @@ -228,6 +228,8 @@ com.kuhy.focusstatus # --- User-requested productive apps --- com.stronglifts.app +com.kuhy.workout_app +com.shazam.android com.ichi2.anki com.metrolist.music org.mozilla.fenix diff --git a/phone_focus_mode/deploy.sh b/phone_focus_mode/deploy.sh index cc220c3..dbcea47 100755 --- a/phone_focus_mode/deploy.sh +++ b/phone_focus_mode/deploy.sh @@ -18,6 +18,7 @@ PHONE_IP="${1:-}" ACTION="${2:---deploy}" REMOTE_DIR="/data/local/tmp/focus_mode" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +NEEDS_GPS_FETCH=0 # set to 1 by check_coords when local coords are placeholder # Source shared config constants (BROWSER_PACKAGES, REMOTE_DIR, etc.) # shellcheck source=config.sh @@ -56,6 +57,7 @@ usage() { echo " --list List all third-party apps and whitelist status" echo " --pull-log Download log file locally" echo " --find-pkg Show installed packages matching a filter (e.g. --find-pkg pomodoro)" + echo " --capture-coords Capture current GPS as home location (run after WiFi setup)" echo " --hosts-status Show hosts enforcer status on the phone" echo " --hosts-log Show hosts enforcer log on the phone" echo " --launcher-status Show launcher enforcer status on the phone" @@ -84,15 +86,17 @@ check_coords() { local lat lon lat="$(grep '^.*HOME_LAT=' "$SCRIPT_DIR/config.sh" "$SCRIPT_DIR/config_secrets.sh" 2>/dev/null | tail -1 | cut -d'"' -f2)" lon="$(grep '^.*HOME_LON=' "$SCRIPT_DIR/config.sh" "$SCRIPT_DIR/config_secrets.sh" 2>/dev/null | tail -1 | cut -d'"' -f2)" - # Allow redacted values locally - real coords live only on the phone if [ "$lat" = "0.000000" ] && [ "$lon" = "0.000000" ]; then - echo "ERROR: Home coordinates not set (all zeros). Set them in config_secrets.sh." + echo "ERROR: Home coordinates not set (all zeros)." exit 1 fi - if [ -z "$lat" ] || [ -z "$lon" ]; then - echo " Home location: (not set locally - will use values on phone)" - else + # If both look like valid floats, use them; otherwise auto-capture from phone GPS. + if [[ -n "$lat" && "$lat" =~ ^[+-]?[0-9]+\.[0-9]+$ && -n "$lon" && "$lon" =~ ^[+-]?[0-9]+\.[0-9]+$ ]]; then + NEEDS_GPS_FETCH=0 echo " Home location: $lat, $lon" + else + NEEDS_GPS_FETCH=1 + echo " Home location: placeholder — will be captured from phone GPS at deploy time." fi } @@ -153,6 +157,144 @@ compute_file_hash() { md5sum "$path" | awk '{print $1}' } +# ============================================================ +# GPS HOME COORDINATE CAPTURE +# ============================================================ +# Called when config_secrets.sh has placeholder/non-numeric coords. +# Enables Android location, waits up to GPS_MAX_WAIT_SECS for a +# network/fused fix, and prints "lat lon" on stdout. All progress +# messages go to stderr so the caller can capture only the coords. +GPS_MAX_WAIT_SECS=90 + +fetch_home_coords_from_phone() { + echo " Enabling location services on phone..." >&2 + adb_cmd shell settings put secure location_mode 3 2>/dev/null || true + + echo " Waiting for network/fused location fix (up to ${GPS_MAX_WAIT_SECS}s)..." >&2 + local waited=0 coords="" + while [[ -z "$coords" && $waited -lt $GPS_MAX_WAIT_SECS ]]; do + sleep 3 + waited=$((waited + 3)) + # Format on Android 10+: " last location=Location[fused LAT,LON ...]" + local raw + raw="$(adb_cmd shell dumpsys location 2>/dev/null \ + | grep 'last location=Location\[' \ + | grep -oE '[+-]?[0-9]+\.[0-9]+,[+-]?[0-9]+\.[0-9]+' \ + | head -1 || true)" + [[ -n "$raw" ]] && coords="$raw" + printf '.' >&2 + done + printf '\n' >&2 + + if [[ -z "$coords" ]]; then + echo "ERROR: No location fix after ${GPS_MAX_WAIT_SECS}s." >&2 + echo " Make sure the phone has cellular or WiFi data, then retry." >&2 + echo " Or set HOME_LAT/HOME_LON manually in config_secrets.sh." >&2 + return 1 + fi + + local lat="${coords%,*}" + local lon="${coords#*,}" + echo " GPS fix acquired: ${lat}, ${lon}" >&2 + printf '%s %s' "$lat" "$lon" +} + +# ============================================================ +# MAGISK SYSTEMLESS HOSTS AUTO-INSTALL +# ============================================================ +# Creates the module dir+module.prop if absent, removes disable +# markers if disabled, then reboots the device and waits up to +# HOSTS_MODULE_REBOOT_WAIT_SECS for it to come back with the +# magic-mount active. No-ops if the module is already OK. +HOSTS_MODULE_REBOOT_WAIT_SECS=180 + +ensure_magisk_hosts_module() { + local state="absent" + if adb_root "test -d /data/adb/modules/hosts" >/dev/null 2>&1; then + if adb_root "test -f /data/adb/modules/hosts/disable -o -f /data/adb/modules/hosts/remove" >/dev/null 2>&1; then + state="disabled" + elif adb_root "test -f /system/etc/hosts" >/dev/null 2>&1; then + state="ok" + else + state="not-mounted" + fi + fi + + if [[ "$state" == "ok" ]]; then + echo " Magisk Systemless Hosts: active." + return 0 + fi + + echo " Magisk Systemless Hosts state: ${state} — auto-installing..." + + case "$state" in + absent) + adb_root "mkdir -p /data/adb/modules/hosts/system/etc" + # module.prop is required for Magisk to recognise and process the module. + adb_root "printf 'id=hosts\nname=Systemless Hosts\nversion=v1\nversionCode=1\nauthor=Magisk\ndescription=Replace /system/etc/hosts\n' \ + > /data/adb/modules/hosts/module.prop" + # Seed a minimal hosts file so the mount target exists at first boot. + adb_root "printf '127.0.0.1 localhost\n::1 localhost\n' \ + > /data/adb/modules/hosts/system/etc/hosts" + adb_root "chmod 644 /data/adb/modules/hosts/system/etc/hosts" + ;; + disabled) + adb_root "rm -f /data/adb/modules/hosts/disable \ + /data/adb/modules/hosts/remove \ + /data/adb/modules/hosts/update" + ;; + not-mounted) + : # module exists and enabled, just needs a reboot + ;; + esac + + echo " Rebooting phone to activate Magisk Hosts module..." + adb_cmd reboot + # Give the device time to actually begin shutting down before we poll. + sleep 20 + + echo " Waiting for device to come back (up to ${HOSTS_MODULE_REBOOT_WAIT_SECS}s)..." + local waited=0 + # Re-establish wireless ADB connection if needed. + while true; do + if [[ -n "${PHONE_IP:-}" ]]; then + adb connect "${PHONE_IP}:5555" >/dev/null 2>&1 || true + fi + if adb_cmd shell echo ok 2>/dev/null | grep -q '^ok$'; then + break + fi + sleep 3 + waited=$((waited + 3)) + if [[ $waited -ge $HOSTS_MODULE_REBOOT_WAIT_SECS ]]; then + echo "ERROR: Device did not come back after ${HOSTS_MODULE_REBOOT_WAIT_SECS}s." + echo " Check USB connection or re-enable wireless ADB, then run deploy again." + exit 1 + fi + printf '.' + done + printf '\n' + + # Wait for Magisk early-init and root to be ready. + echo " Waiting for Magisk root to be available..." + waited=0 + while ! adb_root "id" 2>/dev/null | grep -q "uid=0"; do + sleep 3 + waited=$((waited + 3)) + [[ $waited -ge 60 ]] && echo "ERROR: Root not available after reboot." && exit 1 + printf '.' + done + printf '\n' + + # Final assertion: the magic-mount must now be active. + if ! adb_root "test -f /system/etc/hosts" >/dev/null 2>&1; then + echo "ERROR: /system/etc/hosts is not magic-mounted after reboot." + echo " Magisk may not have applied the module correctly." + echo " Check the Magisk app for module errors and run deploy again." + exit 1 + fi + echo " Magisk Systemless Hosts module is now active." +} + # ============================================================ # AURORA STORE # ============================================================ @@ -216,6 +358,9 @@ do_deploy() { fi echo " Root confirmed." + echo "[2.5] Ensuring Magisk Systemless Hosts module..." + ensure_magisk_hosts_module + echo "[3/7] Creating directories on device..." # Use world-writable staging dir so non-root adb push works adb_cmd shell "mkdir -p /data/local/tmp/focus_stage" @@ -324,6 +469,26 @@ PY_EOF # Only push config_secrets.sh if phone doesn't already have one if adb_root "test -f $REMOTE_DIR/config_secrets.sh" 2>/dev/null; then echo " config_secrets.sh already exists on phone - skipping (preserving real coords)" + elif [[ "${NEEDS_GPS_FETCH}" -eq 1 ]]; then + # Local config_secrets.sh has placeholder coords — capture current GPS from the phone. + # The phone is assumed to be at home during setup, so current location = home location. + local gps_result="" gps_lat="" gps_lon="" + if gps_result="$(fetch_home_coords_from_phone 2>&1)"; then + gps_lat="${gps_result% *}" + gps_lon="${gps_result#* }" + adb_root "printf '#!/system/bin/sh\n# Home coordinates auto-captured from GPS at deploy time\nexport HOME_LAT=\"${gps_lat}\"\nexport HOME_LON=\"${gps_lon}\"\n' \ + > $REMOTE_DIR/config_secrets.sh" + echo " Home coordinates written to phone: ${gps_lat}, ${gps_lon}" + else + # GPS unavailable (no WiFi/cellular yet on fresh phone). + # Write stub coords — focus mode stays OFF, hosts/DNS blocking still works. + # User should run: ./deploy.sh [ip] --capture-coords after configuring WiFi. + adb_root "printf '#!/system/bin/sh\n# STUB: run ./deploy.sh --capture-coords after WiFi setup\nexport HOME_LAT=\"0.000001\"\nexport HOME_LON=\"0.000001\"\n' \ + > $REMOTE_DIR/config_secrets.sh" + echo " WARNING: GPS capture failed — focus mode location enforcement is DISABLED." + echo " Hosts/DNS blocking is active. After configuring WiFi, run:" + echo " ADB_SERIAL=${ADB_SERIAL:-\$PHONE_IP:5555} ./deploy.sh --capture-coords" + fi else echo " Pushing config_secrets.sh (first install)..." adb_cmd push "$SCRIPT_DIR/config_secrets.sh" "/data/local/tmp/focus_stage/config_secrets.sh" @@ -376,60 +541,10 @@ PY_EOF adb_root "chattr +i $REMOTE_DIR/hosts.sha256.workout 2>/dev/null; true" fi - # ---- Magisk Systemless Hosts module (REQUIRED) ---- - # This module magic-mounts /data/adb/modules/hosts/system/etc/hosts - # as /system/etc/hosts at boot — the only way to create that file on - # this ROM's hardware-read-only system partition. - # - # The module must be ENABLED in the Magisk app by the user (one-time, - # after each factory reset). We CANNOT enable it programmatically. - # Without it, no app-level hosts blocking is possible, so we STOP here - # and require user action before the deploy can proceed. - local magisk_hosts_ok=0 - local magisk_hosts_state="absent" - if adb_root "test -d /data/adb/modules/hosts" 2>/dev/null; then - if adb_root "test -f /data/adb/modules/hosts/disable -o -f /data/adb/modules/hosts/remove" 2>/dev/null; then - magisk_hosts_state="disabled" - elif ! adb_root "test -f /system/etc/hosts" 2>/dev/null; then - # Module dir exists, no disable marker, but the magic-mount - # has not happened yet. Either the user just enabled it but - # has not rebooted, or the module is in a broken state. - magisk_hosts_state="not-mounted" - else - magisk_hosts_ok=1 - magisk_hosts_state="ok" - fi - fi - - if [[ "$magisk_hosts_ok" -eq 0 ]]; then - echo "" - echo "╔══════════════════════════════════════════════════════════════════╗" - echo "║ ACTION REQUIRED — Deploy cannot continue ║" - echo "╠══════════════════════════════════════════════════════════════════╣" - if [[ "$magisk_hosts_state" == "not-mounted" ]]; then - echo "║ Magisk 'Systemless Hosts' module is enabled on disk but the ║" - echo "║ /system/etc/hosts magic-mount has NOT happened yet. ║" - echo "║ This means the device has not been rebooted since the module ║" - echo "║ was last toggled on. Without the magic-mount, no hosts-file ║" - echo "║ blocking is possible (the partition is hardware read-only). ║" - echo "║ ║" - echo "║ Steps to fix: ║" - echo "║ 1. Reboot the phone now (adb reboot, or hold power) ║" - echo "║ 2. After reboot, re-run this deploy command ║" - else - echo "║ The Magisk 'Systemless Hosts' module is not enabled. ║" - echo "║ Without it, hosts-file blocking is impossible on this device ║" - echo "║ (the system partition is hardware read-only even with root). ║" - echo "║ ║" - echo "║ Steps to fix: ║" - echo "║ 1. Open the Magisk app on the phone ║" - echo "║ 2. Tap the Modules tab (puzzle-piece icon) ║" - echo "║ 3. Find 'Systemless Hosts' and toggle it ON ║" - echo "║ 4. Reboot the phone when prompted ║" - echo "║ 5. Re-run this deploy command ║" - fi - echo "╚══════════════════════════════════════════════════════════════════╝" - echo "" + # Magisk Systemless Hosts module was ensured (and rebooted if needed) in + # step [2.5] above. Sanity-assert it's still active before writing to it. + if ! adb_root "test -f /system/etc/hosts" 2>/dev/null; then + echo "ERROR: /system/etc/hosts not magic-mounted — run deploy again." exit 1 fi @@ -604,6 +719,27 @@ do_pull_log() { echo "Done." } +do_capture_coords() { + # Standalone GPS capture for post-WiFi-setup use. + # Overwrites config_secrets.sh on the phone with the current location. + connect_adb + if ! adb_root "id" 2>/dev/null | grep -q "uid=0"; then + echo "ERROR: Root not available." + exit 1 + fi + echo "Capturing home coordinates from phone GPS..." + local gps_result gps_lat gps_lon + gps_result="$(fetch_home_coords_from_phone)" + gps_lat="${gps_result% *}" + gps_lon="${gps_result#* }" + adb_root "printf '#!/system/bin/sh\n# Home coordinates auto-captured from GPS\nexport HOME_LAT=\"${gps_lat}\"\nexport HOME_LON=\"${gps_lon}\"\n' \ + > $REMOTE_DIR/config_secrets.sh" + echo "Home coordinates updated on phone: ${gps_lat}, ${gps_lon}" + echo "Restarting focus daemon to apply new coordinates..." + adb_root "sh $REMOTE_DIR/focus_ctl.sh restart" 2>/dev/null || true + echo "Done." +} + do_find_pkg() { local filter="${3:-}" if [ -z "$filter" ]; then @@ -656,6 +792,7 @@ case "$ACTION" in --hosts-log) connect_adb; adb_root "sh $REMOTE_DIR/focus_ctl.sh hosts-log 100" ;; --launcher-status) do_control "launcher-status" ;; --launcher-log) connect_adb; adb_root "sh $REMOTE_DIR/focus_ctl.sh launcher-log 100" ;; + --capture-coords) do_capture_coords ;; --snapshot-launcher) do_snapshot_launcher ;; --install-aurora) do_install_aurora ;; *) echo "Unknown action: $ACTION"; usage ;;