protected-broadcast 规范使用(ERROR: Sending non-protected broadcast)

本文详细解析了Android中Protected Broadcast的使用规范,包括其发送条件、系统广播要求及普通广播的保护措施等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章出处:https://blog.youkuaiyun.com/shift_wwx/article/details/82350455

请转载的朋友标明出处,请支持原创~~

相关博文:

Android基础总结之五:BroadcastReceiver

Android 中broadcast 注册过程解析

Android 中broadcast 发送过程解析

protected-broadcast 规范使用(ERROR: Sending non-protected broadcast)

先来看下log:

11-05 00:00:09.609   688  3933 W ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.sendBroadcast:966 com.android.server.pm.PackageManagerService.sendPermissionAccessBroadcast:5429 com.android.server.pm.PackageManagerService.checkUidPermission:5436 android.app.ActivityManager.checkUidPermission:3981 com.android.server.am.UidRecord.updateHasInternetPermission:113 
11-05 00:00:09.610   688  3933 E ActivityManager: Sending non-protected broadcast action_shift_permission_access_log from system 688:system/1000 pkg android
11-05 00:00:09.610   688  3933 E ActivityManager: java.lang.Throwable
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:19280)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:19885)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:20027)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at android.app.ContextImpl.sendBroadcast(ContextImpl.java:970)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.pm.PackageManagerService.sendPermissionAccessBroadcast(PackageManagerService.java:5429)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.pm.PackageManagerService.checkUidPermission(PackageManagerService.java:5436)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at android.app.ActivityManager.checkUidPermission(ActivityManager.java:3981)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.UidRecord.updateHasInternetPermission(UidRecord.java:113)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.addProcessNameLocked(ActivityManagerService.java:6910)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.newProcessRecordLocked(ActivityManagerService.java:12517)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3791)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.startProcessLocked(ActivityManagerService.java:3706)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.BroadcastQueue.processNextBroadcast(BroadcastQueue.java:1359)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.finishReceiver(ActivityManagerService.java:20131)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:283)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2971)
11-05 00:00:09.610   688  3933 E ActivityManager: 	at android.os.Binder.execTransact(Binder.java:697)

这是我在一次开发中出现的,系统中需要发送一个应用自定义的广播,send 之后会报出Sending non-protected broadcast 的异常。

借此机会来解析protected broadcast 的使用,我们在 Android 中broadcast 发送过程解析 中了解了broadcast 发送的整个过程,通过Context 的接口最终会调用到AMS 中broadcastIntent()。

1. protected broadcast 必须要特殊uid

        if (!isCallerSystem) {
            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);

            }

这段code 比较简单,如果isProtectedBroadcast 为true,即该广播为protected broadcast,那么变量isCallerSystem 不能为false。即,要求如果是protected broadcast 必须要特殊的uid,不然会丢出SecurityException,这特殊uid 指的是:

        switch (UserHandle.getAppId(callingUid)) {
            case ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.persistent;
                break;
        }

ROOT_UID、SYSTEM_UID、PHONE_UID、BLUETOOTH_UID、NFC_UID 或者是persistent 的app都可以让变量isCallerSystem 为true,躲开这个SecurityException。

2. 如果通过系统发送广播,广播必须是protected broadcast

            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
    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
                || 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)
                || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
                || TelephonyIntents.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 (receivers != null && receivers.size() > 0
                && (intent.getPackage() != null || intent.getComponent() != null)) {
            boolean allProtected = true;
            for (int i = receivers.size()-1; i >= 0; i--) {
                Object target = receivers.get(i);
                if (target instanceof ResolveInfo) {
                    ResolveInfo ri = (ResolveInfo)target;
                    if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
                        allProtected = false;
                        break;
                    }
                } else {
                    BroadcastFilter bf = (BroadcastFilter)target;
                    if (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) {
            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());
        }
    }

通过code 可以看到这个标题不是很正确,条件要求该广播除了是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)
                || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
                || TelephonyIntents.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)

这里标题之所以这样标注,主要是为了突出protected(保护),指的是对于普通的广播进行的保护。

上面code 看到除了code 中指定的这些特殊的action,以及系统AndroidManifest.xml 中特殊规定的protected broadcast(详见framewors/base/core/res/AndroidManifest.xml):

    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />                             
    <protected-broadcast android:name="android.intent.action.SCREEN_ON" /> 

通过code 知道如果想不抛出 exception,那变量allProtected 必须为true 并返回,那对于普通的广播要求:

  • 必须指明package
        if (receivers != null && receivers.size() > 0
                && (intent.getPackage() != null || intent.getComponent() != null)) {

要求广播发送的package 必须指出来,或者直接将package 、class 做成component 。

  • 静态广播的exported为true的时候必须同时加上permission限制
        if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
            allProtected = false;
            break;
        }

