Implement Resource Checker for Hardware Capability Detection

Resolved 💬 2 comments Opened Oct 20, 2025 by sanchitmonga22 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 ↗