您的位置:首页 > 移动开发 > Android开发

Android项目:手机安全卫士(13)—— 通讯卫士之电话拦截与挂断

2016-03-17 17:19 756 查看

Android项目:手机安全卫士(13)—— 通讯卫士之电话拦截与挂断

1 介绍

上一节我们讲了黑名单数据的存储等 CRUD 操作,今天,就到了它们发挥作用的时候了,通讯卫士功法终于要练成了。我们实现了手机的短信、电话拦截功能。

关于项目相关文章,请访问:

Android 项目:手机安全卫士(7)—— 手机防盗功能

Android 项目:手机安全卫士(8)—— 管理员权限

Android 项目:手机安全卫士(9)—— 手机号码归属地查询

Android 项目:手机安全卫士(10)—— 电话归属地显示

Android 项目:手机安全卫士(11)—— 归属地提示框拖拽效果

Android 项目:手机安全卫士(12)—— 通讯卫士之电话、短信黑名单设置与拦截

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

2 添加拦截服务的设置菜单

在设置界面,添加一个 Item,用于设置是否开启黑名单拦截服务,效果如下:



聪明如你,不知道有没有发现,我们的设置界面已经越来越完善了,Item 也越来越多咯,哈哈。这里给出它的后台代码:

/** 初始化黑名单设置 */
private void initBlackNumber() {
final SettingItemView sivBlackNumber = (SettingItemView) findViewById(R.id.siv_black_number);
// 根据服务是否运行来更新checkbox
boolean serviceRunning = ServiceStatusUtils.isServiceRunning(this,
"net.xwdoor.mobilesafe.service.BlackNumberService");
sivBlackNumber.setChecked(serviceRunning);

sivBlackNumber.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sivBlackNumber.setChecked(!sivBlackNumber.isChecked());
Intent service = new Intent(getApplicationContext(),
BlackNumberService.class);
if(sivBlackNumber.isChecked()){
startService(service);
}else {
stopService(service);
}
}
});
}


每次进入设置界面都会初始化,判断拦截服务是否开启,从而设置选中状态。然后根据用户的设置情况,实时开启或停止服务。当然,再次之前,咱们需要创建一个 Service:BlackNumberService,这个工作比较简单,使用 Android Studio 的“自动化服务”开始创建:

/**
* 黑名单服务
*
* Created by XWdoor on 2016/3/17 017 13:41.
* 博客:http://blog.csdn.net/xwdoor
*/
public class BlackNumberService extends Service {

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
super.onCreate();

//拦截短信

// 拦截电话
}

@Override
public void onDestroy() {
super.onDestroy();

//停止监听短信

// 停止监听来电
}
}


BlackNumberService 主要用于黑名单的电话与短信拦截,下面就来一一讲解。

3 黑名单短信拦截

先来做短信拦截的服务,这个比较简单,我记得,前面实现手机防盗功能的时候,我们也用了短信监听与拦截功能,这次的代码差不多,我们只要稍作修改,符合业务需求即可。创建广播 BlackNumberSmsReceiver:

/**
* 监听短信广播:黑名单监听
*
* Created by XWdoor on 2016/3/17 017 14:03.
* 博客:http://blog.csdn.net/xwdoor
*/
public class BlackNumberSmsReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
for (Object pdu : pdus) {
SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
String address = message.getOriginatingAddress();
String msg = message.getMessageBody();

BlackNumberDao mNumberDao = BlackNumberDao.getInstance(context);
boolean exist = mNumberDao.find(address);
if (exist) {// 在黑名单中
// 1, 2, 3
int mode = mNumberDao.findMode(address);// 获取拦截模式
if (mode > 1) {// 2, 3才拦截
abortBroadcast();
}
}
mNumberDao = null;
}
}
}


哈哈,也就几行代码嘛,这里我们只做了最简单的拦截,就是根据电话号码进行拦截:若短信号码存在黑名单中,则拦截短信。还有一个问题,就是 abortBroadcast() 方法可能在高版本中就不够用了,还需要查询存储短信的数据库,然后删除数据库中的数据。

有了 BlackNumberSmsReceiver 广播,就可以在服务 BlackNumberService 中实现骚扰短信的拦截功能,在 onCreate() 方法中添加以下代码:

//拦截短信
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(Integer.MAX_VALUE);
mSmsReceiver = new BlackNumberSmsReceiver();
// 同等条件下, 动态注册的广播比静态注册的更先获取广播内容
registerReceiver(mSmsReceiver,filter);


服务开启的时候开启短信拦截服务,同样的,在服务停止的时候,需要关闭短信拦截服务,在 onDestroy() 方法中添加以下代码:

//停止监听短信
unregisterReceiver(mSmsReceiver);
mSmsReceiver = null;


4 黑名单电话拦截与挂断

接下来就是本文的重点了,实现了短信拦截,就轮到电话拦截了。首先在 BlackNumberService 服务中实现电话监听的功能,在 onCreate() 方法中添加以下代码:

// 拦截电话
mTM = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
mPhoneListener = new BlackNumberPhoneListener(this);
mTM.listen(mPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);


同时,在 onDestroy() 方法中添加以下代码,用于停止监听电话:

