IMEI是设备唯一性的一个重要指标,这篇文章对IMEI获取做一些分析,以达到以下两个目的:
1、梳理Android源码中获取IMEI流程
2、理解获取IMEI时,源码中权限调用流程
备注:以下源码分析,针对的是Android 6.0.1源码
在Android代码中,我们需要获取设备的IMEI,只需调用下面方法
TelephonyManager telephonyMgr = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
imei = telephonyMgr.getDeviceId();
接下来就看看源码中是怎样获取IMEI的。
/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getDeviceId() {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
return telephony.getDeviceId(mContext.getOpPackageName());
} catch (RemoteException ex) {
......
}
}
在TelephonyManager中,可以看到是先获取ITelephony对象,然后在通过该Interface的getDeviceid()方法来获取IMEI的。ITelephony是一个aidl的接口,所以接下来我们需要找到实现这个接口的对应实现类
/**
* Implementation of the ITelephony interface.
*/
public class PhoneInterfaceManager extends ITelephony.Stub {
......
/**
* Returns the unique device ID of phone, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
@Override
public String getDeviceId(String callingPackage) {
if (!canReadPhoneState(callingPackage, "getDeviceId")) {
return null;
}
final Phone phone = PhoneFactory.getPhone(0);
if (phone != null) {
return phone.getDeviceId();
} else {
return null;
}
}
......
}
首先,通过上面类的声明可以看到,PhoneInterfaceManager这个类实现了ITelephony.stub接口,因此就是这个接口的对应的实现类。接下来我们看看对应实现的getDeviceId()方法。
这个方法首先通过canReadPhoneState()方法来判断权限是否通过,如果没有权限直接就返回null了。权限通过之后才会去获取deviceId。
我们这次分析的目的,一个是权限流程分析,一个是IMEI获取流程分析,这里我们通过canReadPhoneState()方法看看权限相关的判断流程,然后在接着看IMEI的获取流程。
1、权限判断流程
通过上面canReadPhoneState()函数入口,我们看看具体的实现。
private boolean canReadPhoneState(String callingPackage, String message) {
try {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
// SKIP checking for run-time permission since caller or self has PRIVILEDGED permission
return true;
} catch (SecurityException e) {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
message);
}
if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return false;
}
return true;
}
该函数中,首先在try代码块中尝试获取READ_PRIVILEGED_PHONE_STATE权限,如果有这个权限就直接默认权限通过了。一般这个私有权限是系统的应用有,第三方应用都是没有的,所以会抛出安全异常,走到catch代码块中。
在catch代码块,就是真正的去检查应用是否有对应需要的READ_PHONE_STATE权限了。接下来我们需要看看该方法的具体实现
class ContextImpl extends Context {
......
@Override
public void enforceCallingOrSelfPermission(
String permission, String message) {
enforce(permission,
checkCallingOrSelfPermission(permission),
true,
Binder.getCallingUid(),
message);
}
@Override
public int checkCallingOrSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission