You have built a robust Android background service. It runs perfectly on Google Pixel devices and the Android Emulator. However, within minutes of deploying it to a Xiaomi device running MIUI, the process is abruptly terminated. Alarms fail to fire, WorkManager jobs are ignored, and foreground services disappear.
This is a known, pervasive issue in mobile app development. While standard Android battery optimization relies on predictable states like Doze mode and App Standby Buckets, OEMs like Xiaomi (MIUI), Huawei, and Samsung implement custom, aggressive task killers.
This guide details the technical root cause of MIUI's process management and provides a complete, modern Kotlin solution to prevent your WorkManager and Foreground Services from being killed.
The Root Cause: AOSP vs. MIUI PowerKeeper
To solve this, you must understand how MIUI diverges from the Android Open Source Project (AOSP).
In standard AOSP, WorkManager relies on the JobScheduler API. When you schedule a periodic sync, the OS evaluates battery level, network state, and Doze restrictions before executing the job. If you use a Foreground Service, standard Android guarantees the process remains alive as long as a persistent notification is displayed, barring extreme low-memory situations.
MIUI ignores these rules. Xiaomi devices utilize a custom system service called PowerKeeper (com.miui.powerkeeper).
When a user turns off the screen or swipes your app away from the "Recents" menu, PowerKeeper aggressively force-stops the application process. Once force-stopped, all registered PendingIntent alarms are canceled, and WorkManager cannot execute because the OS refuses to wake the application package. Furthermore, MIUI disables the "AutoStart" permission by default for almost all third-party apps, meaning the app cannot wake itself up upon receiving network events or system reboots.
The Fix: Bypassing MIUI Battery Optimization
Because PowerKeeper operates at the system level, you cannot programmatically bypass it. Any attempt to silently circumvent OEM task killers is flagged as malware behavior by Google Play Protect.
The only technically sound solution is to detect the MIUI environment and guide the user through a specific UX flow to manually whitelist your app. This involves two steps: enabling "AutoStart" and setting the MIUI Battery Saver to "No Restrictions."
Step 1: Updating the AndroidManifest.xml
Starting in Android 11 (API level 30), package visibility restrictions prevent your app from querying other installed applications. To check if the MIUI Security Center exists on the device, you must declare it in your AndroidManifest.xml using the <queries> element.
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for standard Android battery optimization bypass -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<queries>
<!-- Required to resolve MIUI AutoStart and Battery Saver intents -->
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<package android:name="com.miui.securitycenter" />
</intent>
</queries>
<application>
<!-- Your app components -->
</application>
</manifest>
Step 2: Creating the Intent Resolution Utility
Xiaomi frequently changes the component names responsible for these settings across different MIUI and HyperOS versions. To handle this, we must iterate through a list of known component paths.
Below is a robust Kotlin utility class that handles standard Android battery optimization alongside MIUI-specific overrides.
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
object BatteryOptimizationUtil {
/**
* Checks if the app is currently ignoring standard Android battery optimizations.
*/
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return powerManager.isIgnoringBatteryOptimizations(context.packageName)
}
/**
* Requests standard AOSP battery exemption.
* Note: Use with caution to comply with Google Play Developer Policies.
*/
@SuppressLint("BatteryLife")
fun requestStandardExemption(context: Context) {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:${context.packageName}")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
}
}
/**
* Attempts to navigate the user to the MIUI AutoStart settings screen.
*/
fun navigateToMiuiAutoStart(context: Context): Boolean {
val buildInfo = Build.MANUFACTURER.lowercase()
if (buildInfo != "xiaomi" && buildInfo != "poco" && buildInfo != "redmi") {
return false
}
val intents = listOf(
Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.powercenter.PowerSettings"))
)
for (intent in intents) {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
if (isIntentResolvable(context, intent)) {
context.startActivity(intent)
return true
}
}
return false
}
private fun isIntentResolvable(context: Context, intent: Intent): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.resolveActivity(
intent,
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
) != null
} else {
@Suppress("DEPRECATION")
context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null
}
}
}
Step 3: Implementing the User Experience
Do not trigger these intents on the application launch. Google Play reviews flag unexpected system settings prompts. Instead, create a dedicated "Background Sync Settings" screen within your app.
Explain to the user why they need to change these settings. Provide clear instructions:
- Tap "Enable AutoStart".
- Set Battery Saver to "No Restrictions".
// Example usage in a ViewModel or Activity
fun onResolveBackgroundIssuesClicked(context: Context) {
if (!BatteryOptimizationUtil.isIgnoringBatteryOptimizations(context)) {
// Fallback to standard Android settings if MIUI specific intent fails
val miuiSuccess = BatteryOptimizationUtil.navigateToMiuiAutoStart(context)
if (!miuiSuccess) {
BatteryOptimizationUtil.requestStandardExemption(context)
}
}
}
Deep Dive: Ensuring WorkManager Resiliency
Even after whitelisting the app, you must implement WorkManager correctly to maximize survival rates. Using WorkManager MIUI effectively requires explicitly declaring the work as an Expedited Job or a Foreground Service.
For long-running background tasks, wrap your Worker execution in a Foreground Service boundary. This forces the OS to recognize the task as critical.
import android.content.Context
import android.content.pm.ServiceInfo
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
class DataSyncWorker(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
setForeground(createForegroundInfo())
// Execute long-running sync logic here
return try {
performNetworkSync()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
private fun createForegroundInfo(): ForegroundInfo {
val notification = NotificationCompat.Builder(applicationContext, "sync_channel")
.setContentTitle("Syncing Data")
.setContentText("Background sync in progress...")
.setSmallIcon(android.R.drawable.ic_popup_sync)
.setOngoing(true)
.build()
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Android 14+ requires an explicit foreground service type
ForegroundInfo(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
} else {
ForegroundInfo(1, notification)
}
}
}
Common Pitfalls and Edge Cases
Google Play Store Policy Violations
Requesting ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS is highly scrutinized by Google. If your app is not an alarm clock, task automation app, or medical monitoring device, Google may reject the update. To mitigate this, use ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS (which opens the general battery optimization list) instead of the direct REQUEST intent, forcing the user to manually find your app in the list.
Foreground Service Types in Android 14
If you target API 34+, you must declare a specific foregroundServiceType in your manifest and pass it to ForegroundInfo. MIUI is particularly strict about API 34 compliance. If your declared type does not match your app's actual behavior (e.g., using location without active GPS tracking), MIUI will crash the app with a SecurityException.
The HyperOS Transition
Xiaomi is actively replacing MIUI with HyperOS. While the underlying PowerKeeper architecture remains similar, the Security Center package may evolve. The isIntentResolvable check in the provided Kotlin utility ensures your app degrades gracefully (falling back to standard Android settings) rather than crashing with an ActivityNotFoundException if HyperOS alters the component path.
Conclusion
Handling an Android background service on customized OEM operating systems requires defensive programming. By understanding that MIUI aggressively halts processes via PowerKeeper, you can stop relying solely on AOSP APIs. Implementing targeted intent routing to Xiaomi's AutoStart and Battery settings, combined with properly constructed Foreground WorkManager tasks, ensures your application maintains reliable background execution across the fragmented Android ecosystem.