android電話、短信黑白名單攔截、電話錄音
功能描述:
總的來說這是一個防騷擾的應用,設置黑名單,白名單,通話錄音名單。添加到黑名單的聯係人或號碼將被拒絕來電或短信;添加到白名單的聯係人或號碼將通過來電或短信(除白名單以外的號碼將被拒絕來電或短信),因此邏輯上黑名單和白名單是不能同時開啟的;添加到通話錄音列表的聯係人或號碼,連接通話時將會開啟錄音,掛斷時完成錄音。
先上圖,接著分析實現這幾個部分的關鍵技術點,最後附上安裝程序apk和工程源碼.
要重點具備的知識:
電話攔截部分:
電話是手機最基本的服務,自然在係統服務中可以獲取:TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
我們要對電話進行攔截,自然首先想到就就是我們怎麼獲知電話何時是響起、何時接通、掛斷?我剛開始做的時候首先想到的就是電話響起、接通和掛斷的時候會不會主動發送一個廣播?沒錯,來電的時候會發出一個action為android.intent.action.PHONE_STATE所以我們寫一廣播接收器:
public class PhoneReceiver extendsBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// 電話服務管理
TelephonyManager manager =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
//處理攔截邏輯
}
}
但是到這裏有碰到疑問了,我知道此時是來電了,但是我怎麼知道用戶進一步的動作,響鈴?接聽?掛斷?。查閱了些資料,原來TelephonyManager可以綁定一個監聽器監聽電話狀態: PhoneStateListener
private PhoneStateListener listener = new PhoneStateListener() {
/*
*@see TelephonyManager#CALL_STATE_IDLE 值為0
*
*@see TelephonyManager#CALL_STATE_RINGING 值為1
*
*@see TelephonyManager#CALL_STATE_OFFHOOK 值為2
*/
@Override
public void onCallStateChanged(int state,String incomingNumber) {
super.onCallStateChanged(state,incomingNumber);
switch (state) {
caseTelephonyManager.CALL_STATE_IDLE: //掛斷時
break;
caseTelephonyManager.CALL_STATE_RINGING: // 響鈴時
// 可以在這裏加上電話攔截邏輯
break;
caseTelephonyManager.CALL_STATE_OFFHOOK: // 接起電話 // 可以這裏加上電話錄音的處理邏輯
break;
default:
break;
}
}
};
//注冊監聽器
manager.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);
自此,我們可以成功的監控電話的狀態了。這個是時候估計你也想做個小小的測試了吧,別忘了,添加權限:
<!-- 讀取電話狀態權限-->
<uses-permission ndroid:name="android.permission.READ_PHONE_STATE"/>
下麵說說電話攔截過濾邏輯:
攔截過濾的原理很簡單,相信大家都想得到,就是檢查來電號碼和我們預設的黑名單或者白名單列表裏的號碼依依做對比,如果需要攔截強製掛斷,否則放過。Ok,現在問題的關鍵就是如何強製掛斷,你可能首先找TelephonyManager是否提供了相應的方法,我開始也是這樣認為的,比較遺憾,我又錯了。Internet是個不錯的東西,最後到網上看到有人用了這個類:ITelephony。
這個類在android私有api中,所以你在官方文檔上是查不到的,下載android的源碼可以看到位於frameworks/base/telephony/java/com/android/internal/telephony 目錄下,包名為:com.android.internal.telephony
其實這裏用的了AIDL(IPC跨進程通訊),關於AIDL不清楚的,可以google一下了解下概念,然後可以到這裏參考下一個小例子:
https://mgssnake.iteye.com/blog/655866
運行調試理解下這個小例子之後估計你讀AIDL也有個大概的認識。我對AIDL表麵化而粗陋的理解就是提供服務的一段注冊一個服務這個服務必實現或者能提供一個實現了AIDL接口中內部抽象類Stub類的子類(提供服務端必定有一個Sub的子類),在調用服務的一端通過AIDL的路徑查詢獲取服務端的這個Stub的子類的實例binder,在通過Sub的靜態方法asInterface(binder)獲取服務代理,從而可以隨心所以操作遠端提供的服務。
關於ITelephony,可以參考下這裏有篇文章:
https://hi.baidu.com/wentaokou/blog/item/3e4fe9d0e5fdc02e9a5027f0.html
ok,大概了解了這些現在回到我們的項目中:
在工程目錄下新建一個包com.android.internal.telephony 將android源碼中的ITelephony.aidl考進來,這是好像編譯不過,原因是在這個文件中引用了另一個aidl文件:NeighboringCellInfo.aidl這個文件位於android源碼frameworks/base/telephony/java/android/telephony目錄中,包名為:android.telephony,所以我們再建一個包:android.telephony將NeighboringCellInfo.aidl拷貝進來即可。如果你還在糾結於怎麼沒有android源碼,文章的最後會貼上本項目源碼或者直接q我向我要。
下麵重點看看如何獲取ITelephony的服務。
//通過反射取得android.os.ServiceManager的getService方法對象
Method method =Class.forName("android.os.ServiceManager")
.getMethod("getService", String.class);
//執行方法傳入參數Service.TELEPHONY_SERVICE,此處返回的應當是ITelephony.Stub的子類對象
IBinder binder =(IBinder) method.invoke(null,
new Object[] {Service.TELEPHONY_SERVICE });
//轉化成一個代理對象
ITelephonytelephony = ITelephony.Stub.asInterface(binder);
//調用掛斷電話的方法
telephony.endCall();
短信攔截部分:
同樣的原理當係統檢查的有短信來時首先會像係統發送廣播:android.provider.Telephony.SMS_RE,所以我們寫一廣播接收器SMSReciever即可。
public class SMSRecieverextends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//接受sm傳來的數據
Bundle bundle = intent.getExtras();
if(bundle!=null){
//通過pdus可以獲得收到的所有信息
Object[] objects = (Object[]) bundle.get("pdus");
//構建對象對象數組
SmsMessage[] messages = new SmsMessage[objects.length];
for (int i = 0; i < messages.length; i++) {
messages[i]= SmsMessage.createFromPdu((byte[])objects[i]);
}
for (SmsMessagesmsMessage : messages) {
String number = smsMessage.getOriginatingAddress()
//攔截過濾
Boolean flag = check(number);
if(flag){//如果攔截,調用abortBroadcast()方法中斷廣播
abortBroadcast();
}
}
}
}
}
這部分比較簡單但在注冊廣播接收器的時候要注意一點:
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(2147483647);//優先級盡量開大下不然攔截不到
registerReceiver(new SMSReciever(), filter);
你應該想的到,一個廣播的發送,接收廣播的廣播接收器應該不隻一個,短信廣播更不列外,手機短信來的時候一定有廣播負責通知並啟動短信鈴聲、一定有廣播負責將短信寫入數據庫等,而我們的目的就是要在這些動作發生之前攔截掉,那麼我們怎麼就能保證我們寫的廣播接收器先於係統內置的廣播接收器接收到廣播呢。
如果多個廣播接收器中含有相同的action,那麼他們接收到廣播的順序是根據他們的Priority屬性來判斷的,Priority值越大優先級也高,越先接受到廣播,在某個廣播接收器中調用了abortBroadcast()方法終端廣播,優先級比他低的廣播接收器就接受不到該廣播了。
所以在我們的攔截短信的廣播接收器中盡量將優先級設置大些,保險起見你可以設置為整型的最大值。看下圖:
電話錄音部分:
邏輯很簡單,不解釋,上關鍵代碼:
// 進行錄音
private void recordCalling() {
try {
Log.v("TAG", "recordCalling");
recorder = new MediaRecorder();
// 讀麥克風的聲音
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 輸出格式.3gp
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// 編碼方式
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 存放的位置是放在sdcard目錄下,當然你可以自己指定一個更合理的目錄
recorder.setOutputFile(Environment.getExternalStorageDirectory()
.getAbsolutePath()
+"/" + System.currentTimeMillis() + ".3gp"); recorder.prepare();
recorder.start();
recording = true;
} catch (Exception e) {
e.printStackTrace();
}
}
// 停止錄音
private void stopRecord() {
Log.v("TAG", "stopRecord");
if (recording) {
recorder.stop();
recorder.release();
recording = false;
}
}
關於MediaRecorder 最詳細最權威的介紹見google官方文檔:https://developer.android.com/reference/android/media/MediaRecorder.html
源碼下載鏈接:https://huangjijie123456.iteye.com/admin/blogs/1545310
最後更新:2017-04-02 17:09:26