溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

發(fā)布時(shí)間:2020-06-07 15:25:47 來源:網(wǎng)絡(luò) 閱讀:1400 作者:屠夫章哥 欄目:移動(dòng)開發(fā)

#############需求#############

最近我的一位同事大概是利用下面的方法監(jiān)聽電話的狀態(tài)

????????https://www.pocketdigi.com/20110725/417.html?


????????https://blog.csdn.net/lyen2010/article/details/42590099 ?

????????相關(guān)demo下載鏈接:https://download.csdn.net/download/weiyirong/6872889

本來我是將系統(tǒng)的ITelephony.aidl復(fù)制進(jìn)去,但是編譯會(huì)報(bào)not fount the import class xx錯(cuò)誤,于是看了一下上面的demo,對(duì)aidl作了簡(jiǎn)化:

interface?ITelephony?{
????boolean?endCall();

????void?answerRingingCall();
????boolean?enableDataConnectivity();

????boolean?disableDataConnectivity();

????boolean?isDataConnectivityPossible();
}

????? ? aidl文件及目錄建好,重新rebuild工程,就會(huì)在對(duì)應(yīng)的build目錄下生成對(duì)應(yīng)的java文件

????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼????

然后我同事說遇到兩個(gè)問題

????????1.電話接通之后,無法掛斷

????????2.通話狀態(tài)無法正常顯示(準(zhǔn)確的說是:通話中的狀態(tài)不顯示)? ?

我寫了個(gè)測(cè)試demo

????????掛斷方法:

????public?void?hangup_call(View?view)?{
????????Log.e(TAG,"hangup_call?START");
????????...
????}

? ? ? ? 接通電話之后,點(diǎn)擊掛斷按鈕:無響應(yīng),日志也沒有打。? ? ? ? ? ? ? ??

????????我隱隱約約感覺線程阻塞了,果然接聽加上線程就解決了問題。上面的問題就是接聽電話阻塞了主線程,所以掛斷按鈕點(diǎn)擊不了,廣播也阻塞了。

????????


#############源碼研究#############

以下源碼基于android6.0.1


撥號(hào)源碼詳解:

????? ?https://so.csdn.net/so/search/s.do?q=Android6.0%E7%9A%84phone%E5%BA%94%E7%94%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90&t=blog&u=u014386544


撥號(hào)源碼架構(gòu):

????????https://www.jianshu.com/p/ca4ab4e9817f/? ? ?(一個(gè)電話應(yīng)用,實(shí)際由多個(gè)代碼模塊組成。由dialer下的Android.mk可以看出)


????????https://blog.csdn.net/wds1181977/article/details/61920067? (介紹各個(gè)模塊的作用)



一、撥號(hào)

????? 假如我不想調(diào)用系統(tǒng)的INTENT來打電話(最直接的方法就是調(diào)用ITelephony,文章開頭已經(jīng)介紹過),那么就得對(duì)系統(tǒng)打電話的INTENT做一番探索了。

????????

????? 通過調(diào)用Intent.ACTION_CALL,會(huì)打開系統(tǒng)的撥號(hào)引用。??

????????向外撥號(hào),發(fā)現(xiàn)當(dāng)前顯示的activity是(通過sdk工具Android源碼個(gè)個(gè)擊破之撥號(hào)源碼):

????????????????????InCallActivity.java

????????現(xiàn)在,我們需求是想自己實(shí)現(xiàn)撥號(hào)級(jí)來電接聽,不走系統(tǒng)UI 。

????????所以,我的思路是先確定Intent.ACTION_CALL是被誰處理了,怎么處理的,如何調(diào)起InCallActivity,InCallActivity又干了啥。

  • ? InCallActivity被誰打開的

    ?在xref全局搜索,發(fā)現(xiàn)InCallPresenter.java這個(gè)類

    ?Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

    通過官方的解釋,可以知道此類正是管理通話狀態(tài), 開啟InCallActivity。

    搜索InCallPresenter 又會(huì)被很多類調(diào)用,可見此類是集中處理通話狀態(tài)的核心類。其中有?InCallServiceImpl,InCallServiceImpl注冊(cè)在dialer的清單文件里:

    Android源碼個(gè)個(gè)擊破之撥號(hào)源碼



