148 lines
4 KiB
Go
148 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type PropEntry struct {
|
|
Key string `json:"key"`
|
|
Value string `json:"value"`
|
|
Category string `json:"category"`
|
|
}
|
|
|
|
// GetAllProps returns all system properties as structured entries.
|
|
func (a *App) GetAllProps() ([]PropEntry, error) {
|
|
out, err := a.runAdbShell("getprop")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get props: %w", err)
|
|
}
|
|
|
|
var props []PropEntry
|
|
for _, line := range strings.Split(out, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if !strings.HasPrefix(line, "[") {
|
|
continue
|
|
}
|
|
|
|
// Format: [key]: [value]
|
|
line = strings.TrimPrefix(line, "[")
|
|
parts := strings.SplitN(line, "]: [", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
|
|
key := parts[0]
|
|
value := strings.TrimSuffix(parts[1], "]")
|
|
|
|
props = append(props, PropEntry{
|
|
Key: key,
|
|
Value: value,
|
|
Category: categorizeProp(key),
|
|
})
|
|
}
|
|
|
|
sort.Slice(props, func(i, j int) bool {
|
|
return props[i].Key < props[j].Key
|
|
})
|
|
|
|
return props, nil
|
|
}
|
|
|
|
// SetProp sets a system property value.
|
|
// Note: many system props are read-only and require root to change.
|
|
func (a *App) SetProp(key, value string) (string, error) {
|
|
if key == "" {
|
|
return "", fmt.Errorf("key cannot be empty")
|
|
}
|
|
// Validate key characters
|
|
for _, ch := range key {
|
|
if ch == ';' || ch == '&' || ch == '|' || ch == '`' || ch == '$' || ch == '\n' {
|
|
return "", fmt.Errorf("invalid character in property key")
|
|
}
|
|
}
|
|
|
|
// Try setprop first (works for some props without root)
|
|
_, err := a.runAdbShell("setprop", key, value)
|
|
if err != nil {
|
|
// Try with root
|
|
_, err2 := a.runAdbShell("su", "-c", fmt.Sprintf("setprop %s %s", key, value))
|
|
if err2 != nil {
|
|
return "", fmt.Errorf("failed to set prop (may be read-only or need root): %w", err)
|
|
}
|
|
}
|
|
|
|
// Verify
|
|
newVal, _ := a.runAdbShell("getprop", key)
|
|
return fmt.Sprintf("Set %s = %s", key, strings.TrimSpace(newVal)), nil
|
|
}
|
|
|
|
// GetProp gets a single property value.
|
|
func (a *App) GetProp(key string) (string, error) {
|
|
if key == "" {
|
|
return "", fmt.Errorf("key cannot be empty")
|
|
}
|
|
out, err := a.runAdbShell("getprop", key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(out), nil
|
|
}
|
|
|
|
// categorizeProp assigns a category to a property based on its key prefix.
|
|
func categorizeProp(key string) string {
|
|
switch {
|
|
// Match by substring first so these group regardless of prefix.
|
|
case strings.Contains(key, "uwb"):
|
|
return "UWB"
|
|
case strings.Contains(key, "satellite"):
|
|
return "Satellite"
|
|
// Verified Boot / AVB + post-quantum signature schemes (Android 17 PQC).
|
|
case strings.Contains(key, "vbmeta") || strings.Contains(key, "avb") ||
|
|
strings.Contains(key, "pqc") || strings.Contains(key, "dilithium") ||
|
|
strings.Contains(key, "ml_dsa") || strings.Contains(key, "ml-dsa") ||
|
|
strings.Contains(key, "sphincs") || strings.Contains(key, "falcon"):
|
|
return "Verified Boot / PQC"
|
|
case strings.HasPrefix(key, "ro.build"):
|
|
return "Build"
|
|
case strings.HasPrefix(key, "ro.product"):
|
|
return "Product"
|
|
case strings.HasPrefix(key, "ro.boot"):
|
|
return "Boot"
|
|
case strings.HasPrefix(key, "ro.hardware"):
|
|
return "Hardware"
|
|
case strings.HasPrefix(key, "ro.crypto"):
|
|
return "Security"
|
|
case strings.HasPrefix(key, "ro.debuggable") ||
|
|
strings.HasPrefix(key, "service.adb"):
|
|
return "Debug"
|
|
case strings.HasPrefix(key, "persist."):
|
|
return "Persist"
|
|
case strings.HasPrefix(key, "sys."):
|
|
return "System"
|
|
case strings.HasPrefix(key, "net.") ||
|
|
strings.HasPrefix(key, "dhcp.") ||
|
|
strings.HasPrefix(key, "wifi."):
|
|
return "Network"
|
|
case strings.HasPrefix(key, "gsm.") ||
|
|
strings.HasPrefix(key, "ril.") ||
|
|
strings.HasPrefix(key, "telephony."):
|
|
return "Telephony"
|
|
case strings.HasPrefix(key, "dalvik."):
|
|
return "Dalvik/ART"
|
|
case strings.HasPrefix(key, "init."):
|
|
return "Init"
|
|
case strings.HasPrefix(key, "dev."):
|
|
return "Device"
|
|
case strings.HasPrefix(key, "audio.") ||
|
|
strings.HasPrefix(key, "media."):
|
|
return "Media"
|
|
case strings.HasPrefix(key, "camera."):
|
|
return "Camera"
|
|
case strings.HasPrefix(key, "bluetooth."):
|
|
return "Bluetooth"
|
|
default:
|
|
return "Other"
|
|
}
|
|
}
|