Skip to content

Android 14 Broadcast Receiver Exported Flag Requirement

Problem

Android 14 enforces stricter security requirements for broadcast receivers. Apps now receive a SecurityException with the message: "One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts" if they register receivers without explicit export flags.

This error commonly occurs:

  • When using SDKs/libraries with outdated broadcast registration patterns (like Tuya's com.thingclips.smart)
  • During runtime on Android 14 devices (API 34+)
  • When registering receivers for non-system broadcasts without export flags

1. Conditional Receiver Registration

Override registerReceiver() in your Activity or Application class with version-specific logic:

Kotlin Implementation

kotlin
// In MainActivity.kt or MainApplication.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build

override fun registerReceiver(receiver: BroadcastReceiver?, filter: IntentFilter?): Intent? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        super.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
    } else {
        super.registerReceiver(receiver, filter)
    }
}

Java Implementation

java
// In MainActivity.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import androidx.annotation.Nullable;

@Override
public Intent registerReceiver(
    @Nullable BroadcastReceiver receiver, 
    IntentFilter filter
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        return super.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
    } else {
        return super.registerReceiver(receiver, filter);
    }
}

Testing Recommendation

Test on multiple device models. Some manufacturers require this override in MainActivity, others in MainApplication.

2. Update Affected Libraries

For com.thingclips.smart (Tuya SDK) users, upgrade to v6.1.0+:

gradle
// build.gradle
implementation 'com.thingclips.smart:thingclips-smart:6.1.0'

After updating:

  1. Sync Gradle files
  2. Perform clean build: ./gradlew clean build
  3. Verify receivers in AndroidManifest.xml have explicit android:exported flags:
xml
<receiver
    android:name=".YourReceiver"
    android:exported="true" /> <!-- or false if internal only -->

3. Use AndroidX ContextCompat

For manual receiver registration with backward compatibility:

kotlin
ContextCompat.registerReceiver(
    context,
    receiver,
    intentFilter,
    ContextCompat.RECEIVER_EXPORTED // Use NOT_EXPORTED for app-only broadcasts
)

4. Internal Receiver Handling

Local Broadcasts (App-Only)

For receivers solely communicating within your app:

kotlin
// Deprecated but functional alternative
LocalBroadcastManager.getInstance(this)
    .registerReceiver(receiver, intentFilter)

Security Note

RECEIVER_EXPORTED: Allows broadcasts from other apps
RECEIVER_NOT_EXPORTED: Restricts broadcasts to your app only
Always choose the most restrictive option that meets your use case

Context & Best Practices

  1. Platform Requirement: The export flag enforcement began in Android 13 (Tiramisu) and became mandatory in Android 14

  2. Recipient Choices:

    • Use ContextCompat.RECEIVER_EXPORTED for:
      • Receiving broadcasts from other apps
      • System broadcasts without protected permissions
    • Use ContextCompat.RECEIVER_NOT_EXPORTED for:
      • Internal app components communication
      • Broadcasts with sensitive data
  3. Testing: Always validate solutions on:

    • Physical Android 14 devices
    • Multiple OEM implementations (Samsung, Xiaomi, etc.)
    • Emulators with the latest system images

::: note Performance Prefer LocalBroadcastManager or alternative IPC methods like LiveData for app-internal communication to reduce system overhead. :::

Additional Considerations

  • For React Native/Ionic/Capacitor: Apply the Activity/Application method overrides shown above
  • For missing context issues: Always check nullability with requireContext()
  • Update all broadcast-dependent libraries (e.g., BillingClient v6.1.0+)
gradle
// Example: Update Play billing library
implementation 'com.android.billingclient:billing:6.2.0'