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

转载请注明出处: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 {
    @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;
            ......
        }
    }

    ......

}

 

2. getImeiForSlot(int slotIndex, String callingPackage)

@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对象并调用其方法来实现具体功能的。

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

 

三、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.
 *
 * 

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()方法

 

2. getImei(int slotIndex)

/**
 * 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接口及其实现,大家如果有兴趣也可以自己在代码中自己阅读一下,相信通过自己的学习以后,可以更好的理解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/

 

你可能感兴趣的:(Android,系统分析)