編輯:關於Android編程
當手機Modem狀態改變後會將狀態變化信息通知到上層,通過《Android 4.4 Kitkat Phone工作流程淺析(八)__Phone狀態分析》和《Android 4.4 Kitkat Phone工作流程淺析(六)__InCallActivity顯示更新流程》的分析,我們知道了Phone狀態的類型,以及這些狀態的上報流程,而本文主要分析Phone狀態改變之後是如何通知到三方應用的。
Phone狀態對於三方應用來講主要包括:TelephonyManager.CALL_STATE_IDLE、TelephonyManager.CALL_STATE_RINGING、TelephonyManager.CALL_STATE_OFFHOOK三種。對於三方應用,通常使用兩種方法來獲知Phone狀態的改變即:
①監聽Phone狀態改變廣播;
②使用PhoneStateListener監聽Phone狀態。PhoneStateListener不僅能監聽Phone狀態改變,同時還能監聽數據連接狀態、Phone服務狀態等狀態改變信息;
兩種方法所對應的相關代碼如下:
//1.注冊廣播監聽Phone狀態改變 //1.1 動態注冊廣播 public class MainActivity extends Activity { private PhoneStateChangedReceiver mPhoneStateChangedReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Dynamic register the broadcast IntentFilter filter = new IntentFilter(); //android.intent.action.PHONE_STATE filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); //android.intent.action.NEW_OUTGOING_CALL filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL); mPhoneStateChangedReceiver = new PhoneStateChangedReceiver(); registerReceiver(mPhoneStateChangedReceiver, filter); } @Override protected void onDestroy() { //unregister the broadcast when activity destroyed unregisterReceiver(mPhoneStateChangedReceiver); super.onDestroy(); } //內部類廣播 public static class PhoneStateChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //Action is android.intent.action.PHONE_STATE //or android.intent.action.NEW_OUTGOING_CALL String action = intent.getAction(); Log.i(Seven,action is +action); if (Intent.ACTION_NEW_OUTGOING_CALL.equals(action)) { //android.intent.extra.PHONE_NUMBER String outgoingNum = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); Log.i(Seven, It's outgoing call. Number is:+outgoingNum); return; } //State is RINGING/OFFHOOK/IDLE String state = intent.getStringExtra(state); //Only state is Ringing can get the incoming_number String incomingNum = intent.getStringExtra(incoming_number); //MTK add for dual SIM support String simId = intent.getStringExtra(simId); Log.i(Seven, state is +state); Log.i(Seven, incomingNum is +incomingNum); Log.i(Seven, simId is +simId); } } } //1.2 靜態注冊廣播 //與動態注冊的區別主要是需要在AndroidManifest.xml中添加標簽 //注意:無論是靜態還是動態注冊均需要添加相應的權限 2.使用PhoneStateListener監聽Phone狀態改變 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //TELEPHONY_SERVICE equals phone TelephonyManager mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); mTelephonyManager.listen(new PhoneStateListener(){ @Override public void onCallStateChanged(int state, String incomingNumber) { switch (state) { case TelephonyManager.CALL_STATE_IDLE: Log.i(Seven,Call state is IDLE); break; case TelephonyManager.CALL_STATE_RINGING: Log.i(Seven,Call state is RINGING); break; case TelephonyManager.CALL_STATE_OFFHOOK: Log.i(Seven,Call state is OFFHOOK); break; default: break; } super.onCallStateChanged(state, incomingNumber); } }, PhoneStateListener.LISTEN_CALL_STATE); }
以上兩種方法均可實現監聽Phone狀態的改變,通過廣播的方式可以監聽去電狀態和Phone狀態改變,而如果通過PhoneStateListener則只能監聽Phone狀態改變。這裡我們先以MT流程為例,從代碼的執行流程上分析Phone狀態改變後是如何通知到三方應用的,後文也會順帶分析去電廣播的發起流程。
通過前面文章的分析,我們知道Phone狀態的改變是Modem發起的,之後通過framework、TeleService並最終通過InCallUI表現到界面上,整個流程如圖1:
通過圖1可以知道,所有的狀態都是從Modem發起並經過Telephony Framework處理之後再向上傳遞的,也是正是在Telephony Framework中,系統將Phone相關狀態告知給了三方應用。
當Phone狀態改變之後,RILJ將相關信息反饋到GsmCallTracker的handleCallProgressInfo()方法中 (AOSP是handlePollCalls) 進行相關的處理。在該方法中會進行Phone狀態的轉換,在上一篇文章《Android 4.4 Kitkat Phone工作流程淺析(八)__Phone狀態分析》中我們有詳細分析,這裡不再詳細解釋。
在handleCallProgressInfo()的updatePhoneState()方法中進行Call.State(Internal)和PhoneConstants.State轉換時,有如下代碼:
private void updatePhoneState() { //... ...省略 if (mState != oldState) { mPhone.notifyPhoneStateChanged(); } }當PhoneConstants.State發生改變時便會觸發mPhone.notifyPhoneStateChanged()。因為我們這裡處理的是GSMPhone,自然而然mPhone為GSMPhone的對象,因此繼續跳轉到GSMPhone.java中,關鍵代碼如下:
void notifyPhoneStateChanged() { //... ...省略 mNotifier.notifyPhoneState(this); }這裡mNotifier實際上是DefaultPhoneNotifier的實例,因此繼續跳轉到DefaultPhoneNotifier中的notifyPhoneState()方法中:
public void notifyPhoneState(Phone sender) { Call ringingCall = sender.getRingingCall(); String incomingNumber = ; if (ringingCall != null && ringingCall.getEarliestConnection() != null){ //如果當前正在響鈴,則獲取來電號碼 incomingNumber = ringingCall.getEarliestConnection().getAddress(); } try { //通過conertCallState方法將Phoneconstants.State轉換為TelephonyManager.CALL_STATE_XX mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber); } catch (RemoteException ex) { // system process is dead } }mRewgistry是TelephonyRegistry的對象,TelephonyRegistry為系統服務在SystemServer中完成注冊,mRegistry的實例化是在DefaultPhoneNotifier的構造方法中,如下:
//這裡MTK做了一些修改,原生並沒有使用public權限 public DefaultPhoneNotifier() { mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( telephony.registry)); }也就是說經過了前面的層層調用之後,最終跳轉到了TelephonyRegistry服務中的notifyCallState()方法,如下:
public void notifyCallState(int state, String incomingNumber) { //檢查是否具有相關修改Phone狀態的權限 if (!checkNotifyPermission(notifyCallState())) { return; } synchronized (mRecords) { mCallState = state; mCallIncomingNumber = incomingNumber; for (Record r : mRecords) { if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { //回調onCallStateChanged方法 r.callback.onCallStateChanged(state, incomingNumber); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } //發送Phone狀態改變的系統廣播 broadcastCallStateChanged(state, incomingNumber); }
到這裡已經找到了整個Phone狀態改變通知發起的地方,分別是這裡的onCallStateChanged回調和broadcastCallStateChanged發起廣播。整個流程如圖2所示:
圖 2 三方應用接收Phone狀態改變流程
通過前面的分析,可以很清楚的了解Phone狀態的通知流程。接下來將從以下三個方面對細節進行展開分析:
1. broadcast廣播類型以及其中攜帶的數據;
2. 關鍵對象初始化流程,包括mPhone、mNotifier、mRegistry對象的實例化流程;
3. PhoneStateListener監聽機制分析,包括Phone狀態注冊監聽原理,以及onCallStateChanged回調執行流程;
前文的例子中有提到,Phone狀態改變之後會在TelephonyRegistry中調用broadcallCallStateChanged()方法將相關狀態廣播出去,關鍵代碼如下:
private void broadcastCallStateChanged(int state, String incomingNumber) { //... ...省略 //TelephonyManager.ACTION_PHONE_STATE_CHANGED 即android.intent.action.PHONE_STATE Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); //PhoneConstants.STATE_KEY 即字符串state intent.putExtra(PhoneConstants.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); //如果incomingNumber不為空則添加到intent的Extra中,對應的key為incoming_number if (!TextUtils.isEmpty(incomingNumber)) { intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); } //MTK Dual SIM support,對應key為simId if (FeatureOption.MTK_GEMINI_SUPPORT) { intent.putExtra(PhoneConstants.GEMINI_SIM_ID_KEY, mySimId); } //發送廣播,接收者為所有人,接收者需要有READ_PHONE_STATE權限 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PHONE_STATE); }通過代碼可以看到,這裡Intent的Action是android.intent.action.PHONE_STATE,其中的Extras有state,如果是來電則incoming_number為來電號碼,以及MTK自己加入的雙卡支持,simId用於標識SIM卡,在這裡即表示哪一張SIM卡狀態發生了改變,最後通過sendBroadcastAsUser將廣播發送出去,廣播接收者需要具有READ_PHONE_STATE權限才可以接收到對應的廣播消息。
在Android 4.0之前,並非使用sendBroadcastAsUser,而是使用sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE)發起廣播通知,兩者相比,前者多了一個UserHandle.ALL的參數,該參數系Google在Android中添加用於區分用戶組的標志。
在Android 4.2 之後Android引入多用戶支持,目前Android平台多用戶僅支持平板設備。雖然在Android 4.2 中我們已經可以看到Android多用戶支持的影子,但直到Android 4.4 google還是沒有正式推出。因為Android 平板和手機共用一套代碼,因此這裡簡單的提一下Android 4.4中多用戶所支持的用戶組。
Android 4.4 用戶分為:UserHandle.ALL、UserHandle.CURRENT、UserHandle.CURRENT_OR_SELF、UserHandle.OWNER四種類型,每種類型的handle不同。詳細描述如下:
/** A user handle to indicate all users on the device */ //設備上所有用戶均可接收到廣播 // handle = -1 UserHandle.ALL /** A user handle to indicate the current user of the device */ //設備上當前用戶可接收到廣播 // handle = -2 UserHandle.CURRENT /** A user handle to indicate that we would like to send to the current * user, but if this is calling from a user process then we will send it * to the caller's user instead of failing wiht a security exception */ //handle = -3 //設備上當前用戶或者該應用所屬用戶可接收到廣播 UserHandle.CURRENT_OR_SELF /** A user handle to indicate the primary/owner user of the device */ // handle = 0 //設備所有者可接收到廣播 UserHandle.OWNER這裡的廣播范圍可以排序為:UserHandle.ALL > UserHandle.OWNER > UserHandle.CURRENT_OR_SELF > UserHandle.CURRENT。注意UserHandle.ALL包含了所有用戶,而OWNER僅僅是設備持有者(注意guest用戶)。
通過前文的例子可以看到,除了注冊監聽PHONE_STATE_CHANGED廣播以外,我們還監聽了NEW_OUTGOING_CALL的廣播。該廣播是在主動呼叫時發起的,當用戶使用手機撥打電話時系統會發出該廣播,也就是說在MO流程發起時會收到以上兩種廣播信息。
在《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》中已經詳細分析了MO的發起流程和經過,通過該文的分析可以知道MO的發起點在Dialer中,經過PhoneCommon處理之後交給TeleService,並在這裡觸發了相應的NEW_OUTGOING_CALL廣播。整個流程如圖3所示,其中因為PhoneCommon部分省略,詳細流程請參閱《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》。
圖 3 OUT_GOING_CALL廣播發起流程
NEW_OUTGOING_CALL廣播的發起流程關鍵代碼如下:public static void sendNewCallBroadcast(Context context, Intent intent, String number, boolean callNow, BroadcastReceiver receiver) { //ACTION is android.intent.action.NEW_OUTGOING_CALL Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); if (number != null) { //android.intent.extra.PHONE_NUMBER 呼叫號碼 broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); } //將intent中的部分Extras賦值到broadcastIntent中 CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent); //android.phone.extra.ALREADY_CALLED callNow is false broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow); //android.phone.extra.ORIGINAL_URI URI信息 broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString()); //該Flag可以是廣播優先級更高 broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); //這是MTK自己加入一些Extras PhoneUtils.checkAndCopyPrivateExtras(intent, broadcastIntent); //使用有序廣播將撥號狀態發送出去 //需要permission android.Manifest.permission.PROCESS_OUTGOING_CALLS //廣播對象為UserHandle.OWNER //需要PERMISSION為:android.Manifest.permission.PROCESS_OUTGOING_CALLS //receiver為該有序廣播最後負責接收的對象 //scheduler表示是否運行在其它線程中,這裡為null表示運行在主線程中 //number初始化數據 context.sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER, PERMISSION, receiver, null, // scheduler Activity.RESULT_OK, // initialCode number, // initialData: initial value for the result data null); // initialExtras }
該廣播為有序廣播,其中指定廣播面向的用戶組是UserHandle.OWNER,接收廣播所需要的權限android.Manifest.permisson.PROCES_OUTGOING_CALL,參數中的receiver為OutgoingCallReceiver的對象,由它來最後接收並處理該廣播,initialData為number。
MTK在該廣播的Intent中添加了一些數據,如下:
public static void checkAndCopyPrivateExtras(final Intent origIntent, Intent newIntent) { int slot = origIntent.getIntExtra(Constants.EXTRA_SLOT_ID, -1); if (-1 != slot) { //卡槽信息,據此可判斷卡1卡2 newIntent.putExtra(Constants.EXTRA_SLOT_ID, slot); } if (FeatureOption.MTK_VT3G324M_SUPPORT) { boolean isVideoCall = origIntent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false); if (isVideoCall) { //如果支持VideoCall則添加相關key newIntent.putExtra(Constants.EXTRA_IS_VIDEO_CALL, isVideoCall); } } long simId = origIntent.getLongExtra(Constants.EXTRA_ORIGINAL_SIM_ID, Settings.System.DEFAULT_SIM_NOT_SET); if (-1 != simId) { //SIM卡Id newIntent.putExtra(Constants.EXTRA_ORIGINAL_SIM_ID, simId); } boolean isIpCall = origIntent.getBooleanExtra(Constants.EXTRA_IS_IP_DIAL, false); if (isIpCall) { //如果是互聯網電話則添加相關的key newIntent.putExtra(Constants.EXTRA_IS_IP_DIAL, isIpCall); } boolean isFollowSimManagement = origIntent.getBooleanExtra(Constants.EXTRA_FOLLOW_SIM_MANAGEMENT, false); if (isFollowSimManagement) { //是否遵從SIM卡管理 newIntent.putExtra(Constants.EXTRA_FOLLOW_SIM_MANAGEMENT, isFollowSimManagement); } }
要特別注意該廣播的最後兩項參數:String initialData和Bundle initialExtras,前者用於存放String類型的初始化數據,後者可存放Bundle類型的初始化數據。在有序廣播的使用過程中,我們可以通過getResultData()獲取到有序廣播中的initialData值,並且可以使用setResultData(String data)重新設置有序廣播的initialData值。同樣,使用getResultExtras(boolean makeMap)可以獲取initialExtras的Bundle值,其中參數makeMap為true表示,如果之前initialExtras為null則創建一個內容為空的Bundle對象;false則表示返回為null;在處理完Bundle值之後可以通過setResultExtras(Bundle extras)將數據設置回廣播中繼續傳遞。
注意:在分析過程中我發現一個google設計上的風險。有序廣播發出去之後最後會在 OutGoingCallReceiver.java ( AOSP代碼會在OutGoingCallBroadcaster.java )中進行處理,通過“number = getResultData()”將呼叫號碼取出。但如果有惡意應用監聽了NEW_OUTGOIONG_CALL廣播並篡改其中的initialData為惡意號碼,最終將會導致用戶無法正常撥打電話。
例如使用以下惡意代碼:
以上代碼將會導致用戶在撥打任何號碼時,均指向3333333333該惡意號碼,從而影響用戶的正常呼叫。對於廠商來說可以使用一些方法進行規避,如通過獲取intent中的Intent.EXTRA_PHONE_NUMBER來獲取number的值,即“number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)”來獲取實際的呼叫號碼,從而可以避免以上情況的發生。//設置接收權限等級為最高 public class Thief extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thief); } public static class ThiefReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { setResultData(3333333333);//設置呼叫號碼為惡意號碼 abortBroadcast();//截斷廣播 } } }
在前文分析中我們使用了mPhone、mNotifier、mRegistry等關鍵對象,從而能夠跳轉到對應的處理方法中進行處理,但這些關鍵對象是在何時何地進行初始化的呢?
在《Android 4.4 Kitkat Phone工作流程淺析(三)__MO(去電)流程分析》中已經分析了Phone對象的初始化流程,通過跟蹤makeDefaultPhone()的流程可以知道最終是以GSMPhone來完成Phone對象初始化的。在GsmCallTracker中需要查看“mPhone.notifyPhoneStateChanged()”方法,mPhone的初始化工作在GsmCallTracker的構造方法中完成,而mNotifier則在GSMPhone的父類PhoneBase中完成初始化。整個流程如圖4:
從圖4中可以看到mPhone實際就是GSMPhone的實例,而mNotifier則是DefaultPhoneNotifier的實例,DefaultPhoneNotifier實現了PhoneNotifier接口。對於mRegistry來講,通過Binder的方式獲取到了TelephonyRegistry的實例,而TelephonyRegistry是在SystemServer中完成添加的,關鍵代碼如下:
TelephonyRegistry telephonyRegistry = null; telephonyRegistry = new TelephonyRegistry(context); ServiceManager.addService(telephony.registry, telephonyRegistry);
在前文的例子中,使用PhoneStateListener的方式可以實現Phone狀態改變的監聽,最後在PhoneStateListener的覆寫方法中獲知Phone狀態的改變。通過TelephonyManager的listen方法實現監聽器的添加,如下:
listen(PhoneStateListener listener, int events)這裡的listener為PhoneStateListener對象,events為需要監聽的類型,這些類型在PhoneStateListener中定義( LISTEN_XXXX ),如需同時監聽多種狀態可使用或|將多個狀態連接。之後在TelephonyManager的listen方法中繼續處理,關鍵代碼如下:
private static ITelephonyRegistry sRegistry; public TelephonyManager(Context context) { //... ...省略 if (sRegistry == null) { sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( telephony.registry)); } //... ...省略 MTK雙卡 public void listen(PhoneStateListener listener, int events) { try { Boolean notifyNow = true; sRegistry.listen(pkgForDebug, listener.callback, events, notifyNow); //... ...省略 MTK雙卡 } }可以看到,最終的調用還是在TelephonyRegistry中,而sRegistry在TelephonyManager的構造方法中完成實例化。TelephonyRegistry中listen關鍵代碼如下:
public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { //... ...省略 if (events != 0) { //檢查權限,某些SATE需要對應的去權限 checkListenerPermission(events); synchronized (mRecords) { // register Record r = null; find_and_add: { //callback實際為new IPhoneStateListener.Stub()對象,且stub繼承自Binder //Binder實現了IBinder接口,asBinder返回stub.this也就是Binder對象 IBinder b = callback.asBinder(); final int N = mRecords.size(); for (int i = 0; i < N; i++) { r = mRecords.get(i); //如果之前添加過該PhoneStateListener則直接跳轉到find_and_add標簽末尾 if (b == r.binder) { break find_and_add; } } //如果之前沒有添加過該PhoneStateListenerListener,將新Listener的信息 //添加到mRecords列表中 r = new Record(); r.binder = b; r.callback = callback; r.pkgForDebug = pkgForDebug; r.callerUid = callerUid; mRecords.add(r); } //... ...省略 r.events = events; if (notifyNow) { //... ...省略 //注冊完成之後,notifyNow為true立刻執行一次 if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { r.callback.onCallStateChanged(mCallState, mCallIncomingNumber); } catch (RemoteException ex) { remove(r.binder); } } //... ...省略通過前面的分析已經知道,Phone狀態改變之後最終會調用TelephonyRegistry中的notifyCallState()方法,並執行以下關鍵代碼:
for (Record r : mRecords) { if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { //回調所有注冊了LISTEN_CALL_STATE狀態PhoneStateListener的onCallStateChanged方法 r.callback.onCallStateChanged(state, incomingNumber); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } }
整個onCallStateChanged方法的回調關鍵流程:
1. 往監聽列表mRecords中添加監聽對象;
2. 狀態改變即從mRecords中遍歷監聽對象並發起回調;
r.callback中存放的是new IPhoneStateListener.Stub()的實例,onCallStateChanged方法關鍵調用如下:
IPhoneStateListener callback = new IPhoneStateListener.Stub() { //... ...省略 public void onCallStateChanged(int state, String incomingNumber) { Message.obtain(mHandler, LISTEN_CALL_STATE, state, 0, incomingNumber).sendToTarget(); } //... ...省略 }; Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { //... ...省略 case LISTEN_CALL_STATE: PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj); break; //... ...省略 } } };最後調用PhoneStateListener的onCallStateChanged()方法。因為我們在實例化PhoneStateListener對象時,是通過匿名類或繼承等方式實現了PhoneStateListener的子類,並在其中覆寫了onCallStateChanged()方法,所以這裡便會回調到我們自己實現的onCallStateChanged()方法中,並最終獲得Phone狀態的變化通知。
全文主要分析了Phone狀態改變之後,如何將Phone狀態通知到三方應用,具體包含broadcast廣播和onCallStateChanged()等方法的回調。同時針對其中的細節之處分成了三個部分進行分析:
1. broadcast廣播類型以及其中攜帶的數據;
廣播具體包含兩種類型:即TelephonyManager.ACITON_PHONE_STATE_CHANGED和Intent.ACITON_NEW_OUTGOING_CALL;後者僅在MO流程發起時通過TeleService發出,同時由於該廣播是有序廣播,允許三方監聽者獲取並修改其initData,從而有可能導致無法發起正常呼叫的問題。如何利用該漏洞,文中已給出相關代碼,附件也會包含該測試APK,用戶安裝該APK並運行一次之後,無論使用何種方式撥打電話均會指向無效號碼3333333333。
2. 關鍵對象初始化流程,包括mPhone、mNotifier、mRegistry對象的實例化流程;
這些關鍵對象大多數跟隨Telephony的啟動並完成初始化,弄清楚這些關鍵對象的實例,對整個流程的分析至關重要。
3. PhoneStateListener監聽機制分析,包括Phone狀態注冊監聽原理,以及onCallStateChanged回調執行流程;
通過TelephonyManager注冊PhoneStateListener,我們可以在其回調函數onCallStateChanged中獲知Phone狀態的改變信息。Phone狀態改變的通知實際的發起者是TelephonyRegistry,其含有兩個重要的鏈表即mRecords和mRemoveList,前者負責保存所有PhoneStateListener的信息,後者記錄所有需要刪除的PhoneStateListener的binder對象,通俗點講就是mRecords記錄添加的監聽者,mRemoveList用於記錄刪除的監聽者。
TelephonyManager是Android暴露給三方應用與Telephony交互的接口即相當於代理,實際獲取Phone狀態是通過TelephonyRegistry實現的。Telephony實際上提供了多種服務功能,包括TelephonyRegistry、PhoneInterfaceManager、PhoneSubInfo等,這些服務並不直接對三方應用開放,而是通過TelephonyManager這個大管家來管理。
文中涉及原始圖片以及惡意劫持呼叫測試代碼,免積分下載。
Phone狀態監聽測試Demo,免積分下載。
音頻條形圖如下圖所示就是這次的音頻條形圖:由於只是自定義View的用法,我們就不去真實地監聽音頻輸入了,隨機模擬一些數字即可。如果要實現一個如上圖的靜態音頻條形圖,相信大
今天碰到一個關於Button的問題:android Button上面的英文字符串會自動變成大寫,運行的Android 5.1版本,如下圖所示:圖1:Button圖2:Te
微信中刪除的聊天記錄如何找回?相信大家都有過誤刪了重要的微信聊天記錄而在那裡拼命懊悔的經歷,那都是因為沒有找到一個很好的解決方法。其實在科技如此發達的今天,
在Android系統中提供了多種存儲技術.通過這些存儲技術可以將數據存儲在各種存儲介質上.比如sharedpreferences可以將數據保存著應用軟件的私有存儲區,這些