diff --git a/.github/workflows/shell-tests.yml b/.github/workflows/shell-tests.yml index f960def0..d3611a83 100644 --- a/.github/workflows/shell-tests.yml +++ b/.github/workflows/shell-tests.yml @@ -26,8 +26,6 @@ jobs: run: | set -euo pipefail tests=( - test_android_guardian_post_fs_data.sh - test_android_guardian_service.sh test_i3blocks_persist_common.sh test_music_parallelism.sh test_setup_dns_blocker.sh diff --git a/AGENTS.md b/AGENTS.md index 2896b259..4572c068 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,9 @@ Extracted to their own repos: [`steam-backlog-enforcer`](https://github.com/kuhy [`focus-owner`](https://github.com/kuhyx/focus-owner), [`mtk-root`](https://github.com/kuhyx/mtk-root), [`system-maintenance`](https://github.com/kuhyx/system-maintenance) (lives at -`~/system-maintenance`). +`~/system-maintenance`), +[`android-guardian`](https://github.com/kuhyx/android-guardian) (lives at +`~/android-guardian`). Archived work: [`testsAndMisc-archive`](https://github.com/kuhyx/testsAndMisc-archive). ## Adding a Flutter app diff --git a/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json b/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json new file mode 100644 index 00000000..c4e3b8e6 --- /dev/null +++ b/docs/superpowers/contracts/extract-android-guardian-2026-08-23.json @@ -0,0 +1,20 @@ +{ + "title": "Extract android_guardian and settle the heavy_job_lock seam", + "objective": "Move the Magisk module to its own public repo. The directory it lived in also held heavy_job_lock.sh, a shared lock library that is NOT part of the module, so this extraction has to split the directory rather than move it wholesale. It also inherits a packaging script whose module path was already broken before the extraction.", + "acceptance_criteria": [ + "The module and both its test suites are in the new public repo, tests passing from a clean clone, shellcheck exiting 0 with no severity filter.", + "heavy_job_lock.sh lands where its installer is, justified by measuring which consumers read the repo copy versus the installed /usr/local/bin copy - not by the ad-hoc '>2 consumers' rule.", + "install_pacman_wrapper.sh's relative path to heavy_job_lock.sh resolves after the move.", + "update_android_hosts.sh points at a real location, and a missing checkout fails with an actionable message at a single funnel rather than a bare cp error deep in a helper.", + "The module still builds end-to-end from the new checkout.", + "shell-tests.yml names no removed suite; the coverage allowlist names no removed path.", + "periodic_background/utils/ is fully emptied, not left as a one-file directory.", + "pre-commit passes without --no-verify." + ], + "out_of_scope": [ + "Moving heavy_job_lock.sh to ~/utils: the measurement does not support it, and every consumer but the installer already reads the installed copy.", + "The adb push half of update_android_hosts.sh, which needs a device in a writable state.", + "digital_wellbeing itself, which is the next and last extraction." + ], + "verifier": "bash tests/*.sh in the new repo passes; shellcheck exits 0 there; ANDROID_GUARDIAN_DIR=/nonexistent produces the guard message; a real run builds the zip (182331 domains); shellcheck and check_shell_coverage.sh --all clean here; check_directory_depth.sh --all continues to fall." +} diff --git a/docs/superpowers/evidence/extract-android-guardian-2026-08-23.json b/docs/superpowers/evidence/extract-android-guardian-2026-08-23.json new file mode 100644 index 00000000..c9500136 --- /dev/null +++ b/docs/superpowers/evidence/extract-android-guardian-2026-08-23.json @@ -0,0 +1,48 @@ +{ + "intent": "Extract the android_guardian Magisk module into its own public repo, settle where heavy_job_lock.sh belongs, and fix a pre-existing broken path in the packaging script rather than carrying it forward.", + "scope": [ + "periodic_background/utils/android_guardian/ (5 files) -> github.com/kuhyx/android-guardian as module/, cloned to ~/android-guardian", + "Its two test suites in linux_configuration/tests/ moved with it", + "heavy_job_lock.sh moved to digital_wellbeing/, its only installer - NOT to ~/utils", + "update_android_hosts.sh + lib/android_module.sh repointed and guarded", + "shell-tests.yml, shell-coverage-allowlist.txt cleaned", + "periodic_background/utils/ is now empty and removed" + ], + "changes": [ + "Extracted the module and both suites in one filter-repo pass (4 commits), renaming the module directory to module/ and fixing the test paths that named the old monorepo location.", + "Moved heavy_job_lock.sh into digital_wellbeing/ instead of ~/utils. Measured: of its consumers, only install_pacman_wrapper.sh reads the repo copy; pacman_wrapper.sh, makepkg_capped.sh and meta/scripts/run_with_heavy_lock.sh all read the installed /usr/local/bin path. It belongs with its installer.", + "Fixed a pre-existing bug: update_android_hosts.sh set GUARDIAN_MODULE_DIR to $SCRIPT_DIR/android_guardian, a sibling directory that never existed at that path, so packaging could not have worked from this checkout. It now reads ${ANDROID_GUARDIAN_DIR:-$HOME/android-guardian}/module.", + "Added require_guardian_module() and called it from build_module(), the single funnel every packaging path goes through, so a missing checkout fails with an actionable message instead of a bare 'cp: cannot stat' four helpers deep." + ], + "verification": [ + { + "command": "bash tests/*.sh (extracted repo)", + "result": "pass", + "evidence": "Both suites pass after the path rewrite; shellcheck exits 0 over every script with .shellcheckrc carried across." + }, + { + "command": "ANDROID_GUARDIAN_DIR=/nonexistent bash update_android_hosts.sh install", + "result": "pass", + "evidence": "Stops at the guard with the clone command and the override variable named, rather than failing inside cp." + }, + { + "command": "bash update_android_hosts.sh install (with ~/android-guardian cloned)", + "result": "pass", + "evidence": "Module built: 182331 blocked domains, a 1149321-byte zip. It then failed at `adb push` with 'remote couldn't create file' - a device-side storage condition, not a path problem, and the build half is what this change touches." + }, + { + "command": "shellcheck on both edited scripts; check_shell_coverage.sh --all", + "result": "pass", + "evidence": "Both clean, exit 0." + } + ], + "risks": [ + "The adb push step was not verified end-to-end: the device refused to create the file on /sdcard. That path is unchanged by this commit, but it does mean the full install flow is unproven today.", + "update_android_hosts.sh now depends on a sibling checkout. It fails loudly with instructions rather than silently, which is the tradeoff taken.", + "heavy_job_lock.sh now sits inside digital_wellbeing, so it will move again when that subsystem is extracted; install_pacman_wrapper.sh's relative path was updated to match and stays correct through that move." + ], + "rollback": [ + "git revert to restore the module, both tests and heavy_job_lock.sh's old location.", + "Re-run check_shell_coverage.sh --all and the two suites to confirm the restored state." + ] +} diff --git a/linux_configuration/scripts/periodic_background/utils/heavy_job_lock.sh b/linux_configuration/scripts/periodic_background/digital_wellbeing/heavy_job_lock.sh similarity index 100% rename from linux_configuration/scripts/periodic_background/utils/heavy_job_lock.sh rename to linux_configuration/scripts/periodic_background/digital_wellbeing/heavy_job_lock.sh diff --git a/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh b/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh index ed6b4054..38416943 100755 --- a/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh +++ b/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh @@ -41,7 +41,7 @@ WHITELIST_SOURCE="$(dirname "$0")/pacman_whitelist.txt" GREYLIST_SOURCE="$(dirname "$0")/pacman_greylist.txt" MAKEPKG_CAPPED_SOURCE="$(dirname "$0")/makepkg_capped.sh" MKPKG_SOURCE="$(dirname "$0")/mkpkg.sh" -HEAVY_LOCK_SOURCE="$(dirname "$0")/../../utils/heavy_job_lock.sh" +HEAVY_LOCK_SOURCE="$(dirname "$0")/../heavy_job_lock.sh" INSTALL_DIR="/usr/local/bin" WRAPPER_DEST="${INSTALL_DIR}/pacman_wrapper" LOCK_LIB_DEST="${INSTALL_DIR}/pacman_lock_lib.sh" diff --git a/linux_configuration/scripts/periodic_background/utils/android_guardian/blocked_apps.txt b/linux_configuration/scripts/periodic_background/utils/android_guardian/blocked_apps.txt deleted file mode 100644 index 4fc2b8ce..00000000 --- a/linux_configuration/scripts/periodic_background/utils/android_guardian/blocked_apps.txt +++ /dev/null @@ -1,206 +0,0 @@ -# Android Guardian - Blocked Apps List -# One package name per line -# Lines starting with # are comments - -# ===== Food Delivery Apps ===== -# Uber Eats -com.ubercab.eats -com.ubercab - -# Glovo -com.glovo -com.glovo.courier - -# Wolt -com.wolt.android - -# Bolt Food / Bolt -com.bolt.deliveryclient -ee.mtakso.client -ee.mtakso.food -ee.mtakso - -# Pyszne.pl / Takeaway -com.takeaway.android -com.pyszne.pl -com.takeaway.android.pl - -# Jush (Polish quick delivery) -pl.jush.app -pl.jush.android - -# Delio (Polish delivery) -pl.delio.app -pl.delio.android -com.delio.app - -# Lisek (Polish quick delivery) -pl.lisek.app -com.lisek.app - -# Stava (Polish delivery) -pl.stava.app - -# Barbora (Polish grocery delivery) -pl.barbora.app -com.barbora.pl - -# Frisco (Polish grocery delivery) -pl.frisco.app -com.frisco.android - -# Szama (Polish food delivery) -pl.szama.app - -# Biedronka online grocery -pl.biedronka.app -com.jeronimo.biedronka - -# DoorDash -com.dd.doordash - -# Grubhub -com.grubhub.android - -# Deliveroo -com.deliveroo.orderapp - -# Just Eat -com.justeat.app.uk -com.justeat.android - -# Postmates -com.postmates.android - -# Seamless -com.seamless.consumer - -# Foodpanda -com.global.foodpanda.android - -# Getir (quick delivery) -com.getir -com.getir.express - -# Flink -com.flink.android -com.flink.consumer - -# GoPuff -com.gopuff.android - -# Instacart -com.instacart.client - -# Gorillas -com.gorillas.consumer - -# ===== Browsers (to prevent bypassing blocks) ===== -# Firefox (allowed - hosts blocking is sufficient) -# org.mozilla.firefox -# org.mozilla.firefox_beta -# org.mozilla.fenix - -# Chrome (comment out if needed for some functionality) -# com.android.chrome - -# Opera -com.opera.browser -com.opera.mini.native -com.opera.gx - -# Brave -com.brave.browser - -# Samsung Internet -com.sec.android.app.sbrowser - -# Microsoft Edge -com.microsoft.emmx - -# DuckDuckGo -com.duckduckgo.mobile.android - -# Kiwi Browser -com.kiwibrowser.browser - -# Vivaldi -com.vivaldi.browser - -# UC Browser -com.UCMobile.intl - -# Tor Browser -org.torproject.torbrowser - -# ===== YouTube Apps ===== -com.google.android.youtube -com.vanced.android.youtube -app.revanced.android.youtube -app.revanced.android.apps.youtube.music - -# ===== Alternative YouTube Frontend Apps ===== -# NewPipe -org.schabi.newpipe -org.schabi.newpipelegacy - -# LibreTube -com.github.libretube - -# SkyTube -free.rm.skytube.oss -free.rm.skytube.extra - -# Clipious (Invidious client) -com.github.lamarios.clipious - -# Hyperion (YouTube client) -us.spotco.hyperion - -# GrayJay -com.futo.platformplayer - -# PipePipe -InfinityLoop1309.NewPipeEnhanced - -# Materialious -com.materialious.app - -# VueTube -app.vuetube.app - -# ===== Fast Food Apps ===== -# McDonald's -com.mcdonalds.app -com.mcdonalds.mobileapp - -# KFC -com.yum.kfcpolska -com.kfc.app - -# Burger King -com.emn8.mobilem8.nativeapp.bk -com.burgerking - -# Pizza Hut -com.pizzahut.app - -# Domino's -com.dominospizza - -# Subway -com.subway.mobile - -# ===== Other Distracting Apps ===== -# TikTok -com.zhiliaoapp.musically -com.ss.android.ugc.trill - -# Instagram (optional - comment out if needed) -# com.instagram.android - -# Facebook (optional) -# com.facebook.katana - -# Twitter/X -# com.twitter.android diff --git a/linux_configuration/scripts/periodic_background/utils/android_guardian/module.prop b/linux_configuration/scripts/periodic_background/utils/android_guardian/module.prop deleted file mode 100644 index f0b3803d..00000000 --- a/linux_configuration/scripts/periodic_background/utils/android_guardian/module.prop +++ /dev/null @@ -1,6 +0,0 @@ -id=android_guardian -name=Android Guardian (Hosts + App Blocker) -version=1.1 -versionCode=2 -author=linux-configuration -description=Persistent hosts blocking and app installation guard. Protected by watchdog - cannot be disabled from Magisk UI. Only controllable via ADB. diff --git a/linux_configuration/scripts/periodic_background/utils/android_guardian/post-fs-data.sh b/linux_configuration/scripts/periodic_background/utils/android_guardian/post-fs-data.sh deleted file mode 100755 index 7c08e308..00000000 --- a/linux_configuration/scripts/periodic_background/utils/android_guardian/post-fs-data.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/system/bin/sh -# Runs early in boot - set up hosts file and start watchdog -# MODDIR is set by Magisk and points to this module's directory -GUARDIAN_DIR="${ANDROID_GUARDIAN_DIR:-/data/adb/android_guardian}" -WATCHDOG_SCRIPT="${ANDROID_GUARDIAN_WATCHDOG_SCRIPT:-$GUARDIAN_DIR/watchdog.sh}" -LOG_FILE="$GUARDIAN_DIR/guardian.log" - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] post-fs-data: $*" >>"$LOG_FILE" -} - -write_watchdog_script() { -cat >"$WATCHDOG_SCRIPT" <<'WATCHDOG' -#!/system/bin/sh -# Secondary watchdog - runs independently of module state -# Even if module is "disabled" in Magisk UI, this keeps running and undoes it -GUARDIAN_DIR="${ANDROID_GUARDIAN_DIR:-/data/adb/android_guardian}" -MODULE_DIR="${ANDROID_GUARDIAN_MODULE_DIR:-/data/adb/modules/android_guardian}" -LOG_FILE="$GUARDIAN_DIR/watchdog.log" -CONTROL_FILE="$GUARDIAN_DIR/control" -HOSTS_BACKUP="$GUARDIAN_DIR/hosts.backup" -MODULE_HOSTS="$MODULE_DIR/system/etc/hosts" -LOOP_SLEEP_SECONDS=3 -HOSTS_CHECK_EVERY_TICKS=10 - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$LOG_FILE" -} - -is_enabled() { - if [ ! -f "$CONTROL_FILE" ]; then - return 1 - fi - - IFS= read -r guardian_state < "$CONTROL_FILE" || guardian_state="" - [ "$guardian_state" = "ENABLED" ] -} - -protect_module_flags() { - if [ -f "$MODULE_DIR/disable" ]; then - log "ALERT: Module disable detected via Magisk UI - removing disable flag" - rm -f "$MODULE_DIR/disable" - fi - - if [ -f "$MODULE_DIR/remove" ]; then - log "ALERT: Module removal detected via Magisk UI - removing remove flag" - rm -f "$MODULE_DIR/remove" - fi -} - -protect_hosts() { - if [ ! -f "$HOSTS_BACKUP" ] || [ ! -f "$MODULE_HOSTS" ]; then - return - fi - - if ! cmp -s "$MODULE_HOSTS" "$HOSTS_BACKUP"; then - log "ALERT: Hosts tampering detected - restoring" - cp "$HOSTS_BACKUP" "$MODULE_HOSTS" - fi -} - -log "=== Watchdog starting ===" - -tick_count=0 -while true; do - protect_module_flags - - if is_enabled; then - if [ $((tick_count % HOSTS_CHECK_EVERY_TICKS)) -eq 0 ]; then - protect_hosts - fi - fi - - tick_count=$((tick_count + 1)) - sleep "$LOOP_SLEEP_SECONDS" -done -WATCHDOG -} - -start_watchdog() { - nohup sh "$WATCHDOG_SCRIPT" >/dev/null 2>&1 & -} - -post_fs_main() { - mkdir -p "$GUARDIAN_DIR" - log "Guardian module loading" - write_watchdog_script - chmod 755 "$WATCHDOG_SCRIPT" - - if [ "${ANDROID_GUARDIAN_POST_FS_SKIP_WATCHDOG_START:-0}" -ne 1 ]; then - start_watchdog - log "Watchdog started" - return - fi - - log "Watchdog generation complete (start skipped)" -} - -if [ "${ANDROID_GUARDIAN_POST_FS_SKIP_MAIN:-0}" -ne 1 ]; then - post_fs_main -fi diff --git a/linux_configuration/scripts/periodic_background/utils/android_guardian/service.sh b/linux_configuration/scripts/periodic_background/utils/android_guardian/service.sh deleted file mode 100755 index dcbc298c..00000000 --- a/linux_configuration/scripts/periodic_background/utils/android_guardian/service.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/system/bin/sh -# Android Guardian Service - runs at boot -# This service: -# 1. Monitors and protects the hosts file -# 2. Blocks installation of forbidden apps -# 3. Prevents module from being disabled via Magisk UI -# 4. Can only be stopped via ADB with the correct command - -MODDIR=${0%/*} -GUARDIAN_DIR="${ANDROID_GUARDIAN_DIR:-/data/adb/android_guardian}" -LOG_FILE="$GUARDIAN_DIR/guardian.log" -BLOCKED_APPS_FILE="$GUARDIAN_DIR/blocked_apps.txt" -CONTROL_FILE="$GUARDIAN_DIR/control" -HOSTS_BACKUP="$GUARDIAN_DIR/hosts.backup" -MODULE_DIR="${ANDROID_GUARDIAN_MODULE_DIR:-/data/adb/modules/android_guardian}" -SYSTEM_HOSTS_FILE="${ANDROID_GUARDIAN_SYSTEM_HOSTS_FILE:-/system/etc/hosts}" -MODULE_HOSTS_FILE="${ANDROID_GUARDIAN_MODULE_HOSTS_FILE:-$MODDIR/system/etc/hosts}" -DISABLE_FILE="$MODULE_DIR/disable" -REMOVE_FILE="$MODULE_DIR/remove" -LOOP_SLEEP_SECONDS=5 -HOSTS_CHECK_EVERY_TICKS=6 -APPS_CHECK_EVERY_TICKS=12 - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$LOG_FILE" -} - -initialize_service() { - mkdir -p "$GUARDIAN_DIR" - - if [ ! -f "$CONTROL_FILE" ]; then - echo "ENABLED" >"$CONTROL_FILE" - fi - - log "=== Android Guardian starting ===" - - # Enable wireless ADB on boot (persistent port 5555) - setprop service.adb.tcp.port 5555 - stop adbd - start adbd - log "Wireless ADB enabled on port 5555" -} - -# Function to check if guardian is enabled (via ADB control, not Magisk UI) -is_enabled() { - [ "$(cat "$CONTROL_FILE" 2>/dev/null)" = "ENABLED" ] -} - -# Function to protect module from being disabled via Magisk UI -protect_module() { - # Remove disable file if someone tried to disable via Magisk - if [ -f "$DISABLE_FILE" ]; then - log "Module disable attempt detected via Magisk UI! Re-enabling..." - rm -f "$DISABLE_FILE" - log "Module re-enabled" - fi - - # Remove remove file if someone tried to uninstall via Magisk - if [ -f "$REMOVE_FILE" ]; then - log "Module removal attempt detected via Magisk UI! Blocking..." - rm -f "$REMOVE_FILE" - log "Module removal blocked" - fi -} - -# Function to restore hosts file if tampered -protect_hosts() { - if [ -f "$HOSTS_BACKUP" ]; then - if ! cmp -s "$SYSTEM_HOSTS_FILE" "$HOSTS_BACKUP"; then - log "Hosts file tampering detected! Restoring..." - cp "$HOSTS_BACKUP" "$MODULE_HOSTS_FILE" - log "Hosts file restored" - fi - fi -} - -# Function to uninstall blocked apps -check_blocked_apps() { - if [ ! -f "$BLOCKED_APPS_FILE" ]; then - return - fi - - installed_packages=$(pm list packages 2>/dev/null) || installed_packages="" - if [ -z "$installed_packages" ]; then - return - fi - installed_packages=" -$installed_packages -" - - while IFS= read -r package || [ -n "$package" ]; do - # Skip comments and empty lines - case "$package" in - \#* | "") continue ;; - esac - - # Check if package is installed - case "$installed_packages" in - *" -package:$package -"*) - log "Blocked app detected: $package - Uninstalling..." - if pm uninstall "$package" 2>/dev/null; then - log "Uninstalled: $package" - else - log "Failed to uninstall: $package" - fi - ;; - esac - done <"$BLOCKED_APPS_FILE" -} - -guardian_loop() { - tick_count=0 - while true; do - # ALWAYS protect module from UI disabling (even if guardian is "disabled" via ADB) - # This ensures only ADB can control the guardian - protect_module - - if is_enabled; then - if [ $((tick_count % HOSTS_CHECK_EVERY_TICKS)) -eq 0 ]; then - protect_hosts - fi - - if [ $((tick_count % APPS_CHECK_EVERY_TICKS)) -eq 0 ]; then - check_blocked_apps - fi - fi - - tick_count=$((tick_count + 1)) - sleep "$LOOP_SLEEP_SECONDS" - done -} - -service_main() { - initialize_service - guardian_loop & - log "Guardian service started (PID: $!)" -} - -if [ "${ANDROID_GUARDIAN_SKIP_MAIN:-0}" -ne 1 ]; then - service_main -fi diff --git a/linux_configuration/scripts/periodic_background/utils/android_guardian/uninstall.sh b/linux_configuration/scripts/periodic_background/utils/android_guardian/uninstall.sh deleted file mode 100755 index b229f21a..00000000 --- a/linux_configuration/scripts/periodic_background/utils/android_guardian/uninstall.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/system/bin/sh -# Cleanup when module is uninstalled -GUARDIAN_DIR="/data/adb/android_guardian" - -# Only allow uninstall if control file says DISABLED -if [ -f "$GUARDIAN_DIR/control" ]; then - status=$(cat "$GUARDIAN_DIR/control") - if [ "$status" != "DISABLED" ]; then - echo "Guardian is still enabled! Use ADB to disable first:" - echo " adb shell 'echo DISABLED > /data/adb/android_guardian/control'" - exit 1 - fi -fi - -# Clean up guardian data -rm -rf "$GUARDIAN_DIR" diff --git a/linux_configuration/scripts/single_use/utils/lib/android_module.sh b/linux_configuration/scripts/single_use/utils/lib/android_module.sh index 217f24b3..db3864bf 100755 --- a/linux_configuration/scripts/single_use/utils/lib/android_module.sh +++ b/linux_configuration/scripts/single_use/utils/lib/android_module.sh @@ -9,6 +9,8 @@ build_module() { local tmp_dir="$WORK_DIR/guardian_module" local module_zip="$WORK_DIR/android_guardian.zip" + require_guardian_module + echo "[BUILD] Building Android Guardian module..." >&2 rm -rf "$tmp_dir" diff --git a/linux_configuration/scripts/single_use/utils/update_android_hosts.sh b/linux_configuration/scripts/single_use/utils/update_android_hosts.sh index a9e856a6..18a9253b 100755 --- a/linux_configuration/scripts/single_use/utils/update_android_hosts.sh +++ b/linux_configuration/scripts/single_use/utils/update_android_hosts.sh @@ -10,10 +10,25 @@ source "$SCRIPT_DIR/../../lib/common.sh" # shellcheck source=../../lib/android.sh source "$SCRIPT_DIR/../../lib/android.sh" -GUARDIAN_MODULE_DIR="$SCRIPT_DIR/android_guardian" +# The module moved to github.com/kuhyx/android-guardian. This pointed at a +# sibling directory that never existed here even before the extraction, so the +# script could not have packaged a module from this path; prefer a checkout and +# say so loudly rather than failing later inside zip. +GUARDIAN_MODULE_DIR="${ANDROID_GUARDIAN_DIR:-$HOME/android-guardian}/module" GUARDIAN_DATA_DIR="/data/adb/android_guardian" MODULE_DEST="/data/adb/modules/android_guardian" +# Fail here, not four helpers deep inside a cp. Every packaging step below +# reads GUARDIAN_MODULE_DIR unguarded, so an absent checkout would otherwise +# surface as a bare "cp: cannot stat". +require_guardian_module() { + [[ -f "$GUARDIAN_MODULE_DIR/module.prop" ]] && return 0 + die "android-guardian module not found at ${GUARDIAN_MODULE_DIR}. + It lives in its own repo now: + git clone https://github.com/kuhyx/android-guardian ~/android-guardian + Or point this script elsewhere with ANDROID_GUARDIAN_DIR." +} + # Ensure android-tools (adb) is installed ensure_adb_installed() { if command -v adb &>/dev/null; then @@ -90,7 +105,6 @@ source "$SCRIPT_DIR/lib/android_module.sh" # shellcheck source=lib/android_commands.sh source "$SCRIPT_DIR/lib/android_commands.sh" - # Main # Initialize Android script (handles sudo, sets WORK_DIR) init_android_script "$@" diff --git a/linux_configuration/tests/test_android_guardian_post_fs_data.sh b/linux_configuration/tests/test_android_guardian_post_fs_data.sh deleted file mode 100755 index 810e51d2..00000000 --- a/linux_configuration/tests/test_android_guardian_post_fs_data.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -# Regression tests for android_guardian post-fs-data watchdog generation. - -set -euo pipefail - -SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) -REPO_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd) -TARGET_SCRIPT="$REPO_DIR/scripts/periodic_background/utils/android_guardian/post-fs-data.sh" - -fail() { - printf 'FAIL: %s\n' "$1" >&2 - exit 1 -} - -assert_file_contains() { - local file_path="$1" - local pattern="$2" - local context="$3" - grep -q -- "$pattern" "$file_path" || fail "$context" -} - -assert_file_not_contains() { - local file_path="$1" - local pattern="$2" - local context="$3" - if grep -q -- "$pattern" "$file_path"; then - fail "$context" - fi -} - -TMP_DIR=$(mktemp -d) -cleanup() { - rm -rf "$TMP_DIR" -} -trap cleanup EXIT - -GUARDIAN_DIR="$TMP_DIR/guardian" -MODULE_DIR="$TMP_DIR/module" -WATCHDOG_SCRIPT="$GUARDIAN_DIR/watchdog.sh" -mkdir -p "$GUARDIAN_DIR" "$MODULE_DIR/system/etc" - -printf 'Generating watchdog script in a temp Android guardian directory...\n' -ANDROID_GUARDIAN_DIR="$GUARDIAN_DIR" \ -ANDROID_GUARDIAN_MODULE_DIR="$MODULE_DIR" \ -ANDROID_GUARDIAN_WATCHDOG_SCRIPT="$WATCHDOG_SCRIPT" \ -ANDROID_GUARDIAN_POST_FS_SKIP_WATCHDOG_START=1 \ -sh "$TARGET_SCRIPT" - -[[ -f "$WATCHDOG_SCRIPT" ]] || fail 'watchdog script should be generated' -[[ -x "$WATCHDOG_SCRIPT" ]] || fail 'watchdog script should be executable' - -printf 'Checking generated watchdog cadence and lower-fork host protection...\n' -assert_file_contains "$WATCHDOG_SCRIPT" '^HOSTS_CHECK_EVERY_TICKS=10$' \ - 'watchdog should throttle host protection to every 10 ticks' -assert_file_contains "$WATCHDOG_SCRIPT" '^LOOP_SLEEP_SECONDS=3$' \ - 'watchdog should keep the 3 second base sleep' -assert_file_contains "$WATCHDOG_SCRIPT" 'cmp -s' \ - 'watchdog should use cmp for host integrity checks' -assert_file_not_contains "$WATCHDOG_SCRIPT" 'md5sum' \ - 'watchdog should not use md5sum in the hot loop anymore' -assert_file_not_contains "$WATCHDOG_SCRIPT" 'cut -d' \ - 'watchdog should not pipe hashes through cut anymore' - -printf 'Checking test hooks and generation log entry...\n' -assert_file_contains "$TARGET_SCRIPT" 'ANDROID_GUARDIAN_POST_FS_SKIP_MAIN' \ - 'post-fs-data should support skipping main for tests' -assert_file_contains "$TARGET_SCRIPT" 'ANDROID_GUARDIAN_POST_FS_SKIP_WATCHDOG_START' \ - 'post-fs-data should support generating without starting the watchdog' -assert_file_contains "$GUARDIAN_DIR/guardian.log" 'Watchdog generation complete (start skipped)' \ - 'post-fs-data should log skipped watchdog starts during tests' - -printf 'android_guardian post-fs-data regression checks passed.\n' diff --git a/linux_configuration/tests/test_android_guardian_service.sh b/linux_configuration/tests/test_android_guardian_service.sh deleted file mode 100755 index c018bd71..00000000 --- a/linux_configuration/tests/test_android_guardian_service.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -# Regression tests for android_guardian service loop cadence. - -set -euo pipefail - -SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) -REPO_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd) -TARGET_SCRIPT="$REPO_DIR/scripts/periodic_background/utils/android_guardian/service.sh" - -fail() { - printf 'FAIL: %s\n' "$1" >&2 - exit 1 -} - -assert_equals() { - local expected="$1" - local actual="$2" - local context="$3" - if [[ "$expected" != "$actual" ]]; then - fail "$context (expected: '$expected', actual: '$actual')" - fi -} - -TMP_DIR=$(mktemp -d) -cleanup() { - rm -rf "$TMP_DIR" -} -trap cleanup EXIT - -WORKTREE="$TMP_DIR/worktree" -mkdir -p "$WORKTREE/scripts/periodic_background/utils/android_guardian" -cp "$TARGET_SCRIPT" "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" - -printf 'Checking skip-main avoids boot side effects...\n' -SKIP_MAIN_GUARDIAN_DIR="$TMP_DIR/skip-main-guardian" -ANDROID_GUARDIAN_SKIP_MAIN=1 \ - ANDROID_GUARDIAN_DIR="$SKIP_MAIN_GUARDIAN_DIR" \ - ANDROID_GUARDIAN_MODULE_DIR="$TMP_DIR/skip-main-module" \ - sh "$TARGET_SCRIPT" - -[[ ! -d "$SKIP_MAIN_GUARDIAN_DIR" ]] || - fail 'skip-main should prevent guardian boot setup side effects' - -printf 'Checking guardian loop cadence constants...\n' -hosts_ticks=$(grep '^HOSTS_CHECK_EVERY_TICKS=' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" | cut -d= -f2) -apps_ticks=$(grep '^APPS_CHECK_EVERY_TICKS=' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" | cut -d= -f2) -sleep_seconds=$(grep '^LOOP_SLEEP_SECONDS=' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" | cut -d= -f2) - -assert_equals '6' "$hosts_ticks" 'hosts protection should run every 6 ticks' -assert_equals '12' "$apps_ticks" 'blocked-app scan should run every 12 ticks' -assert_equals '5' "$sleep_seconds" 'guardian loop should keep the 5 second base sleep' - -printf 'Checking guardian loop protects module every tick...\n' -grep -q 'protect_module' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" || - fail 'guardian loop must always protect the module each tick' - -printf 'Checking blocked-app scans are cached per pass...\n' -# Literal search pattern: the $ must reach grep as text, not be expanded -# by this shell. -# shellcheck disable=SC2016 -grep -q 'installed_packages=$(pm list packages 2>/dev/null)' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" || - fail 'guardian service should cache installed packages once per blocked-app scan' - -if grep -q 'pm list packages 2>/dev/null | grep -q' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh"; then - fail 'guardian service should not re-run pm list packages through grep for every blocked app' -fi - -printf 'Checking guardian loop uses skip-main guard for tests...\n' -grep -q 'ANDROID_GUARDIAN_SKIP_MAIN' "$WORKTREE/scripts/periodic_background/utils/android_guardian/service.sh" || - fail 'guardian service should support skip-main testing' - -printf 'android_guardian service regression checks passed.\n'