转载请注明出处:https://blog.csdn.net/turtlejj/article/details/84861020,谢谢~

Telephony模块中大量的使用了AIDL,但网上却很少有文章,把AIDL的用法、作用以及为什么要使用AIDL讲解的比较细致的文章。因此,我希望通过通过对ITelephony的分析,以点带面,能让更多的同学掌握AIDL在Telephony模块中的应用,以便能更好地理解Telephony模块的功能。

由于篇幅的原因,我们并不会在本篇文章中对AIDL的基本用法进行讲解,如果有对AIDL完全不了解的同学,可以先自行百度,或者查阅Google相关文档(这里的链接是中文文档)来进行学习,在有了一定的基础后,再来看这篇文章,我相信会更有帮助。

我会尽量用通俗易懂的词语来进行描述,但难免可能会有比喻不当的情况出现。同时,也请大家对博客中的错误以及纰漏予以指出,我一定第一时间进行更正。

话不多说,我们这就开始吧~

一、简介

AIDL(Android Interface Definition Language),即"Android接口定义语言"。是Android中进行进程间通信的一种解决方案,其本质是Android的Binder机制。

顾名思义,AIDL是用来编写接口用的,其编写出来的接口与普通的接口之间的区别就是,使用AIDL编写的接口是可以进行跨进程调用的;而普通的接口,只能在本进程中实现并调用,无法实现跨进程调用。

在Telephony模块中,Google的工程师通过AIDL在framework中编写了ITelephony.aidl,声明了一系列的方法,并在PhoneApp中使用PhoneInterfaceManager.java实现了ITelephony.aidl中的接口。

其中ITelephony作为客户端,而PhoneInterfaceManager作为服务器。调用ITelephony中的方法,其实就是远程调用了PhoneInterfaceManager中的具体实现,从而完成Telephony相关的操作和设置。

下面我们将详细为大家进行分析。

二、ITelephony.aidl与PhoneInterfaceManager.java

(一) ITelephony.aidl

首先,我们来说一说ITelephony.aidl这个文件。ITelephony.aidl文件可以在以下目录中找到

frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl

由于代码太长,我就不在这里全部贴出来了,大家可以自己从源码中找到该文件进行查看;如果没有下载安卓源码,也可以在AndroidXRef 网站中查看该文件。

首先,我们来看对ITelephony的描述。ITelephony接口是与phone对象相互作用的,我们可以理解为,使用ITelephony来间接调用phone对象的方法。并且,Google推荐不要直接使用该接口中的声明的方法,而是尽可能地通过TelephonyManager来进行调用。

简而言之,如果我们想要对phone对象进行操作,或调用其方法,我们应该通过TelephonyManager中提供的方法来进行操作。TelephonyManager中的方法其实就是通过ITelephony来间接调用phone对象的。后面我们会通过分析代码来具体说明。

在注释的最后,有一个{@hide},表明,ITelephony在SDK中是隐藏的,开发者无法直接调用到。

/*** Interface used to interact with the phone.  Mostly this is used by the* TelephonyManager class.  A few places are still using this directly.* Please clean them up if possible and use TelephonyManager instead.** {@hide}*/
interface ITelephony {......
}

由于ITelephony接口中声明的方法很多,超过100个,因此我们这里只举一些比较常见且通俗易懂的方法进行列举。

1. 通话相关

// 拨打电话
void dial(String number);// 挂断电话
boolean endCall();// 接听呼入电话
void answerRingingCall();=======================================================// 返回电话是否处于摘机状态,即是否存在active call或holding call
boolean isOffhook(String callingPackage);// 判断电话是否在响铃
boolean isRinging(String callingPackage);// 判断电话是否处于idle状态
boolean isIdle(String callingPackage);

2. 网络模式相关

