#!/system/bin/sh
# Boot Timing & Verification Evasion
#
# Controls WHEN custom drivers and modifications are applied relative
# to Android's verification checkpoints:
#
#   1. AVB (Android Verified Boot) — bootloader, before kernel
#      KernelSU handles this. We don't touch partitions. PASS.
#
#   2. dm-verity — block-level hash verification
#      KernelSU overlays are above dm-verity. Stock blocks untouched. PASS.
#
#   3. Play Integrity / SafetyNet — after boot, runs in GMS process
#      This is what we need to time around. First check is ~10-30s
#      after boot_completed. Periodic re-checks every few hours.
#
#   4. Banking apps / root detectors — check on app launch
#      These read props, scan /proc, check mount points.
#
# Strategy:
#   - post-fs-data: ONLY stock-safe props. No mods. No spoofs.
#   - boot_completed: Wait for first PI attestation to finish
#   - Apply mods AFTER attestation window closes
#   - Run a watcher that detects PI re-checks and temporarily
#     unmounts spoofs + restores stock props during the check

MODDIR="/data/adb/modules/driver-manager"
CONFDIR="$MODDIR/config"
LOGFILE="$MODDIR/driver-manager.log"

mlog() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [boot_timing] $1" >> "$LOGFILE"
}

# =================================================================
# Phase 1: Detect when Play Integrity has completed first check
# =================================================================
# GMS runs attestation via com.google.android.gms.unstable
# DroidGuard collects device state in this window
# We watch for this process to start and finish

wait_for_pi_pass() {
    mlog "Waiting for Play Integrity first attestation..."

    # Wait for GMS to be fully running
    TIMEOUT=120
    ELAPSED=0
    while [ $ELAPSED -lt $TIMEOUT ]; do
        # Check if GMS unstable (DroidGuard) has started
        GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null)
        if [ -n "$GMS_PID" ]; then
            mlog "DroidGuard detected (PID $GMS_PID), waiting for completion..."

            # Wait for the DroidGuard process to finish its attestation
            # It typically runs for 5-15 seconds during initial check
            DROID_WAIT=0
            while [ $DROID_WAIT -lt 30 ]; do
                sleep 1
                DROID_WAIT=$((DROID_WAIT + 1))

                # Check if it's still actively doing attestation
                # DroidGuard CPU usage drops after attestation completes
                if [ -d "/proc/$GMS_PID" ]; then
                    CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}')
                    if [ -n "$CPU" ] && [ "$CPU" -lt 5 ] && [ $DROID_WAIT -gt 10 ]; then
                        mlog "DroidGuard idle after ${DROID_WAIT}s — attestation likely complete"
                        break
                    fi
                else
                    mlog "DroidGuard process exited after ${DROID_WAIT}s"
                    break
                fi
            done

            # Extra safety margin
            sleep 5
            mlog "PI attestation window closed"
            return 0
        fi

        sleep 2
        ELAPSED=$((ELAPSED + 2))
    done

    # If GMS never started (unlikely), just wait a safe fixed time
    mlog "GMS not detected after ${TIMEOUT}s — using fixed delay"
    sleep 30
    return 0
}

# =================================================================
# Phase 2: Apply modifications after PI passes
# =================================================================
apply_after_pi() {
    mlog "Applying modifications..."

    # Apply driver spoofs
    SPOOF_ENABLED=$(cat "$CONFDIR/spoof_enabled" 2>/dev/null || echo "0")
    if [ "$SPOOF_ENABLED" = "1" ]; then
        sh "$MODDIR/scripts/driver_spoof.sh" apply
        mlog "Driver spoofs applied"
    fi

    # Set non-stock props that we held back during PI window
    # These are the props that root detectors look for
    resetprop input.gamepad.enabled true
    resetprop persist.sys.usb.otg 1

    mlog "All modifications applied"
}

# =================================================================
# Phase 3: PI Watcher — monitor for re-attestation and hide
# =================================================================
# Play Integrity can re-check periodically (every few hours) or
# when an app requests it (banking app launch, Google Pay, etc.)
# We watch for DroidGuard activity and temporarily go clean.

PI_WATCHER_PID=""

start_pi_watcher() {
    mlog "Starting PI watcher daemon..."

    (
        while true; do
            sleep 10

            # Check if DroidGuard is doing a new attestation
            GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null)
            if [ -z "$GMS_PID" ]; then
                continue
            fi

            # Check if it's actively using CPU (attestation in progress)
            CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}')
            PREV_CPU="$CPU"
            sleep 2
            CPU2=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}')

            if [ -n "$CPU" ] && [ -n "$CPU2" ]; then
                DELTA=$((CPU2 - CPU))
                if [ "$DELTA" -gt 10 ]; then
                    # DroidGuard is active — temporarily hide
                    mlog "PI re-check detected! Temporarily restoring stock..."
                    hide_for_pi
                fi
            fi
        done
    ) &
    PI_WATCHER_PID=$!
    echo "$PI_WATCHER_PID" > "$MODDIR/run/pi_watcher.pid"
    mlog "PI watcher started (PID $PI_WATCHER_PID)"
}

