Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AmolPardeshi99/android-performance-skills/llms.txt

Use this file to discover all available pages before exploring further.

Binder calls to system services or other processes are synchronous and can block for tens to hundreds of milliseconds — more under system load. Calling PackageManager, ActivityManager, ContentResolver.query, or any other IPC on the main thread is a direct ANR risk.

Binder IPC on the Main Thread

Every call to a system service crosses a process boundary via the Binder driver. The calling thread blocks until system_server responds. Under normal conditions this takes a few milliseconds, but under system load — other app launches, ContentProvider queries, heavy CPU usage — the same call can block for hundreds of milliseconds or longer, pushing past the 5-second input ANR threshold.

Bad: synchronous Binder calls in a lifecycle callback

// ❌ BAD: blocking Binder calls on Main
class StartupFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // All of these are synchronous Binder calls to system services:
        val packages = requireContext().packageManager.getInstalledPackages(0)   // 200–500 ms
        val wifi     = Settings.Global.getInt(requireContext().contentResolver, Settings.Global.WIFI_ON, 0)
        val contacts = requireContext().contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
    }
}

Correct: move Binder calls to Dispatchers.IO

// ✅ CORRECT: move Binder calls to Dispatchers.IO
class StartupViewModel(private val pm: PackageManager) : ViewModel() {
    fun loadInstalledPackages() {
        viewModelScope.launch {
            val packages = withContext(Dispatchers.IO) { pm.getInstalledPackages(0) }
            _packageList.value = packages
        }
    }
}

Detecting violations with StrictMode

Enable StrictMode in debug builds so that any Binder call on the main thread surfaces immediately in Logcat rather than silently shipping to production.
if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(
        StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()        // Logcat in dev
            // .penaltyDeath()   // Use in CI to hard-fail on any violation
            .build()
    )
}

ContentProvider and AppInitializer on Main Thread

ContentProviders are initialized on the main thread during process startup, before Application.onCreate(). Any heavy work in ContentProvider.onCreate() directly blocks cold start and can cause startup ANRs.

Bad: synchronous SDK init inside a ContentProvider

// ❌ BAD: synchronous SDK init in a ContentProvider (runs on Main at process start)
class MySdkInitProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        HeavySdk.init(context!!)  // 300ms of reflection + I/O on Main at cold start
        return true
    }
}

Correct option A: initialize on a background thread in Application.onCreate

// ✅ CORRECT option A: remove ContentProvider; init in Application.onCreate on background thread
class MyApp : Application() {
    @ApplicationScope @Inject lateinit var appScope: CoroutineScope

    override fun onCreate() {
        super.onCreate()
        appScope.launch(Dispatchers.IO) { HeavySdk.init(applicationContext) }
    }
}

Correct option B: Jetpack App Startup with an async initializer

// ✅ CORRECT option B: Jetpack App Startup with async initializer
class MySdkInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        // Runs synchronously but you can chain dependencies explicitly
        // For async init: store a Deferred and await it lazily when first needed
    }
    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

Binder Thread Pool Exhaustion — System-Induced ANR

Querying multiple system services sequentially on the main thread at startup places burst load on system_server’s Binder thread pool. Under concurrent app launches or ContentProvider queries, this can saturate all 15 threads and cause your calls to block indefinitely — even if your code is otherwise correct.
The Binder driver limits each process to a maximum of 15 threads in its thread pool (BINDER_MAX_POOL_THREADS = 15). When all Binder threads in system_server are busy, your Binder calls block indefinitely on the calling side — regardless of how clean your main thread is. This is a system-induced ANR, but you can reduce your exposure to it.

Bad: sequential system service calls at startup

// ❌ BAD: querying multiple system services sequentially on startup
class StartupActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Each call below is a Binder round-trip to system_server.
        // Under load, any one of them can block for hundreds of ms or seconds.
        val pm   = packageManager.getInstalledPackages(0)          // Binder → PMS
        val accs = getSystemService(AccountManager::class.java)
                     .getAccountsByType("com.google")              // Binder → AMS
        val crs  = contentResolver.query(CONTACTS_URI, ...)        // Binder → ContentProvider
    }
}

Correct: batch all system service calls on the IO dispatcher

// ✅ CORRECT: batch and defer all system service calls off the main thread
class StartupViewModel : ViewModel() {
    val startupData = viewModelScope.async(Dispatchers.IO) {
        val pm    = appContext.packageManager.getInstalledPackages(0)
        val accts = accountManager.getAccountsByType("com.google")
        StartupData(pm, accts)
    }
}

ANR log diagnosis signal for Binder thread pool pressure

When reading an ANR trace, look for this pattern in the main thread stack:
at com.android.internal.os.BinderProxy.transact
If the main thread shows BinderProxy.transact and the CPU log shows system_server at very high usage (>100% across its threads), suspect Binder thread-pool pressure rather than app logic. The fix is to move calls off the main thread and reduce burst volume at startup.

OEM App-Freezing ANR (Power-Optimization False Positive)

OEM Android distributions — particularly Asian-market ROMs from OPPO, Vivo, Xiaomi, and Huawei — implement aggressive app-freezing policies (e.g., HansManager, RefrigeratorManager) to save battery. When the screen turns off, they send a SIGSTOP-equivalent to background apps. If your app receives an input event while frozen, the system may time out waiting for an acknowledgment and generate an ANR — even though your app code is entirely correct.

How to identify in the log

# App was frozen at screen-off:
OemPowerManager : freeze uid: 10182 package: com.example.myapp reason: LcdOff

# Back key dispatched to the frozen app — no response for 5s → ANR
WindowManager: Input event dispatching timed out sending to com.example.myapp

# System unfreezes app only *after* ANR is triggered:
OemPowerManager : unfreeze uid: 10182 package: com.example.myapp reason: Signal
The tell-tale signature is unfreeze … reason: Signal appearing within 1–2 seconds after the am_anr event. The app was correct — it was simply not allowed to respond.
OEM freeze ANRs are a system bug — don’t spend time profiling your app code. The freeze/unfreeze log pattern is definitive evidence that the failure occurred at the OS layer, not in your application.

Resolution steps

1

Do not profile your app code

The thread stacks will look normal because the app was externally frozen. Time spent in Perfetto or StrictMode here is wasted.
2

Report to the OEM

Collect the freeze/unfreeze log and file a report through the OEM’s developer relations channel. Include the exact log lines and the am_anr timestamp for correlation.
3

Minimize work in onResume and onStart

As a mitigation, keep onResume and onStart as lightweight as possible so your app can re-respond quickly after being unfrozen.
4

Tag false positives in your ANR dashboard

In your ANR monitoring pipeline, automatically tag any ANR where unfreeze … reason: Signal appears within 1–2 seconds of am_anr. These are OEM-system false positives and should not count toward your app’s ANR rate.

Build docs developers (and LLMs) love