当黑名单号码打进来时,自动挂断,并且在通话历史记录中删除该条记录。
挂断电话的操作可以通过PackageManager对象来实现,但是在android1.5以后,该方法没有暴露出来,需要通过AIDL来实现。
一个是ITelephony.aidl,从网上搜索下载:
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.os.Bundle; import java.util.List; import android.telephony.NeighboringCellInfo; /** * Interface used to interact with the phone. Mostly this is used by the * TelephonyManager class. A few places are still using this directly. * Please clean them up if possible and use TelephonyManager insteadl. * * {@hide} */ interface ITelephony { /** * Dial a number. This doesn't place the call. It displays * the Dialer screen. * @param number the number to be dialed. If null, this * would display the Dialer screen with no number pre-filled. */ void dial(String number); /** * Place a call to the specified number. * @param number the number to be called. */ void call(String number); /** * If there is currently a call in progress, show the call screen. * The DTMF dialpad may or may not be visible initially, depending on * whether it was up when the user last exited the InCallScreen. * * @return true if the call screen was shown. */ boolean showCallScreen(); /** * Variation of showCallScreen() that also specifies whether the * DTMF dialpad should be initially visible when the InCallScreen * comes up. * * @param showDialpad if true, make the dialpad visible initially, * otherwise hide the dialpad initially. * @return true if the call screen was shown. * * @see showCallScreen */ boolean showCallScreenWithDialpad(boolean showDialpad); /** * End call or go to the Home screen * * @return whether it hung up */ boolean endCall(); /** * Answer the currently-ringing call. * * If there's already a current active call, that call will be * automatically put on hold. If both lines are currently in use, the * current active call will be ended. * * TODO: provide a flag to let the caller specify what policy to use * if both lines are in use. (The current behavior is hardwired to * "answer incoming, end ongoing", which is how the CALL button * is specced to behave.) * * TODO: this should be a oneway call (especially since it's called * directly from the key queue thread). */ void answerRingingCall(); /** * Silence the ringer if an incoming call is currently ringing. * (If vibrating, stop the vibrator also.) * * It's safe to call this if the ringer has already been silenced, or * even if there's no incoming call. (If so, this method will do nothing.) * * TODO: this should be a oneway call too (see above). * (Actually *all* the methods here that return void can * probably be oneway.) */ void silenceRinger(); /** * Check if we are in either an active or holding call * @return true if the phone state is OFFHOOK. */ boolean isOffhook(); /** * Check if an incoming phone call is ringing or call waiting. * @return true if the phone state is RINGING. */ boolean isRinging(); /** * Check if the phone is idle. * @return true if the phone state is IDLE. */ boolean isIdle(); /** * Check to see if the radio is on or not. * @return returns true if the radio is on. */ boolean isRadioOn(); /** * Check if the SIM pin lock is enabled. * @return true if the SIM pin lock is enabled. */ boolean isSimPinEnabled(); /** * Cancels the missed calls notification. */ void cancelMissedCallsNotification(); /** * Supply a pin to unlock the SIM. Blocks until a result is determined. * @param pin The pin to check. * @return whether the operation was a success. */ boolean supplyPin(String pin); /** * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated * without SEND (so <code>dial</code> is not appropriate). * * @param dialString the MMI command to be executed. * @return true if MMI command is executed. */ boolean handlePinMmi(String dialString); /** * Toggles the radio on or off. */ void toggleRadioOnOff(); /** * Set the radio to on or off */ boolean setRadio(boolean turnOn); /** * Request to update location information in service state */ void updateServiceLocation(); /** * Enable location update notifications. */ void enableLocationUpdates(); /** * Disable location update notifications. */ void disableLocationUpdates(); /** * Enable a specific APN type. */ int enableApnType(String type); /** * Disable a specific APN type. */ int disableApnType(String type); /** * Allow mobile data connections. */ boolean enableDataConnectivity(); /** * Disallow mobile data connections. */ boolean disableDataConnectivity(); /** * Report whether data connectivity is possible. */ boolean isDataConnectivityPossible(); Bundle getCellLocation(); /** * Returns the neighboring cell information of the device. */ List<NeighboringCellInfo> getNeighboringCellInfo(); int getCallState(); int getDataActivity(); int getDataState(); }此文件使用了NeighboringCellInfo.aidl:
package android.telephony; parcelable NeighboringCellInfo;将这两个文件按照包名目录放好。
创建服务CallFirewallService:
package com.example.mobilesafe.service; import java.lang.reflect.Method; import android.app.Service; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.provider.CallLog; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.telephony.ITelephony; import com.example.mobilesafe.db.BlackNumberDao; public class CallFirewallService extends Service { public static final String TAG = "CallFirewallService"; public static final int STOP_SMS = 1; public static final int STOP_CALL = 2; public static final int STOP_SMSCALL = 4; private TelephonyManager tm; private MyPhoneListener listener; private BlackNumberDao dao; @Override public IBinder onBind(Intent intent) { return null; } /** * 当服务第一次被创建的时候 调用 */ @Override public void onCreate() { super.onCreate(); dao = new BlackNumberDao(this); // 注册系统的电话状态改变的监听器. listener = new MyPhoneListener(); tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); // 系统的电话服务 就监听了 电话状态的变化, tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); } private class MyPhoneListener extends PhoneStateListener { @Override public void onCallStateChanged(int state, String incomingNumber) { switch (state) { case TelephonyManager.CALL_STATE_RINGING:// 手机铃声正在响. //starttime = System.currentTimeMillis(); // 判断 incomingNumber 是否是黑名单号码 int mode = dao.findNumberMode(incomingNumber); if ((mode & STOP_CALL) != 0) { // 黑名单号码 Log.i(TAG, "挂断电话"); //挂断电话 endcall(incomingNumber); } break; case TelephonyManager.CALL_STATE_IDLE: // 手机的空闲状态 break; case TelephonyManager.CALL_STATE_OFFHOOK:// 手机接通通话的状态 break; } super.onCallStateChanged(state, incomingNumber); } } /** * 取消电话状态的监听. */ @Override public void onDestroy() { super.onDestroy(); tm.listen(listener, PhoneStateListener.LISTEN_NONE); listener = null; } /** * 显示添加黑名单号码的notification * @param incomingNumber *//* public void showNotification(String incomingNumber) { //1.创建一个notification的管理者 String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); //2.创建一个notification int icon = R.drawable.notification; CharSequence tickerText = "拦截到一个一声响号码"; long when = System.currentTimeMillis(); Notification notification = new Notification(icon, tickerText, when); //3.定义notification的具体内容 和点击事件 Context context = getApplicationContext(); CharSequence contentTitle = "发现响一声号码"; CharSequence contentText = "号码为:"+incomingNumber; notification.flags = Notification.FLAG_AUTO_CANCEL; Intent notificationIntent = new Intent(this, CallSmsSafeActivity.class); notificationIntent.putExtra("blacknumber", incomingNumber); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT ); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); //4.利用notification的manager 显示一个notification mNotificationManager.notify(0, notification); }*/ /** * 挂断电话 * 需要拷贝两个aidl文件 * 添加权限<uses-permission android:name="android.permission.CALL_PHONE" /> * * @param incomingNumber */ public void endcall(String incomingNumber) { try { //使用反射获取系统的service方法 Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class); IBinder binder = (IBinder) method.invoke(null, new Object[]{TELEPHONY_SERVICE}); //通过aidl实现方法的调用 ITelephony telephony = ITelephony.Stub.asInterface(binder); telephony.endCall();//该方法是一个异步方法,他会新开启一个线程将呼入的号码存入数据库中 //deleteCallLog(incomingNumber); // 注册一个内容观察者 观察uri数据的变化 getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, new MyObserver(new Handler(), incomingNumber)); } catch (Exception e) { e.printStackTrace(); } } /** * 定义自己的内容观察者 , * 在构造方法里面传递 观察的号码 * * @author */ private class MyObserver extends ContentObserver { private String incomingNumber; public MyObserver(Handler handler, String incomingNumber) { super(handler); this.incomingNumber = incomingNumber; } /** * 数据库内容发生改变的时候调用的方法 */ @Override public void onChange(boolean selfChange) { super.onChange(selfChange); //立即执行删除操作 deleteCallLog(incomingNumber); //停止数据的观察 getContentResolver().unregisterContentObserver(this); } } /** * 删除呼叫记录 * * @param incomingNumber */ private void deleteCallLog(String incomingNumber) { // 呼叫记录内容提供者对应的uri Uri uri = Uri.parse("content://call_log/calls"); // CallLog.Calls.CONTENT_URI; Cursor cursor = getContentResolver().query(uri, new String[]{"_id"}, "number=?", new String[]{incomingNumber}, null); while (cursor.moveToNext()) { String id = cursor.getString(0); getContentResolver().delete(uri, "_id=?", new String[]{id}); } cursor.close(); } }
其中showNotification是显示拦截的黑名单通知。
endcall注册了一个内容观察者,当拦截成功后,由于历史记录中会有此次拨打记录。
这样内容观察者就可以接收到数据的变化,进而将此次号码的通话记录删除掉。
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.CALL_PHONE" />