在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);
				
android 11 发布了 android 11 以前使用的MAC的设备唯一码的方式被废弃了,因为11不让用了,然后就去官方上找推荐了; 之前的获取MAC的地址,有兴趣的可以点进去看看 官方推荐使用UUID ;这个好像目前几个大厂的APP都在使用的方法 具体的就直接上代码吧; 下面的代码直接获取UUID public static String getUUID() { return UUID.randomUUID().toString().replaceAll("-", ""); //获取IMEI码 TelephonyManager telephonyManager = (TelephonyManager) this .getSystemService(Context.TELEPHONY_SERVICE); String IMEI = t...
按照正常的获取Android 8.0 之前都是可以拿到我们的Imei码的,就是广为流传的那些形式,我就意义赘述了,到了Android 9.0 之后就出现了部分手机厂商的机型获取不到我们的IMEI码,例如 '‘一加’'等厂商,而且其他厂商的在获取的时候也需要获取我们的 READ_PHONE_STATE权限 不过也能在大部分的手机上通用;也不算太大的缺点,但是到了我们的Android 10 开始,...