Skip to main content

Programmatically Requesting 'Autostart' Permissions in MIUI

 You have likely encountered this scenario: Your Android application relies on WorkManager for critical background synchronization or AlarmManager for time-sensitive notifications. You test it on a Google Pixel or an emulator, and it functions perfectly.

Then, your crash analytics or support tickets start filling up with reports from users with Xiaomi (Redmi/Poco) devices. The app isn't crashing, but background tasks simply vanish when the app is swiped away from the "Recents" menu.

The culprit is not your code; it is MIUI’s aggressive battery optimization logic, specifically the proprietary "Autostart" permission. By default, Xiaomi restricts apps from restarting in the background unless the user explicitly grants this permission. Since there is no standard Android API to grant this automatically, we must engineer a programmatic solution to navigate the user to the correct settings page.

The Root Cause: AOSP vs. MIUI

To solve this, we must understand why the deviation exists. Standard Android (AOSP) relies on signals like BOOT_COMPLETED or JobScheduler logic to wake apps up. The operating system handles the queue and execution.

However, many Chinese OEMs (Xiaomi, Huawei, Oppo, Vivo) implement "White List" logic rather than "Black List" logic for background processes to maximize battery life ratings. In MIUI, the Security Center acts as a gatekeeper. Even if you hold a standard WAKE_LOCK or use foreground services, if the Security Center decides your app shouldn't run, it kills the process at the kernel level immediately after the user swipes the app away.

The "Autostart" toggle in MIUI allows an app to receive system broadcasts (like boot completion) and restart background services. This setting is hidden inside the "Security" system app, not the standard Android "Settings" menu.

The Technical Solution

Because "Autostart" is not a standard Android permission (like CAMERA or ACCESS_FINE_LOCATION), you cannot request it via the ActivityCompat.requestPermissions workflow. There is no boolean return value to check if it is granted.

Instead, we must use an Implicit Intent to target the specific Activity within the MIUI Security package.

1. Detection Strategy

First, valid code must ensure we are running on a Xiaomi device. Checking Build.MANUFACTURER is the standard approach.

2. The Intent List

Xiaomi has changed the package and class name of the Autostart activity multiple times across MIUI versions (MIUI 10, 11, 12, and HyperOS). A robust solution attempts to launch the most recent known Intents and handles failures gracefully.

Here is a modern, production-ready utility class in Kotlin.

Implementation: The XiaomiUtilities Class

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log

object XiaomiUtilities {

    private const val BRAND_XIAOMI = "xiaomi"
    private const val BRAND_REDMI = "redmi"
    private const val BRAND_POCO = "poco"

    // List of known components for the Autostart setting page
    // Ordered by newest to oldest to prioritize modern MIUI versions
    private val AUTOSTART_INTENTS = arrayOf(
        Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
        Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.powercenter.PowerSettings")),
        Intent("miui.intent.action.OP_AUTO_START").addCategory(Intent.CATEGORY_DEFAULT),
        Intent("miui.intent.action.POWER_HIDE_MODE_APP_LIST").addCategory(Intent.CATEGORY_DEFAULT),
        Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.powercenter.autostart.AutoStartManagementActivity"))
    )

    /**
     * Checks if the device is manufactured by Xiaomi/Redmi/Poco
     */
    fun isXiaomiDevice(): Boolean {
        val manufacturer = Build.MANUFACTURER.lowercase()
        return manufacturer.contains(BRAND_XIAOMI) || 
               manufacturer.contains(BRAND_REDMI) || 
               manufacturer.contains(BRAND_POCO)
    }

