Obtaining device information such as IMEI number on Android 11 requires android.permission.READ_PRIVILEGED_PHONE_STATE permission, and this permission is only granted to system-level applications. In the project, if the targetSdkVersion value is less than 29, the result is null, and if it is greater than 28, a SecurityException error will be reported.
1. Get ICCID
public String getICCID(Context context) {<!-- --> TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); String simSerialNumber = telephonyManager. getSimSerialNumber(); return simSerialNumber; } or 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); // Card 1 iccid = si1. getIccId(); } } return iccid; }
2. The system checks the permissions of the application
- Source path: 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; } }
You can see that the getSimSerialNumber() method requires the declaration of android.permission.READ_PRIVILEGED_PHONE_STATE permission
Then call the getIccSerialNumberForSubscriber() method of IPhoneSubInfo
- Source path: frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneSubInfoController.java
public class PhoneSubInfoController extends IPhoneSubInfo.Stub {<!-- --> Part of the code is omitted. . . \t public String getIccSerialNumberForSubscriber(int subId, String callingPackage, String callingFeatureId) {<!-- --> return callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(subId, callingPackage, callingFeatureId, "getIccSerialNumber", (phone) -> phone.getIccSerialNumber()); } Part of the code is omitted. . . private <T> T callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(int subId, String callingPackage, @Nullable String callingFeatureId, String message, CallPhoneMethodHelper<T> callMethodHelper) {<!-- --> // Call related methods of Phone to check permissions return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, message, callMethodHelper, (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage) -> TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers( aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)); } }
In the TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers method, determine whether there is permission to obtain the ICCID
- Source path: 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(); // Whether the calling package has operator privileges // 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 & amp; & amp; checkCarrierPrivilegeForAnySubId(context, uid)) {<!-- --> return true; } PermissionManager permissionManager = (PermissionManager) context.getSystemService( Context.PERMISSION_SERVICE); // Check if authorized by device identity if (permissionManager. checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId, pid, uid) == PackageManager.PERMISSION_GRANTED) {<!-- --> return true; } return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage, message); } // Report failure when the application with the given pid/uid cannot access the requested identifier private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid, int uid, String callingPackage, String message) {<!-- --> ApplicationInfo callingPackageInfo = null; Part of the code is omitted. . . 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 & amp; & amp; ( 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."); }
As can be seen from the above code, if the calling application information is not empty and the value of targetSdkVersion is less than 29, and the READ_PHONE_STATE permission has been granted, false will be returned. At this time, if the iccid value obtained by the application is null, no error will be reported; if these conditions are not met, a SecurityException will be thrown directly.
So in checkPrivilegedReadPermissionOrCarrierPrivilegePermission, authorize directly according to the package name.
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 & amp; & amp; 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 & amp; & amp; 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); }