Android受限广播保护机制

广播(Broadcast)作为消息传递的一种方式,在Android系统中有着广泛的应用。系统提供了一系列的广播发送、接收接口,可以非常方便的实现广播的传递。系统中相当部分的状态变化也都是通过广播的方式通知到应用的,例如:系统启动完成(Intent.ACTION_BOOT_COMPLETED),电池状态变化(Intent.ACTION_BATTERY_CHANGED),网络连接状态变化(Intent.CONNECTIVITY_ACTION)等。

        但同时也引发一个问题,如果任意应用都能随意发送此类广播,势必会引起系统状态的混乱,因此系统一定会有一套机制来保护此类所谓的受限广播。以下以Intent.ACTION_SCREEN_ON为例,深入探究下Android的受限广播保护机制。

?
1
2
3
4
5
6
7
8
/**
  * Broadcast Action: Sent after the screen turns on.
  *
  * <p class="note">This is a protected intent that can only be sent
  * by the system.
  */
@SdkConstant (SdkConstantType.BROADCAST_INTENT_ACTION)
public  static  final  String ACTION_SCREEN_ON =  "android.intent.action.SCREEN_ON" ;

        Intent.ACTION_SCREEN_ON为屏幕点亮后由系统电源管理服务(PowerManagerService)所发,试想如果任意应用都能随意发送这个广播,则容易被恶意应用利用,造成监听该广播的应用状态、逻辑混乱。因此该广播一定是所谓的受限广播,这点在注释中也得到证实。

This is a protected intent that can only be sent by the system.

?
1
2
3
4
Intent screenOnIntent =  new  Intent(Intent.ACTION_SCREEN_ON);
screenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
context.sendOrderedBroadcastAsUser(screenIntent, UserHandle.ALL,  null ,
         receiver, handler,  0 null null );

        系统中发送该广播的代码如上,当然Android中还开放了如下非常丰富的发送广播的接口。这些方法有一个共同点,都调用了ActivityManagerService中的broadcastIntent接口,而这些开放出来的不同的发送广播接口,只是在调用broadcastIntent时传递的参数有所差异,如 ordered  sticky 等。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public  void  sendBroadcast(Intent intent);
public  void  sendBroadcast(Intent intent,
             String receiverPermission);
public  void  sendOrderedBroadcast(Intent intent,
             String receiverPermission);
public  void  sendOrderedBroadcast(Intent intent,
             String receiverPermission, BroadcastReceiver resultReceiver,
             Handler scheduler,  int  initialCode, String initialData,
             Bundle initialExtras);
public  void  sendBroadcastAsUser(Intent intent, UserHandle user);
public  void  sendBroadcastAsUser(Intent intent, UserHandle user,
             String receiverPermission);
public  void  sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
             String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
             int  initialCode, String initialData, Bundle initialExtras);
public  void  sendStickyBroadcast(Intent intent);
public  void  sendStickyOrderedBroadcast(Intent intent,
             BroadcastReceiver resultReceiver,
             Handler scheduler,  int  initialCode, String initialData,
             Bundle initialExtras);
