电话状态权限及IMEI获取流程源码分析

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, 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<String> 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.
     *
     * <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;
        }
    }
  
    ......

从上面可以看到,首先是通过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了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值