package adbembed import ( "embed" "fmt" "os" "path/filepath" "runtime" ) //go:embed all:bin var binFS embed.FS // Resolve returns the path to a usable adb binary. // Order: embedded binary → ANDROID_HOME → PATH. func Resolve() (string, error) { if path, err := extractEmbedded(); err == nil { return path, nil } return findOnSystem() } func extractEmbedded() (string, error) { name := embeddedName() data, err := binFS.ReadFile("bin/" + name) if err != nil || len(data) < 1024 { return "", fmt.Errorf("no embedded binary") } cacheDir, err := os.UserCacheDir() if err != nil { return "", err } dir := filepath.Join(cacheDir, "droidscope", "adb") if err := os.MkdirAll(dir, 0755); err != nil { return "", err } dest := filepath.Join(dir, adbExeName()) if err := os.WriteFile(dest, data, 0755); err != nil { return "", err } return dest, nil } func findOnSystem() (string, error) { if home := os.Getenv("ANDROID_HOME"); home != "" { candidate := filepath.Join(home, "platform-tools", adbExeName()) if _, err := os.Stat(candidate); err == nil { return candidate, nil } } // Fall back to PATH return findInPath() } func embeddedName() string { os_ := runtime.GOOS arch := runtime.GOARCH if os_ == "windows" { return fmt.Sprintf("adb-%s-%s.exe", os_, arch) } return fmt.Sprintf("adb-%s-%s", os_, arch) } func adbExeName() string { if runtime.GOOS == "windows" { return "adb.exe" } return "adb" }