non-protected broadcast场景分析及解决

news/2025/2/14 6:17:01/

non-protected broadcast场景分析及解决

在两个app之间互相送消息使用BroadcastReceiver,有时在运行过程中在logcat工具中会发现大片的飘红消息。

要消除这些错误信息,需要在广播的 SenderReceiver 做部分的修改。

错误信息分析

由于 发送端 的 Manifest 文件中使用了android:sharedUserId=“android.uid.system”

android:sharedUserId

​ 多个app共享的一个Linux用户ID名。Android默认情况下会为每个app分配一个唯一的用户ID。而若在多个app中设置这个属性值为同一值,那么这些app共享同一个用户ID,前提是app的证书相同。有相同用户ID的apps可以互相访问彼此的数据,如果需要,他们可以运行在同一进程中。

这个属性在API 29时过时。

sharedUserId在 PackageManager 会引起不确定的行为。基于此,不鼓励sharedUserId的使用并且这个属性在未来Android的某个版本会被移除。相应地,使用适合的通信机制,例如 Service 和 ContentProvider,促使共享组件的互操作性。已经存在的apps不能直接移除这个值。在这些应用程序中,添加android:sharedUserMaxSdkVersion=“32”,以避免在新用户安装时使用共享用户ID。

发送端 设置了值 android.uid.system,在app发送广播时,会调用到 ActivityManagerService 方法 broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId)

—> broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList)

在方法内针对个参数情况进行判断。

“android.uid.system” 对应与 Process.SYSTEM_UID,可以在 PackageManagerService 构造方法中查看对应关系。

   // ...   mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 方法签名: SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) // ...

在这个方法里,可以看到一段代码。

// ...final boolean isCallerSystem;switch (UserHandle.getAppId(callingUid)) {case ROOT_UID:case SYSTEM_UID: // android.uid.system 值,可以在PackageManagerService构造方法中查看映射关系case PHONE_UID:case BLUETOOTH_UID:case NFC_UID:case SE_UID:case NETWORK_STACK_UID:isCallerSystem = true; // 发送的app使system appbreak;default:isCallerSystem = (callerApp != null) && callerApp.isPersistent();break;}// First line security check before anything else: stop non-system apps from// sending protected broadcasts.if (!isCallerSystem) { // 若不是 system app,而发送的是protected broadcast,进程会抛出SecurityExceptionif (isProtectedBroadcast) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " from pid="+ callingPid + ", uid=" + callingUid;Slog.w(TAG, msg);throw new SecurityException(msg);} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {// Special case for compatibility: we don't want apps to send this,// but historically it has not been protected and apps may be using it// to poke their own app widget.  So, instead of making it protected,// just limit it to the caller.if (callerPackage == null) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " from unknown caller.";Slog.w(TAG, msg);throw new SecurityException(msg);} else if (intent.getComponent() != null) {// They are good enough to send to an explicit component...  verify// it is being sent to the calling app.if (!intent.getComponent().getPackageName().equals(callerPackage)) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " to "+ intent.getComponent().getPackageName() + " from "+ callerPackage;Slog.w(TAG, msg);throw new SecurityException(msg);}} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}// ...if (isCallerSystem) { // System app会检查// 调用到这个方法内,就可以看到最后在logcat中看到的 non-protected broadcast 字样的信息checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,isProtectedBroadcast, receivers);}