public  void  sendStickyBroadcastAsUser(Intent intent, UserHandle user);
public  void  sendStickyOrderedBroadcastAsUser(Intent intent,
             UserHandle user, BroadcastReceiver resultReceiver,
             Handler scheduler,  int  initialCode, String initialData,
             Bundle initialExtras);

        在ActivityManagerService的broadcastIntent方法中,首先是对Intent的flags做了一些校验,不是本文讨论范围之内,紧接着便是调用核心的broadcastIntentLocked方法,我们看到,受限保护的检验正是在这里做的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private  final  int  broadcastIntentLocked(ProcessRecord callerApp,
         String callerPackage, Intent intent, String resolvedType,
         IIntentReceiver resultTo,  int  resultCode, String resultData,
         Bundle map, String requiredPermission,
         boolean  ordered,  boolean  sticky,  int  callingPid,  int  callingUid,
         int  userId) {
     ......
     /*
      * Prevent non-system code (defined here to be non-persistent
      * processes) from sending protected broadcasts.
      */
     int  callingAppId = UserHandle.getAppId(callingUid);
     if  (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
         || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID ||
         callingUid ==  0 ) {
         // Always okay.
     else  if  (callerApp ==  null  || !callerApp.persistent) {
         try  {
             if  (AppGlobals.getPackageManager().isProtectedBroadcast(
                     intent.getAction())) {
                 String msg =  "Permission Denial: not allowed to send broadcast "
                         + intent.getAction() +  " from pid="
                         + callingPid +  ", uid="  + callingUid;
                 Slog.w(TAG, msg);
                 throw  new  SecurityException(msg);
             }
         catch  (RemoteException e) {
             Slog.w(TAG,  "Remote exception" , e);
             return  ActivityManager.BROADCAST_SUCCESS;
         }
     }
 
     ......        
}

        在以上这段代码中,首先查看调用者的UID,如果是  ROOT:0   SYSTEM_UID:1000   PHONE_UID:1001   BLUETOOTH_UID:1002   SHELL_UID:2000  其中之一,则默认拥有发送受限广播权限,跳过校验过程。否则调用AppGlobals.getPackageManager().isProtectedBroadcast()判断是否为受限广播,如是则抛出权限异常,终止广播流程。

        至此,受限广播校验流程基本清晰,那么问题来了,AppGlobals.getPackageManager().isProtectedBroadcast()是以什么依据来判断是否受限广播呢?继续从代码中找答案。

?
1
2
3
4
5
public  boolean  isProtectedBroadcast(String actionName) {
     synchronized  (mPackages) {
         return  mProtectedBroadcasts.contains(actionName);
     }
}

        在PackageManagerService中维护了一个散列表mProtectedBroadcasts,用以标识哪些广播是受限的,而AppGlobals.getPackageManager().isProtectedBoradcast()所做的仅仅查询所发广播是否在这个列表中。继续跟谁在维护这个受限广播表,发现只有PackageManagerService.scanPackageLI会往受限表中添加元素,而添加的元素则来自pkg.protectedBroadcasts。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private  PackageParser.Package scanPackageLI(PackageParser.Package pkg,
         int  parseFlags,  int  scanMode,  long  currentTime, UserHandle user) {
     ......
     synchronized  (mPackages) {
         ......
         if  (pkg.protectedBroadcasts !=  null ) {
             N = pkg.protectedBroadcasts.size();
             for  (i= 0 ; i<N; i++) {
                 mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
             }
         }
         ......
     }
 
     return  pkg;
}

        需要说明的是,scanPackageLI会在以下几种情况触发:构造PackageManagerService(扫描apks)、安装全新应用、安装应用更新、卸载系统应用、SD卡Mount/Unmount、APP目录(Framework/System/Vendor)新增文件。scanpackageLI被触发后,会将pkg.protectedcasts中的元素取出,添加到受限表中,由于HashSet的唯一性,不用担心重复添加的问题。因此判断是否受限广播的关键,落在了pkg.protectedBroadcasts上。

        protectedBroadcasts是Package类中维护的列表,元素由PackageParser在解析apk时添加。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private  Package parsePackage(
     Resources res, XmlResourceParser parser,  int  flags, String[] outError)
     throws  XmlPullParserException, IOException {
     
     while  ((type = parser.next()) != XmlPullParser.END_DOCUMENT
             && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
 
         String tagName = parser.getName();
         ......
         else  if  (tagName.equals( "protected-broadcast" )) {
             sa = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
 
             // Note: don't allow this value to be a reference to a resource
             // that may change.
             String name = sa.getNonResourceString(
                     com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
 
             sa.recycle();
 
             if  (name !=  null  && (flags&PARSE_IS_SYSTEM) !=  0 ) {
                 if  (pkg.protectedBroadcasts ==  null ) {
                     pkg.protectedBroadcasts =  new  ArrayList<String>();
                 }
                 if  (!pkg.protectedBroadcasts.contains(name)) {
                     pkg.protectedBroadcasts.add(name.intern());
                 }
             }
 
             XmlUtils.skipCurrentTag(parser);
             
         }
     }
     ......
     return  pkg;
}

        从PackageParser.parsePackage方法中可以清楚的看到,pkg.protectedBroadcasts对应AndroidManifest.xml中的 protected-broadcast 标签。回到本文开头的Intent.ACTION_SCREEN_ON广播,我们可以在frameworks/base/core/res/AndroidManifest.xml中找到,对应的package为system/framework/framework-res.apk

?
1
2
3
4
5
6
7
8
9
<? xml  version = "1.0"  encoding = "utf-8" ?>
 
< manifest  xmlns:android = "http://schemas.android.com/apk/res/android"
     package = "android"  coreApp = "true"  android:sharedUserId = "android.uid.system"
     android:sharedUserLabel = "@string/android_system_label" >
     ......
     < protected-broadcast  android:name = "android.intent.action.SCREEN_ON"  />
     ......
</ manifest >

        至此,我们将受限广播的声明、解析、校验过程全部了解完了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值