// 获取当前数据业务注册在哪种RAT上
int getDataNetworkTypeForSubscriber(int subId, String callingPackage);// 获取当前语音业务注册在哪种RAT上
int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage);============================================================================// 设置网络注册模式为自动选网
void setNetworkSelectionModeAutomatic(int subId);// 设置网络注册模式为手动选网
boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,boolean persistSelection);// 获取搜网后的结果
CellNetworkScanResult getCellNetworkScanResults(int subId);// 发起搜网请求
int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,in IBinder binder);// 停止搜网
void stopNetworkScan(int subId, int scanId);// 设置网络模式(即,4G/3G/2G, 3G/2G, 2G only等)
boolean setPreferredNetworkType(int subId, int networkType);

3. 手机相关信息

// 获取设备ID
String getDeviceId(String callingPackage);// 获取IMEI
String getImeiForSlot(int slotIndex, String callingPackage);// 获取MEID
String getMeidForSlot(int slotIndex, String callingPackage);// 获取设备软件版本号
String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage);

4. Sim卡相关

// 使用PIN码解锁Sim卡
boolean supplyPin(String pin);// 使用PUK码修改PIN码
boolean supplyPuk(String puk, String pin);// 判断是否插入了Sim卡
boolean hasIccCard();// 获取默认Sim卡
int getDefaultSim();

我们可以看到,ITelephony中声明的方法几乎涵盖了Telephony模块的方方面面,一旦实现了这些方法,就可以通过这些方法接打电话、设置网络模式、获取手机信息等操作。那么这些方法在哪里实现呢,那就是我们接下来要讲的PhoneInterfaceManager类。

(二) PhoneInterfaceManager.java

PhoneInterfaceManager.java文件可以在以下目录中找到:

packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java

PhoneInterfaceManager的init()方法会在PhoneGlobals类的onCreate()方法中被调用。我们要关注的是最后的publish()方法,publish()方法调用ServiceManager的addService()方法,启动PhoneInterfaceManager。这使得PhoneInterfaceManager可以作为ITelephony的服务端,ITelephony接口中所声明的功能,将在被调用时由PhoneInterfaceManager中的实现来完成。

/*** Initialize the singleton PhoneInterfaceManager instance.* This is only done once, at startup, from PhoneApp.onCreate().*/
/* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {synchronized (PhoneInterfaceManager.class) {if (sInstance == null) {sInstance = new PhoneInterfaceManager(app, phone);} else {Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);}return sInstance;}
}/** Private constructor; @see init() */
private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {mApp = app;mPhone = phone;mCM = PhoneGlobals.getInstance().mCM;mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);mMainThreadHandler = new MainThreadHandler();mTelephonySharedPreferences =PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());mSubscriptionController = SubscriptionController.getInstance();mNetworkScanRequestTracker = new NetworkScanRequestTracker();publish();
}private void publish() {if (DBG) log("publish: " + this);ServiceManager.addService("phone", this);
}

接下来,我们通过两个方法来看一看PhoneInterfaceManager类中是如何实现ITelephony接口中的方法的。

1. setPreferredNetworkType(int subId, int networkType)