? ? 二、屏蔽原始UI

????????????? 1.


    • https://blog.csdn.net/zuiaikg703/article/details/8178028??

???? 電話的UI都是通過InCallActivity添加Fragment實(shí)現(xiàn)的,所以,只要把其UI屏蔽掉就可以了。(修改源碼要一針見血,找到最佳修改位置。這樣既能減少工作量又能避免修改錯(cuò)誤)

????????????????

? ? ? ? ? ? ? 2.但是來電時(shí)顯示的那個(gè)懸浮窗是如何出現(xiàn)的呢?如何屏蔽掉?

??????????????https://blog.csdn.net/u012439416/article/details/78946337

??????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????????? 首先,得從“來電”來出發(fā)尋找,其實(shí)這個(gè)UI就叫做StatusBarNotifier

????????????? 在InCallPresenter里找到一些蛛絲馬跡:

????????????? ?Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

? ? ? ? ? ? ? ? 看看官方對(duì)此類的解釋

????????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????????????準(zhǔn)備把下面的方法注釋掉,應(yīng)該就不會(huì)顯示通知欄

????????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

? ? ? ? ? ? ? ? 經(jīng)測(cè)試,注釋上面的方法,確實(shí)可以屏蔽來電時(shí)顯示的通知欄。

??

? ? 三、主動(dòng)撥號(hào)之后無法掛斷的問題

????????????在(二.1),屏蔽IncallUI之后,發(fā)現(xiàn)主動(dòng)往外打電話之后,其他的按鈕沒法點(diǎn)擊。

???????public?void?call(View?view)?{
????????Log.d(TAG,?"call:?");
/*????????//取得一個(gè)Callintent
????????final?Intent?intent?=?CallUtil.getCallIntent("13121116227");

????????//交給DialerUtils去處理
????????DialerUtils.startActivityWithErrorToast(this,?intent);*/

????????callThread?=?new?Thread(){
????????????@Override
????????????public?void?run()?{
????????????????try?{
????????????????????ITelephony?iTelephony?=?PhoneUtils.getITelephony((TelephonyManager)MainActivity.this.getSystemService(
????????????????????????????Context.TELEPHONY_SERVICE));
????????????????????iTelephony.call(getPackageName(),"17316107592");
????????????????}?catch?(Exception?e)?{
????????????????????Log.e(TAG,?"[Broadcast]Exception111="+e.getMessage(),?e);
????????????????}
????????????}
????????};
????????callThread.start();
????}

? ? ? ? 所以我猜想有2種原因:

????????1)打電話時(shí)InCallActivity彈出來了(眼睛看不見),擋住了我的Activity

????????2)系統(tǒng)電話的call方法阻塞了UI線程

????????

????? ? 是否是系統(tǒng)的UI擋住了我的Activity:

????????????? 將上面的InCallActivity的布局顯示出來,果然,是有個(gè)系統(tǒng)的撥打電話的界面。

??????????????? Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

???????????? 那么如何隱藏并這個(gè)activity界面并屏蔽它的所有時(shí)間

???????????? 1)將InCallActivity的theme變成透明

??????????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????????????????? ?

????????????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

???????????? 2)屏蔽InCallActivity的點(diǎn)擊事件

???????????????????? 在InCallActivity的onCreate方法里加上下面代碼

??????????this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

? ? ? ? ? ? ?3)屏蔽InCallActivity對(duì)狀態(tài)欄的改變

?????????????????????????? ??

??????

????????對(duì)系統(tǒng)電話的call方法發(fā)起追蹤研究:

????????1.找service

????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

??????????

??????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????????

????????? ? 注意Context.TELEPHONY_SERVICE的值是“phone”

????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

???????????? 通過搜索“extends ITelephony.Stub?”或者“phone”可以找到ITelephony的Service類/packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java:

???????????? 可以看到這個(gè)service

?????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

?????????????上面的init方法在PhoneGlobals的onCreate方法里被調(diào)用

??????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????? ?2.分析service的相應(yīng)call方法

??????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

????????????????

????????????????Android源碼個(gè)個(gè)擊破之撥號(hào)源碼

