#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Auto Config Script for Zygisk-MyInjector
通过交互式命令行快速配置应用注入

完整工作流程:
  1. 运行自动配置脚本（会自动检查并设置 SELinux）
     cd scripts
     ./auto_config.py
  
  2. 按提示选择设备、应用和配置（全部使用默认值即可）
     脚本会自动完成：
     - 生成配置文件
     - 推送到设备
     - 应用配置
     - 重启应用
     - 端口转发
     - 快速测试
  
  3. 如果测试成功，直接使用 Frida 连接
     frida -H 127.0.0.1:27042 Gadget -l your_script.js

快速测试:
  # 测试连接
  frida -H 127.0.0.1:27042 Gadget -e "console.log('Connected! PID:', Process.id)"
  
  # 列举模块
  frida -H 127.0.0.1:27042 Gadget -e "Process.enumerateModules().forEach(m => console.log(m.name))"
"""

import subprocess
import json
import sys
import os
import tempfile
import shutil
from typing import List, Dict, Optional
from pathlib import Path

try:
    from prompt_toolkit import prompt
    from prompt_toolkit.completion import FuzzyWordCompleter
    from prompt_toolkit.styles import Style
except ImportError:
    print("Error: prompt_toolkit is required. Install it with:")
    print("  pip install prompt_toolkit")
    sys.exit(1)


# Define style for prompts
style = Style.from_dict({
    'prompt': 'ansicyan bold',
})

# Frida Gadget version
FRIDA_VERSION = "16.0.7"
MODULE_PATH = "/data/adb/modules/zygisk-myinjector"
SO_STORAGE_DIR = f"{MODULE_PATH}/so_files"

# Local cache directory for downloaded gadgets
SCRIPT_DIR = Path(__file__).parent
CACHE_DIR = SCRIPT_DIR / '.cache' / 'frida-gadgets'


class ADBHelper:
    """ADB helper class for device and package operations"""
    
    def __init__(self, device_id: Optional[str] = None):
        self.device_id = device_id
        self.base_cmd = ['adb']
        if device_id:
            self.base_cmd.extend(['-s', device_id])
    
    def run(self, args: List[str], check=True) -> subprocess.CompletedProcess:
        """Run adb command"""
        cmd = self.base_cmd + args
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=check)
            return result
        except subprocess.CalledProcessError as e:
            print(f"Error running command: {' '.join(cmd)}")
            print(f"Error: {e.stderr}")
            if check:
                raise
            return e
    
    @staticmethod
    def get_devices() -> List[Dict[str, str]]:
        """Get list of connected devices"""
        result = subprocess.run(['adb', 'devices', '-l'], capture_output=True, text=True)
        lines = result.stdout.strip().split('\n')[1:]  # Skip header
        devices = []
        
        for line in lines:
            if not line.strip():
                continue
            parts = line.split()
            if len(parts) >= 2:
                device_id = parts[0]
                status = parts[1]
                
                # Parse device info
                model = ''
                product = ''
                for part in parts[2:]:
                    if part.startswith('model:'):
                        model = part.split(':', 1)[1]
                    elif part.startswith('product:'):
                        product = part.split(':', 1)[1]
                
                devices.append({
                    'id': device_id,
                    'status': status,
                    'model': model,
                    'product': product
                })
        
        return devices
    
    def get_packages(self, include_system: bool = False) -> List[str]:
        """Get list of installed packages"""
        args = ['shell', 'pm', 'list', 'packages']
        if not include_system:
            args.append('-3')  # Third-party apps only
        
        result = self.run(args)
        packages = []
        for line in result.stdout.strip().split('\n'):
            if line.startswith('package:'):
                pkg = line.split(':', 1)[1].strip()
                packages.append(pkg)
        
        return sorted(packages)
    
    def push_file(self, local_path: str, remote_path: str) -> bool:
        """Push file to device"""
        result = self.run(['push', local_path, remote_path], check=False)
        return result.returncode == 0
    
    def send_broadcast(self, action: str, component: str, extras: Dict[str, str]) -> bool:
        """Send broadcast with extras"""
        args = ['shell', 'am', 'broadcast', '-n', component, '-a', action]
        
        for key, value in extras.items():
            args.extend(['--es', key, value])
        
        result = self.run(args, check=False)
        if result.returncode == 0:
            print(f"Broadcast sent successfully")
            print(result.stdout)
            return True
        else:
            print(f"Failed to send broadcast")
            print(result.stderr)
            return False
    
    def get_arch(self) -> str:
        """Get device CPU architecture"""
        result = self.run(['shell', 'getprop', 'ro.product.cpu.abi'])
        arch = result.stdout.strip()
        return arch
    
    def file_exists(self, path: str) -> bool:
        """Check if file exists on device"""
        result = self.run(['shell', f'su -c "test -f {path} && echo exists"'], check=False)
        return 'exists' in result.stdout


def select_device() -> Optional[str]:
    """Interactive device selection"""
    devices = ADBHelper.get_devices()
    
    if not devices:
        print("Error: No devices found. Please connect a device and enable USB debugging.")
        return None
    
    if len(devices) == 1:
        device = devices[0]
        print(f"Using device: {device['id']} ({device['model'] or device['product']})")
        return device['id']
    
    print("\n=== Connected Devices ===")
    for idx, device in enumerate(devices, 1):
        model_info = device['model'] or device['product'] or 'Unknown'
        print(f"{idx}. {device['id']} - {model_info} [{device['status']}]")
    
    while True:
        try:
            choice = input(f"\nSelect device (1-{len(devices)}): ").strip()
            idx = int(choice) - 1
            if 0 <= idx < len(devices):
                selected = devices[idx]
                print(f"Selected: {selected['id']} ({selected['model'] or selected['product']})")
                return selected['id']
            else:
                print(f"Invalid choice. Please enter 1-{len(devices)}")
        except (ValueError, KeyboardInterrupt):
            print("\nDevice selection cancelled")
            return None


def select_package(adb: ADBHelper) -> Optional[str]:
    """Interactive package selection with fuzzy completion"""
    print("\n=== Loading app packages ===")
    
    # Ask if include system apps
    while True:
        choice = input("Include system apps? (y/N): ").strip().lower()
        if choice in ['', 'n', 'no']:
            include_system = False
            break
        elif choice in ['y', 'yes']:
            include_system = True
            break
        else:
            print("Please enter 'y' or 'n'")
    
    packages = adb.get_packages(include_system=include_system)
    
    if not packages:
        print("Error: No packages found")
        return None
    
    print(f"Found {len(packages)} packages")
    
    # Create fuzzy completer
    completer = FuzzyWordCompleter(packages)
    
    print("\n=== Select Target App ===")
    print("Tip: Use Tab for auto-completion, type to filter")
    
    try:
        package = prompt(
            'Package name: ',
            completer=completer,
            style=style
        ).strip()
        
        if package and package in packages:
            print(f"Selected: {package}")
            return package
        elif package:
            print(f"Warning: '{package}' not found in package list, using anyway")
            return package
        else:
            print("No package selected")
            return None
    except KeyboardInterrupt:
        print("\nSelection cancelled")
        return None


def configure_gadget() -> Optional[Dict]:
    """Interactive gadget configuration"""
    print("\n=== Gadget Configuration ===")
    
    gadget_config = {}
    
    # Gadget name
    gadget_name = input("Gadget SO name (default: libgadget.so): ").strip()
    gadget_config['gadgetName'] = gadget_name or 'libgadget.so'
    
    # Mode selection
    print("\nSelect mode:")
    print("1. Server mode (listen for connections)")
    print("2. Script mode (execute script)")
    
    while True:
        choice = input("Mode (1/2, default: 1): ").strip()
        if choice in ['', '1']:
            gadget_config['mode'] = 'server'
            break
        elif choice == '2':
            gadget_config['mode'] = 'script'
            break
        else:
            print("Invalid choice. Please enter 1 or 2")
    
    if gadget_config['mode'] == 'server':
        # Server mode configuration
        address = input("Listen address (default: 0.0.0.0): ").strip()
        gadget_config['address'] = address or '0.0.0.0'
        
        port = input("Listen port (default: 27042): ").strip()
        try:
            gadget_config['port'] = int(port) if port else 27042
        except ValueError:
            print("Invalid port, using default 27042")
            gadget_config['port'] = 27042
        
        print("\nPort conflict behavior:")
        print("1. fail - Exit if port is in use")
        print("2. ignore - Continue anyway")
        print("3. close - Close existing connection")
        conflict = input("On port conflict (1/2/3, default: 1): ").strip()
        conflict_map = {'1': 'fail', '2': 'ignore', '3': 'close', '': 'fail'}
        gadget_config['onPortConflict'] = conflict_map.get(conflict, 'fail')
        
        print("\nOn load behavior:")
        print("1. resume - Continue immediately (recommended)")
        print("2. wait - Wait for connection (for debugging)")
        load = input("On load (1/2, default: 1): ").strip()
        load_map = {'1': 'resume', '2': 'wait', '': 'resume'}
        gadget_config['onLoad'] = load_map.get(load, 'resume')
        
    else:
        # Script mode configuration
        script_path = input("Script path (default: /data/local/tmp/script.js): ").strip()
        gadget_config['scriptPath'] = script_path or '/data/local/tmp/script.js'
    
    return gadget_config


def download_frida_gadget(arch: str) -> Optional[str]:
    """Download Frida Gadget for specified architecture (with local caching)"""
    # Map Android arch to Frida naming
    arch_map = {
        'arm64-v8a': 'arm64',
        'armeabi-v7a': 'arm',
        'x86': 'x86',
        'x86_64': 'x86_64'
    }
    
    frida_arch = arch_map.get(arch)
    if not frida_arch:
        print(f"Unsupported architecture: {arch}")
        return None
    
    # Check local cache first
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    cached_file = CACHE_DIR / f"frida-gadget-{FRIDA_VERSION}-android-{frida_arch}.so"
    
    if cached_file.exists():
        print(f"\n✓ Using cached Frida Gadget {FRIDA_VERSION} for {arch}")
        print(f"  Cache: {cached_file}")
        # Copy to temp location for consistent behavior
        temp_dir = tempfile.mkdtemp(prefix='frida_gadget_')
        temp_file = os.path.join(temp_dir, 'libgadget.so')
        shutil.copy2(str(cached_file), temp_file)
        return temp_file
    
    # Download if not in cache
    url = f"https://github.com/frida/frida/releases/download/{FRIDA_VERSION}/frida-gadget-{FRIDA_VERSION}-android-{frida_arch}.so.xz"
    
    print(f"\nDownloading Frida Gadget {FRIDA_VERSION} for {arch}...")
    print(f"URL: {url}")
    
    # Create temp directory
    temp_dir = tempfile.mkdtemp(prefix='frida_gadget_')
    xz_file = os.path.join(temp_dir, f'frida-gadget.so.xz')
    so_file = os.path.join(temp_dir, 'libgadget.so')
    
    try:
        # Download
        result = subprocess.run(['curl', '-L', '-o', xz_file, url], 
                              capture_output=True, text=True, check=False)
        if result.returncode != 0:
            print(f"Failed to download: {result.stderr}")
            shutil.rmtree(temp_dir)
            return None
        
        print("✓ Downloaded")
        
        # Decompress
        print("Decompressing...")
        result = subprocess.run(['xz', '-d', xz_file], 
                              capture_output=True, text=True, check=False)
        if result.returncode != 0:
            print(f"Failed to decompress: {result.stderr}")
            shutil.rmtree(temp_dir)
            return None
        
        # Rename
        decompressed = xz_file.replace('.xz', '')
        os.rename(decompressed, so_file)
        
        print("✓ Decompressed")
        
        # Save to cache for future use
        try:
            shutil.copy2(so_file, str(cached_file))
            print(f"✓ Cached for future use: {cached_file}")
        except Exception as e:
            print(f"Warning: Failed to cache gadget: {e}")
        
        return so_file
        
    except Exception as e:
        print(f"Error downloading Frida Gadget: {e}")
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)
        return None


def ensure_gadget_installed(adb: ADBHelper, gadget_name: str = 'libgadget.so') -> bool:
    """Ensure Frida Gadget is installed on device"""
    gadget_path = f"{SO_STORAGE_DIR}/{gadget_name}"
    
    print(f"\n=== Checking Frida Gadget ===")
    
    # Check if gadget already exists
    if adb.file_exists(gadget_path):
        print(f"✓ Gadget found: {gadget_path}")
        return True
    
    print(f"Gadget not found in: {gadget_path}")
    print("Need to download and install Frida Gadget")
    
    # Get device architecture
    arch = adb.get_arch()
    print(f"Device architecture: {arch}")
    
    # Download gadget
    local_gadget = download_frida_gadget(arch)
    if not local_gadget:
        print("Failed to download Frida Gadget")
        return False
    
    try:
        # Push to device temp location
        print("\nPushing to device...")
        if not adb.push_file(local_gadget, '/data/local/tmp/libgadget.so'):
            print("Failed to push gadget to device")
            return False
        
        print("✓ Pushed to device")
        
        # Copy to SO storage with root
        print(f"Installing to {gadget_path}...")
        result = adb.run(['shell', f'su -c "mkdir -p {SO_STORAGE_DIR}"'], check=False)
        result = adb.run(['shell', 
                         f'su -c "cp /data/local/tmp/libgadget.so {gadget_path}"'], 
                        check=False)
        
        if result.returncode != 0:
            print(f"Failed to install gadget: {result.stderr}")
            return False
        
        # Set permissions
        adb.run(['shell', f'su -c "chmod 755 {gadget_path}"'], check=False)
        
        # Verify
        if adb.file_exists(gadget_path):
            print(f"✓ Gadget installed successfully: {gadget_path}")
            # Clean up temp file on device
            adb.run(['shell', 'rm -f /data/local/tmp/libgadget.so'], check=False)
            return True
        else:
            print("Failed to verify gadget installation")
            return False
            
    finally:
        # Clean up local temp file
        temp_dir = os.path.dirname(local_gadget)
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)


def generate_config_files(package_name: str, gadget_config: Dict) -> tuple:
    """Generate config.json and gadget config content"""
    
    # Prepare SO file reference for gadget
    gadget_name = gadget_config['gadgetName']
    gadget_so_ref = {
        "name": gadget_name,
        "storedPath": f"{SO_STORAGE_DIR}/{gadget_name}",
        "originalPath": f"{SO_STORAGE_DIR}/{gadget_name}"
    }
    
    # Generate main module config.json
    module_config = {
        "enabled": True,
        "hideInjection": False,
        "injectionDelay": 2,
        "globalSoFiles": [],
        "perAppConfig": {
            package_name: {
                "enabled": True,
                "soFiles": [gadget_so_ref],  # Include gadget SO file
                "injectionMethod": "standard",
                "gadgetConfig": gadget_config,
                "useGlobalGadget": False
            }
        },
        "globalGadgetConfig": None
    }
    
    # Generate gadget config content based on mode
    if gadget_config['mode'] == 'server':
        gadget_config_content = {
            "interaction": {
                "type": "listen",
                "address": gadget_config['address'],
                "port": gadget_config['port'],
                "on_port_conflict": gadget_config['onPortConflict'],
                "on_load": gadget_config['onLoad']
            }
        }
    else:  # script mode
        gadget_config_content = {
            "interaction": {
                "type": "script",
                "path": gadget_config['scriptPath']
            }
        }
    
    return (
        json.dumps(module_config, indent=2, ensure_ascii=False),
        json.dumps(gadget_config_content, indent=2, ensure_ascii=False)
    )


def quick_test(adb: ADBHelper, port: int = 27042):
    """Quick test connectivity after configuration"""
    print("\n=== Quick Test ===")
    print("Testing Frida connectivity...\n")
    
    # Check if frida is installed
    result = subprocess.run(['which', 'frida'], capture_output=True, text=True)
    if result.returncode != 0:
        print("⚠️  Frida CLI not found. Please install it with:")
        print("    pip install frida-tools")
        return False
    
    # Test 1: Basic connection test
    print("Test 1: Basic connection...")
    # 使用 stdin 输入命令和 exit，避免交互式 REPL 导致超时
    test_input = "console.log('Connected! PID:', Process.id)\nexit\n"
    test_cmd = ['frida', '-H', f'127.0.0.1:{port}', 'Gadget']
    
    result = subprocess.run(test_cmd, input=test_input, capture_output=True, text=True, timeout=10)
    if result.returncode == 0 and 'Connected!' in result.stdout:
        print("✓ Connection successful!")
        # 提取并显示 PID
        for line in result.stdout.split('\n'):
            if 'Connected! PID:' in line:
                print(f"  {line.strip()}")
                break
    else:
        print("✗ Connection failed")
        print(f"  Error: {result.stderr.strip() if result.stderr else 'Unknown error'}")
        print("\nTroubleshooting:")
        print("  1. Check if the target app is running")
        print("  2. Verify port forwarding: adb forward tcp:{} tcp:{}".format(port, port))
        print("  3. Check logcat for errors: adb logcat -s Gadget:*")
        return False
    
    # Test 2: Enumerate modules
    print("\nTest 2: Enumerate loaded modules...")
    test_input = "Process.enumerateModules().slice(0, 5).forEach(m => console.log('  -', m.name))\nexit\n"
    test_cmd = ['frida', '-H', f'127.0.0.1:{port}', 'Gadget']
    
    result = subprocess.run(test_cmd, input=test_input, capture_output=True, text=True, timeout=10)
    if result.returncode == 0:
        print("✓ Modules enumerated:")
        # 提取并显示模块列表
        in_output = False
        for line in result.stdout.split('\n'):
            if '  -' in line:
                in_output = True
                print(line)
            elif in_output and line.strip() == '':
                break
    else:
        print("✗ Failed to enumerate modules")
        print(f"  Error: {result.stderr.strip()}")
    
    return True


def setup_port_forward(adb: ADBHelper, port: int) -> bool:
    """Setup ADB port forwarding"""
    print(f"\n=== Setting up port forwarding ===")
    print(f"Forwarding local port {port} to device port {port}...")
    
    result = adb.run(['forward', f'tcp:{port}', f'tcp:{port}'], check=False)
    if result.returncode == 0:
        print(f"✓ Port forwarding established: tcp:{port} -> tcp:{port}")
        return True
    else:
        print(f"✗ Failed to setup port forwarding")
        print(f"  Error: {result.stderr}")
        return False


def check_and_set_selinux(adb: ADBHelper) -> bool:
    """Check SELinux status and set to Permissive if needed"""
    print("\n=== Checking SELinux Status ===")
    
    # Check current SELinux status
    result = adb.run(['shell', 'getenforce'], check=False)
    if result.returncode != 0:
        print("⚠️  Failed to check SELinux status")
        return True  # Continue anyway
    
    status = result.stdout.strip()
    print(f"Current SELinux mode: {status}")
    
    if status == 'Permissive':
        print("✓ SELinux is already in Permissive mode")
        return True
    elif status == 'Enforcing':
        print("\n⚠️  SELinux is in Enforcing mode")
        print("   Zygisk modules cannot read config files when SELinux is Enforcing.")
        print("   We need to set it to Permissive mode.")
        
        # Ask user for confirmation
        while True:
            choice = input("\nSet SELinux to Permissive? (Y/n): ").strip().lower()
            if choice in ['', 'y', 'yes']:
                break
            elif choice in ['n', 'no']:
                print("\nWarning: Continuing with SELinux Enforcing may cause injection to fail.")
                print("You can manually set it later with: adb shell \"su -c 'setenforce 0'\"")
                return True
            else:
                print("Please enter 'y' or 'n'")
        
        # Set SELinux to Permissive
        print("\nSetting SELinux to Permissive...")
        result = adb.run(['shell', 'su', '-c', 'setenforce 0'], check=False)
        
        if result.returncode == 0:
            print("✓ SELinux set to Permissive mode")
            print("  Note: This setting will reset after reboot")
            return True
        else:
            print("✗ Failed to set SELinux to Permissive")
            print(f"  Error: {result.stderr.strip()}")
            print("\nPlease manually run: adb shell \"su -c 'setenforce 0'\"")
            return False
    else:
        print(f"Unknown SELinux status: {status}")
        return True


def restart_app(adb: ADBHelper, package_name: str):
    """Restart the target application"""
    print(f"\n=== Restarting Application ===")
    
    # Force stop
    print(f"Stopping {package_name}...")
    result = adb.run(['shell', 'am', 'force-stop', package_name], check=False)
    if result.returncode == 0:
        print("✓ App stopped")
    else:
        print("⚠️  Failed to stop app")
    
    # Get main activity
    print(f"\nGetting launch activity...")
    result = adb.run(['shell', 'pm', 'dump', package_name, '|', 'grep', '-A', '1', 'android.intent.action.MAIN'], check=False)
    
    # Try to start the app
    print(f"Starting {package_name}...")
    result = adb.run(['shell', 'monkey', '-p', package_name, '-c', 'android.intent.category.LAUNCHER', '1'], check=False)
    
    if result.returncode == 0:
        print("✓ App started")
        print("  Note: The app should now load with Frida Gadget injected")
        return True
    else:
        print("⚠️  Failed to start app automatically")
        print("  Please start the app manually from the device")
        return False


def main():
    """Main entry point"""
    print("=" * 60)
    print("Zygisk-MyInjector Auto Config Tool")
    print("=" * 60)
    
    # Step 1: Select device
    device_id = select_device()
    if not device_id:
        sys.exit(1)
    
    adb = ADBHelper(device_id)
    
    # Step 2: Check and set SELinux
    if not check_and_set_selinux(adb):
        print("\nError: Failed to configure SELinux")
        print("The injection may fail. Please fix SELinux manually and try again.")
        sys.exit(1)
    
    # Step 3: Select package
    package_name = select_package(adb)
    if not package_name:
        sys.exit(1)
    
    # Step 4: Configure gadget
    gadget_config = configure_gadget()
    if not gadget_config:
        sys.exit(1)
    
    # Step 5: Ensure Frida Gadget is installed
    if not ensure_gadget_installed(adb, gadget_config['gadgetName']):
        print("\nError: Failed to install Frida Gadget")
        print("Please manually download and install libgadget.so")
        sys.exit(1)
    
    # Step 6: Generate config files
    print("\n=== Generating Configuration Files ===")
    config_json, gadget_config_json = generate_config_files(package_name, gadget_config)
    
    print("\nGenerated config.json:")
    print(config_json)
    print("\nGenerated gadget config:")
    print(gadget_config_json)
    
    # Step 7: Save to temp files
    temp_dir = os.path.join(os.path.dirname(__file__), '.tmp')
    os.makedirs(temp_dir, exist_ok=True)
    
    config_file = os.path.join(temp_dir, 'config.json')
    gadget_name = gadget_config['gadgetName'].replace('.so', '.config.so')
    gadget_config_file = os.path.join(temp_dir, gadget_name)
    
    with open(config_file, 'w', encoding='utf-8') as f:
        f.write(config_json)
    
    with open(gadget_config_file, 'w', encoding='utf-8') as f:
        f.write(gadget_config_json)
    
    print(f"\nConfig files saved to: {temp_dir}")
    
    # Step 8: Push to device
    print("\n=== Pushing Files to Device ===")
    
    remote_config = '/data/local/tmp/zygisk_config.json'
    remote_gadget_config = f'/data/local/tmp/{gadget_name}'
    
    if not adb.push_file(config_file, remote_config):
        print("Error: Failed to push config.json")
        sys.exit(1)
    print(f"✓ Pushed config.json -> {remote_config}")
    
    if not adb.push_file(gadget_config_file, remote_gadget_config):
        print("Error: Failed to push gadget config")
        sys.exit(1)
    print(f"✓ Pushed gadget config -> {remote_gadget_config}")
    
    # Step 9: Send broadcast
    print("\n=== Sending Broadcast to Apply Config ===")
    
    success = adb.send_broadcast(
        action='com.jiqiu.configapp.APPLY_CONFIG',
        component='com.jiqiu.configapp/.ConfigApplyReceiver',
        extras={
            'package_name': package_name,
            'tmp_config_path': remote_config,
            'tmp_gadget_config_path': remote_gadget_config
        }
    )
    
    if success:
        print("\n✓ Configuration applied successfully!")
        print(f"\nThe app '{package_name}' has been configured.")
        
        # 自动完成工作流程
        print("\n=== Completing Workflow ===")
        
        # Step 1: Restart app
        restart_app(adb, package_name)
        
        # Step 2: Setup port forwarding
        port = gadget_config.get('port', 27042)
        if setup_port_forward(adb, port):
            # Step 3: Quick test
            import time
            print("\nWaiting 3 seconds for app to initialize...")
            time.sleep(3)
            
            try:
                test_success = quick_test(adb, port)
                if test_success:
                    # 彩色打印 frida 命令
                    print("\n" + "="*60)
                    print("\033[1;32m✓ All tests passed!\033[0m\n")
                    print("\033[1;36mYou can now use Frida with the following commands:\033[0m\n")
                    
                    # Interactive mode
                    print("\033[1;33m# Interactive REPL:\033[0m")
                    print(f"\033[1;32mfrida -H 127.0.0.1:{port} Gadget\033[0m\n")
                    
                    # Execute script
                    print("\033[1;33m# Execute JavaScript code:\033[0m")
                    print(f"\033[1;32mfrida -H 127.0.0.1:{port} Gadget -e 'YOUR_CODE_HERE'\033[0m\n")
                    
                    # Load script file
                    print("\033[1;33m# Load script file:\033[0m")
                    print(f"\033[1;32mfrida -H 127.0.0.1:{port} Gadget -l your_script.js\033[0m\n")
                    
                    print("="*60)
            except Exception as e:
                print(f"\n⚠️  Test failed: {e}")
                print("\nYou can manually test with:")
                if gadget_config['mode'] == 'server':
                    print(f"\033[1;32mfrida -H 127.0.0.1:{port} Gadget -l your_script.js\033[0m")
    else:
        print("\n✗ Failed to apply configuration")
        print("Please check logcat for details:")
        print(f"  adb -s {device_id} logcat -s ConfigApplyReceiver:* ConfigManager:*")
        sys.exit(1)


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print("\n\nOperation cancelled by user")
        sys.exit(130)