@Override
public boolean setPreferredNetworkType(int subId, int networkType) {// 做权限判断TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setPreferredNetworkType");if (DBG) log("setPreferredNetworkType: subId " + subId + " type " + networkType);// 将要设置网络模式的Msg发送给Handler// 由于设置网络模式是耗时操作,需要给Modem发消息,因此这里采用异步的方式Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));// 如果Msg发送成功,则更新Settings的数据库if (success) {Settings.Global.putInt(mPhone.getContext().getContentResolver(),Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);}// 返回结果return success;
}private final class MainThreadHandler extends Handler {@Overridepublic void handleMessage(Message msg) {......switch (msg.what) {......// Handler处理设置网络模式的Msgcase CMD_SET_PREFERRED_NETWORK_TYPE:request = (MainThreadRequest) msg.obj;onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);int networkType = (Integer) request.argument;// 调用getPhoneFromRequest()方法得到phone对象,并调用其setPreferredNetworkType()方法,最终发送RIL命令给ModemgetPhoneFromRequest(request).setPreferredNetworkType(networkType, onCompleted);break;......}}......}

2. getImeiForSlot(int slotIndex, String callingPackage)

@Override
public String getImeiForSlot(int slotIndex, String callingPackage) {// 通过传入的slotIndex获取到对应的phone对象Phone phone = PhoneFactory.getPhone(slotIndex);// 如果获取到的phone对象为空,返回nullif (phone == null) {return null;}// 获取当前phone对象中所插Sim卡的subIdint subId = phone.getSubId();// 做权限判断,若没有相应权限,则返回nullif (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage, "getImeiForSlot")) {return null;}// 调用phone对象的getImei()方法,并返回其IMEI值return phone.getImei();
}

不难发现,其实在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。

由于代码量实在太大,我们这里就不再过多举例了。如果大家对其他的方法感兴趣,也可以自行阅读代码中的实现。

三、TelephonyManager.java

刚才在阅读ITelephony的接口注释时,我们看到,Google指出应该使用TelephonyManager来调用ITelephony中的方法而不是直接使用ITelephony。并且,ITelephony在SDK中被隐藏了,App的开发过程中是没办法获取到ITelephony接口的。

那么我们接下来就看一看TelephonyManager这个类。

frameworks/base/telephony/java/android/telephony/TelephonyManager.java

TelephonyManager是一个系统服务,任何应用都可以通过调用Context类中提供的实例方法getSystemService(Context.TELEPHONY_SERVICE),来获取到TelephonyManager的实例对象。

TelephonyManager telephonyManager =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);

那么对应到我们刚才在介绍PhoneInterfaceManager类时所讲的两个方法,我们来看看TelephonyManager中是如何调用他们的。

获取ITelephony

首先,TelephonyManager中定义了getITelephony(),该方法会以ITelephony的实例作为返回值

/**
* @hide
*/
private ITelephony getITelephony() {return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}

1. setPreferredNetworkType(int subId, int networkType)

/*** Set the preferred network type.* Used for device configuration by some CDMA operators.** <p>Requires Permission:* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling* app has carrier privileges (see {@link #hasCarrierPrivileges}).** @param subId the id of the subscription to set the preferred network type for.* @param networkType the preferred network type, defined in RILConstants.java.* @return true on success; false on any failure.* @hide*/
public boolean setPreferredNetworkType(int subId, int networkType) {try {// 调用getITelephony()方法得到ITelephony的实例对象ITelephony telephony = getITelephony();if (telephony != null) {// 调用ITelephony实例对象的setPreferredNetworkType()方法// 实际上是远程调用了PhoneInterfaceManager中的setPreferredNetworkType()方法return telephony.setPreferredNetworkType(subId, networkType);}} catch (RemoteException ex) {Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);} catch (NullPointerException ex) {Rlog.e(TAG, "setPreferredNetworkType NPE", ex);}return false;
}/*** Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.** <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).** @return true on success; false on any failure.*/
public boolean setPreferredNetworkTypeToGlobal() {return setPreferredNetworkTypeToGlobal(getSubId());
}/*** Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.** <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).** @return true on success; false on any failure.* @hide*/
public boolean setPreferredNetworkTypeToGlobal(int subId) {return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
}

整理一下调用关系:

1) App获取TelephonyManager的实例对象

2) 调用TelephonyManager的setPreferredNetworkType()方法

3) setPreferredNetworkType()方法中获取到ITelephony的实例对象

4) 调用ITelephony的setPreferredNetworkType()方法

5) 远程调用了PhoneInterfaceManager中的setPreferredNetworkType()方法

2. getImei(int slotIndex)