hide_for_pi() {
    # Temporarily unmount all driver spoofs
    sh "$MODDIR/scripts/driver_spoof.sh" restore 2>/dev/null

    # Remove non-stock props
    resetprop --delete input.gamepad.enabled 2>/dev/null
    resetprop --delete persist.sys.usb.otg 2>/dev/null
    resetprop --delete vendor.powervr.opencl.allowfp16 2>/dev/null
    resetprop --delete vendor.powervr.opencl.profiling 2>/dev/null
    resetprop --delete bluetooth.le.no_location_permission_scan 2>/dev/null

    # Ensure boot state looks clean
    resetprop ro.boot.verifiedbootstate green 2>/dev/null
    resetprop ro.boot.flash.locked 1 2>/dev/null
    resetprop ro.boot.vbmeta.device_state locked 2>/dev/null

    mlog "Hiding for PI check..."

    # Wait for DroidGuard to finish
    WAIT=0
    while [ $WAIT -lt 60 ]; do
        sleep 2
        WAIT=$((WAIT + 2))

        GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null)
        if [ -z "$GMS_PID" ]; then
            break
        fi

        CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}')
        sleep 1
        CPU2=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}')
        DELTA=$((CPU2 - CPU))

        if [ "$DELTA" -lt 5 ]; then
            break
        fi
    done

    # Extra safety margin
    sleep 5

    mlog "PI check finished, re-applying modifications..."
    apply_after_pi
}

# =================================================================
# Boot prop spoofing — make boot state look locked/verified
# =================================================================
spoof_boot_props() {
    # These props are checked by Play Integrity and root detectors
    # They should reflect a locked, verified device

    # Verified boot state
    resetprop ro.boot.verifiedbootstate green
    resetprop ro.boot.flash.locked 1
    resetprop ro.boot.vbmeta.device_state locked
    resetprop ro.boot.veritymode enforcing

    # Remove KernelSU traces from props
    resetprop --delete ro.kernelsu.version 2>/dev/null
    resetprop --delete ro.kernelsu.versionCode 2>/dev/null

    # Stock build fingerprint (don't modify — just ensure it's not overridden)
    # resetprop ro.build.fingerprint should already be stock

    # Ensure SELinux appears enforcing
    resetprop ro.build.selinux 1

    mlog "Boot props spoofed (green/locked/enforcing)"
}

# =================================================================
# Main entry point — called from service.sh
# =================================================================
case "$1" in
    run)
        # Full boot timing sequence
        spoof_boot_props
        wait_for_pi_pass
        apply_after_pi
        start_pi_watcher
        mlog "Boot timing sequence complete"
        ;;
    hide)
        # Manual hide trigger
        hide_for_pi
        ;;
    unhide)
        # Manual re-apply
        apply_after_pi
        ;;
    stop)
        # Stop PI watcher
        PID=$(cat "$MODDIR/run/pi_watcher.pid" 2>/dev/null)
        if [ -n "$PID" ]; then
            kill "$PID" 2>/dev/null
            rm -f "$MODDIR/run/pi_watcher.pid"
            mlog "PI watcher stopped"
        fi
        ;;
    status)
        PID=$(cat "$MODDIR/run/pi_watcher.pid" 2>/dev/null)
        if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
            echo "PI watcher: running (PID $PID)"
        else
            echo "PI watcher: not running"
        fi

        # Check boot props
        echo "Boot state: $(getprop ro.boot.verifiedbootstate)"
        echo "Flash lock: $(getprop ro.boot.flash.locked)"
        echo "VBMeta: $(getprop ro.boot.vbmeta.device_state)"
        echo "SELinux: $(getenforce 2>/dev/null || echo unknown)"

        # Check for KSU prop leaks
        KSU_VER=$(getprop ro.kernelsu.version 2>/dev/null)
        if [ -n "$KSU_VER" ]; then
            echo "WARNING: KernelSU version visible: $KSU_VER"
        else
            echo "KernelSU props: hidden"
        fi
        ;;
    *)
        echo "Usage: boot_timing.sh {run|hide|unhide|stop|status}"
        echo ""
        echo "  run     Full boot sequence (wait for PI, apply mods, start watcher)"
        echo "  hide    Temporarily hide all mods (for manual PI trigger)"
        echo "  unhide  Re-apply all mods"
        echo "  stop    Stop PI watcher daemon"
        echo "  status  Show current state"
        ;;
esac
