Skip to main content

Migrating App Background Services: From MIUI to Xiaomi HyperOS

 For years, Android developers maintaining long-running background tasks have battled MIUI's aggressive background execution limits. Workarounds often involved reverse-engineered intents routing users to hidden menus within the com.miui.securitycenter to manually enable "AutoStart" or disable battery restrictions.

With the introduction of Xiaomi HyperOS, these legacy workarounds are obsolete. Code that successfully maintained background states on MIUI 14 is now experiencing silent process deaths, ActivityNotFoundException crashes, and erratic scheduling. Navigating the MIUI to HyperOS migration requires abandoning proprietary hacks and strictly adopting modern Android 14+ architectural standards, coupled with new OS-specific permission fallbacks.

The Root Cause: Why MIUI Hacks Fail on HyperOS

MIUI relied on a custom daemon known as PowerKeeper integrated deeply with its proprietary Security App. When an app was moved to the background, PowerKeeper would aggressively freeze the process unless the app was explicitly whitelisted by the user via the "AutoStart" UI.

HyperOS rebuilds this architecture entirely. Built on an Android 14 core, Xiaomi HyperOS development standardizes power management by leaning heavily into AOSP's Application Exit Info (AEI) and strict Foreground Service (FGS) type validations.

The breakage occurs for three primary reasons:

  1. Intent Resolution Failures: The legacy com.miui.securitycenter.action.AUTO_START intent is heavily restricted or entirely absent depending on the specific device region ROM.
  2. Android 14 FGS Enforcement: HyperOS strictly enforces the Android 14 requirement that all foreground services declare a valid foregroundServiceType in the manifest and request the corresponding specific permission.
  3. Cross-Device Resource Allocation: HyperOS's new "Alive" framework dynamically reallocates CPU resources based on cross-device interconnectivity (e.g., streaming an app to a Xiaomi tablet). Background services consuming high memory without an active system-level binding are instantly killed to preserve interconnect bandwidth.

The Fix: Modernizing Background Execution

To ensure Android OS compatibility across both standard AOSP devices and Xiaomi HyperOS, applications must utilize the WorkManager API for deferrable tasks and strongly-typed Foreground Services for continuous execution. Furthermore, we must update our battery optimization request paths.

Step 1: Implementing Expedited WorkManager Tasks

For tasks that require immediate execution but do not need to persist indefinitely (e.g., syncing data after receiving a high-priority FCM payload), use Expedited Work requests. HyperOS respects the OutOfQuotaPolicy parameters much better than MIUI did.

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import androidx.work.WorkerParameters

class DataSyncWorker(
    context: Context,
    parameters: WorkerParameters
) : CoroutineWorker(context, parameters) {

    override suspend fun doWork(): Result {
        return try {
            // Execute deep synchronization logic here
            performSystemSync()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }

    override suspend fun getForegroundInfo(): ForegroundInfo {
        // Required for Expedited Work in Android 14 / HyperOS
        val notification = NotificationHelper.createSyncNotification(applicationContext)
        return ForegroundInfo(NOTIFICATION_ID, notification)
    }
}

// Enqueueing the work
val syncRequest = OneTimeWorkRequestBuilder<DataSyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context).enqueue(syncRequest)

Step 2: Strictly Typed Foreground Services

If your app handles media playback, location tracking, or VoIP, you must declare the precise FGS type. HyperOS will immediately throw a SecurityException if a service starts without this declaration.

AndroidManifest.xml:

<!-- Android 14 / HyperOS requirement -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<service
    android:name=".services.ContinuousSyncService"
    android:exported="false"
    android:foregroundServiceType="dataSync" />

ContinuousSyncService.kt:

import android.app.Service
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import androidx.core.app.ServiceCompat

class ContinuousSyncService : Service() {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = NotificationHelper.createActiveServiceNotification(this)
        
        // Critical: Must specify type on Android 14+ for HyperOS to prevent process death
        val type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
        } else {
            0
        }

        ServiceCompat.startForeground(
            this,
            SERVICE_NOTIFICATION_ID,
            notification,
            type
        )

        executeLongRunningTask()
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

Step 3: The HyperOS Battery Optimization Fallback

Unlike MIUI, HyperOS correctly intercepts the standard AOSP battery optimization intent. You no longer need to target the Xiaomi Security App directly. Requesting exemption from battery optimizations using the standard API is now the officially supported method.

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings

fun requestHyperOSBatteryExemption(context: Context) {
    val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    val packageName = context.packageName

    if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
        @SuppressLint("BatteryLife")
        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
            data = Uri.parse("package:$packageName")
            flags = Intent.FLAG_ACTIVITY_NEW_TASK
        }
        
        if (intent.resolveActivity(context.packageManager) != null) {
            context.startActivity(intent)
        }
    }
}

Deep Dive: Execution Context and System Properties

The architectural shift in Xiaomi HyperOS development relies heavily on the Linux kernel's cgroups (control groups) via Android's ProcessState.

When an app is pushed to the background, HyperOS analyzes its OOM (Out of Memory) adjustment score (oom_adj). Standard background processes are quickly moved to the cached state. By standardizing your implementation with Android 14 FGS types, you signal the Activity Manager to assign a highly visible oom_adj score, preventing the HyperOS memory management daemon from terminating the process.

For developers working in Enterprise mobile management (EMM), device provisioning has also changed. Device Policy Controllers (DPCs) using Android Enterprise APIs can no longer bypass background restrictions silently on HyperOS without explicitly granting the DISALLOW_APPS_CONTROL user restriction. MDM profiles must be updated to ensure that managed configurations explicitly exempt mission-critical enterprise apps from the HyperOS battery saver features at the policy level.

Common Pitfalls and Edge Cases

Exact Alarms Denial

HyperOS denies SCHEDULE_EXACT_ALARM by default. If your background service relies on exact timing via AlarmManager, the app will crash if you attempt to set an exact alarm without checking permission. Always check alarmManager.canScheduleExactAlarms() and fallback to setWindow() or WorkManager if false.

High-Priority FCM Payloads

In MIUI, developers often used standard FCM payloads to wake the app. HyperOS heavily restricts background waking. To wake a HyperOS device reliably, FCM messages must be marked with priority: "high" in the notification payload. Furthermore, HyperOS implements daily quotas for high-priority messages; exceeding this quota will downgrade subsequent messages to normal priority, delaying background execution until the user manually unlocks the device.

Detecting HyperOS Programmatically

In rare cases where you must differentiate between MIUI and HyperOS for analytics or specific UI prompts, you can query system properties. However, rely on API level checks first.

import android.annotation.SuppressLint

@SuppressLint("PrivateApi")
fun isHyperOS(): Boolean {
    return try {
        val buildClass = Class.forName("android.os.SystemProperties")
        val getMethod = buildClass.getMethod("get", String::class.java)
        val version = getMethod.invoke(null, "ro.mi.os.version.name") as String
        version.isNotEmpty()
    } catch (e: Exception) {
        false
    }
}

Conclusion

The transition from MIUI to HyperOS is a significant step toward Android standardization. By stripping away legacy reverse-engineered intents, adopting strongly-typed Foreground Services, and utilizing the robust scheduling of WorkManager, developers can guarantee application stability. Aligning with these core Android 14 principles ensures your application remains persistent, performant, and reliable on the new HyperOS architecture.