Implement Resource Checker for Hardware Capability Detection

Status Fixed / completed
Maintainer reply None cached
Activity 2 comments · opened Oct 20, 2025 · closed Oct 20, 2025

Priority

HIGH

Context

The Resource Checker monitors system resources (CPU, GPU, memory, thermal state) to determine which models can run efficiently. The iOS SDK has a complete ResourceChecker. The Kotlin SDK is missing this infrastructure.

Current State

  • Status: Infrastructure does not exist
  • Impact: Cannot determine optimal model selection based on device capabilities
  • iOS Has: Complete ResourceChecker with hardware detection

iOS Reference Implementation

  • Service: sdk/runanywhere-swift/Sources/RunAnywhere/Infrastructure/ResourceChecker.swift
  • Detectors: ProcessorDetector, GPUDetector, NeuralEngineDetector, ThermalMonitor

iOS Architecture Pattern

public final class ResourceChecker: Sendable {
    public func checkResources() async -> ResourceCapability
    public var availableMemory: Int { get async }
    public var thermalState: ThermalState { get async }
}

public struct ResourceCapability {
    public let processorType: ProcessorType
    public let gpuCapability: GPUCapability
    public let neuralEngineAvailable: Bool
    public let availableMemoryGB: Int
}

Implementation Plan

1. Define Common Interfaces (commonMain)

// commonMain/kotlin/com/runanywhere/sdk/infrastructure/resources/ResourceChecker.kt
interface ResourceChecker {
    suspend fun checkResources(): ResourceCapability
    val availableMemoryMB: Long
    val thermalState: ThermalState
}

data class ResourceCapability(
    val processorType: ProcessorType,
    val gpuCapability: GPUCapability,
    val acceleratorAvailable: Boolean,
    val availableMemoryMB: Long,
    val thermalState: ThermalState,
    val batteryLevel: Int? = null
)

enum class ProcessorType {
    ARM_V7, ARM_V8, ARM64, X86, X86_64, UNKNOWN
}

enum class GPUCapability {
    NONE, LOW, MEDIUM, HIGH, ULTRA
}

enum class ThermalState {
    NOMINAL, FAIR, SERIOUS, CRITICAL
}

2. Platform Implementations

Android (androidMain)
// androidMain/kotlin/com/runanywhere/sdk/infrastructure/resources/AndroidResourceChecker.kt
class AndroidResourceChecker(
    private val context: Context
) : ResourceChecker {
    
    private val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    private val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    
    override suspend fun checkResources(): ResourceCapability {
        return ResourceCapability(
            processorType = detectProcessorType(),
            gpuCapability = detectGPUCapability(),
            acceleratorAvailable = detectNNAPISupport(),
            availableMemoryMB = availableMemoryMB,
            thermalState = thermalState,
            batteryLevel = getBatteryLevel()
        )
    }
    
    override val availableMemoryMB: Long
        get() {
            val memInfo = ActivityManager.MemoryInfo()
            activityManager.getMemoryInfo(memInfo)
            return memInfo.availMem / (1024 * 1024)
        }
    
    override val thermalState: ThermalState
        get() {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                val thermalService = context.getSystemService(PowerManager::class.java)
                when (thermalService.currentThermalStatus) {
                    PowerManager.THERMAL_STATUS_NONE -> ThermalState.NOMINAL
                    PowerManager.THERMAL_STATUS_LIGHT -> ThermalState.FAIR
                    PowerManager.THERMAL_STATUS_MODERATE -> ThermalState.SERIOUS
                    else -> ThermalState.CRITICAL
                }
            } else {
                ThermalState.NOMINAL
            }
        }
    
    private fun detectProcessorType(): ProcessorType {
        val abi = Build.SUPPORTED_ABIS.firstOrNull() ?: ""
        return when {
            abi.contains("arm64") -> ProcessorType.ARM64
            abi.contains("armeabi") -> ProcessorType.ARM_V7
            abi.contains("x86_64") -> ProcessorType.X86_64
            abi.contains("x86") -> ProcessorType.X86
            else -> ProcessorType.UNKNOWN
        }
    }
    
    private fun detectGPUCapability(): GPUCapability {
        val glRenderer = try {
            val glView = GLSurfaceView(context)
            // Get GL_RENDERER string
            "Unknown"
        } catch (e: Exception) {
            "Unknown"
        }
        
        return when {
            glRenderer.contains("Adreno 7") -> GPUCapability.ULTRA
            glRenderer.contains("Adreno 6") -> GPUCapability.HIGH
            glRenderer.contains("Mali-G") -> GPUCapability.HIGH
            glRenderer.contains("Adreno 5") -> GPUCapability.MEDIUM
            else -> GPUCapability.LOW
        }
    }
    
    private fun detectNNAPISupport(): Boolean {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
    }
    
    private fun getBatteryLevel(): Int {
        val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}
