Android11 授权应用获取IMEI号和ICCID

2023-05-16

在Android11上获取IMEI号等设备信息需要android.permission.READ_PRIVILEGED_PHONE_STATE权限,而这个权限又只授予系统级应用。项目中如果targetSdkVersion值小于29获取到的是null,大于28报SecurityException错误。

1.获取ICCID
 public String getICCID(Context context) {
	TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
	String simSerialNumber = telephonyManager.getSimSerialNumber();
	return simSerialNumber;
 }

 或者

 public static String getICCID(Context context) {
    String iccid;
    TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    iccid = tm.getSimSerialNumber();
    if (iccid == null || iccid.length() < 20) {
        SubscriptionManager sm = SubscriptionManager.from(context);
        List<SubscriptionInfo> sis = sm.getActiveSubscriptionInfoList();
        if (sis.size() >= 1) {
            SubscriptionInfo si1 = sis.get(0); // 卡一
            iccid = si1.getIccId();
        }
    }
    return iccid;
 }
2.系统对应用的权限检查
  • 源码路径:frameworks/base/telephony/java/android/telephony/TelephonyManager.java
 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
 @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
 public String getSimSerialNumber() {
      return getSimSerialNumber(getSubId());
 }

 @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
 @UnsupportedAppUsage
 public String getSimSerialNumber(int subId) {
     try {
         IPhoneSubInfo info = getSubscriberInfoService();
         if (info == null)
             return null;
         return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
                 mContext.getAttributionTag());
     } catch (RemoteException ex) {
         return null;
     } catch (NullPointerException ex) {
         // This could happen before phone restarts due to crashing
         return null;
     }
 }

可以看到getSimSerialNumber()方法要求声明android.permission.READ_PRIVILEGED_PHONE_STATE权限
然后调用IPhoneSubInfo的getIccSerialNumberForSubscriber()方法

  • 源码路径:frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneSubInfoController.java
 public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
 	省略部分代码。。。
 	
 	public String getIccSerialNumberForSubscriber(int subId, String callingPackage,
            String callingFeatureId) {
    	return callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(subId, callingPackage,
                callingFeatureId, "getIccSerialNumber", (phone) -> phone.getIccSerialNumber());
    }
    
    省略部分代码。。。
    
 	private <T> T callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(int subId,
            String callingPackage, @Nullable String callingFeatureId, String message,
            CallPhoneMethodHelper<T> callMethodHelper) {
        // 调用Phone的相关方法,进行权限检查
        return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId,
                message, callMethodHelper,
                (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)->
                        TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(
                                aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage));
    }
 }

在TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers方法中判断是否有权限获取ICCID

  • 源码路径:frameworks/base/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
 public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
         String callingPackage, @Nullable String callingFeatureId, String message) {
     return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
              context, subId, callingPackage, callingFeatureId, message, false);
 }
 
 private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
         Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
         String message, boolean allowCarrierPrivilegeOnAnySub) {
     int uid = Binder.getCallingUid();
     int pid = Binder.getCallingPid();
     
     // 调用包是否具有运营商特权
     // If the calling package has carrier privileges for specified sub, then allow access.
     if (checkCarrierPrivilegeForSubId(context, subId)) return true;

     // If the calling package has carrier privileges for any subscription
     // and allowCarrierPrivilegeOnAnySub is set true, then allow access.
     if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId(context, uid)) {
         return true;
     }
        
     PermissionManager permissionManager = (PermissionManager) context.getSystemService(
             Context.PERMISSION_SERVICE);
     // 检查是否通过设备标识授权
     if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
             pid, uid) == PackageManager.PERMISSION_GRANTED) {
         return true;
     }

     return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
             message);
 }
 
 //当具有给定pid/uid的应用程序无法访问所请求的标识符时,报告失败
 private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
         int uid, String callingPackage, String message) {
     ApplicationInfo callingPackageInfo = null;
     
     省略部分代码。。。
     
     Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":"
             + subId);
     // if the target SDK is pre-Q then check if the calling package would have previously
     // had access to device identifiers.
     if (callingPackageInfo != null && (
             callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
         if (context.checkPermission(
                 android.Manifest.permission.READ_PHONE_STATE,
                 pid,
                 uid) == PackageManager.PERMISSION_GRANTED) {
             return false;
         }
         if (checkCarrierPrivilegeForSubId(context, subId)) {
             return false;
         }
     }
     throw new SecurityException(message + ": The user " + uid
             + " does not meet the requirements to access device identifiers.");
 }    

