现象
在Android V平台上,应用注册非Protected广播时,如果没有加导出flag会抛出异常导致进程crash。
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.SecurityException: com.demo.myapplication: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
官方的protected-broadcast参考
AndroidManifest.xml - OpenGrok cross reference for /frameworks/base/core/res/AndroidManifest.xml (aospxref.com)
平台规则
在Android 13 或更高版本为目标平台的应用内,在调用registerReceiver()时必须指定导出行为(exported true or false),如果未指定导出行为,可能会导致应用崩溃。
android:exported="true"
参考:广播概览 | Background work | Android Developers
代码实现
接收广播
应用可以通过两种方式接收广播:通过清单声明的接收器 和上下文注册的接收器
1、【静态】声明广播接收器法
1、指定 <receiver> 元素。
<!-- If this receiver listens for broadcasts sent from the system or from
other apps, even other apps that you own, set android:exported to "true". -->
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="APP_SPECIFIC_BROADCAST" />
</intent-filter>
</receiver>
intent 过滤器指定您的接收器所订阅的广播操作。
2、创建 BroadcastReceiver
子类并实现 onReceive(Context, Intent)
。通过 以下示例中的广播接收器会记录日志并显示内容 以下内容:
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
String log = sb.toString();
Log.d(TAG, log);
ActivityNameBinding binding =
ActivityNameBinding.inflate(layoutInflater);
val view = binding.root;
setContentView(view);
Snackbar.make(view, log, Snackbar.LENGTH_LONG).show();
}
}
系统软件包管理器会在应用安装时注册接收器。 然后,接收器将成为应用的单独入口点,这意味着 确保系统可以启动应用并传递广播(如果应用未 。
系统会创建一个新的
BroadcastReceiver
组件 对象来处理它接收的每个广播。仅此对象有效 在调用onReceive(Context, Intent)
期间有效。将代码添加到 返回时,系统会将该组件 活动状态。
2、【动态】上下文注册的接收器(常用)
上下文注册的接收器可以接收广播,前提是它们的注册 上下文有效。例如,如果您在一个
Activity
上下文,只要 activity 不被销毁,您就会收到广播。如果您 注册到应用上下文,那么,只要应用 正在运行。
先创建 BroadcastReceiver和IntentFilter,再通过调用registerReceiver()方法来注册接收器。
RECEIVER_EXPORTED 导出flag定义
源码:
Context.java - OpenGrok cross reference for /frameworks/base/core/java/android/content/Context.java (aospxref.com)
/**
* <p>For apps targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
* either {@link #RECEIVER_EXPORTED} or {@link #RECEIVER_NOT_EXPORTED} must be
* specified if the receiver is not being registered for <a
* href="{@docRoot}guide/components/broadcasts#system-broadcasts">system broadcasts</a>
* or a {@link SecurityException} will be thrown. See {@link
* #registerReceiver(BroadcastReceiver, IntentFilter, int)} to register a receiver with
* flags.
*/
/**
* Flag for {@link #registerReceiver}: The receiver can receive broadcasts from other Apps.
* Has the same behavior as marking a statically registered receiver with "exported=true"
*/
public static final int RECEIVER_EXPORTED = 0x2;
/**
* @deprecated Use {@link #RECEIVER_NOT_EXPORTED} or {@link #RECEIVER_EXPORTED} instead.
* @hide
*/
@Deprecated
@TestApi
public static final int RECEIVER_EXPORTED_UNAUDITED = RECEIVER_EXPORTED;
/**
* Flag for {@link #registerReceiver}: The receiver cannot receive broadcasts from other Apps.
* Has the same behavior as marking a statically registered receiver with "exported=false"
*/
public static final int RECEIVER_NOT_EXPORTED = 0x4;
整改方案※
运行时注册的广播接收器必须指定导出行为。
1、查看服务或应用内是否注册动态广播,且该广播属于非Procted系统广播
protected-broadcast
2、按照监听情况,在调用registerReceiver时添加标志位参数RECEIVER_EXPORTED 或RECEIVER_NOT_EXPORTED
//RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
BroadcastReceiver br = new MyBroadcastReceiver();
//系统广播
//写法1
//IntentFilter filter = new IntentFilter();
//filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
//写法2
//IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
//registerReceiver(mSimStateReceiver, intentFilter);
//写法3:自定义
IntentFilter filter = new IntentFilter("com.demo.myintentfilter");
boolean isListenBroadcaseFromOtherAps = false;
int receiverFlags;
if (isListenBroadcaseFromOtherAps) {
receiverFlags = RECEIVER_EXPORTED;
} else {
receiverFlags = RECEIVER_NOT_EXPORTED;
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
源码kotlin参考:
源码Java参考:
ScreenshotHelper.java - OpenGrok cross reference for /frameworks/base/core/java/com/android/internal/util/ScreenshotHelper.java (aospxref.com)
public ScreenshotHelper(Context context) {
mContext = context;
IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
}
如上参考:Android 14 之 动态注册Broadcast必须声明exported属性以 Android 14 为目标并动态 - 掘金 (juejin.cn)