当黑名单号码打进来时,自动挂断,并且在通话历史记录中删除该条记录。
挂断电话的操作可以通过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" />