在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异常。文章来源:https://www.toymoban.com/news/detail-437799.html
所以在checkPrivilegedReadPermissionOrCarrierPrivilegePermission中直接根据包名授权。文章来源地址https://www.toymoban.com/news/detail-437799.html
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);
}
到了这里,关于Android11 授权应用获取IMEI号和ICCID的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!