AIDL在Telephony中的应用 —— ITelephony 详解 (以Android 9.0源码讲解)
轉(zhuǎn)載請(qǐng)注明出處:https://blog.csdn.net/turtlejj/article/details/84861020,謝謝~
?
??????? Telephony模塊中大量的使用了AIDL,但網(wǎng)上卻很少有文章,把AIDL的用法、作用以及為什么要使用AIDL講解的比較細(xì)致的文章。因此,我希望通過通過對(duì)ITelephony的分析,以點(diǎn)帶面,能讓更多的同學(xué)掌握AIDL在Telephony模塊中的應(yīng)用,以便能更好地理解Telephony模塊的功能。
??????? 由于篇幅的原因,我們并不會(huì)在本篇文章中對(duì)AIDL的基本用法進(jìn)行講解,如果有對(duì)AIDL完全不了解的同學(xué),可以先自行百度,或者查閱Google相關(guān)文檔(這里的鏈接是中文文檔)來進(jìn)行學(xué)習(xí),在有了一定的基礎(chǔ)后,再來看這篇文章,我相信會(huì)更有幫助。
??????? 我會(huì)盡量用通俗易懂的詞語來進(jìn)行描述,但難免可能會(huì)有比喻不當(dāng)?shù)那闆r出現(xiàn)。同時(shí),也請(qǐng)大家對(duì)博客中的錯(cuò)誤以及紕漏予以指出,我一定第一時(shí)間進(jìn)行更正。
??????? 話不多說,我們這就開始吧~
?
一、簡介
??????? AIDL(Android Interface Definition Language),即"Android接口定義語言"。是Android中進(jìn)行進(jìn)程間通信的一種解決方案,其本質(zhì)是Android的Binder機(jī)制。
??????? 顧名思義,AIDL是用來編寫接口用的,其編寫出來的接口與普通的接口之間的區(qū)別就是,使用AIDL編寫的接口是可以進(jìn)行跨進(jìn)程調(diào)用的;而普通的接口,只能在本進(jìn)程中實(shí)現(xiàn)并調(diào)用,無法實(shí)現(xiàn)跨進(jìn)程調(diào)用。
??????? 在Telephony模塊中,Google的工程師通過AIDL在framework中編寫了ITelephony.aidl,聲明了一系列的方法,并在PhoneApp中使用PhoneInterfaceManager.java實(shí)現(xiàn)了ITelephony.aidl中的接口。
??????? 其中ITelephony作為客戶端,而PhoneInterfaceManager作為服務(wù)器。調(diào)用ITelephony中的方法,其實(shí)就是遠(yuǎn)程調(diào)用了PhoneInterfaceManager中的具體實(shí)現(xiàn),從而完成Telephony相關(guān)的操作和設(shè)置。
??????? 下面我們將詳細(xì)為大家進(jìn)行分析。
?
二、ITelephony.aidl與PhoneInterfaceManager.java
?
? (一) ITelephony.aidl
??????? 首先,我們來說一說ITelephony.aidl這個(gè)文件。ITelephony.aidl文件可以在以下目錄中找到
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl??????? 由于代碼太長,我就不在這里全部貼出來了,大家可以自己從源碼中找到該文件進(jìn)行查看;如果沒有下載安卓源碼,也可以在AndroidXRef 網(wǎng)站中查看該文件。
??????? 首先,我們來看對(duì)ITelephony的描述。ITelephony接口是與phone對(duì)象相互作用的,我們可以理解為,使用ITelephony來間接調(diào)用phone對(duì)象的方法。并且,Google推薦不要直接使用該接口中的聲明的方法,而是盡可能地通過TelephonyManager來進(jìn)行調(diào)用。
??????? 簡而言之,如果我們想要對(duì)phone對(duì)象進(jìn)行操作,或調(diào)用其方法,我們應(yīng)該通過TelephonyManager中提供的方法來進(jìn)行操作。TelephonyManager中的方法其實(shí)就是通過ITelephony來間接調(diào)用phone對(duì)象的。后面我們會(huì)通過分析代碼來具體說明。
??????? 在注釋的最后,有一個(gè){@hide},表明,ITelephony在SDK中是隱藏的,開發(fā)者無法直接調(diào)用到。
/*** 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個(gè),因此我們這里只舉一些比較常見且通俗易懂的方法進(jìn)行列舉。
1. 通話相關(guān)
// 撥打電話 void dial(String number);// 掛斷電話 boolean endCall();// 接聽呼入電話 void answerRingingCall();=======================================================// 返回電話是否處于摘機(jī)狀態(tài),即是否存在active call或holding call boolean isOffhook(String callingPackage);// 判斷電話是否在響鈴 boolean isRinging(String callingPackage);// 判斷電話是否處于idle狀態(tài) boolean isIdle(String callingPackage);2. 網(wǎng)絡(luò)模式相關(guān)
// 獲取當(dāng)前數(shù)據(jù)業(yè)務(wù)注冊(cè)在哪種RAT上 int getDataNetworkTypeForSubscriber(int subId, String callingPackage);// 獲取當(dāng)前語音業(yè)務(wù)注冊(cè)在哪種RAT上 int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage);============================================================================// 設(shè)置網(wǎng)絡(luò)注冊(cè)模式為自動(dòng)選網(wǎng) void setNetworkSelectionModeAutomatic(int subId);// 設(shè)置網(wǎng)絡(luò)注冊(cè)模式為手動(dòng)選網(wǎng) boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,boolean persistSelection);// 獲取搜網(wǎng)后的結(jié)果 CellNetworkScanResult getCellNetworkScanResults(int subId);// 發(fā)起搜網(wǎng)請(qǐng)求 int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,in IBinder binder);// 停止搜網(wǎng) void stopNetworkScan(int subId, int scanId);// 設(shè)置網(wǎng)絡(luò)模式(即,4G/3G/2G, 3G/2G, 2G only等) boolean setPreferredNetworkType(int subId, int networkType);3. 手機(jī)相關(guān)信息
// 獲取設(shè)備ID String getDeviceId(String callingPackage);// 獲取IMEI String getImeiForSlot(int slotIndex, String callingPackage);// 獲取MEID String getMeidForSlot(int slotIndex, String callingPackage);// 獲取設(shè)備軟件版本號(hào) String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage);4. Sim卡相關(guān)
// 使用PIN碼解鎖Sim卡 boolean supplyPin(String pin);// 使用PUK碼修改PIN碼 boolean supplyPuk(String puk, String pin);// 判斷是否插入了Sim卡 boolean hasIccCard();// 獲取默認(rèn)Sim卡 int getDefaultSim();??????? 我們可以看到,ITelephony中聲明的方法幾乎涵蓋了Telephony模塊的方方面面,一旦實(shí)現(xiàn)了這些方法,就可以通過這些方法接打電話、設(shè)置網(wǎng)絡(luò)模式、獲取手機(jī)信息等操作。那么這些方法在哪里實(shí)現(xiàn)呢,那就是我們接下來要講的PhoneInterfaceManager類。
?
? (二) PhoneInterfaceManager.java
??????? PhoneInterfaceManager.java文件可以在以下目錄中找到:
packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java??????? PhoneInterfaceManager的init()方法會(huì)在PhoneGlobals類的onCreate()方法中被調(diào)用。我們要關(guān)注的是最后的publish()方法,publish()方法調(diào)用ServiceManager的addService()方法,啟動(dòng)PhoneInterfaceManager。這使得PhoneInterfaceManager可以作為ITelephony的服務(wù)端,ITelephony接口中所聲明的功能,將在被調(diào)用時(shí)由PhoneInterfaceManager中的實(shí)現(xiàn)來完成。
/*** 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); }??????? 接下來,我們通過兩個(gè)方法來看一看PhoneInterfaceManager類中是如何實(shí)現(xiàn)ITelephony接口中的方法的。
1. setPreferredNetworkType(int subId, int networkType)
@Override public boolean setPreferredNetworkType(int subId, int networkType) {// 做權(quán)限判斷TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setPreferredNetworkType");if (DBG) log("setPreferredNetworkType: subId " + subId + " type " + networkType);// 將要設(shè)置網(wǎng)絡(luò)模式的Msg發(fā)送給Handler// 由于設(shè)置網(wǎng)絡(luò)模式是耗時(shí)操作,需要給Modem發(fā)消息,因此這里采用異步的方式Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));// 如果Msg發(fā)送成功,則更新Settings的數(shù)據(jù)庫if (success) {Settings.Global.putInt(mPhone.getContext().getContentResolver(),Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);}// 返回結(jié)果return success; }private final class MainThreadHandler extends Handler {@Overridepublic void handleMessage(Message msg) {......switch (msg.what) {......// Handler處理設(shè)置網(wǎng)絡(luò)模式的Msgcase CMD_SET_PREFERRED_NETWORK_TYPE:request = (MainThreadRequest) msg.obj;onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);int networkType = (Integer) request.argument;// 調(diào)用getPhoneFromRequest()方法得到phone對(duì)象,并調(diào)用其setPreferredNetworkType()方法,最終發(fā)送RIL命令給ModemgetPhoneFromRequest(request).setPreferredNetworkType(networkType, onCompleted);break;......}}......}?
2. getImeiForSlot(int slotIndex, String callingPackage)
@Override public String getImeiForSlot(int slotIndex, String callingPackage) {// 通過傳入的slotIndex獲取到對(duì)應(yīng)的phone對(duì)象Phone phone = PhoneFactory.getPhone(slotIndex);// 如果獲取到的phone對(duì)象為空,返回nullif (phone == null) {return null;}// 獲取當(dāng)前phone對(duì)象中所插Sim卡的subIdint subId = phone.getSubId();// 做權(quán)限判斷,若沒有相應(yīng)權(quán)限,則返回nullif (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage, "getImeiForSlot")) {return null;}// 調(diào)用phone對(duì)象的getImei()方法,并返回其IMEI值return phone.getImei(); }??????? 不難發(fā)現(xiàn),其實(shí)在PhoneInterfaceManager類中,就是通過獲取phone對(duì)象并調(diào)用其方法來實(shí)現(xiàn)具體功能的。
??????? 由于代碼量實(shí)在太大,我們這里就不再過多舉例了。如果大家對(duì)其他的方法感興趣,也可以自行閱讀代碼中的實(shí)現(xiàn)。
?
三、TelephonyManager.java
??????? 剛才在閱讀ITelephony的接口注釋時(shí),我們看到,Google指出應(yīng)該使用TelephonyManager來調(diào)用ITelephony中的方法而不是直接使用ITelephony。并且,ITelephony在SDK中被隱藏了,App的開發(fā)過程中是沒辦法獲取到ITelephony接口的。
??????? 那么我們接下來就看一看TelephonyManager這個(gè)類。
frameworks/base/telephony/java/android/telephony/TelephonyManager.java??????? TelephonyManager是一個(gè)系統(tǒng)服務(wù),任何應(yīng)用都可以通過調(diào)用Context類中提供的實(shí)例方法getSystemService(Context.TELEPHONY_SERVICE),來獲取到TelephonyManager的實(shí)例對(duì)象。
TelephonyManager telephonyManager =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);??????? 那么對(duì)應(yīng)到我們剛才在介紹PhoneInterfaceManager類時(shí)所講的兩個(gè)方法,我們來看看TelephonyManager中是如何調(diào)用他們的。
? 獲取ITelephony
??????? 首先,TelephonyManager中定義了getITelephony(),該方法會(huì)以ITelephony的實(shí)例作為返回值
/** * @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 {// 調(diào)用getITelephony()方法得到ITelephony的實(shí)例對(duì)象ITelephony telephony = getITelephony();if (telephony != null) {// 調(diào)用ITelephony實(shí)例對(duì)象的setPreferredNetworkType()方法// 實(shí)際上是遠(yuǎn)程調(diào)用了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); }??????? 整理一下調(diào)用關(guān)系:
??????????? 1) App獲取TelephonyManager的實(shí)例對(duì)象
??????????? 2) 調(diào)用TelephonyManager的setPreferredNetworkType()方法
??????????? 3) setPreferredNetworkType()方法中獲取到ITelephony的實(shí)例對(duì)象
??????????? 4) 調(diào)用ITelephony的setPreferredNetworkType()方法
??????????? 5) 遠(yuǎn)程調(diào)用了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) {// 調(diào)用getITelephony()方法得到ITelephony的實(shí)例對(duì)象ITelephony telephony = getITelephony();if (telephony == null) return null;try {// 調(diào)用ITelephony實(shí)例對(duì)象的getImeiForSlot()方法// 實(shí)際上是遠(yuǎn)程調(diào)用了PhoneInterfaceManager中的getImeiForSlot()方法return telephony.getImeiForSlot(slotIndex, getOpPackageName());} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {return null;} }??????? 整理一下調(diào)用關(guān)系:
??????????? 1) App獲取TelephonyManager的實(shí)例對(duì)象
??????????? 2) 調(diào)用TelephonyManager的getImei()方法
??????????? 3) getImei()方法中獲取到ITelephony的實(shí)例對(duì)象
??????????? 4) 調(diào)用ITelephony的getImeiForSlot()方法
??????????? 5) 遠(yuǎn)程調(diào)用了PhoneInterfaceManager中的getImeiForSlot()方法
?
四、分析與思考
??????? ITelephony、PhoneInterfaceManager與TelephonyManager三者之間的調(diào)用關(guān)系已經(jīng)說明白了。但是,不知道大家有沒有想過,Android為什么要在Telephony模塊中使用AIDL呢?
??????? 首先,我們知道,phone對(duì)象(雙卡手機(jī)有兩個(gè)phone對(duì)象)是在com.android.phone這個(gè)進(jìn)程中被創(chuàng)建出來的,他僅能在com.android.phone進(jìn)程內(nèi)部被使用。如果我們寫了一個(gè)App,想要獲取手機(jī)的IMEI碼,那么就需要調(diào)用phone對(duì)象的getImei()方法。可是,我們所寫的App肯定不會(huì)跑在com.android.phone進(jìn)程之中,那就不可能拿到phone對(duì)象。拿不到phone對(duì)象,那怎么才能調(diào)用到phone的對(duì)象的方法呢?
??????? 這個(gè)時(shí)候,PhoneInterfaceManager類就派上用場了,PhoneInterfaceManager是跑在com.android.phone進(jìn)程中服務(wù),其可以調(diào)用到phone進(jìn)程的所有方法。而PhoneInterfaceManager實(shí)現(xiàn)了ITelephony接口中的方法,還記得我們?cè)谖恼麻_頭說的么,用AIDL定義的接口是可以跨進(jìn)程調(diào)用的。
??????? TelephonyManager是一個(gè)任意應(yīng)用都可以獲取到的系統(tǒng)服務(wù),當(dāng)使用TelephonyManager調(diào)用ITelephony中的方法時(shí),其實(shí)是跨進(jìn)程調(diào)用了com.android.phone進(jìn)程中PhoneInterfaceManager的具體實(shí)現(xiàn)。
??????? 這樣,無論在哪個(gè)線程中,都能通過AIDL間接地遠(yuǎn)程調(diào)用到phone對(duì)象的方法,最終實(shí)現(xiàn)我們想要完成的工作。
?
五、Telephony相關(guān)模塊的AIDL列舉
??????? 下面表格中,我列舉了Telephony相關(guān)模塊中的一些AIDL接口及其實(shí)現(xiàn),大家如果有興趣也可以自己在代碼中自己閱讀一下,相信通過自己的學(xué)習(xí)以后,可以更好的理解Telephony相關(guān)模塊中關(guān)于AIDL的使用。
| No. | 名字(定義) | 名字(字符串) | AIDL | path | 實(shí)現(xiàn) | 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/ |
?
總結(jié)
以上是生活随笔為你收集整理的AIDL在Telephony中的应用 —— ITelephony 详解 (以Android 9.0源码讲解)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 任务栏没有计算机快捷方式,电脑程序在运行
- 下一篇: Android R版本 MtkSetti