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


    IMEI是设备唯一性的一个重要指标,这篇文章对IMEI获取做一些分析,以达到以下两个目的:

    1、梳理Android源码中获取IMEI流程

    2、理解获取IMEI时,源码中权限调用流程

    备注:以下源码分析,针对的是Android 6.0.1源码

    在Android代码中,我们需要获取设备的IMEI,只需调用下面方法

     

    1. TelephonyManager telephonyMgr = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    2. imei = telephonyMgr.getDeviceId();


    接下来就看看源码中是怎样获取IMEI的。

     

    1. /**
    2.  * Returns the unique device ID, for example, the IMEI for GSM and the MEID
    3.  * or ESN for CDMA phones. Return null if device ID is not available.
    4.  *
    5.  *

      Requires Permission:

    6.  *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
    7.  */
    8. public String getDeviceId() {
    9.     try {
    10.         ITelephony telephony = getITelephony();
    11.         if (telephony == null)
    12.             return null;
    13.         return telephony.getDeviceId(mContext.getOpPackageName());
    14.     } catch (RemoteException ex) {
    15.         ......
    16.     }
    17. }


    在TelephonyManager中,可以看到是先获取ITelephony对象,然后在通过该Interface的getDeviceid()方法来获取IMEI的。ITelephony是一个aidl的接口,所以接下来我们需要找到实现这个接口的对应实现类

    1. /**
    2.  * Implementation of the ITelephony interface.
    3.  */
    4. public class PhoneInterfaceManager extends ITelephony.Stub {
    5.   
    6.     ......
    7.   
    8.    /**
    9.      * Returns the unique device ID of phone, for example, the IMEI for
    10.      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
    11.      *
    12.      *

      Requires Permission:

    13.      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
    14.      */
    15.     @Override
    16.     public String getDeviceId(String callingPackage) {
    17.         if (!canReadPhoneState(callingPackage, "getDeviceId")) {
    18.             return null;
    19.         }
    20.  
    21.         final Phone phone = PhoneFactory.getPhone(0);
    22.         if (phone != null) {
    23.             return phone.getDeviceId();
    24.         } else {
    25.             return null;
    26.         }
    27.     }
    28.   
    29.     ......
    30. }


            首先,通过上面类的声明可以看到,PhoneInterfaceManager这个类实现了ITelephony.stub接口,因此就是这个接口的对应的实现类。接下来我们看看对应实现的getDeviceId()方法。

            这个方法首先通过canReadPhoneState()方法来判断权限是否通过,如果没有权限直接就返回null了。权限通过之后才会去获取deviceId。

            我们这次分析的目的,一个是权限流程分析,一个是IMEI获取流程分析,这里我们通过canReadPhoneState()方法看看权限相关的判断流程,然后在接着看IMEI的获取流程。

    1、权限判断流程

         通过上面canReadPhoneState()函数入口,我们看看具体的实现。

    1. private boolean canReadPhoneState(String callingPackage, String message) {
    2.     try {
    3.         mApp.enforceCallingOrSelfPermission(
    4.                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
    5.  
    6.         // SKIP checking for run-time permission since caller or self has PRIVILEDGED permission
    7.         return true;
    8.     } catch (SecurityException e) {
    9.         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
    10.                 message);
    11.     }
    12.  
    13.     if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
    14.             callingPackage) != AppOpsManager.MODE_ALLOWED) {
    15.         return false;
    16.     }
    17.  
    18.     return true;
    19. }


          该函数中,首先在try代码块中尝试获取READ_PRIVILEGED_PHONE_STATE权限,如果有这个权限就直接默认权限通过了。一般这个私有权限是系统的应用有,第三方应用都是没有的,所以会抛出安全异常,走到catch代码块中。

           在catch代码块,就是真正的去检查应用是否有对应需要的READ_PHONE_STATE权限了。接下来我们需要看看该方法的具体实现

    1. class ContextImpl extends Context {
    2.     ......
    3.   
    4.     @Override
    5.     public void enforceCallingOrSelfPermission(
    6.             String permission, String message) {
    7.         enforce(permission,
    8.                 checkCallingOrSelfPermission(permission),
    9.                 true,
    10.                 Binder.getCallingUid(),
    11.                 message);
    12.     }
    13.   
    14.     @Override
    15.     public int checkCallingOrSelfPermission(String permission) {
    16.         if (permission == null) {
    17.             throw new IllegalArgumentException("permission is null");
    18.         }
    19.  
    20.         return checkPermission(permission, Binder.getCallingPid(),
    21.                 Binder.getCallingUid());
    22.     }
    23.   
    24.     @Override
    25.     public int checkPermission(String permission, int pid, int uid) {
    26.         if (permission == null) {
    27.             throw new IllegalArgumentException("permission is null");
    28.         }
    29.  
    30.         try {
    31.             return ActivityManagerNative.getDefault().checkPermission(
    32.                     permission, pid, uid);
    33.         } catch (RemoteException e) {
    34.             return PackageManager.PERMISSION_DENIED;
    35.         }
    36.     }
    37.      
    38. }
    39.   
    40. public abstract class ActivityManagerNative extends Binder implements IActivityManager {
    41.     ......
    42.   
    43.     public int checkPermission(String permission, int pid, int uid)
    44.             throws RemoteException {
    45.         Parcel data = Parcel.obtain();
    46.         Parcel reply = Parcel.obtain();
    47.         data.writeInterfaceToken(IActivityManager.descriptor);
    48.         data.writeString(permission);
    49.         data.writeInt(pid);
    50.         data.writeInt(uid);
    51.         mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0);
    52.         reply.readException();
    53.         int res = reply.readInt();
    54.         data.recycle();
    55.         reply.recycle();
    56.         return res;
    57.     }
    58.   
    59.     ......
    60. }


            从上面调用可以看到,首先是在ContextImpl中开始经过层层调用,在进程中最终调用到ActivityManagerNative中,通过Parcel传输相关数据,完成一次IPC操作来获取权限是否通过的结果。

            上述IPC操作,最终会调用到ActivityManagerService中完成,下面我们看看ActivityMangerService中的执行情况

    1. public final class ActivityManagerService extends ActivityManagerNative
    2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {   
    3.     ......
    4.   
    5.     /**
    6.      * As the only public entry point for permissions checking, this method
    7.      * can enforce the semantic that requesting a check on a null global
    8.      * permission is automatically denied.  (Internally a null permission
    9.      * string is used when calling {@link #checkComponentPermission} in cases
    10.      * when only uid-based security is needed.)
    11.      *
    12.      * This can be called with or without the global lock held.
    13.      */
    14.     @Override
    15.     public int checkPermission(String permission, int pid, int uid) {
    16.         if (permission == null) {
    17.             return PackageManager.PERMISSION_DENIED;
    18.         }
    19.         return checkComponentPermission(permission, pid, uid, -1, true);
    20.     }
    21.   
    22.     /**
    23.      * This can be called with or without the global lock held.
    24.      */
    25.     int checkComponentPermission(String permission, int pid, int uid,
    26.             int owningUid, boolean exported) {
    27.         if (pid == MY_PID) {
    28.             return PackageManager.PERMISSION_GRANTED;
    29.         }
    30.         return ActivityManager.checkComponentPermission(permission, uid,
    31.                 owningUid, exported);
    32.     }
    33.      
    34. }
    35.   
    36. public class ActivityManager {
    37.     ......
    38.   
    39.     /** @hide */
    40.     public static int checkComponentPermission(String permission, int uid,
    41.             int owningUid, boolean exported) {
    42.         // Root, system server get to do everything.
    43.         final int appId = UserHandle.getAppId(uid);
    44.         if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
    45.             return PackageManager.PERMISSION_GRANTED;
    46.         }
    47.         // Isolated processes don't get any permissions.
    48.         if (UserHandle.isIsolated(uid)) {
    49.             return PackageManager.PERMISSION_DENIED;
    50.         }
    51.         // If there is a uid that owns whatever is being accessed, it has
    52.         // blanket access to it regardless of the permissions it requires.
    53.         if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
    54.             return PackageManager.PERMISSION_GRANTED;
    55.         }
    56.         // If the target is not exported, then nobody else can get to it.
    57.         if (!exported) {
    58.             /*
    59.             RuntimeException here = new RuntimeException("here");
    60.             here.fillInStackTrace();
    61.             Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
    62.                     here);
    63.             */
    64.             return PackageManager.PERMISSION_DENIED;
    65.         }
    66.         if (permission == null) {
    67.             return PackageManager.PERMISSION_GRANTED;
    68.         }
    69.         try {
    70.             return AppGlobals.getPackageManager()
    71.                     .checkUidPermission(permission, uid);
    72.         } catch (RemoteException e) {
    73.             // Should never happen, but if it does... deny!
    74.             Slog.e(TAG, "PackageManager is dead?!?", e);
    75.         }
    76.         return PackageManager.PERMISSION_DENIED;
    77.     }
    78. }


    从上面调用可以看到,从ActivityManagerService的方法checkPermission()开始,最终会调用到ActivityManager的checkCompomentPermission()方法,最终完成权限的判断。

    从ActivityManager的checkCompomentPermission()方法中一堆的if语句,其实也不难发现,权限的判断,主要还是和appId,uid,pid等参数相关,一般第三方app都是通过PackageManagerService来判断的。

    下面是PMS中具体的判断逻辑,这里就不再进一步展开分析了,有兴趣的同学可以继续看看。

    1. public class PackageManagerService extends IPackageManager.Stub {  
    2.     ......
    3.   
    4.     @Override
    5.     public int checkUidPermission(String permName, int uid) {
    6.         final int userId = UserHandle.getUserId(uid);
    7.  
    8.         if (!sUserManager.exists(userId)) {
    9.             return PackageManager.PERMISSION_DENIED;
    10.         }
    11.  
    12.         synchronized (mPackages) {
    13.             Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
    14.             if (obj != null) {
    15.                 final SettingBase ps = (SettingBase) obj;
    16.                 final PermissionsState permissionsState = ps.getPermissionsState();
    17.                 if (permissionsState.hasPermission(permName, userId)) {
    18.                     return PackageManager.PERMISSION_GRANTED;
    19.                 }
    20.                 // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
    21.                 if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
    22.                         .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
    23.                     return PackageManager.PERMISSION_GRANTED;
    24.                 }
    25.             } else {
    26.                 ArraySet perms = mSystemPermissions.get(uid);
    27.                 if (perms != null) {
    28.                     if (perms.contains(permName)) {
    29.                         return PackageManager.PERMISSION_GRANTED;
    30.                     }
    31.                     if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
    32.                             .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
    33.                         return PackageManager.PERMISSION_GRANTED;
    34.                     }
    35.                 }
    36.             }
    37.         }
    38.  
    39.         return PackageManager.PERMISSION_DENIED;
    40.     }
    41. }

    2、IMEI获取流程

          下面我们回到前面PhoneInterfaceManager中的getDeviceId()方法,继续分析获取IMEI的流程。

    1. /**
    2.  * Implementation of the ITelephony interface.
    3.  */
    4. public class PhoneInterfaceManager extends ITelephony.Stub {
    5.   
    6.     ......
    7.   
    8.    /**
    9.      * Returns the unique device ID of phone, for example, the IMEI for
    10.      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
    11.      *
    12.      *

      Requires Permission:

    13.      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
    14.      */
    15.     @Override
    16.     public String getDeviceId(String callingPackage) {
    17.         if (!canReadPhoneState(callingPackage, "getDeviceId")) {
    18.             return null;
    19.         }
    20.  
    21.         final Phone phone = PhoneFactory.getPhone(0);
    22.         if (phone != null) {
    23.             return phone.getDeviceId();
    24.         } else {
    25.             return null;
    26.         }
    27.     }
    28.   
    29.     ......

    从上面可以看到,首先是通过PhoneFactory拿到一个Phone对象,Phone是一个Interface,初步的实现类是PhoneBase。

    1. public abstract class PhoneBase extends Handler implements Phone {
    2.   
    3. }


    看到对应类的声明,其实也是一个虚类,在这个类中也没有实现getDeviceId()方法,因此需要继续看看PhoneBase的实现类,发现有这么几个,分别是

    CDMAPhone
    GSMPhone
    ImsPhoneBase
    SipPhoneBase

    1. public class CDMAPhone extends PhoneBase {
    2.     ......
    3.   
    4.     //returns MEID or ESN in CDMA
    5.     @Override
    6.     public String getDeviceId() {
    7.         String id = getMeid();
    8.         if ((id == null) || id.matches("^0*$")) {
    9.             Rlog.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
    10.             id = getEsn();
    11.         }
    12.         return id;
    13.     }
    14. }
    15.   
    16. public class GSMPhone extends PhoneBase {
    17.     ......
    18.   
    19.     @Override
    20.     public String getDeviceId() {
    21.         return mImei;
    22.     }
    23. }
    24.   
    25. abstract class ImsPhoneBase extends PhoneBase {
    26.     ......
    27.   
    28.     @Override
    29.     public String getDeviceId() {
    30.         return null;
    31.     }
    32. }
    33.   
    34. abstract class SipPhoneBase extends PhoneBase {
    35.     ......
    36.   
    37.     @Override
    38.     public String getDeviceId() {
    39.         return null;
    40.     }
    41.   
    42. }

    可以看出,GSMPhone应该是提供IMEI的类。下面我们具体看看在GSMPhone中,mImei这个全局变量是怎样赋值的。

     

    1. public class GSMPhone extends PhoneBase {
    2.     ......
    3.   
    4.     @Override
    5.     public void handleMessage (Message msg) {
    6.          ......
    7.          switch (msg.what) {
    8.             case EVENT_RADIO_AVAILABLE: {
    9.                 mCi.getBasebandVersion(
    10.                         obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
    11.  
    12.                 mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
    13.                 mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
    14.                 mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
    15.                 startLceAfterRadioIsAvailable();
    16.             }
    17.             break;
    18.             ......
    19.   
    20.             case EVENT_GET_IMEI_DONE:
    21.                 ar = (AsyncResult)msg.obj;
    22.  
    23.                 if (ar.exception != null) {
    24.                     break;
    25.                 }
    26.  
    27.                 mImei = (String)ar.result;
    28.             break;
    29.             ......
    30.     }
    31. }

    通过上面代码可以看到,在handleMessage()方法中,收到EVENT_GET_IMEI_DONE消息后,就得到了IMEI。进一步发现,上面有一行代码

                 mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
    发送了这个消息出去,于是我们继续看看前面的调用mCi是什么。
           mCi是GSMPhone基类PhoneBase中的一个全局变量,其类型为CommandsInterface,为一个Interface,接下来就看看这个Interface的实现,这里就举出所有实现类型了,实现这个Interface的是RIL这个类

     

    1. public final class RIL extends BaseCommands implements CommandsInterface {
    2.     ......
    3.   
    4.     @Override
    5.     public void
    6.     getIMEI(Message result) {
    7.         RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result);
    8.  
    9.         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
    10.  
    11.         send(rr);
    12.     }
    13.   
    14.     private void
    15.     send(RILRequest rr) {
    16.         Message msg;
    17.  
    18.         if (mSocket == null) {
    19.             rr.onError(RADIO_NOT_AVAILABLE, null);
    20.             rr.release();
    21.             return;
    22.         }
    23.  
    24.         msg = mSender.obtainMessage(EVENT_SEND, rr);
    25.  
    26.         acquireWakeLock();
    27.  
    28.         msg.sendToTarget();
    29.     }
    30. }

    看到上面代码,在RIL类中,将获取IMEI的消息进行一些包装,然后在send()方法中通过mSender对象发送出去了。mSender是什么了?其实也在RIL这个类中,是个内部类,叫RILSender

    1. class RILSender extends Handler implements Runnable {
    2.     ......
    3.   
    4.     @Override public void
    5.     handleMessage(Message msg) {
    6.          ......
    7.          switch (msg.what) {
    8.                 case EVENT_SEND:
    9.                     try {
    10.                         LocalSocket s;
    11.  
    12.                         s = mSocket;
    13.  
    14.                         if (s == null) {
    15.                             rr.onError(RADIO_NOT_AVAILABLE, null);
    16.                             rr.release();
    17.                             decrementWakeLock();
    18.                             return;
    19.                         }
    20.  
    21.                         synchronized (mRequestList) {
    22.                             mRequestList.append(rr.mSerial, rr);
    23.                         }
    24.  
    25.                         byte[] data;
    26.  
    27.                         data = rr.mParcel.marshall();
    28.                         rr.mParcel.recycle();
    29.                         rr.mParcel = null;
    30.  
    31.                         if (data.length > RIL_MAX_COMMAND_BYTES) {
    32.                             throw new RuntimeException(
    33.                                     "Parcel larger than max bytes allowed! "
    34.                                                           + data.length);
    35.                         }
    36.  
    37.                         // parcel length in big endian
    38.                         dataLength[0] = dataLength[1] = 0;
    39.                         dataLength[2] = (byte)((data.length >> 8) & 0xff);
    40.                         dataLength[3] = (byte)((data.length) & 0xff);
    41.  
    42.                         //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes");
    43.  
    44.                         s.getOutputStream().write(dataLength);
    45.                         s.getOutputStream().write(data);
    46.                     } catch {
    47.                         ......
    48.                     }
    49.     }
    50. }

    熟悉电话框架这块的同学应该比较了解,电话这块,其实也是一个RILSender和一个RILReceiver,分别用于发送和接收。两个分别运行在不同线程,全双工工通信,处理RILRequest和RILResponse。

    这里不展开讲Android的电话框架,直接进入处理部分,看看上面发送的消息,是怎样被处理的。

    1. /**
    2.  * Call from RIL to us to make a RIL_REQUEST
    3.  *
    4.  * Must be completed with a call to RIL_onRequestComplete()
    5.  *
    6.  * RIL_onRequestComplete() may be called from any thread, before or after
    7.  * this function returns.
    8.  *
    9.  * Will always be called from the same thread, so returning here implies
    10.  * that the radio is ready to process another command (whether or not
    11.  * the previous command has completed).
    12.  */
    13. static void
    14. onRequest (int request, void *data, size_t datalen, RIL_Token t) {
    15.     ......
    16.   
    17.     case RIL_REQUEST_GET_IMEI:
    18.         p_response = NULL;
    19.         err = at_send_command_numeric("AT+CGSN", &p_response);
    20.  
    21.         if (err < 0 || p_response->success == 0) {
    22.             RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
    23.         } else {
    24.             RIL_onRequestComplete(t, RIL_E_SUCCESS,
    25.                 p_response->p_intermediates->line, sizeof(char *));
    26.         }
    27.         at_response_free(p_response);
    28.         break;
    29.   
    30.    ......
    31.   
    32. }
    33. -------------------------------------------------------------------------------
    34. int at_send_command_numeric (const char *command,
    35.                                  ATResponse **pp_outResponse)
    36. {
    37.     int err;
    38.     err = at_send_command_full (command, NUMERIC, NULL,
    39.                                     NULL, 0, pp_outResponse);
    40.     ......
    41.     return err;
    42. }
    43. -------------------------------------------------------------------------------
    44. static int at_send_command_full (const char *command, ATCommandType type,
    45.                     const char *responsePrefix, const char *smspdu,
    46.                     long long timeoutMsec, ATResponse **pp_outResponse)
    47. {
    48.     int err;
    49.     ......
    50.     err = at_send_command_full_nolock(command, type,
    51.                     responsePrefix, smspdu,
    52.                     timeoutMsec, pp_outResponse);
    53.  
    54.     ......
    55.     return err;
    56. }
    57. -------------------------------------------------------------------------------
    58. /**
    59.  * Internal send_command implementation
    60.  * Doesn't lock or call the timeout callback
    61.  *
    62.  * timeoutMsec == 0 means infinite timeout
    63.  */
    64.  
    65. static int at_send_command_full_nolock (const char *command, ATCommandType type,
    66.                     const char *responsePrefix, const char *smspdu,
    67.                     long long timeoutMsec, ATResponse **pp_outResponse)
    68. {
    69.     int err = 0;
    70.     ......
    71.  
    72.     err = writeline (command);
    73.  
    74.     s_type = type;
    75.     s_responsePrefix = responsePrefix;
    76.     s_smsPDU = smspdu;
    77.     sp_response = at_response_new();
    78.  
    79.     while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
    80.         if (timeoutMsec != 0) {
    81.             ......
    82.         } else {
    83.             err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
    84.         }
    85.         ......
    86.     }
    87.  
    88.     ......
    89.     err = 0;
    90. error:
    91.     clearPendingCommand();
    92.     return err;
    93. }

    上面代码比较多,主要是与硬件层打交道,将获取IMEI的命令写到硬件层,然后等待结果。这样子,通过写命令到硬件层,然后等待对应的IMEI数据被写到对应申请的内存地址后,就可以返回了。

    接着底层通过相应的IPC通道将数据返回给调用进程,调用进程读取对应的数据,就拿到了IMEI了。

  • 相关阅读:
    #力扣:2894. 分类求和并作差@FDDLC
    【CSS】css转换、css过渡、css动画_09
    【Win11 搭建miniconda 的pytorch1.12环境】
    VoxEdit 主题创作大赛:将 90 年代的复古元素带入 Web3
    Java-API简析_java.net.Inet6Address类(基于 Latest JDK)(浅析源码)
    sed命令在Mac和Linux下的不同
    开源数据集分享———猫脸码客
    Python----break关键字对while...else结构的影响
    IDEA03:数据库CDC、Kafka和连接器Debezium配置
    疫情之下,企业管理如何做好?疫情防控管理系统助力企业战疫
  • 原文地址:https://blog.csdn.net/xiaopangcame/article/details/127789893