編輯:關於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();
最後,不要忘記在清單文件中加入對應的權限:
最新Android ListView 下拉刷新 上滑加載
開發項目過程中基本都會用到listView的下拉刷新和上滑加載更多,之前大家最常用的應該是pull to refresh或它的變種版吧,google官方在最新的andro
重寫MPAndroidChart顯示標記
MPAndroidChart是實現圖表功能的優秀控件, 可以完成大多數繪制需求. 對於修改第三方庫而言, 優秀的架構是繼承開發, 而不是把源碼拆分出去. MP在顯示標記控
Android studio 插件之 GsonFormat (自動生成javabean)
概述相信大家在做開發的過程中都寫過很多的javabean ,很多情況下 都是一個列表數據就是一個單獨的javabean,如果大家自己敲的話費時費力 還很容易敲錯。今天給大
Android之手勢的識別與處理(雙擊onDoubleTap、滑動onFling、拖動onScroll)
概述:一般情況下,我們知道View類有個View.OnTouchListener內部接口,通過重寫他的onTouch(View v, MotionEvent event)