要求exported 为true的时候,必须有permission 进行限制。

如果exported 为false,那当然不会有影响。

  • 动态广播的filter 中必须指定permission
        BroadcastFilter bf = (BroadcastFilter)target;
        if (bf.requiredPermission == null) {
            allProtected = false;
            break;
        }

综合,对于不是系统中指定的广播,需要要满足上面3个条件,这就是标题中说到的保护。

3. 总结

  • protected 广播必须是特殊uid 才能发送
  • 系统发送的广播如果是protected 广播,可以正常发送
  • 系统code 中的特殊广播,可以正常发送
  • 对于静态注册的普通广播,要求exported为true 的时候必须指定广播要求的permission进行限制
  • 对于动态注册的普通广播,要求广播必须指定permission
  • 对于普通广播,在满足上面3、4点要求的同时,该广播的package 或者component 必须指定

<think>嗯,用户问的是关于“Sending non-protected broadcast phoneincomingCall from system 1400:system/1000 pkg android”这条日志消息的信息。首先,我需要理解这条日志的具体含义,可能涉及Android系统的广播机制。 首先,分解日志的结构。消息中提到发送了一个非保护的广播,类型是phoneincomingCall,来源是系统UID 1400(system/1000),包名是android。非保护广播可能指的是没有设置权限的广播,这可能会带来安全风险,因为任何应用都可以接收它。 接下来,考虑可能的原因。系统应用发送广播时,如果没有声明适当的权限,就可能被归类为非保护。需要检查发送广播的代码,看看是否有添加权限。另外,Android版本升级可能改变了广播的保护策略,比如在较新的版本中某些广播需要权限。 然后,潜在的影响。恶意应用可能监听这类广播,获取敏感信息,比如来电状态,导致隐私泄露。还可能被用来触发其他恶意行为,比如在来电时启动攻击。 解决方案方面,建议开发者在使用sendBroadcast()时,使用带有权限的方法,比如sendBroadcast(Intent, String),并声明相应权限。同时,在接收端设置权限要求,确保只有授权应用能接收。另外,检查系统应用的代码,确认是否需要调整广播发送方式,或者改用更安全的通信方法,如直接调用组件或绑定服务。 最后,总结一下,这条日志提示系统发送了一个未受保护的广播,可能存在安全隐患,需要开发者采取权限保护措施来增强安全性。</think>你的问题涉及Android系统广播的安全机制,下面分步骤解释: 1. **日志含义解析** 这条日志表示:UID为1400的系统进程(Android系统核心组件)正在发送一个未受保护的`phoneincomingCall`广播。完整日志格式为: $$packageName (pid) is sending non-protected broadcast actionName from system uid UID$$ 2. **关键概念说明** - **非保护广播(Non-protected broadcast)**:指未通过`android:permission`属性声明发送权限的广播 - **UID 1400**:属于`system_server`进程(Android系统核心服务) - **pkg android**:广播发送方为系统框架层代码 3. **可能触发场景** - 系统电话服务检测到来电时,通过广播通知其他组件 - 广播发送代码未添加权限声明: ```java // 未保护的方式 context.sendBroadcast(new Intent("phoneincomingCall")); // 正确应声明权限 context.sendBroadcast(intent, "android.permission.PRIVILEGED"); ``` 4. **安全风险分析** - 信息泄露:任何应用(包括恶意应用)均可注册接收此广播 - 状态伪造:攻击者可能伪造来电状态触发异常行为 - 违反Android兼容性定义文档(CDD)要求:自Android 8.0起,敏感广播必须显式声明权限 5. **解决方案建议** (1) 发送方修正: ```xml <!-- AndroidManifest.xml中添加权限声明 --> <uses-permission android:name="android.permission.BROADCAST_PHONE_STATE"/> ``` (2) 接收方限制: ```xml <receiver android:name=".PhoneStateReceiver" android:permission="android.permission.READ_PRIVILEGED_PHONE_STATE"> <intent-filter> <action android:name="phoneincomingCall"/> </intent-filter> </receiver> ``` (3) 系统级优化: - 对于系统内部组件通信,建议改用`LocalBroadcastManager` - 关键状态通知建议改用`ContentObserver`或`Binder`直连 6. **适配建议** - 针对不同Android版本调整策略: - Android 9+:需使用`sendBroadcast(Intent, String)` - Android 13+:对`PHONE_STATE`广播实施更严格限制 - 使用`adb shell dumpsys package broadcasts`检查广播保护状态 这类日志提示系统需要加强广播安全防护,建议结合Android V2签名方案和权限白名单机制,确保敏感状态变更通知的安全性。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值