在Android11上获取IMEI号等设备信息需要android.permission.READ_PRIVILEGED_PHONE_STATE权限,而这个权限又只授予系统级应用。项目中如果targetSdkVersion值小于29获取到的是null,大于28报SecurityException错误。
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 sis = sm.getActiveSubscriptionInfoList();if (sis.size() >= 1) {SubscriptionInfo si1 = sis.get(0); // 卡一iccid = si1.getIccId();}}return iccid;}
@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)@UnsupportedAppUsagepublic 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 crashingreturn null;}}
可以看到getSimSerialNumber()方法要求声明android.permission.READ_PRIVILEGED_PHONE_STATE权限
然后调用IPhoneSubInfo的getIccSerialNumberForSubscriber()方法
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 callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(int subId,String callingPackage, @Nullable String callingFeatureId, String message,CallPhoneMethodHelper callMethodHelper) {// 调用Phone的相关方法,进行权限检查return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId,message, callMethodHelper,(aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)->TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage));}}
在TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers方法中判断是否有权限获取ICCID
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 checkif (callingPackage != null && callingPackage.equals("com.xxx.xxx")) {Log.d(LOG_TAG, "com.xxx.xxx skip permission check of "+message);return true;}// add endreturn reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,message);}
下一篇:核心越狱防护