/*** Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not* available.** <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).** @param slotIndex of which IMEI is returned*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getImei(int slotIndex) {// 调用getITelephony()方法得到ITelephony的实例对象ITelephony telephony = getITelephony();if (telephony == null) return null;try {// 调用ITelephony实例对象的getImeiForSlot()方法// 实际上是远程调用了PhoneInterfaceManager中的getImeiForSlot()方法return telephony.getImeiForSlot(slotIndex, getOpPackageName());} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {return null;}
}

整理一下调用关系:

1) App获取TelephonyManager的实例对象

2) 调用TelephonyManager的getImei()方法

3) getImei()方法中获取到ITelephony的实例对象

4) 调用ITelephony的getImeiForSlot()方法

5) 远程调用了PhoneInterfaceManager中的getImeiForSlot()方法

四、分析与思考

ITelephony、PhoneInterfaceManager与TelephonyManager三者之间的调用关系已经说明白了。但是,不知道大家有没有想过,Android为什么要在Telephony模块中使用AIDL呢?

首先,我们知道,phone对象(双卡手机有两个phone对象)是在com.android.phone这个进程中被创建出来的,他仅能在com.android.phone进程内部被使用。如果我们写了一个App,想要获取手机的IMEI码,那么就需要调用phone对象的getImei()方法。可是,我们所写的App肯定不会跑在com.android.phone进程之中,那就不可能拿到phone对象。拿不到phone对象,那怎么才能调用到phone的对象的方法呢?

这个时候,PhoneInterfaceManager类就派上用场了,PhoneInterfaceManager是跑在com.android.phone进程中服务,其可以调用到phone进程的所有方法。而PhoneInterfaceManager实现了ITelephony接口中的方法,还记得我们在文章开头说的么,用AIDL定义的接口是可以跨进程调用的。

TelephonyManager是一个任意应用都可以获取到的系统服务,当使用TelephonyManager调用ITelephony中的方法时,其实是跨进程调用了com.android.phone进程中PhoneInterfaceManager的具体实现。

这样,无论在哪个线程中,都能通过AIDL间接地远程调用到phone对象的方法,最终实现我们想要完成的工作。

五、Telephony相关模块的AIDL列举

下面表格中,我列举了Telephony相关模块中的一些AIDL接口及其实现,大家如果有兴趣也可以自己在代码中自己阅读一下,相信通过自己的学习以后,可以更好的理解Telephony相关模块中关于AIDL的使用。

No. 名字(定义) 名字(字符串) AIDL path 实现 path
1 Null "telephony.registry" ITelephonyRegistry frameworks/base/telephony/ TelephonyRegistry frameworks/base/services/core/
2 Context.TELEPHONY_SERVICE "phone" ITelephony PhoneInterfaceManager packages/services/Telephony/
3 Null "iphonesubinfo" IPhoneSubInfo PhoneSubInfoController frameworks/opt/telephony/
4 CARRIER_CONFIG_SERVICE "carrier_config" ICarrierConfigLoader CarrierConfigLoader packages/services/Telephony/
5 Null "isub" ISub SubscriptionController frameworks/opt/telephony/
6 Context.TELECOM_SERVICE "telecom" ITelecomService frameworks/base/telecom/ TelecomServiceImpl中的mBinderImpl packages/services/Telecomm/

AIDL在Telephony中的应用 —— ITelephony 详解 (以Android 9.0源码讲解)相关推荐

  1. word2vec 中的数学原理详解(六)若干源码细节

    word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,它简单.高效,因此引起了很多人的关注.由于 word2vec 的作者 Tomas Miko ...

  2. Android中mesure过程详解 (结合Android 4.0.4 最新源码)

    如何遍历并绘制View树?之前的文章Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)中提到invalidate()最后会发起一个View树遍历的请求,并通 ...

  3. Android中layout过程详解 (结合Android 4.0.4 最新源码)

    上一篇文章Android中mesure过程详解 (结合Android 4.0.4 最新源码)介绍了View树的measure过程,相对与measure过程,本文介绍的layout过程要简单多了,正如l ...

  4. Android 8.0学习(32)---Android 8.0源码目录结构详解

    Android 8.0源码目录结构详解 android的移植按如下流程:     (1)android linux 内核的普通驱动移植,让内核可以在目标平台上运行起来.     (2)正确挂载文件系统 ...

  5. [Linux运维基础]全家桶详解!Linux中RPM包、wget下载、YUM安装、tar包、zip等包管理方式区别与参数详解,附wget下载源码包编译安装方法

    文章目录 一.RPM.tar.gz 1.rpm包格式 2.rpm包管理 3.tar包管理参数 二.wget 1.wget参数 2.wget下载源码包后编译安装 三.YUM 1.YUM工作原理 2. Y ...

  6. android中gravity什么意思,详解介绍android:layout_gravity 和 android:gravity 之间的区别

    android开发必遇问题,最有可能忘记两者之间的区别的问题之一 如下是google搜索出来的结果 记忆方法 联想/形像記法喎? f/ware/vc/"="" targe ...

  7. Android APP:Preference使用详解和实例(附源码)

    Android APP:Preference使用详解和实例 一.Preference 是Android app中重要的控件之一,Settings 模块大部分都是通过Preference 实现的,这里将 ...

  8. C++文件操作详解,实用文件辅助类源码分享,建议收藏自用!

    学习C++的小伙伴,应该会经常遇见对文件进行操作的需求,例如读写文件,作为一个使用频率较高的操作,我们每次重复地编写代码,就是浪费劳动力了,所以作者将自己常用的文件操作封装成了一个类,需要的小伙伴自取 ...

  9. android 详解画图,android画图之贝塞尔曲线讲解详解

    首先对于<赛贝尔曲线>不是很了解的童鞋,请自觉白度百科.google等等... 为了方便偷懒的童鞋,这里给个<贝赛尔曲线>百科地址,以及一段话简述<贝赛尔曲线>: ...

最新文章

  1. 用Python写一份独特的元宵节祝福
  2. Matplotlib: “Unknown projection '3d'” error
  3. 【Linux】一步一步学Linux——curl命令(193)
  4. Versions maven plugin 修改版本
  5. Minidao_1.6.2版本发布,超轻量Java持久化框架
  6. IPC通信:Posix消息队列的属性设置
  7. windows 7下不能使用telnet方法
  8. 简单方法去除WPS广告
  9. oracle里round函数补0,Oracle的Round函数
  10. 阿里巴巴未来十年使命、愿景和价值观
  11. Excel信息批量替换Word模板生成新文件
  12. threejs 加载两个场景_three.js 场景切换
  13. Visual Studio 2008 安装出错 无法安装 的解决办法
  14. Xen、OpenVZ、KVM、Hyper-V、VMWare虚拟化技术介绍
  15. rsa不同编程语言互相加解密
  16. 杰理之linein为DAC进使用打断方式播放提示音,一直有linein背景音【篇】
  17. pycharm调试时出现十分缓慢,变量数据没法预览的解决方法
  18. 安卓逆向从入门到嗝屁之一道入门级的CTF题目
  19. 高仿钉钉和小米的日历控件
  20. DeFi:数字银行的下一站?

热门文章

  1. 初中生计算机课程教案,初中信息技术上机课题【初中信息技术课上机实验教学】...
  2. 2022-2028全球与中国模板系统(SOM)市场现状及未来发展趋势
  3. 计算机学开发,Lazarus
  4. 注意:出海企业选择CRM系统和实施团队时需要避开这些坑
  5. mui中innerHTML问题
  6. 实验三 字符类型及其操作(新)
  7. 【深度学习】全连接层 (Full Connection,FC)
  8. 从魔术师到统计学家 2
  9. Matlab 计算 FFT 的方法及幅值问题
  10. 大厂面试题含答案(一)