JVM (jvmMain)
// jvmMain/kotlin/com/runanywhere/sdk/infrastructure/resources/JvmResourceChecker.kt
class JvmResourceChecker : ResourceChecker {
    
    private val runtime = Runtime.getRuntime()
    private val osBean = ManagementFactory.getOperatingSystemMXBean()
    
    override suspend fun checkResources(): ResourceCapability {
        return ResourceCapability(
            processorType = detectProcessorType(),
            gpuCapability = detectGPUCapability(),
            acceleratorAvailable = false, // No NNAPI on JVM
            availableMemoryMB = availableMemoryMB,
            thermalState = ThermalState.NOMINAL, // No thermal API on JVM
            batteryLevel = null
        )
    }
    
    override val availableMemoryMB: Long
        get() = runtime.freeMemory() / (1024 * 1024)
    
    override val thermalState: ThermalState
        get() = ThermalState.NOMINAL
    
    private fun detectProcessorType(): ProcessorType {
        val arch = System.getProperty("os.arch").lowercase()
        return when {
            arch.contains("aarch64") -> ProcessorType.ARM64
            arch.contains("arm") -> ProcessorType.ARM_V8
            arch.contains("amd64") || arch.contains("x86_64") -> ProcessorType.X86_64
            arch.contains("x86") -> ProcessorType.X86
            else -> ProcessorType.UNKNOWN
        }
    }
    
    private fun detectGPUCapability(): GPUCapability {
        // Try to detect GPU via system properties
        return GPUCapability.MEDIUM // Default for desktop
    }
}

Files to Create/Modify

New Files (commonMain)

  • [ ] infrastructure/resources/ResourceChecker.kt
  • [ ] infrastructure/resources/models/ResourceCapability.kt
  • [ ] infrastructure/resources/models/ProcessorType.kt
  • [ ] infrastructure/resources/models/GPUCapability.kt
  • [ ] infrastructure/resources/models/ThermalState.kt

New Files (androidMain)

  • [ ] infrastructure/resources/AndroidResourceChecker.kt

New Files (jvmMain)

  • [ ] infrastructure/resources/JvmResourceChecker.kt

Files to Modify

  • [ ] foundation/ServiceContainer.kt (add resource checker)

Testing Requirements

  • [ ] Unit tests for ResourceCapability
  • [ ] Android platform tests (memory, thermal, processor)
  • [ ] JVM platform tests
  • [ ] GPU detection tests
  • [ ] NNAPI detection tests
  • [ ] Battery monitoring tests

Success Criteria

  • [ ] Correctly detects processor type on Android and JVM
  • [ ] Accurately reports available memory
  • [ ] Thermal state monitoring works on Android
  • [ ] GPU capability detection is accurate
  • [ ] NNAPI support detection works
  • [ ] Integrated into ServiceContainer

Estimated Effort

1-2 weeks (80-100 hours)

Dependencies

  • Depends on: #147 (privacy-first logging)
  • Blocks: Model selection optimization
  • Related to: #148 (DeviceCapability)

🤖 Generated with Claude Code

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