从上面代码可以看到,如果发起调用的应用信息不为空并且targetSdkVersion值小于29,且READ_PHONE_STATE权限已授予,就返回false。此时应用获取的iccid值为null不会报错;如果不满足这些条件直接抛出SecurityException异常。

所以在checkPrivilegedReadPermissionOrCarrierPrivilegePermission中直接根据包名授权。

 private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
         Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
         String message, boolean allowCarrierPrivilegeOnAnySub) {
     int uid = Binder.getCallingUid();
     int pid = Binder.getCallingPid();
     
     // If the calling package has carrier privileges for specified sub, then allow access.
     if (checkCarrierPrivilegeForSubId(context, subId)) return true;

     // If the calling package has carrier privileges for any subscription
     // and allowCarrierPrivilegeOnAnySub is set true, then allow access.
     if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId(context, uid)) {
         return true;
     }
        
     PermissionManager permissionManager = (PermissionManager) context.getSystemService(
             Context.PERMISSION_SERVICE);
     if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
             pid, uid) == PackageManager.PERMISSION_GRANTED) {
         return true;
     }
     
     // add start for skip permission check
     if (callingPackage != null && callingPackage.equals("com.xxx.xxx")) {
         Log.d(LOG_TAG, "com.xxx.xxx skip permission check of "+message);
         return true;
     }
     // add end

     return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
             message);
 }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android11 授权应用获取IMEI号和ICCID 的相关文章

