編輯:關於Android編程
要說攔截Android系統來電,就不得不說起在低版本的時候Android提供給開發者使用的一個方法:endCall(),但由於谷歌後來考慮到對於一部手機來說,最重要的功能就是打電話了,如果這個功能隨隨便便就被人屏蔽了,安全性太差,所以在高版本的Android將這個方法屏蔽了,不再在TelephoneManager中暴露這個方法。
那麼我們下面的目標就是要想辦法調用到這個方法,當然首先我們還是需要實現一個廣播接收者,來接收電話狀態改變的廣播,這裡使用在服務中動態注冊廣播接收者的方法來實現,主要好處在於便於控制廣播接收者的生命周期,同時也能比靜態注冊有更高的權限
private static final String PHONE = "PHONE"; private static final String BOTH = "BOTH"; private static final String SMS = "SMS"; private TelephonyManager tm; private BlacklistDao dao; private inCommingCallReceiver callReceiver; private PhoneStateListener listener; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); dao = new BlacklistDao(this, 1); callReceiver = new inCommingCallReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.PHONE_STATE"); filter.addAction("android.provider.Telephony.SMS_RECEIVED"); filter.setPriority(Integer.MAX_VALUE); listener = new PhoneStateListener() { private BlacklistItem blacklistItem; @Override public void onCallStateChanged(int state, String incomingNumber) { super.onCallStateChanged(state, incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_RINGING:// 如果是來電的時候 blacklistItem = dao.queryItem(incomingNumber); if (blacklistItem != null) { String type = blacklistItem.getType(); if ((BOTH.equals(type) || PHONE.equals(type))) { System.out.println("掛斷電話"); hangUpCallFromBlacklist(incomingNumber);//掛斷電話的方法 } } break; default: break; } } }; registerReceiver(callReceiver, filter);// 注冊廣播接收者 }
在服務的onDestroy()方法中取消注冊廣播接收者:
@Override public void onDestroy() { super.onDestroy(); System.out.println("關閉黑名單服務"); unregisterReceiver(callReceiver); // 取消監聽 tm.listen(listener, PhoneStateListener.LISTEN_NONE); // listener = null; }
內部類廣播接收者,用於接收電話狀態改變的廣播:
/** * 監聽來電 * * @author Alex * */ private class inCommingCallReceiver extends BroadcastReceiver { private BlacklistItem blacklistItem; @Override public void onReceive(Context context, Intent intent) { if ("android.intent.action.PHONE_STATE".equals(intent .getAction())) { // 如果收到的是電話狀態的變化 tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); } } }
按照我的慣例,還是從安卓系統的源碼入手,由於endCall方法原來是在TelephoneManager中的,所以我們不妨從TelephoneManager的實例化方法getSystemService入手:
我們可以發現這個方法是定義在ContextWrapper中的,而ContextWrapper又是繼承自Context,我們不妨進入到Context的源碼中去一探究竟:
在Context中,我們只找到了一個getSystemService的抽象方法,那麼如何去找實現方法呢,在java中,抽象類的實現類一般名字都是在抽象類名後面加上一個"Impl",於是我們去搜索源碼中的ContextImpl.java,找到之後打開發現:
我們發現getSystemService實際上返回了一個ServiceFetcher對象的一個getService方法的結果,我們來看看ServiceFetcher的getService方法:
在這裡我們可以看到,在定義了一個static塊中,注冊了很多不同的service服務,而這些gerService方法都是由ServiceManager來調用的,返回值是一個IBinder對象。接下來我們可以在文件的導入包的部分找到ServiceManager的位置是在android/os下的:
由於getService返回的是一個IBinder對象,我們只要找到這個getService方法的實現,就可以傳入TELEPONY_SERVICE從而拿到真正的TelephonyManager所代理的那個遠程服務綁定對象,從而調用隱藏在其中的endCall方法。
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+0NLUy7XEysejrNTaU2VydmljZU1uYWdlci5qYXZh1tCjrM7Sw8fV0rW9wctnZXRTZXJ2aWNlt723qLXEyrXP1jo8L3A+CjxwPjxpbWcgc3JjPQ=="/uploadfile/Collfiles/20140924/20140924090000134.png" alt="\">
我們可以發現,ServiceManager這個類也是一個隱藏類,我們無法在我們的代碼中直接拿到這個類來調用其中的getService方法來獲取IBinder對象,那麼我們要如何做呢?
這裡就只有使用反射的方法來處理:
Class clazz = CallSmsSafeService.class.getClassLoader().loadClass( "android.os.ServiceManager"); Method method = clazz.getMethod("getService", String.class); IBinder binder = (IBinder) method.invoke(null, TELEPHONY_SERVICE);通過上面的反射做法,我們拿到了對應於TelephonyManager的IBinder對象,下面我們需要做的利用aidl來調用遠程方法,既然是使用的TelephonyManager的IBinder對象,我們再進入到TelephonyManager的源碼中去看看:
我們發現,在TelephonyManager中,類似於getCallState()這類的方法基本都返回的是getITelephony()的返回值調用的方法,那麼這個getITelephy()是什麼呢:
我們發現,返回的實際上是一個ITelephony對象,而且是以一種調用遠程服務方法的形式返回的;
我們在文件的頭部找到ITelephony的位置:
打開上面的目錄,我們發現ITelephony是一個aidl文件,進入其中,我們可以找到endCall方法:
由於在ITelephony.aidl的頭部有如下信息:
我們想要通過aidl來調用遠程服務telephony的方法endCall(),我們需要將Telehpony.aidl拷貝到我們工程中新建的com.android.internal.telephony包中,同時將android.telephony.NeighboringCellInfo.aidl文件拷貝到工程中新建的android.telephony包中,這樣在gen目中下就會自動生成一個對應的ITelephony.java文件。至此,我們就可以使用下面的語句來調用遠程服務的endCall方法:
ITelephony.Stub.asInterface(binder).endCall();
最後,不要忘記在清單文件中加入對應的權限:
開發項目過程中基本都會用到listView的下拉刷新和上滑加載更多,之前大家最常用的應該是pull to refresh或它的變種版吧,google官方在最新的andro
MPAndroidChart是實現圖表功能的優秀控件, 可以完成大多數繪制需求. 對於修改第三方庫而言, 優秀的架構是繼承開發, 而不是把源碼拆分出去. MP在顯示標記控
概述相信大家在做開發的過程中都寫過很多的javabean ,很多情況下 都是一個列表數據就是一個單獨的javabean,如果大家自己敲的話費時費力 還很容易敲錯。今天給大
概述:一般情況下,我們知道View類有個View.OnTouchListener內部接口,通過重寫他的onTouch(View v, MotionEvent event)