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.
- *
- *
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.
- *
- *
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, Binder.getCallingPid(),
- Binder.getCallingUid());
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid) {
- if (permission == null) {
- throw new IllegalArgumentException("permission is null");
- }
-
- try {
- return ActivityManagerNative.getDefault().checkPermission(
- permission, pid, uid);
- } catch (RemoteException e) {
- return PackageManager.PERMISSION_DENIED;
- }
- }
-
- }
-
- public abstract class ActivityManagerNative extends Binder implements IActivityManager {
- ......
-
- public int checkPermission(String permission, int pid, int uid)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeString(permission);
- data.writeInt(pid);
- data.writeInt(uid);
- mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0);
- reply.readException();
- int res = reply.readInt();
- data.recycle();
- reply.recycle();
- return res;
- }
-
- ......
- }
从上面调用可以看到,首先是在ContextImpl中开始经过层层调用,在进程中最终调用到ActivityManagerNative中,通过Parcel传输相关数据,完成一次IPC操作来获取权限是否通过的结果。
上述IPC操作,最终会调用到ActivityManagerService中完成,下面我们看看ActivityMangerService中的执行情况
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
-
- /**
- * As the only public entry point for permissions checking, this method
- * can enforce the semantic that requesting a check on a null global
- * permission is automatically denied. (Internally a null permission
- * string is used when calling {@link #checkComponentPermission} in cases
- * when only uid-based security is needed.)
- *
- * This can be called with or without the global lock held.
- */
- @Override
- public int checkPermission(String permission, int pid, int uid) {
- if (permission == null) {
- return PackageManager.PERMISSION_DENIED;
- }
- return checkComponentPermission(permission, pid, uid, -1, true);
- }
-
- /**
- * This can be called with or without the global lock held.
- */
- int checkComponentPermission(String permission, int pid, int uid,
- int owningUid, boolean exported) {
- if (pid == MY_PID) {
- return PackageManager.PERMISSION_GRANTED;
- }
- return ActivityManager.checkComponentPermission(permission, uid,
- owningUid, exported);
- }
-
- }
-
- public class ActivityManager {
- ......
-
- /** @hide */
- public static int checkComponentPermission(String permission, int uid,
- int owningUid, boolean exported) {
- // Root, system server get to do everything.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
- return PackageManager.PERMISSION_GRANTED;
- }
- // Isolated processes don't get any permissions.
- if (UserHandle.isIsolated(uid)) {
- return PackageManager.PERMISSION_DENIED;
- }
- // If there is a uid that owns whatever is being accessed, it has
- // blanket access to it regardless of the permissions it requires.
- if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- // If the target is not exported, then nobody else can get to it.
- if (!exported) {
- /*
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
- here);
- */
- return PackageManager.PERMISSION_DENIED;
- }
- if (permission == null) {
- return PackageManager.PERMISSION_GRANTED;
- }
- try {
- return AppGlobals.getPackageManager()
- .checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- // Should never happen, but if it does... deny!
- Slog.e(TAG, "PackageManager is dead?!?", e);
- }
- return PackageManager.PERMISSION_DENIED;
- }
- }
从上面调用可以看到,从ActivityManagerService的方法checkPermission()开始,最终会调用到ActivityManager的checkCompomentPermission()方法,最终完成权限的判断。
从ActivityManager的checkCompomentPermission()方法中一堆的if语句,其实也不难发现,权限的判断,主要还是和appId,uid,pid等参数相关,一般第三方app都是通过PackageManagerService来判断的。
下面是PMS中具体的判断逻辑,这里就不再进一步展开分析了,有兴趣的同学可以继续看看。
- public class PackageManagerService extends IPackageManager.Stub {
- ......
-
- @Override
- public int checkUidPermission(String permName, int uid) {
- final int userId = UserHandle.getUserId(uid);
-
- if (!sUserManager.exists(userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
-
- synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
- if (obj != null) {
- final SettingBase ps = (SettingBase) obj;
- final PermissionsState permissionsState = ps.getPermissionsState();
- if (permissionsState.hasPermission(permName, userId)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
- .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- ArraySet
perms = mSystemPermissions.get(uid); - if (perms != null) {
- if (perms.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
- .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- }
- }
-
- return PackageManager.PERMISSION_DENIED;
- }
- }
2、IMEI获取流程
下面我们回到前面PhoneInterfaceManager中的getDeviceId()方法,继续分析获取IMEI的流程。
- /**
- * 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.
- *
- *
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;
- }
- }
-
- ......
从上面可以看到,首先是通过PhoneFactory拿到一个Phone对象,Phone是一个Interface,初步的实现类是PhoneBase。
- public abstract class PhoneBase extends Handler implements Phone {
-
- }
看到对应类的声明,其实也是一个虚类,在这个类中也没有实现getDeviceId()方法,因此需要继续看看PhoneBase的实现类,发现有这么几个,分别是
CDMAPhone
GSMPhone
ImsPhoneBase
SipPhoneBase
- public class CDMAPhone extends PhoneBase {
- ......
-
- //returns MEID or ESN in CDMA
- @Override
- public String getDeviceId() {
- String id = getMeid();
- if ((id == null) || id.matches("^0*$")) {
- Rlog.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
- id = getEsn();
- }
- return id;
- }
- }
-
- public class GSMPhone extends PhoneBase {
- ......
-
- @Override
- public String getDeviceId() {
- return mImei;
- }
- }
-
- abstract class ImsPhoneBase extends PhoneBase {
- ......
-
- @Override
- public String getDeviceId() {
- return null;
- }
- }
-
- abstract class SipPhoneBase extends PhoneBase {
- ......
-
- @Override
- public String getDeviceId() {
- return null;
- }
-
- }
可以看出,GSMPhone应该是提供IMEI的类。下面我们具体看看在GSMPhone中,mImei这个全局变量是怎样赋值的。
- public class GSMPhone extends PhoneBase {
- ......
-
- @Override
- public void handleMessage (Message msg) {
- ......
- switch (msg.what) {
- case EVENT_RADIO_AVAILABLE: {
- mCi.getBasebandVersion(
- obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
-
- mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
- mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
- mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
- startLceAfterRadioIsAvailable();
- }
- break;
- ......
-
- case EVENT_GET_IMEI_DONE:
- ar = (AsyncResult)msg.obj;
-
- if (ar.exception != null) {
- break;
- }
-
- mImei = (String)ar.result;
- break;
- ......
- }
- }
通过上面代码可以看到,在handleMessage()方法中,收到EVENT_GET_IMEI_DONE消息后,就得到了IMEI。进一步发现,上面有一行代码
mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
发送了这个消息出去,于是我们继续看看前面的调用mCi是什么。
mCi是GSMPhone基类PhoneBase中的一个全局变量,其类型为CommandsInterface,为一个Interface,接下来就看看这个Interface的实现,这里就举出所有实现类型了,实现这个Interface的是RIL这个类
- public final class RIL extends BaseCommands implements CommandsInterface {
- ......
-
- @Override
- public void
- getIMEI(Message result) {
- RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result);
-
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
- send(rr);
- }
-
- private void
- send(RILRequest rr) {
- Message msg;
-
- if (mSocket == null) {
- rr.onError(RADIO_NOT_AVAILABLE, null);
- rr.release();
- return;
- }
-
- msg = mSender.obtainMessage(EVENT_SEND, rr);
-
- acquireWakeLock();
-
- msg.sendToTarget();
- }
- }
看到上面代码,在RIL类中,将获取IMEI的消息进行一些包装,然后在send()方法中通过mSender对象发送出去了。mSender是什么了?其实也在RIL这个类中,是个内部类,叫RILSender
- class RILSender extends Handler implements Runnable {
- ......
-
- @Override public void
- handleMessage(Message msg) {
- ......
- switch (msg.what) {
- case EVENT_SEND:
- try {
- LocalSocket s;
-
- s = mSocket;
-
- if (s == null) {
- rr.onError(RADIO_NOT_AVAILABLE, null);
- rr.release();
- decrementWakeLock();
- return;
- }
-
- synchronized (mRequestList) {
- mRequestList.append(rr.mSerial, rr);
- }
-
- byte[] data;
-
- data = rr.mParcel.marshall();
- rr.mParcel.recycle();
- rr.mParcel = null;
-
- if (data.length > RIL_MAX_COMMAND_BYTES) {
- throw new RuntimeException(
- "Parcel larger than max bytes allowed! "
- + data.length);
- }
-
- // parcel length in big endian
- dataLength[0] = dataLength[1] = 0;
- dataLength[2] = (byte)((data.length >> 8) & 0xff);
- dataLength[3] = (byte)((data.length) & 0xff);
-
- //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes");
-
- s.getOutputStream().write(dataLength);
- s.getOutputStream().write(data);
- } catch {
- ......
- }
- }
- }
熟悉电话框架这块的同学应该比较了解,电话这块,其实也是一个RILSender和一个RILReceiver,分别用于发送和接收。两个分别运行在不同线程,全双工工通信,处理RILRequest和RILResponse。
这里不展开讲Android的电话框架,直接进入处理部分,看看上面发送的消息,是怎样被处理的。
- /**
- * Call from RIL to us to make a RIL_REQUEST
- *
- * Must be completed with a call to RIL_onRequestComplete()
- *
- * RIL_onRequestComplete() may be called from any thread, before or after
- * this function returns.
- *
- * Will always be called from the same thread, so returning here implies
- * that the radio is ready to process another command (whether or not
- * the previous command has completed).
- */
- static void
- onRequest (int request, void *data, size_t datalen, RIL_Token t) {
- ......
-
- case RIL_REQUEST_GET_IMEI:
- p_response = NULL;
- err = at_send_command_numeric("AT+CGSN", &p_response);
-
- if (err < 0 || p_response->success == 0) {
- RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
- } else {
- RIL_onRequestComplete(t, RIL_E_SUCCESS,
- p_response->p_intermediates->line, sizeof(char *));
- }
- at_response_free(p_response);
- break;
-
- ......
-
- }
- -------------------------------------------------------------------------------
- int at_send_command_numeric (const char *command,
- ATResponse **pp_outResponse)
- {
- int err;
- err = at_send_command_full (command, NUMERIC, NULL,
- NULL, 0, pp_outResponse);
- ......
- return err;
- }
- -------------------------------------------------------------------------------
- static int at_send_command_full (const char *command, ATCommandType type,
- const char *responsePrefix, const char *smspdu,
- long long timeoutMsec, ATResponse **pp_outResponse)
- {
- int err;
- ......
- err = at_send_command_full_nolock(command, type,
- responsePrefix, smspdu,
- timeoutMsec, pp_outResponse);
-
- ......
- return err;
- }
- -------------------------------------------------------------------------------
- /**
- * Internal send_command implementation
- * Doesn't lock or call the timeout callback
- *
- * timeoutMsec == 0 means infinite timeout
- */
-
- static int at_send_command_full_nolock (const char *command, ATCommandType type,
- const char *responsePrefix, const char *smspdu,
- long long timeoutMsec, ATResponse **pp_outResponse)
- {
- int err = 0;
- ......
-
- err = writeline (command);
-
- s_type = type;
- s_responsePrefix = responsePrefix;
- s_smsPDU = smspdu;
- sp_response = at_response_new();
-
- while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
- if (timeoutMsec != 0) {
- ......
- } else {
- err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
- }
- ......
- }
-
- ......
- err = 0;
- error:
- clearPendingCommand();
- return err;
- }
上面代码比较多,主要是与硬件层打交道,将获取IMEI的命令写到硬件层,然后等待结果。这样子,通过写命令到硬件层,然后等待对应的IMEI数据被写到对应申请的内存地址后,就可以返回了。
接着底层通过相应的IPC通道将数据返回给调用进程,调用进程读取对应的数据,就拿到了IMEI了。