// 停止监听来电
mTM.listen(mPhoneListener,PhoneStateListener.LISTEN_NONE);


监听电话需要传入一个 PhoneStateListener 对象,这里我们创建一个类 BlackNumberPhoneListener,继承自 PhoneStateListener,同时复写它的 onCallStateChanged() 方法,代码如下:

@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state){
case TelephonyManager.CALL_STATE_RINGING://电话铃响
boolean exist = mNumberDao.find(incomingNumber);
if(exist){//黑名单中有该号码
int mode = mNumberDao.findMode(incomingNumber);
if (mode == 1 || mode == 3) {
//拦截该号码:挂断电话
endCall();
}
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK://电话摘机
break;
case TelephonyManager.CALL_STATE_IDLE://电话空闲
break;
}
}


当监听到需要拦截的电话时,就需要挂断电话,这个功能实现起来比较复杂,先说思路:在 android 1.X 版本中的 TelephonyManager 有一个 api 接口:endCall(),调用它就可以直接挂断电话,但是马上,google 在 2.X 以上的版本中隐藏了这个 api,因为这个操作太敏感了,这里需要注意,是隐藏 api,而不是删除哦,我们的办法就是想方设法的调用 endCall() 这个方法。至于怎么调用,你先看看代码:

/** 挂断电话 需要权限:android.permission.CALL_PHONE */
private void endCall() {
try {
// TelephonyManager.endCall();
// IBinder b = ServiceManager.getService(ALARM_SERVICE);
// IAlarmManager service = IAlarmManager.Stub.asInterface(b);
// ServiceManager 被隐藏了,需要通过反射来调用
// IBinder b = ServiceManager.getService(TELEPHONY_SERVICE);
Class<?> aClass = Class.forName("android.os.ServiceManager");//通过反射找到ServiceManager
Method method = aClass.getMethod("getService", String.class);//找到ServiceManager的静态方法getService
IBinder b = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);//调用getService()方法,得到IBinder对象
ITelephony service = ITelephony.Stub.asInterface(b);//得到TelephonyManager接口
service.endCall();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}


每句代码我都写了注释,以免以后查阅。通过查看 android 的源代码得知,每次我们调用 getSystemService() 方法时,系统都是通过调用 ServiceManager 类的静态方法 getService() 得到 IBinder 对象,从而得到相应的服务。所以,我们就按照这个思路走,首先,我们通过 java 的反射机制找到 ServiceManager 对象;然后通过 getMethod() 方法找到 getService() 方法;然后调用该方法得到对应的 IBinder 对象,从而得到 TelephonyManager 中的 ITelephony 对象;最后就是调用 endCall() 方法了。

需要声明一下,关于 IBinder、ITelephony、Stub 什么的,我目前也不太懂,相信随着学习的加深,以后肯定会接触的。还有一点就是,使用 IBinder、ITelephony 等接口类需要用到两个 AIDL 文件,这两个文件我的项目中有,需要说明的是,它们的存放位置有点技巧,需要存放在 aidl 目录中,且该目录是与 java 同一级别的目录,说的有点抽象,看看下面的图片就一目了然了,注意,创建了很多 package 哦:



接下的工作就是…什么,你觉得这就结束了吗,不可能,哪有这么简单。以上代码不仅需要权限:
<uses-permission android:name="android.permission.CALL_PHONE" />
,测试后还发现,虽然电话成功拦截了,但是打开通话记录,还能看到被拦截的电话的记录,所以自己挖的坑,还得自己填,我们需要删除通话记录。

5 删除通话记录

调用 endCall() 方法后,传入一个观察者:BlackNumberLogObserver,继承自 ContentObserver,注册观察者的代码如下:

// 注册内容观察者,观察通话记录表的变化
mObserver = new BlackNumberLogObserver(new Handler(), incomingNumber);
mContext.getContentResolver().registerContentObserver(
Uri.parse("content://call_log/calls"), true, mObserver);


类 BlackNumberLogObserver 需要重写 onChange() 方法,用于删除通讯录,以及注销观察者:

// 表的数据发生变化会回调此方法
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);

deleteCallLog(number);
// 注销观察者
mContext.getContentResolver().unregisterContentObserver(mObserver);
}

/**
* 删除通话记录
*
* 需要权限:
* <uses-permission android:name="android.permission.READ_CALL_LOG" />
* <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
*
* @param number 电话
*/
private void deleteCallLog(String number) {
// 和联系人是一个数据库
mContext.getContentResolver().delete(Uri.parse("content://call_log/calls"), "number = ?", new String[]{number});
}


到此,我们的电话拦截才告一段落,可以轻松一下了。

6 总结

今天的内容我认为全是干货啊,特别是电话拦截挂断的功能,以后可以直接拿来用了,省去了不少事。

关于项目相关文章,请访问:

Android 项目:手机安全卫士(7)—— 手机防盗功能

Android 项目:手机安全卫士(8)—— 管理员权限

Android 项目:手机安全卫士(9)—— 手机号码归属地查询

Android 项目:手机安全卫士(10)—— 电话归属地显示

Android 项目:手机安全卫士(11)—— 归属地提示框拖拽效果

Android 项目:手机安全卫士(12)—— 通讯卫士之电话、短信黑名单设置与拦截

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: