Android14 普通应用registerReceiver注册广播报错One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified …
文章目录
一、前言
Android14 普通应用注册广播registerReceiver会报错,提示需要添加一个参数RECEIVER_EXPORTED 或者 RECEIVER_NOT_EXPORTED;系统framework或者系统应用是不会报这个错误的!
这个问题比较好解决,加一个参数就行,但是网上目前没有人对这个问题今天深入分析。
本文对registerReceiver报错进行分析,具体到哪个类哪行代码报错,
分析了解后对于系统其他相关报错能有个认识,或者有分析思路。
RECEIVER_EXPORTED 表示外部应用范围,RECEIVER_NOT_EXPORTED 表示非外部应用范围,是否有作用?
二、Google 对Android14 广播说明
说明图片所示:
对于Android14 主要说明内容:
For apps targeting Build.VERSION_CODES.UPSIDE_DOWN_CAKE, either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED must be specified if the receiver is not being registered for system broadcasts or a SecurityException will be thrown. See registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int) to register a receiver with flags.
Build.VERSION_CODES.UPSIDE_DOWN_CAKE 是Android14的名称,旋转蛋糕。
上面的英文大概意思:
对于以Build为 Android14的应用程序版本代码。
如果接收器没有为系统广播注册,或者将引发SecurityException,
则必须指定RECEIVER_EXPORTED或RECEIVE_NOT_EXPORTED。
请参阅registerReceiver(android.content.BroadcastReceiver,android.contant.IntentFilter,int)注册带有标志的接收器。
相关网址:https://developer.android.google.cn/reference/android/content/Context
搜索 registerReceiver 就可能找到相关代码。
三、Android14 注册广播报错解决和分析
1、报错示例和解决方法
Android普通应用的注册广播的代码:
public class MainActivity extends AppCompatActivity {
public void testBroadcast(View view) { //点击按钮,监听广播
LogUtil.debug("");
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("aa");
//registerReceiver(mReceiver1, intentFilter);//在这里报错,如果没有try catch,会崩溃
//解决方法:添加一个参数Context.RECEIVER_EXPORTED 或者ContextRECEIVER_NOT_EXPORTED
//RECEIVER_EXPORTED 表示可以接收应用外部广播,ContextRECEIVER_NOT_EXPORTED 应用内部广播
registerReceiver(mReceiver2, intentFilter, Context.RECEIVER_EXPORTED);
}
private BroadcastReceiver mReceiver1 = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
LogUtil.debug("action = " + action);
}
}
上面代码就包含了报错的代码示例和正确的写法。
在Android13或者更早的版本,上面的代码都是没有问题的;只有Android14 会报错。
2、报错分析
报错的堆栈信息:
报错最主要相关日志:
Caused by: java.lang.SecurityException: com.demo.android14demo: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
at android.app.IActivityManager.registerReceiverWithFeature
at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1852)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1792)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1780)
at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755)
at com.demo.android14demo.MainActivity.testBroadcast(MainActivity.java:25)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.registerReceiverWithFeature(ActivityManagerService.java:13927)
从报错日志看,MainActivity 注册广播后,ContextImpl.registerReceiverInternal 之后就报错了;
难道报错信息在 ContextImpl.java 代码里面?
查看 ContextImpl.java 源码,并没有发现报错判断和相关关键字,所以是在往下的逻辑进行的报错。
其实就是 IActivityManager的实现类 ActivityManagerService 里面报错的:
release\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
public Intent registerReceiverWithFeature(... IIntentReceiver receiver,
IntentFilter filter, String permission, int userId, int flags) {
//(1)判断是否设置了RECEIVER_EXPORTED 或者 RECEIVER_NOT_EXPORTED 的flag
final boolean explicitExportStateDefined =
(flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
//(2)动态接收广播,这里有判断 callingUid 应用等级,可能是系统应用没有报错的原因
boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
if (!onlyProtectedBroadcasts) { //(3)非保护广播
if (receiver == null && !explicitExportStateDefined) {
// sticky broadcast, no flag specified (flag isn't required)
flags |= Context.RECEIVER_EXPORTED;
} else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {//(4)未设置flag和动态注册的广播---抛出异常
throw new SecurityException(
callerPackage + ": One of RECEIVER_EXPORTED or "
+ "RECEIVER_NOT_EXPORTED should be specified when a receiver "
+ "isn't being registered exclusively for system broadcasts");
// Assume default behavior-- flag check is not enforced
} else if (!requireExplicitFlagForDynamicReceivers && (
(flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
// Change is not enabled, assume exported unless otherwise specified.
flags |= Context.RECEIVER_EXPORTED;
}
} else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
flags |= Context.RECEIVER_EXPORTED;
}
}
}
Android14注册广播报错是在 ActivityManagerService.java 里面一些列判断之后抛出异常报错的。
四、其他
1、Android14 registerReceiver注册广播报错总结
普通应用需要再最后添加参数Context.RECEIVER_EXPORTED 或者 Context.RECEIVER_NOT_EXPORTED;
Android13 或者更低版本测试了并没有这个问题。
其实 Android13 的ActivityManagerService.java也是有大致的流程,也会抛出那个异常过程,
至于为啥没有报错,应该是具体判断里面的流程中,某个属性值的返回有差异所以没进入到抛出异常的过程。
2、Android13 开始AndroidManifest.xml里面的四大组件都是要定义exported属性的,否则会编译报错
activity、service、receiver、provider 这个四大组件都是必须设置 exported 属性,否则会报错
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
即使是Android Studio新建的项目,activity也要添加exported属性,否则编译错误。
3、Context.RECEIVER_EXPORTED 和Context.RECEIVER_NOT_EXPORTED作用?
从字面意思看:EXPORTED表示外部应用,NOT_EXPORTED表示非外部应用,即本应用内。
所以Context.RECEIVER_EXPORTED表示可以接收外部的广播,
Context.RECEIVER_NOT_EXPORTED表示只能接收应用内的广播?
从目前实际测试效果看,并非如此。
测试系统自动广播和app自定义广播,demo示例接收广播,Action代码:
IntentFilter mFilter = new IntentFilter();
mFilter.addAction("action.mytest"); //自定义action
mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);//网络变化广播
mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //wifi开关广播
mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(mReceiver1, mFilter, Context.RECEIVER_EXPORTED);
监听广播不管设置Context.RECEIVER_EXPORTED还是Context.RECEIVER_NOT_EXPORTED,
都是可以接收到网络变化的广播和其他应用自定义发出的广播。
所以从实际来看:
Context.RECEIVER_EXPORTED和Context.RECEIVER_NOT_EXPORTED其实没啥区别,效果是一样的。
也就是说 Context.RECEIVER_NOT_EXPORTED 限制只接受应用内广播,并不会起作用。
但是最好还是都设置成 Context.RECEIVER_EXPORTED ,因为我看了 ActivityManagerService.java 的源码,
发现 Context.RECEIVER_NOT_EXPORTED 的情况有更多判断,甚至有抛出异常的可能!
Android 14 ActivityManagerService.java:
public class ActivityManagerService extends IActivityManager.Stub {
...
public Intent registerReceiverWithFeature(...IntentFilter filter) {
enforceNotIsolatedCaller("registerReceiver");
...
}
/* package */ void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) { //Isolated 表示远程的,外部的
throw new SecurityException("Isolated process not allowed to call " + caller);
}
}
}
从目前情况看,并不会抛出这个异常(外部应用不允许监听)。
Android13 ActivityManagerService.java代码中,
设置了Context.RECEIVER_NOT_EXPORTED 才会进入判断 enforceNotIsolatedCaller :
if ((flags & Context.RECEIVER_NOT_EXPORTED) != 0) {
enforceNotIsolatedCaller("registerReceiver");
}
其实上面代码只是分析了Android14报错,但是并没有分析Android13 的代码为啥不会报错,
具体是哪里的差异,在源码中并未看到,有兴趣的同学自己可以进行研究看看。
4、ActivityManagerService.java 的源码
Android13 ActivityManagerService.java:
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
Android14 ActivityManagerService.java:
http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
Android 系统其他代码也可以使用网址查看:http://aospxref.com 。