diff --git a/android/measure/src/main/java/sh/measure/android/NoopThermalStateManager.kt b/android/measure/src/main/java/sh/measure/android/NoopThermalStateManager.kt new file mode 100644 index 000000000..7dac9659b --- /dev/null +++ b/android/measure/src/main/java/sh/measure/android/NoopThermalStateManager.kt @@ -0,0 +1,20 @@ +package sh.measure.android + +import android.os.PowerManager + +/** + * A no-op implementation of [ThermalStateManager], used for API < Q + * which do not have [PowerManager.OnThermalStatusChangedListener]. + */ +internal class NoopThermalStateManager : ThermalStateManager { + override fun register(powerManager: PowerManager?) { + // No-op + } + + override fun unregister(powerManager: PowerManager?) { + // No-op + } + + override val thermalThrottlingEnabled: Boolean + get() = false +} diff --git a/android/measure/src/main/java/sh/measure/android/PowerStateProvider.kt b/android/measure/src/main/java/sh/measure/android/PowerStateProvider.kt index 8b0e9ef15..99cc09f42 100644 --- a/android/measure/src/main/java/sh/measure/android/PowerStateProvider.kt +++ b/android/measure/src/main/java/sh/measure/android/PowerStateProvider.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.content.IntentFilter import android.os.Build import android.os.PowerManager -import androidx.annotation.RequiresApi import sh.measure.android.logger.LogLevel import sh.measure.android.logger.Logger import sh.measure.android.utils.SystemServiceProvider @@ -15,7 +14,7 @@ internal interface PowerStateProvider { fun register() fun unregister() val lowPowerModeEnabled: Boolean? - val thermalThrottlingEnabled: Boolean? + val thermalThrottlingEnabled: Boolean } internal class PowerStateProviderImpl( @@ -25,8 +24,16 @@ internal class PowerStateProviderImpl( ) : PowerStateProvider { override var lowPowerModeEnabled: Boolean? = null private set - override var thermalThrottlingEnabled: Boolean? = null - private set + + override val thermalThrottlingEnabled: Boolean + get() = thermalStateManager.thermalThrottlingEnabled + + private val thermalStateManager: ThermalStateManager = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ThermalStateManagerImpl() + } else { + NoopThermalStateManager() + } private val powerSaveReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -36,25 +43,12 @@ internal class PowerStateProviderImpl( } } - private val thermalListener = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - PowerManager.OnThermalStatusChangedListener { status -> - thermalThrottlingEnabled = isThermalThrottlingEnabled(status) - } - } else { - null - } - override fun register() { try { updatePowerState() - val filter = IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) context.registerReceiver(powerSaveReceiver, filter) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - systemServiceProvider.powerManager?.addThermalStatusListener(thermalListener!!) - updateThermalState() - } + thermalStateManager.register(systemServiceProvider.powerManager) } catch (e: Exception) { logger.log(LogLevel.Error, "Failed to register power state receiver", e) } @@ -63,9 +57,7 @@ internal class PowerStateProviderImpl( override fun unregister() { try { context.unregisterReceiver(powerSaveReceiver) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - systemServiceProvider.powerManager?.removeThermalStatusListener(thermalListener!!) - } + thermalStateManager.unregister(systemServiceProvider.powerManager) } catch (e: Exception) { logger.log(LogLevel.Error, "Failed to unregister power state receiver", e) } @@ -80,33 +72,4 @@ internal class PowerStateProviderImpl( lowPowerModeEnabled = null } } - - @RequiresApi(Build.VERSION_CODES.Q) - private fun updateThermalState() { - try { - val powerManager = systemServiceProvider.powerManager - val currentStatus = powerManager?.currentThermalStatus - thermalThrottlingEnabled = isThermalThrottlingEnabled(currentStatus) - } catch (e: Exception) { - logger.log(LogLevel.Error, "Failed to update thermal state", e) - thermalThrottlingEnabled = null - } - } - - /** - * Thermal throttling starts considerably affecting UX from "THERMAL_STATUS_SEVERE" status - * onwards. - * - * @return true if thermal status is severe or worse, false otherwise. - */ - private fun isThermalThrottlingEnabled(status: Int?) = when (status) { - PowerManager.THERMAL_STATUS_NONE -> false - PowerManager.THERMAL_STATUS_LIGHT -> false - PowerManager.THERMAL_STATUS_MODERATE -> false - PowerManager.THERMAL_STATUS_SEVERE -> true - PowerManager.THERMAL_STATUS_CRITICAL -> true - PowerManager.THERMAL_STATUS_EMERGENCY -> true - PowerManager.THERMAL_STATUS_SHUTDOWN -> true - else -> null - } } diff --git a/android/measure/src/main/java/sh/measure/android/ThermalStateManager.kt b/android/measure/src/main/java/sh/measure/android/ThermalStateManager.kt new file mode 100644 index 000000000..50301bf1f --- /dev/null +++ b/android/measure/src/main/java/sh/measure/android/ThermalStateManager.kt @@ -0,0 +1,10 @@ +package sh.measure.android + +import android.os.PowerManager + +// Base interface for thermal state management +internal interface ThermalStateManager { + fun register(powerManager: PowerManager?) + fun unregister(powerManager: PowerManager?) + val thermalThrottlingEnabled: Boolean +} diff --git a/android/measure/src/main/java/sh/measure/android/ThermalStateManagerImpl.kt b/android/measure/src/main/java/sh/measure/android/ThermalStateManagerImpl.kt new file mode 100644 index 000000000..116b0083a --- /dev/null +++ b/android/measure/src/main/java/sh/measure/android/ThermalStateManagerImpl.kt @@ -0,0 +1,43 @@ +package sh.measure.android + +import android.os.Build +import android.os.PowerManager +import androidx.annotation.RequiresApi + +/** + * An implementation of [ThermalStateManager] for API > Q where + * [PowerManager.OnThermalStatusChangedListener] is available. + */ +@RequiresApi(Build.VERSION_CODES.Q) +internal class ThermalStateManagerImpl : ThermalStateManager { + private var currentThermalThrottlingState: Boolean = false + + private val thermalListener = PowerManager.OnThermalStatusChangedListener { status -> + currentThermalThrottlingState = isThermalThrottlingEnabled(status) + } + + override fun register(powerManager: PowerManager?) { + powerManager?.let { + it.addThermalStatusListener(thermalListener) + currentThermalThrottlingState = isThermalThrottlingEnabled(it.currentThermalStatus) + } + } + + override fun unregister(powerManager: PowerManager?) { + powerManager?.removeThermalStatusListener(thermalListener) + } + + override val thermalThrottlingEnabled: Boolean + get() = currentThermalThrottlingState + + private fun isThermalThrottlingEnabled(status: Int?) = when (status) { + PowerManager.THERMAL_STATUS_NONE -> false + PowerManager.THERMAL_STATUS_LIGHT -> false + PowerManager.THERMAL_STATUS_MODERATE -> false + PowerManager.THERMAL_STATUS_SEVERE -> true + PowerManager.THERMAL_STATUS_CRITICAL -> true + PowerManager.THERMAL_STATUS_EMERGENCY -> true + PowerManager.THERMAL_STATUS_SHUTDOWN -> true + else -> false + } +}