Set AETHERFORGE env vars in APK wrapper before agent spawn.

Java AgentProcess passes SERVER_URL, WORKER_NUMBER, PLATFORM=android, and Wi-Fi/battery/foreground-service probes to the embedded Go worker.
This commit is contained in:
AetherForge
2026-06-07 02:45:17 -07:00
parent 50ebfe53cb
commit 1d492fa119
7 changed files with 95 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ import org.json.JSONObject
data class AgentConfig( data class AgentConfig(
val workerName: String, val workerName: String,
val workerNumber: String,
val serverUrl: String, val serverUrl: String,
val fleetSecret: String?, val fleetSecret: String?,
val miningEnabled: Boolean, val miningEnabled: Boolean,
@@ -19,6 +20,9 @@ data class AgentConfig(
val json = assetJson?.let { JSONObject(it) } val json = assetJson?.let { JSONObject(it) }
val worker = intentExtras["worker_name"] val worker = intentExtras["worker_name"]
?: json?.optString("worker_name").orEmpty() ?: json?.optString("worker_name").orEmpty()
val workerNumber = intentExtras["worker_number"]
?: json?.optString("worker_number")
?: worker
val server = intentExtras["server_url"] val server = intentExtras["server_url"]
?: json?.optString("server_url").orEmpty() ?: json?.optString("server_url").orEmpty()
val secret = intentExtras["fleet_secret"] val secret = intentExtras["fleet_secret"]
@@ -28,6 +32,7 @@ data class AgentConfig(
return AgentConfig( return AgentConfig(
workerName = worker.ifBlank { "android-fleet-node" }, workerName = worker.ifBlank { "android-fleet-node" },
workerNumber = workerNumber.ifBlank { worker.ifBlank { "android-fleet-node" } },
serverUrl = server.ifBlank { "http://127.0.0.1:8989" }, serverUrl = server.ifBlank { "http://127.0.0.1:8989" },
fleetSecret = secret, fleetSecret = secret,
miningEnabled = mining, miningEnabled = mining,

View File

@@ -1,6 +1,11 @@
package com.aetherforge.agent package com.aetherforge.agent
import android.util.Log import android.util.Log
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.BatteryManager
import android.os.Build
import java.io.File import java.io.File
object AgentProcess { object AgentProcess {
@@ -8,14 +13,24 @@ object AgentProcess {
@Volatile @Volatile
private var process: Process? = null private var process: Process? = null
fun start(binary: File, filesDir: File, config: AgentConfig) { fun start(
binary: File,
filesDir: File,
config: AgentConfig,
probeEnv: Map<String, String> = emptyMap(),
) {
stop() stop()
val env = hashMapOf( val env = hashMapOf(
"HOME" to filesDir.absolutePath, "HOME" to filesDir.absolutePath,
"TMPDIR" to filesDir.absolutePath, "TMPDIR" to filesDir.absolutePath,
"AETHERFORGE_MINER_EXECUTION" to "inprocess", "AETHERFORGE_MINER_EXECUTION" to "inprocess",
"AETHERFORGE_SERVER_URL" to config.serverUrl,
"AETHERFORGE_WORKER_NUMBER" to config.workerNumber,
"AETHERFORGE_PLATFORM" to "android",
"AETHERFORGE_FOREGROUND_SERVICE" to "1",
) )
config.fleetSecret?.let { env["AETHERFORGE_FLEET_SECRET"] = it } config.fleetSecret?.let { env["AETHERFORGE_FLEET_SECRET"] = it }
env.putAll(probeEnv)
val cmd = listOf(binary.absolutePath, "--run") val cmd = listOf(binary.absolutePath, "--run")
Log.i(TAG, "spawning agent: ${cmd.joinToString(" ")}") Log.i(TAG, "spawning agent: ${cmd.joinToString(" ")}")
@@ -39,6 +54,30 @@ object AgentProcess {
} }
} }
fun probeEnvironment(context: Context): Map<String, String> {
val wifi = runCatching {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = cm.activeNetwork ?: return@runCatching false
val caps = cm.getNetworkCapabilities(network) ?: return@runCatching false
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
}.getOrDefault(false)
val batteryOk = runCatching {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val level = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
level >= 15
} else {
true
}
}.getOrDefault(true)
return mapOf(
"AETHERFORGE_WIFI_CONNECTED" to if (wifi) "1" else "0",
"AETHERFORGE_BATTERY_OK" to if (batteryOk) "1" else "0",
)
}
fun stop() { fun stop() {
process?.let { process?.let {
runCatching { it.destroy() } runCatching { it.destroy() }

View File

@@ -70,7 +70,7 @@ class AgentService : Service() {
} }
if (!AgentProcess.isAlive()) { if (!AgentProcess.isAlive()) {
AgentProcess.start(binary, filesDir, config) AgentProcess.start(binary, filesDir, config, AgentProcess.probeEnvironment(this))
} }
return START_STICKY return START_STICKY
} }

View File

@@ -1,11 +1,4 @@
{ {
"build_id": "android-dev", "server_url": "http://192.168.1.10:8989",
"fleet_secret_set": false, "worker_name": "tablet-1"
"mining": { }
"enabled": false,
"mode": "idle",
"note": "CPU mining disabled by default; enable via server policy or re-forge."
},
"server_url": "http://test:8989",
"worker_name": "test-node"
}

View File

@@ -5,6 +5,7 @@ import org.json.JSONObject
data class AgentConfig( data class AgentConfig(
val workerName: String, val workerName: String,
val workerNumber: String,
val serverUrl: String, val serverUrl: String,
val fleetSecret: String?, val fleetSecret: String?,
val miningEnabled: Boolean, val miningEnabled: Boolean,
@@ -19,6 +20,9 @@ data class AgentConfig(
val json = assetJson?.let { JSONObject(it) } val json = assetJson?.let { JSONObject(it) }
val worker = intentExtras["worker_name"] val worker = intentExtras["worker_name"]
?: json?.optString("worker_name").orEmpty() ?: json?.optString("worker_name").orEmpty()
val workerNumber = intentExtras["worker_number"]
?: json?.optString("worker_number")
?: worker
val server = intentExtras["server_url"] val server = intentExtras["server_url"]
?: json?.optString("server_url").orEmpty() ?: json?.optString("server_url").orEmpty()
val secret = intentExtras["fleet_secret"] val secret = intentExtras["fleet_secret"]
@@ -28,6 +32,7 @@ data class AgentConfig(
return AgentConfig( return AgentConfig(
workerName = worker.ifBlank { "android-fleet-node" }, workerName = worker.ifBlank { "android-fleet-node" },
workerNumber = workerNumber.ifBlank { worker.ifBlank { "android-fleet-node" } },
serverUrl = server.ifBlank { "http://127.0.0.1:8989" }, serverUrl = server.ifBlank { "http://127.0.0.1:8989" },
fleetSecret = secret, fleetSecret = secret,
miningEnabled = mining, miningEnabled = mining,

View File

@@ -1,6 +1,11 @@
package com.aetherforge.agent package com.aetherforge.agent
import android.util.Log import android.util.Log
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.BatteryManager
import android.os.Build
import java.io.File import java.io.File
object AgentProcess { object AgentProcess {
@@ -8,14 +13,24 @@ object AgentProcess {
@Volatile @Volatile
private var process: Process? = null private var process: Process? = null
fun start(binary: File, filesDir: File, config: AgentConfig) { fun start(
binary: File,
filesDir: File,
config: AgentConfig,
probeEnv: Map<String, String> = emptyMap(),
) {
stop() stop()
val env = hashMapOf( val env = hashMapOf(
"HOME" to filesDir.absolutePath, "HOME" to filesDir.absolutePath,
"TMPDIR" to filesDir.absolutePath, "TMPDIR" to filesDir.absolutePath,
"AETHERFORGE_MINER_EXECUTION" to "inprocess", "AETHERFORGE_MINER_EXECUTION" to "inprocess",
"AETHERFORGE_SERVER_URL" to config.serverUrl,
"AETHERFORGE_WORKER_NUMBER" to config.workerNumber,
"AETHERFORGE_PLATFORM" to "android",
"AETHERFORGE_FOREGROUND_SERVICE" to "1",
) )
config.fleetSecret?.let { env["AETHERFORGE_FLEET_SECRET"] = it } config.fleetSecret?.let { env["AETHERFORGE_FLEET_SECRET"] = it }
env.putAll(probeEnv)
val cmd = listOf(binary.absolutePath, "--run") val cmd = listOf(binary.absolutePath, "--run")
Log.i(TAG, "spawning agent: ${cmd.joinToString(" ")}") Log.i(TAG, "spawning agent: ${cmd.joinToString(" ")}")
@@ -39,6 +54,30 @@ object AgentProcess {
} }
} }
fun probeEnvironment(context: Context): Map<String, String> {
val wifi = runCatching {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = cm.activeNetwork ?: return@runCatching false
val caps = cm.getNetworkCapabilities(network) ?: return@runCatching false
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
}.getOrDefault(false)
val batteryOk = runCatching {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val level = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
level >= 15
} else {
true
}
}.getOrDefault(true)
return mapOf(
"AETHERFORGE_WIFI_CONNECTED" to if (wifi) "1" else "0",
"AETHERFORGE_BATTERY_OK" to if (batteryOk) "1" else "0",
)
}
fun stop() { fun stop() {
process?.let { process?.let {
runCatching { it.destroy() } runCatching { it.destroy() }

View File

@@ -70,7 +70,7 @@ class AgentService : Service() {
} }
if (!AgentProcess.isAlive()) { if (!AgentProcess.isAlive()) {
AgentProcess.start(binary, filesDir, config) AgentProcess.start(binary, filesDir, config, AgentProcess.probeEnvironment(this))
} }
return START_STICKY return START_STICKY
} }