Android11 授权应用获取IMEI号和ICCID

在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);
 }

猜你喜欢

转载自blog.csdn.net/wxd_csdn_2016/article/details/129596166