? ? ? ? ? ? ? ?可以看到最終也是通過Intent去打開撥號(hào)頁面的。

??? ? 三、主動(dòng)撥號(hào)之后對(duì)方?jīng)]有接聽直接掛斷,ITelephony沒有回調(diào)的問題。

????????? ? ? ?此種情況雖然ITelephony沒有回調(diào),但是系統(tǒng)會(huì)有個(gè)無人接聽的提示語音,可以在此處修改系統(tǒng)源碼,人工發(fā)一個(gè)空閑狀態(tài)廣播。

? ? ??

? ? ? 四、去電,PhoneListener的狀態(tài)瞎回調(diào)的問題。

? ? ? ? ? ? ? ?去電時(shí):android6.0系統(tǒng)會(huì)立馬發(fā)一個(gè)通話中和空閑的狀態(tài),這些其實(shí)都是錯(cuò)誤的回調(diào)。需要人為的去處理,可以根據(jù)時(shí)間來過濾判斷,但是具有不可靠性。

????????????????????????? ? 一般對(duì)方接通電話之后,會(huì)再回調(diào)一個(gè)正確的通話中狀態(tài)。但有時(shí)候不會(huì)回調(diào)。

????????????????

  • ? ? ? ? ? ? ? 第一個(gè)錯(cuò)誤的空閑的狀態(tài)很好解決:

????????????? 點(diǎn)擊撥打電話的按鈕時(shí),將Constants.isPhoneOut設(shè)置為true。

		case?TelephonyManager.CALL_STATE_IDLE:?//?空閑?0
			Log.d(tag,?"--------------------------CALL_STATE_IDLE00---------------------isPhoneOut?=?"?+?Constants.isPhoneOut);
			
			if(Constants.isPhoneOut?==?true){
				//撥打電話時(shí),系統(tǒng)會(huì)誤發(fā)一個(gè)掛斷的廣播,需要自己區(qū)分處理
				Constants.isPhoneOut?=?false;
				return;
			}

?????????????????????但是如果主動(dòng)往外撥打,對(duì)方?jīng)]有接通或者掛斷,是不會(huì)收到空閑回調(diào)的。上面這種判斷就會(huì)有缺陷,所以準(zhǔn)備找到撥打電話的源碼,去掉撥打電話前誤發(fā)的空閑狀態(tài)。

? ? ? ? ? ? 發(fā)現(xiàn)底層的回調(diào)是這樣的:

07-15?17:16:28.591?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?NO_CALLS?->?NO_CALLS
07-15?17:16:28.666?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:28.725?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?PENDING_OUTGOING
07-15?17:16:28.774?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?PENDING_OUTGOING?->?INCALL
07-15?17:16:28.791?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?INCALL
07-15?17:16:28.819?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?INCALL
07-15?17:16:28.828?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?INCALL
07-15?17:16:28.830?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?INCALL
07-15?17:16:29.111?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?OUTGOING
07-15?17:16:30.776?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:30.833?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:30.849?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:30.863?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:30.878?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:16:30.892?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?OUTGOING
07-15?17:18:13.357?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?OUTGOING?->?INCALL
07-15?17:18:13.416?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?INCALL
07-15?17:18:15.404?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?INCALL?->?NO_CALLS
07-15?17:18:15.448?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?NO_CALLS?->?NO_CALLS
07-15?17:18:15.499?5456-5456/com.android.dialer?I/InCall:?InCallPresenter?-?Phone?switching?state22222:?NO_CALLS?->?NO_CALLS

? ? ? ??? ? 撥打電話,系統(tǒng)立馬會(huì)發(fā) NO_CALLS -> NO_CALLS狀態(tài),也就是app會(huì)無緣無故收到空閑狀態(tài)的原因。但是這個(gè)狀態(tài)不好限制。

????????????等了很久,系統(tǒng)會(huì)發(fā)送OUTGOING -> INCALL,也就是通話中狀態(tài),這也是個(gè)誤發(fā)。app不應(yīng)做處理。
????????????最后會(huì)發(fā)個(gè)INCALL -> NO_CALLS狀態(tài),也就是對(duì)方未接聽或者拒接后系統(tǒng)的一個(gè)超時(shí)回調(diào),這里可以發(fā)一個(gè)空閑廣播。app處理這個(gè)空閑廣播,屏蔽api的空閑回調(diào)即可解決空閑問題。

