转载请注明出处: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这个文件。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个,因此我们这里只举一些比较常见且通俗易懂的方法进行列举。
// 拨打电话
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);
// 获取当前数据业务注册在哪种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);
// 获取设备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);
// 使用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文件可以在以下目录中找到:
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接口中的方法的。
@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 {
@Override
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
// Handler处理设置网络模式的Msg
case 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命令给Modem
getPhoneFromRequest(request).setPreferredNetworkType(networkType, onCompleted);
break;
......
}
}
......
}
@Override
public String getImeiForSlot(int slotIndex, String callingPackage) {
// 通过传入的slotIndex获取到对应的phone对象
Phone phone = PhoneFactory.getPhone(slotIndex);
// 如果获取到的phone对象为空,返回null
if (phone == null) {
return null;
}
// 获取当前phone对象中所插Sim卡的subId
int subId = phone.getSubId();
// 做权限判断,若没有相应权限,则返回null
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "getImeiForSlot")) {
return null;
}
// 调用phone对象的getImei()方法,并返回其IMEI值
return phone.getImei();
}
不难发现,其实在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。
由于代码量实在太大,我们这里就不再过多举例了。如果大家对其他的方法感兴趣,也可以自行阅读代码中的实现。
刚才在阅读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中是如何调用他们的。
首先,TelephonyManager中定义了getITelephony(),该方法会以ITelephony的实例作为返回值
/**
* @hide
*/
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
/**
* Set the preferred network type.
* Used for device configuration by some CDMA operators.
*
* 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.
*
*
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.
*
*
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()方法
/**
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
* 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的使用。
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/ |