android: reduce battery usage by not using permanent wakelock (#6176)

* android, desktop: core API event timeout setting

* chat_recv_msg_wait with STM timeout

* Revert "chat_recv_msg_wait with STM timeout"

This reverts commit fdfa2964c5.

* do not use permanent wake lock

* wakelock option, get wakelock on network information change

* remove option
This commit is contained in:
Evgeny
2025-08-19 21:02:50 +01:00
committed by GitHub
parent 24e1680a45
commit 83923b9740
9 changed files with 56 additions and 11 deletions
@@ -46,6 +46,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun onCreate() {
super.onCreate()
AppContextProvider.initialize(this)
if (ProcessPhoenix.isPhoenixProcess(this)) {
return
} else {
@@ -144,11 +144,18 @@ class SimplexService: Service() {
return@withLongRunningApi
}
saveServiceState(self, ServiceState.STARTED)
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG).apply {
acquire()
}
}
// Permanent wakelock prevents deep sleep and results in high battery usage.
// Instead, the app relies on being whitelisted for unrestricted battery usage,
// and also takes wakelock on network events and on network information changes, which allows it to reconnect.
// Network events and information changes are delivered even when device is in deep sleep.
// Possibly, we may need to additionally use "alarms" to wake the app periodically,
// but in all pre-release tests the app was reliably delivering messages in deep sleep, even restored dropped connections.
//
// wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
// newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG).apply {
// acquire()
// }
// }
} finally {
isCheckingNewMessages = false
}
@@ -167,7 +174,7 @@ class SimplexService: Service() {
}
return null
}
private fun createServiceNotification(title: String, text: String): Notification {
val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
@@ -73,6 +73,7 @@ class NetworkObserver {
}
private fun setNetworkInfo(info: UserNetworkInfo) {
getWakeLock(timeout = 180000)
Log.d(TAG, "Network changed: $info")
noNetworkJob.cancel()
if (info.online) {
@@ -13,7 +13,6 @@ import java.lang.ref.WeakReference
import java.util.*
import java.util.concurrent.Semaphore
import kotlin.concurrent.thread
import kotlin.random.Random
actual val appPlatform = AppPlatform.ANDROID
@@ -0,0 +1,28 @@
package chat.simplex.common.platform
import android.content.Context
import android.os.PowerManager
actual fun getWakeLock(timeout: Long): (() -> Unit) {
val context = AppContextProvider.getApplicationContext()
?: throw IllegalStateException("Application context not initialized")
var wakeLock: PowerManager.WakeLock? = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SimplexService::lock").apply {
acquire(timeout)
}
}
return {
wakeLock?.release()
wakeLock = null
}
}
object AppContextProvider {
private var applicationContext: Context? = null
fun initialize(context: Context) {
this.applicationContext = context.applicationContext
}
fun getApplicationContext(): Context? = applicationContext
}
@@ -479,7 +479,7 @@ class AppPreferences {
}
}
private const val MESSAGE_TIMEOUT: Int = 15_000_000
private const val MESSAGE_TIMEOUT: Int = 300_000_000
object ChatController {
var ctrl: ChatCtrl? = -1
@@ -642,6 +642,7 @@ object ChatController {
if (receiverStarted) return
receiverStarted = true
CoroutineScope(Dispatchers.IO).launch {
var releaseLock: (() -> Unit) = {}
while (true) {
/** Global [ctrl] can be null. It's needed for having the same [ChatModel] that already made in [ChatController] without the need
* to change it everywhere in code after changing a database.
@@ -652,7 +653,9 @@ object ChatController {
break
}
try {
releaseLock()
val msg = recvMsg(ctrl)
releaseLock = getWakeLock(timeout = 60000)
if (msg != null) {
val finishedWithoutTimeout = withTimeoutOrNull(60_000L) {
processReceivedMsg(msg)
@@ -0,0 +1,3 @@
package chat.simplex.common.platform
expect fun getWakeLock(timeout: Long): (() -> Unit)
@@ -4,10 +4,8 @@ import SectionBottomSpacer
import SectionTextFooter
import SectionView
import SectionViewSelectable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.capitalize
@@ -15,7 +13,6 @@ import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.style.TextOverflow
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import kotlin.collections.ArrayList
@@ -0,0 +1,6 @@
package chat.simplex.common.platform
actual fun getWakeLock(timeout: Long): (() -> Unit) {
return {}
}