non-protected broadcast场景分析及解决

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 app
                break;
            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,进程会抛出SecurityException
            if (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 shell
            return;
        }

        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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/24345.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

`JOB`的正确打开方式

文章目录 JOB的正确打开方式 简介工作原理使用场景使用方式注意事项启动JOB失败的情况JOB正确打开方式错误方式正确方式进阶方式终极方式 总结 JOB的正确打开方式 最近有一些小伙伴在使用JOB时,由于使用不当,引起一些问题。例如把license占满&#xff0c…

操作系统第四章——文件管理(下)

竹本无心,却节外生枝,藕却有孔,但出淤泥而不染,人生如梦,却却不随人愿,万般皆是命,半点不由人 文章目录 4.1.5 逻辑结构VS物理结构4.1.6 文件的基本操作知识总览创建文件删除文件打开文件关闭文…

【弹性分布式EMA】在智能电网中DoS攻击和虚假数据注入攻击(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease.pdf 目录 1 简介 越来越多的设备,如移动设备、可穿戴设备或其他 IoT(物联网)设备现在正在使用焊接安全元件 (SE)。 这产生了支持 SPI 或 I2C 等物理接口的新需求,以代替以前…

Java 反序列化漏洞

反序列化漏洞是指程序在反序列化期间,通过特殊的调用链引发的一系列安全问题。编程语言中只要存在序列化,反序列化功能就可能存在反序列化的安全问题。这里只针对Java和PHP进行讨论。 序列化漏洞概述 序列化的存在主要是为了存储和传输,将这…

如何设置工业设备的振动监测阈值

工业设备的振动阈值设置是确保设备正常运行和及时维护的关键步骤。本文将介绍一些常见的方法和策略,帮助您正确设置工业设备的振动阈值。 1. ISO 10816 振动烈度表格: ISO 10816 是一项国际标准,提供了设备振动水平的参考值。该标准将设备按…

【移动计算技术(Android)】期末复习

目录 选择题 选择题知识点汇总 Activity Intent Broadcast BroadcastReceiver 如何自定义Receiver 如何注册接收器 Service SharedPreferences 三种访问模式 如何创建 如何存储/修改 如何读取 内部存储 openFileOutput openFileInput SD卡 资源文件 SQLite…

Java学习路线(13)——Collection集合类:List集合与Set集合

一、集合类体系结构 二、部分Collection类型对象 Collection集合特点 List系列集合是有序、可重复、有索引。 ArrayList:有序、可重复、有索引LinkedList:有序、可重复、有索引 Set系列集合是无序、不重复、无索引。 HashSet:无序、不重复…

下载YouTube视频的一种方法

文章目录 工具名称下载方法使用方法1.只下载音频2.下载音频转换成mp3(加上-x –audio-format参数)3.下载视频(带音频)ID:22 | EXT:mp4 | 1280*720 下载的数据集:YouCook2 工具名称 yt-dlp 下载…

ajax使用

说明:ajax是一门异步处理请求的技术;可以实现不重新加载整个页面的情况下,访问后台后服务;比如百度搜索引擎,输入关键字后,下面会实时出现待选项,这就是一种异步处理请求的技术。 原生Ajax 原…

chatgpt赋能python:Python中未定义变量的默认值

Python中未定义变量的默认值 在Python编程中,有时候我们会使用未经定义的变量。如果这些变量没有被定义,那么它们将没有任何值。在这篇文章中,我们将讨论Python中未定义变量默认值的问题,并深入研究为什么这些默认值如此重要。 …

如何保证三个线程按顺序执行?不会我教你

👨‍🎓作者:bug菌 ✏️博客:CSDN、掘金、infoQ、51CTO等 🎉简介:CSDN|阿里云|华为云|51CTO等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12…

linux内核内存管理slab

一、概述 linux内存管理核心是伙伴系统,slab,slub,slob是基于伙伴系统之上提供api,用于内核内存分配释放管理,适用于小内存(小于1页)分配与释放,当然大于1页…

基于html+css的图展示99

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

运动员最佳配对问题——算法设计与分析(C实现)

目录 一、问题简述 二、分析 三、代码展示 四、结果验证 一、问题简述 问题描述:羽毛球队有男女运动员各n人。给定2个n*n矩阵P和Q。P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞争优势;Q[i][j]是男运动员i和女运动员j配合的女运动员竞…

SSM框架学习-拦截器

1. 简介 在Spring框架中,拦截器是一种很重要的组件,它们允许在请求到达控制器之前或之后执行一些代码。拦截器在请求处理的特定点进行拦截,然后通过逻辑来决定是否将控制器的处理传递给下一个处理程序。 在Spring中,拦截器是由实现…

KVM虚拟化技术学习-基础入门

1.虚拟化技术概述 虚拟化[Virtualization]技术最早出现在 20 世纪 60 年代的 IBM ⼤型机系统,在70年代的 System 370 系列中逐渐流⾏起来,这些机器通过⼀种叫虚拟机监控器[Virtual Machine Monitor,VMM]的程序 在物理硬件之上⽣成许多可以运⾏…

Codeforces Round 764 (Div. 3)

比赛链接 Codeforces Round 764 A. Plus One on the SubsetB. Make APC. Division by Two and PermutationD. Palindromes ColoringE. Masha-forgetful A. Plus One on the Subset Example input 3 6 3 4 2 4 1 2 3 1000 1002 998 2 12 11output 3 4 1题意: 你可…

计算机网络考试多选题汇总Ⅱ

https://cadyin.blog.csdn.nethttps://blog.csdn.net/qq_38639612?spm1010.2135.3001.5421 计算机网络考试多选题汇总 1、在Windows中,任务管理器的作用是() A.终止未响应的应用程序 B.终止进程的运行 C.查看系统当前的信息 …

车载网络测试 - CANCANFD - 基础篇_01

目录 问题思考: 一、为什么需要总线? 二、什么是CAN总线? 三、为什么是CAN总线? 四、曾经的车用总线 1、SAEJ1850(Class2) 2、SAEJ1708 3、K-Line 4、BEAN 5、 byteflight, K-Bus 6、D2B 五、当前的车用总线 1、CAN 2、LIN 3、FlexRay 4、MOST 六…