看下具体的 checkBroadcastFromSystem()方法。

    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {// Don't yell about broadcasts sent via shellreturn;}final String action = intent.getAction();if (isProtectedBroadcast // 是否是定义的protected broadcast|| Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)|| Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)|| Intent.ACTION_MEDIA_BUTTON.equals(action)|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)|| Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)|| Intent.ACTION_MASTER_CLEAR.equals(action)|| Intent.ACTION_FACTORY_RESET.equals(action)|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)|| TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)|| SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)|| AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)|| AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {// Broadcast is either protected, or it's a public action that// we've relaxed, so it's fine for system internals to send.return;}// This broadcast may be a problem...  but there are often system components that// want to send an internal broadcast to themselves, which is annoying to have to// explicitly list each action as a protected broadcast, so we will check for that// one safe case and allow it: an explicit broadcast, only being received by something// that has protected itself.if (intent.getPackage() != null || intent.getComponent() != null) { // 基于package和组件判断if (receivers == null || receivers.size() == 0) {// Intent is explicit and there's no receivers.// This happens, e.g. , when a system component sends a broadcast to// its own runtime receiver, and there's no manifest receivers for it,// because this method is called twice for each broadcast,// for runtime receivers and manifest receivers and the later check would find// no receivers.return;}boolean allProtected = true;for (int i = receivers.size()-1; i >= 0; i--) {Object target = receivers.get(i);if (target instanceof ResolveInfo) { // 从Manifest解析获得ResolveInfo对象ResolveInfo ri = (ResolveInfo)target;if (ri.activityInfo.exported && ri.activityInfo.permission == null) {allProtected = false;break;}} else {BroadcastFilter bf = (BroadcastFilter)target; // 基于IntentFilter的解析if (bf.exported && bf.requiredPermission == null) {allProtected = false;break;}}}if (allProtected) {// All safe!return;}}// The vast majority of broadcasts sent from system internals// should be protected to avoid security holes, so yell loudly// to ensure we examine these cases.if (callerApp != null) { // 在判断 allProtected 不能是 true 值下,打印错误信息。Log.wtf(TAG, "Sending non-protected broadcast " + action+ " from system " + callerApp.toShortString() + " pkg " + callerPackage,new Throwable());} else {Log.wtf(TAG, "Sending non-protected broadcast " + action+ " from system uid " + UserHandle.formatUid(callingUid)+ " pkg " + callerPackage,new Throwable());}}

至此,可以看到打印错误信息的位置。

解决方法

打印 "Sending non-protected broadcast " 信息的错误,主要在于Broadcast注册方式及exportedpermission这两个值。对于非系统预定义的protected broadcasts,或个别定义的broadcats,必须有eported ,permission值。

Sender 端:

在Manifest中定义permission,使用 sendBroadcast(Intent intent, String receiverPermission) 方法。

Receiver 端:

在注册receiver时,使用 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) 带有permission参数的方法,参数 broadcastPermission 即是在 Sender 端定义的permission。


http://www.ppmy.cn/news/78875.html

相关文章

基于单片机的家用应急电源设计

摘要 本设计基于STC89C52单片机设计得应急电源,以应急电源为研究对象,单片机设计为控制集成IC,ADC为模数转换控制模块,无源蜂鸣器作为报警电路。系统分为单片机设计最小系统,AD转换控制模块,电源电路&#…

PythonOCR识别扫描版纯图PDF提取汉字的10大方法,力推RapidOCRPDF 可识别纯图PDF 加密签名的PDF 重点是开源免费,某些方面准确度比百度OCR高

下面实例都以下面的测试样例PDF为实验对象 非纯图可复制pdf 纯图PDF TOP1:RapidOCRPDF 可识别纯图PDF也能识别加密签名的PDF 重点是开源免费 https://github.com/RapidAI/RapidOCRPDF # 基于rapidocr_onnxruntime pip install rapidocr_pdf[onnxruntime]# 基于ra…

跟我学c++高级篇——反射的基本实现方式

一、c中的反射基本实现 综合反射在各种高级语言中的应用以及前面分析过的反射的原理,就可以明白,一种概念或者说技术在某种语言中是否拥有,更准确的其实是说是否原生拥有。毕竟,没有可以通过某种技术或者手段模拟出来&#xff0c…

IEEE模板使用注意事项

关于单栏双栏 注意区分用的模板是 {IEEEtran} 还是期刊自己的如 {IEEEtaes} 如果是后者,可能不支持通过直接在文档开头加一行代码直接转单栏。 关于公式 When referring to an equation or formula, use simply “(1),” not “Eq. (1)” or “equation (1),” e…

hadoop3.2.4集成flink 1.17.0

前言 flink安装部署有三种方式 local:单机模式,尽量不使用 standalone: flink自带集群,资源管理由flink集群管理,开发环境测试使用,不需要hadoop集群 flink on yarn: 把资源管理交给yarn实现,计算机资源统一由Haoop…

MQTT中间件Eclipse Mosquitto安装和使用(.asc文件)MQTT监控命令mosquitto_sub(mosquitto C++库源码编译)

昨天弄的,今天忘了不少。。。 文章目录 参考链接安装MQTT服务中间件安装启动与查询卸载与清理 MQTT C支持库安装(使C能使用相关库函数)离线安装(通过源码)ubuntu官网下载软件包编译mosquitto客户端库 mosquitto Docker…

GameFramework框架

官网 Game Framework | 基于 Unity 引擎的游戏框架 介绍 对常用模块封装,规范开发过程,保证产品质量,内置19个模块。 组成 框架主要分两部分:GameFramework(简称GF),UnityGameFramework(简称UGF&#xff09…

奋斗,然后成功:我的架构狮之梦

与代码结缘 2018年,当时听说了一个很厉害的人——吴瀚清老师,也就是大家所熟知的“道哥”。关于他的事情有很多传说,于是我也很快成为了他的小迷弟,把吴瀚清老师当成了自己的偶像。 也是那一年,我买了人生中第一本关…