Android11 authorized application to obtain IMEI number and ICCID

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