? ? ? ? ? ? 但是此時(shí)mInCallActivity為null,所以無法通過mInCallActivity發(fā)空閑廣播,改由mContext發(fā)送廣播。經(jīng)驗(yàn)證,mContext果然不為空。

????????????**Log.d在logcat上不輸出日志,但是Log.i可以輸出日志。**

????????????


??????????



????????????XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

????? ????? ?但是通話中狀態(tài)回調(diào)不準(zhǔn)確的問題則不好解決,但是經(jīng)測(cè)試,發(fā)現(xiàn)系統(tǒng)電話應(yīng)用關(guān)于電話狀態(tài)的判斷則十分準(zhǔn)確,只有從源碼入手了。

?? ? ? ? ? ? ?網(wǎng)絡(luò)上搜到的關(guān)于通話中狀態(tài)判斷的解決方案:https://blog.csdn.net/qwe749082787/article/details/78364112

?????????????????

??????????????https://blog.csdn.net/qwe749082787/article/details/78364112? (可以修改源碼,此處發(fā)一個(gè)廣播),事實(shí)證明此方法行不通,撥打電話還沒有通話就回調(diào)了。

????? ? ? ?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

??????????????

????????? 突然我腦洞大開,查看了一下電話應(yīng)用com.android.dialer的日志,發(fā)現(xiàn),在InCallPresenter類里,在來電接聽會(huì)有系統(tǒng)日志:

????????????????Phone switching state: INCOMING -> INCALL

??????????去電時(shí)會(huì)有系統(tǒng)日志:

????????????????Phone switching state: OUTGOING -> INCALL

??????? ? 而且這個(gè)日志不會(huì)重復(fù),可以在上面發(fā)送兩個(gè)自定義廣播,就能準(zhǔn)確監(jiān)聽到通話中狀態(tài)了。

? ? ? ? ? 在測(cè)試中發(fā)現(xiàn)??OUTGOING -> INCALL,這個(gè)不一定只有去電通話中才會(huì)回調(diào),需要結(jié)合前一個(gè)狀態(tài)變化聯(lián)合判斷:

????????? 所以修改InCallPresenter類,緩存上一次的通話狀態(tài)變化,然后對(duì)比方能準(zhǔn)確判斷通話中的狀態(tài)

?????
????????InCallState?newState?=?getPotentialStateFromCallList(callList);
????????InCallState?oldState?=?mInCallState;
????????Log.d(this,?"onCallListChange?oldState=?"?+?oldState?+?"?newState="?+?newState);
????????newState?=?startOrFinishUi(newState);
????????Log.d(this,?"onCallListChange?newState?changed?to?"?+?newState);

????????String?oldStateLast?=?phone_state_cacheSp.getString("oldState",?"");
????????String?newStateLast?=?phone_state_cacheSp.getString("newState",?"");

????????//?Set?the?new?state?before?announcing?it?to?the?world
????????Log.i(this,?"Phone?switching?state22222:?"?+?oldState?+?"?->?"?+?newState);
????????SharedPreferences.Editor?editor?=?phone_state_cacheSp.edit();
????????editor.putString("oldState",oldState.toString());
????????editor.putString("newState",newState.toString());
????????editor.commit();

		if(mInCallActivity?!=?null){
				if("INCOMING".equals(oldState.toString())?&&?"INCALL".equals(newState.toString())){
????????????mInCallActivity.sendBroadcast(new?Intent("CZ_IN"));
		}else?if("OUTGOING".equals(oldState.toString())?&&?"INCALL".equals(newState.toString())){
			if("PENDING_OUTGOING".equals(oldStateLast)?&&?"OUTGOING".equals(newStateLast)){
????????????????Log.i(this,?"Phone?switching?state22222:?"?+?"這是個(gè)假的通話中狀態(tài),舍棄");
			????return;
????????????}
????????????mInCallActivity.sendBroadcast(new?Intent("CZ_OUT"));

????????}
		}
	
????????mInCallState?=?newState;

????

???????????? ????



??????

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI