背景

今天在NotificationManagerService中去调用PendingIntent.getIntent()时发现了下面这个报错,其中pid=4901, uid=10097分别是来push平台的进程id和应用id,而PendingIntent.getIntent()这个API是@hide的,所以普通应用是无法调用的,反射也调用不到,所以后面给出的解法是针对具有系统权限的开发者的。

W System.err: java.lang.SecurityException: Permission Denial: getIntentForIntentSender() from pid=4901, uid=10097 requires android.permission.GET_INTENT_SENDER_INTENT

W System.err: at com.android.server.am.ActivityManagerService.enforceCallingPermission(ActivityManagerService.java:6291)

W System.err: at com.android.server.am.ActivityManagerService.getIntentForIntentSender(ActivityManagerService.java:5855)

W System.err: at android.app.PendingIntent.getIntent(PendingIntent.java:1135)

W System.err: at com.android.server.notification.xxx.callReplyIntent(xxx.java:64)

W System.err: at com.android.server.notification.NotificationManagerService.checkDisqualifyingFeatures(NotificationManagerService.java:5569)

W System.err: at com.android.server.notification.NotificationManagerService.enqueueNotificationInternal(NotificationManagerService.java:5193)

W System.err: at com.android.server.notification.NotificationManagerService$10.enqueueNotificationWithTag(NotificationManagerService.java:2609)

W System.err: at android.app.INotificationManager$Stub.onTransact(INotificationManager.java:1149)

W System.err: at android.os.Binder.execTransactInternal(Binder.java:1027)

W System.err: at android.os.Binder.execTransact(Binder.java:1000)

复制代码

分析

看报错的调用栈能发现是在AMS中做权限检查(ActivityManagerService.enforceCallingPermission())的时候抛的异常:

// 抛异常的代码

void enforceCallingPermission(String permission, String func) {

if (checkCallingPermission(permission)

== PackageManager.PERMISSION_GRANTED) {

return;

}

String msg = "Permission Denial: " + func + " from pid="

+ Binder.getCallingPid()

+ ", uid=" + Binder.getCallingUid()

+ " requires " + permission;

Slog.w(TAG, msg);

throw new SecurityException(msg);

}

// checkCallingPermission(),往下走就是去检查该用户是否具有android.permission.GET_INTENT_SENDER_INTENT 这个权限了

int checkCallingPermission(String permission) {

return checkPermission(permission,

Binder.getCallingPid(),

Binder.getCallingUid());

}

复制代码

可以看到抛异常是因为checkCallingPermission函数返回false了,也就是调用方push平台没有权限去调这个接口,那为什么我们明明是在NotificationManagerService(这是一个系统服务,管理通知的发送和删除等事务)中去调用这个接口的,这里却判断出来是来自push平台的呢,这里我们需要了解两个Binder相关的概念。

Binder.getCallingPid() 与 Binder.getCallingUid()

每个线程都有一个唯一的IPCThreadState对象记录着当前线程的pid和uid,而这两个id的获取就是通过Binder.getCallingPid() 与 Binder.getCallingUid()这两个方法来获取的。

当线程A通过Binder调用线程B的接口时,B线程的IPCThreadState中保存的uid和pid是线程A的uid和pid,一般我们调用这两个接口来获取线程A的id值来做权限对比。

而此时在线程B中,B是当前进程中的调用方了,若此时线程B通过Binder去调用其他方法,而其他方法又通过这两个方法去获取对应的uid和pid去做权限对比时,获取到的值就还是线程A的,所以我们需要在B通过Binder去调用其他方法前,将线程B中保存的uid和pid做一遍刷新,对于这个Binder提供了另外两个接口:

Binder.clearCallingIdentity() 与 Binder.restoreCallingIdentity(id)

long id = Binder.clearCallingIdentity()

Binder.restoreCallingIdentity(id)

其中Binder.clearCallingIdentity()会帮我们去清除当前线程中的id状态,并将结果返回给我们;而当我们执行完Binder操作后,我们需要恢复线程B中的id值,此时就需要调用Binder.restoreCallingIdentity(id)去执行这个操作。

了解完这两个概念,我们再来看看原来的问题,前面我们说过,这个接口是@hide的,只有具有系统权限的应用才能调用,那么问题就转化为,我们在这里怎么以系统的身份去调这个接口呢?这里就需要用到我们上面说的几个接口了,直接看代码吧:

final long token = Binder.clearCallingIdentity();

Intent intent;

try {

intent = pendingIntent.getIntent();

} finally {

Binder.restoreCallingIdentity(token);

}

复制代码