随机推荐

  • 41(牛客Top100)-104.二叉树的最大深度

    给定一个二叉树 xff0c 找出其最大深度 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数 说明 叶子节点是指没有子节点的节点 思路 xff1a 方法1 xff1a 深度优先搜索 span class token keyword p
  • 42(牛客Top100)-102.二叉树的层序遍历

    给你一个二叉树 xff0c 请你返回其按 层序遍历 得到的节点值 xff08 即逐层地 xff0c 从左到右访问所有节点 xff09 思路 xff1a 用队列按层次遍历 方法1 xff1a 广度优先搜索 span class token k
  • MySQL总结

    1 数据库基础知识 1 1 什么是MySQL MySQL是一个关系型数据库管理系统 xff0c MySQL是最好的 RDBMS Relational Database Management System xff0c 关系数据库管理系统 应用
  • 45(牛客Top100)-121.买卖股票的最优时间

    给定一个数组 prices xff0c 它的第 i 个元素 prices i 表示一支给定股票第 i 天的价格 你只能选择 某一天 买入这只股票 xff0c 并选择在 未来的某一个不同的日子 卖出该股票 设计一个算法来计算你所能获取的最大利
  • 47(牛客Top100)-128.最长连续子序列

    给定一个未排序的整数数组 nums xff0c 找出数字连续的最长序列 xff08 不要求序列元素在原数组中连续 xff09 的长度 请你设计并实现时间复杂度为 O n 的算法解决此问题 思路 xff1a 方法1 xff1a 利用set集合
  • 48(牛客Top100)-136.只出现一次的数字

    给定一个非空整数数组 xff0c 除了某个元素只出现一次以外 xff0c 其余每个元素均出现两次 找出那个只出现了一次的元素 说明 xff1a 你的算法应该具有线性时间复杂度 你可以不使用额外空间来实现吗 xff1f 思路 xff1a 方法
  • stc-isp 51单片机烧录软件的安装

    stc isp的获取 百度网盘链接 xff1a https pan baidu com s 1vDTN2o8ffvczzNQGfyjHng 提取码 xff1a gdzf stc isp安装 此软件是绿色软件 xff0c 双击直接运行即可 开
  • 实习日记。。。

    第一周 第一天7 11 周一 入职第一天 xff0c 一直在数据库建表 xff0c 写了二十来个 xff0c 还有领了工卡和饭卡 xff0c 带我的老大哥请我恰了一顿 xff0c 晚上下班的时候 xff0c 因为舍不得我的电脑所以多待了一个
  • TCP/IP协议栈Lwip的设计与实现:之三

    接上文 xff1a TCP IP协议栈Lwip的设计与实现 xff1a 之二 龙赤子的博客 CSDN博客 目录 10 xff0e TCP处理 10 1概述 10 2数据结构 10 3序列号计算 10 4数据入队和传输 10 5接收段数据 1
  • c++——Unicode编码和多字节编码的区别

    1 VS项目属性不同字符集的区别 单字节字符集 xff1a 顾名思义 xff0c 单字节字符集就是用一个字节表示一个字符 xff0c 简称SBCS ASCII就是单字节字符集 在编码的过程中char类型就是单字节编码 Unicode字符集
  • 蓝桥杯 例题 切割矩形

    include lt bits stdc 43 43 h gt using namespace std int ans 61 0 int f int a int b ans 43 43 if a 61 61 1 amp amp b 61 6
  • c++——Unicode、UTF-8、UTF-16

    计算机起源于美国 xff0c 上个世纪 xff0c 他们对英语字符与二进制位之间的关系做了统一规定 xff0c 并制定了一套字符编码规则 xff0c 这套编码规则被称为ASCII编码 ASCII 编码一共定义了128个字符的编码规则 xff
  • spark MLlib之分类和回归

    MLlib支持多种方法用来处理二分分类 xff0c 多类分类以及回归分析 xff0c 下表列出了问题及对应的处理方法 xff1a 问题类型 支持的方法 二分分类 现行SVM xff0c 逻辑回归 xff0c 决策树 xff0c 贝叶斯 多类
  • 生产者消费者模型详解以及实现

    生产者消费者模式 我们先来看看什么是生产者消费者模式 xff0c 生产者消费者模式是程序设计中非常常见的一种设计模式 xff0c 被广泛运用在解耦 消息队列等场景 在现实世界中 xff0c 我们把生产商品的一方称为生产者 xff0c 把消费
  • 高频面试点:静态链接库与动态链接库

    库是写好的现有的 xff0c 成熟的 xff0c 可以复用的代码 现实中每个程序都要依赖很多基础的底层库 xff0c 不可能每个人的代码都从零开始 xff0c 因此库的存在意义非同寻常 本质上来说库是一种可执行代码的二进制形式 xff0c
  • cordova打包app热更新问题

    定义 xff1a 基于 cordova 框架能将web应用 js html css 图片等 打包成 App 当 App 在终端上安装后 xff0c 不需要重新下载app xff0c 实现内壳更新 原理 xff1a 1 在项目根目录的conf
  • 一文解决 Python读取文件的全部知识

    文件是无处不在的 xff0c 无论我们使用哪种编程语言 xff0c 处理文件对于每个程序员都是必不可少的 文件处理是一种用于创建文件 写入数据和从中读取数据的过程 xff0c Python 拥有丰富的用于处理不同文件类型的包 xff0c 从
  • 总结实验室对转录组及lncRNA数据分析的思路

    继师兄详细地讲述这个思路之后 xff0c 我进行一个归纳总结 xff08 师兄说 xff0c 首先要建立一个思想上的流程 xff0c 再来纠结软件 命令这些细节 xff01 xff01 xff01 xff01 xff01 xff01 xff
  • Android 高通7.1系统默认横屏显示(无G-sensor)

    竖屏横用 xff0c logo用的还是竖屏资源 1 添加宏控制 device qcom msm8909 system prop span class token comment add start span persist span cla
  • Android11 授权应用获取IMEI号和ICCID

    在Android11上获取IMEI号等设备信息需要android permission READ PRIVILEGED PHONE STATE权限 xff0c 而这个权限又只授予系统级应用 项目中如果targetSdkVersion值小于2