    /**
     * Attempts to open the Autostart settings page.
     * Returns true if the intent was successfully launched.
     */
    fun openAutostartSettings(context: Context): Boolean {
        for (intent in AUTOSTART_INTENTS) {
            try {
                // Ensure the intent starts a new task stack if needed
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                
                // Verify the intent resolves to an activity before launching
                // to prevent vague SecurityExceptions
                val resolveInfo = context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
                
                if (resolveInfo != null) {
                    context.startActivity(intent)
                    return true
                }
            } catch (e: Exception) {
                // Log and continue to the next intent in the array
                Log.w("XiaomiUtilities", "Failed to launch intent: $intent", e)
            }
        }
        return false
    }
}

Integrating into the UI Flow

You should not bombard the user with this request on every app launch. Since we cannot programmatically check if the switch is currently "On," we must rely on SharedPreferences to track if we have shown the prompt to the user.

A best practice is to show a dialog explaining why this is necessary before launching the intent. If you launch the intent immediately, the user will be confused by the sudden jump to the system settings.

import android.app.AlertDialog
import android.content.Context
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private fun checkAndRequestAutoStart() {
        if (XiaomiUtilities.isXiaomiDevice()) {
            val sharedPrefs = getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
            val hasAsked = sharedPrefs.getBoolean("has_asked_autostart", false)

            if (!hasAsked) {
                showExplanationDialog(sharedPrefs)
            }
        }
    }

    private fun showExplanationDialog(sharedPrefs: android.content.SharedPreferences) {
        AlertDialog.Builder(this)
            .setTitle("Enable Auto-Start")
            .setMessage("To receive notifications reliably, please enable 'Autostart' for this app in the next screen.")
            .setPositiveButton("Allow") { _, _ ->
                // Mark as asked so we don't nag the user again
                sharedPrefs.edit().putBoolean("has_asked_autostart", true).apply()
                
                val success = XiaomiUtilities.openAutostartSettings(this)
                if (!success) {
                    // Fallback: The user might be on a custom ROM or very old MIUI
                    // Consider directing them to the generic App Info page
                }
            }
            .setNegativeButton("Cancel", null)
            .setCancelable(false)
            .show()
    }
}

Deep Dive: Handling Fragmentation

Why do we iterate through a list of Intents in XiaomiUtilities?

MIUI is not a monolith. It exists across various Android API levels (Android 9 through 14).

  1. Old MIUI (v10/11): Used com.miui.powercenter.autostart.
  2. New MIUI (v12/13): Shifted to com.miui.permcenter.
  3. HyperOS: May introduce new package structures entirely.

The resolveActivity check in the code above is critical. Without it, startActivity throws an ActivityNotFoundException if the specific component doesn't exist on that specific phone model, crashing your app. The loop ensures we try the most specific "modern" path first and fall back gracefully.

Common Pitfalls and Edge Cases

1. The "Unknown" State

The most significant limitation of this approach is the inability to read the setting. A user might click "Allow" in your dialog, be taken to settings, but not actually toggle the switch. Your app has no way of knowing.

  • Strategy: Monitor your backend. If a Xiaomi user stops sending heartbeats, consider showing a "Troubleshoot Notifications" option in your app settings that re-triggers this flow manually.

2. Custom ROMs

Users may flash Xiaomi.eu or other custom ROMs based on MIUI. These ROMs often strip out the heavy-handed security centers or rename packages. The try/catch block combined with resolveActivity makes the provided code safe for these environments—it simply won't open the settings if the app is missing, rather than crashing.

3. Battery Saver Interaction

Even with Autostart enabled, the "Battery Saver" mode in MIUI can still restrict network access.

  • Advanced Fix: While in the Autostart settings, you can also guide users to set "Battery Saver" to "No Restrictions" for your specific app. This requires a separate Intent targeting Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, which is a standard Android API, but often overridden by MIUI's UI.

Conclusion

Building for Android requires navigating a fragmented ecosystem. While Google defines the standards, manufacturers like Xiaomi redefine the rules to prioritize battery life. By using a safe, iterative Intent strategy, you can guide your users to unlock the full functionality of your application without risking crashes on unsupported devices.

Implement the XiaomiUtilities class today to reclaim your background task reliability.