代码很简单,也就是在调用前利用Binder.clearCallingIdentity()将线程状态清除掉,在调用结束时利用Binder.restoreCallingIdentity(token)将线程状态恢复,这样就实现了将原来来自push平台的调用转化为NotificationManagerService的调用,而NotificationManagerService是系统服务,就可以顺利调用到了。

总结

其实这个问题是Android在7.0以上的系统中新增了相关的权限判断后才出现的(感兴趣的可以看下这笔提交),在这之后普通应用就无法调用该接口了,想要调用PendingIntent.getIntent()就必须拥有系统签名,或者像上面这个例子一样,通过Binder提供的接口,巧妙的将原来权限不足的应用的权限升级为系统权限。

本文主要想分享下上文出现的几个Binder相关函数的意义和使用场景,That'all

android intent sender,Android7.0以上调PendingIntent.getIntent()报错相关推荐

  1. android线程改变布局,Android线程中设置控件的值提示报错的解决方法

    本文实例讲述了Android线程中设置控件的值提示报错的解决方法.分享给大家供大家参考,具体如下: 在Android线程中设置控件的值一般会与Handler联合使用,如下: package com.y ...

  2. webview在android8.0,解决Android8.0系统应用打开webView报错

    由于webView存在安全漏洞,谷歌从5.1开始全面禁止系统应用使用webview,使用会导致应用崩溃错误提示:Caused by: java.lang.UnsupportedOperationExc ...

  3. tensorflow1.14.0安装不上,报错

    pip install tensorflow==1.14.0 -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow1.14.0安装不上,报错 M ...

  4. Redis 5.0.3默认配置启动报错解决方法

    Redis 5.0.3默认配置启动报错解决方法 参考文章: (1)Redis 5.0.3默认配置启动报错解决方法 (2)https://www.cnblogs.com/miclesvic/p/1031 ...

  5. MyEclipse导入jquery-1.8.0.min.js等文件报错的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> MyEclipse导入jquery-1.8.0.min.js等文件报错的解决方案 MyEclipse导入jquery-1.8. ...

  6. Android之Android Studio三种方式导入外部资源 以及 报错处理

    Android Studio三种方式导入外部资源 以及 报错处理 android studio提供了三种方式导入外部资源: 1. Library dependency – 在线添加.需联网下载 . 2 ...

  7. mysql 8.0.21 对用户授权报错 near ‘IDENTIFIED BY ‘123456‘ with grant option‘ at line 1

    mysql 8.0.21 对用户授权报错 near 'IDENTIFIED BY '123456' with grant option' at line 1 问题描述 mysql在创建用户,对用户授权 ...

  8. Android Studio 数据库可视化工具(Database Inspector)报错

    Android Studio 数据库可视化工具(Database Inspector)报错 使用这个工具的时候报了错 ,(诸位还有可能是好奇点了一下),然后,就报错了 提示如下: Database I ...

  9. 安装Saas芸众商城系统后提示:{“result“:0,“msg“:““,“data“:{“status“:-4}}报错的原因

    安装Saas芸众商城系统至尊版所遇到的坑我想你也踩过,总结出来! 安装宝塔,本文是用的是lamp,可以酌情考虑. 放入代码和数据库. php使用7.4版本,安装 ionCube, fileinfo, ...

最新文章

  1. asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程...
  2. OpenCV学习笔记之Mat
  3. 消息队列之RabbitMQ
  4. Apache Spark 1.5发布,新特性一览
  5. 换位思考的最高境界是换待遇,所以,换位思考就是空话!!!
  6. ProjectEuler 2
  7. Python基础-2 专用下划线标识符
  8. CSS快速学习10:高度自适应
  9. oracle关闭多余游标,关闭结果集后,Oracle不删除游标
  10. tcpdump 在ubuntu上使用命令学习笔记 /tcp、ip、icmp数据包格式
  11. 如何下载行政区划地图
  12. 大疆无人机无图像传输_无人机短距离图像传输与接收原理
  13. PPT修改母版页码格式后不管用?
  14. no zuo no die
  15. NLP-D23-cs224nkaggle房价预测复习chap5深度学习计算算法R2D7Unicorn
  16. w7 声音图标不见了
  17. android获取手机的国家码
  18. 数据扒一扒《隐秘的角落》到底怎么火的?
  19. gnuplot画双纵坐标图
  20. Java8函数式接口与Lambda表达式

热门文章

  1. 这里面有一些关于依图工作的内容
  2. iOS音频播放 (一):概述 转
  3. JFreechart 在linux下不显示及中文乱码问题
  4. c/c++面试题(1)
  5. 对Linux的cp命令的思考
  6. python 初步学习
  7. 在远程桌面连接中使用命令行参数
  8. CRC冗余校验码的介绍和实现
  9. CISCO安全 ×××技术
  10. CSS3制作3D水晶糖果按钮