Complete rewrite with: - Per-app driver scoping via mount namespace isolation (LSPosed-style) - System-wide driver mode selection - .ko kernel module manager with autoload and dependency tracking - Driver integrity protection (monitor/enforce modes) - Driver registry with auto-discovery and SHA256 hashing - LSPosed-style dark WebUI with 6 tabs: Dashboard, Drivers, Apps, Modules, Protection, Logs - RESTful API handler for WebUI communication - Zygote process monitor for auto-applying scopes to new app launches Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
310 lines
8.8 KiB
Bash
310 lines
8.8 KiB
Bash
#!/system/bin/sh
|
|
# Driver Manager v2 - Driver Registry
|
|
# Discovers, catalogs, and manages system drivers
|
|
|
|
MODDIR=${MODDIR:-/data/adb/modules/driver-manager}
|
|
. "$MODDIR/scripts/core.sh"
|
|
log_init "$LOGDIR/registry.log"
|
|
|
|
REGISTRY="$CONFIGDIR/drivers.json"
|
|
|
|
# --- Driver discovery paths by category ---
|
|
scan_drivers() {
|
|
log "Starting driver scan"
|
|
local tmp="$CONFIGDIR/.drivers_tmp.json"
|
|
|
|
cat > "$tmp" << 'HEADER'
|
|
{"drivers":[
|
|
HEADER
|
|
|
|
local first=true
|
|
|
|
# GPU drivers
|
|
for path in /vendor/lib64/egl /vendor/lib/egl /system/lib64/egl; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/lib*.so; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f" .so)
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "gpu-$name",
|
|
"name": "$name",
|
|
"category": "gpu",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": true,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Vulkan ICD
|
|
for path in /vendor/lib64/hw /vendor/lib/hw; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/vulkan.*.so; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f" .so)
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "vulkan-$name",
|
|
"name": "$name",
|
|
"category": "gpu",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": true,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# WiFi firmware
|
|
for path in /vendor/firmware /vendor/etc/wifi /lib/firmware; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/fw_bcm*.bin "$path"/fw_wcn*.bin "$path"/wlan_*.bin; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f")
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "wifi-$name",
|
|
"name": "$name",
|
|
"category": "wifi",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": true,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Bluetooth firmware
|
|
for path in /vendor/firmware /vendor/etc/bluetooth /lib/firmware; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/*.hcd "$path"/bt_fw*.bin "$path"/BCM*.hcd; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f")
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "bt-$name",
|
|
"name": "$name",
|
|
"category": "bluetooth",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": true,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Audio HAL
|
|
for path in /vendor/lib64/hw /vendor/lib/hw; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/audio*.so; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f" .so)
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "audio-$name",
|
|
"name": "$name",
|
|
"category": "audio",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": true,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Camera HAL
|
|
for path in /vendor/lib64/hw /vendor/lib/hw; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/camera*.so; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f" .so)
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "camera-$name",
|
|
"name": "$name",
|
|
"category": "camera",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": false,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Sensor HAL
|
|
for path in /vendor/lib64/hw /vendor/lib/hw; do
|
|
[ -d "$path" ] || continue
|
|
for f in "$path"/sensors*.so; do
|
|
[ -f "$f" ] || continue
|
|
local name=$(basename "$f" .so)
|
|
local hash=$(sha256 "$f")
|
|
local ctx=$(get_selinux_context "$f")
|
|
local sz=$(file_size "$f")
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "sensor-$name",
|
|
"name": "$name",
|
|
"category": "sensor",
|
|
"path": "$f",
|
|
"hash": "$hash",
|
|
"selinux": "$ctx",
|
|
"size": $sz,
|
|
"protected": false,
|
|
"variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}]
|
|
}
|
|
ENTRY
|
|
done
|
|
done
|
|
|
|
# Kernel modules currently loaded
|
|
if [ -f /proc/modules ]; then
|
|
while read -r name rest; do
|
|
[ "$first" = true ] && first=false || echo "," >> "$tmp"
|
|
cat >> "$tmp" << ENTRY
|
|
{
|
|
"id": "kmod-$name",
|
|
"name": "$name",
|
|
"category": "kernel",
|
|
"path": "/proc/modules",
|
|
"hash": "",
|
|
"selinux": "",
|
|
"size": 0,
|
|
"protected": false,
|
|
"variants": [{"name": "loaded", "path": "", "hash": "", "active": true}]
|
|
}
|
|
ENTRY
|
|
done < /proc/modules
|
|
fi
|
|
|
|
echo "" >> "$tmp"
|
|
echo "]}" >> "$tmp"
|
|
|
|
mv "$tmp" "$REGISTRY"
|
|
log "Driver scan complete"
|
|
}
|
|
|
|
# --- Count drivers ---
|
|
count_drivers() {
|
|
if [ -f "$REGISTRY" ]; then
|
|
grep -c '"id"' "$REGISTRY" 2>/dev/null || echo "0"
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# --- List drivers as JSON ---
|
|
list_drivers() {
|
|
[ -f "$REGISTRY" ] && cat "$REGISTRY" || echo '{"drivers":[]}'
|
|
}
|
|
|
|
# --- Get single driver info ---
|
|
get_driver() {
|
|
local id="$1"
|
|
if [ -f "$REGISTRY" ]; then
|
|
# Extract the driver block matching the id
|
|
awk -v id="$id" '
|
|
/"id".*:.*"/ { found = ($0 ~ "\"" id "\"") }
|
|
found && /\{/ { depth++ }
|
|
found && depth > 0 { print }
|
|
found && /\}/ { depth--; if (depth == 0) { found=0 } }
|
|
' "$REGISTRY"
|
|
fi
|
|
}
|
|
|
|
# --- Add a custom variant to a driver ---
|
|
add_variant() {
|
|
local driver_id="$1"
|
|
local variant_name="$2"
|
|
local variant_path="$3"
|
|
|
|
if [ ! -f "$variant_path" ]; then
|
|
echo '{"error": "Variant file not found"}'
|
|
return 1
|
|
fi
|
|
|
|
local hash=$(sha256 "$variant_path")
|
|
log "Adding variant '$variant_name' to driver '$driver_id': $variant_path (hash: $hash)"
|
|
|
|
# Copy variant to drivers directory
|
|
local dest="$MODDIR/drivers/${driver_id}_${variant_name}_$(basename "$variant_path")"
|
|
cp "$variant_path" "$dest"
|
|
chmod 644 "$dest"
|
|
|
|
echo "{\"status\": \"ok\", \"driver\": \"$driver_id\", \"variant\": \"$variant_name\", \"path\": \"$dest\", \"hash\": \"$hash\"}"
|
|
}
|
|
|
|
# --- Verify a driver's current hash matches registry ---
|
|
verify_driver() {
|
|
local id="$1"
|
|
local reg_path=$(grep -A5 "\"$id\"" "$REGISTRY" 2>/dev/null | grep '"path"' | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/')
|
|
local reg_hash=$(grep -A5 "\"$id\"" "$REGISTRY" 2>/dev/null | grep '"hash"' | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/')
|
|
|
|
if [ -z "$reg_path" ] || [ -z "$reg_hash" ]; then
|
|
echo '{"status": "unknown", "reason": "driver not in registry"}'
|
|
return 1
|
|
fi
|
|
|
|
local cur_hash=$(sha256 "$reg_path")
|
|
if [ "$cur_hash" = "$reg_hash" ]; then
|
|
echo "{\"status\": \"ok\", \"driver\": \"$id\", \"hash\": \"$cur_hash\"}"
|
|
else
|
|
echo "{\"status\": \"changed\", \"driver\": \"$id\", \"expected\": \"$reg_hash\", \"actual\": \"$cur_hash\"}"
|
|
fi
|
|
}
|
|
|
|
# --- Main ---
|
|
case "$1" in
|
|
scan) scan_drivers ;;
|
|
count) count_drivers ;;
|
|
list) list_drivers ;;
|
|
get) get_driver "$2" ;;
|
|
add_variant) add_variant "$2" "$3" "$4" ;;
|
|
verify) verify_driver "$2" ;;
|
|
*) echo "Usage: driver_registry.sh {scan|count|list|get|add_variant|verify} [args]" ;;
|
|
esac
|