一 前言
这两天要研究类似白名单黑名单以及手势自动接听的一些功能,所以呢,自然而然的涉及到怎么自动接听/挂断电话的功能了。
对于自动接听这一块,android4.1版本及其以上的版本和之前的版本处理逻辑不太一样,因为google增加了权限检查...所以,按照以前的方法可能不能实现自动接听了.
二 android低版本自动接听/挂断实现
1. copy android源代码的ITelephony.aidl文件到自己的项目
为什么要copy这个文件到自己的项目中呢?这是因为接听/挂断电话的方法在接口ITelephony.java里面,而这个接口时隐藏的,也就是sdk开发是看不到这个接口的。
比如:
01 |
package com.android.internal.telephony; |
03 |
* Interface used to interact with the phone. Mostly this is used by the |
04 |
* TelephonyManager class. A few places are still using this directly. |
05 |
* Please clean them up if possible and use TelephonyManager insteadl. |
09 |
public interface ITelephony extends android.os.IInterface |
正如上面所说,这个接口ITelephony.java是隐藏的(@hide),它的包名时com.android.internal.telephony,所以,我们在我们的项目里面新建同样的一个包,
然后把系统的ITelephony.aidl拷贝过来.
由于ITelephony.aidl关联了NeighboringCellInfo.aidl,所以也一并拷贝过来。
不过要注意的是,NeighboringCellInfo.aidl所在的的包名是android.telephony;所以,你要新建一个包android.telephony,然后把NeighboringCellInfo.aidl放到
包android.telephony里面。
NeighboringCellInfo.aidl的定义:
1 |
package android.telephony; |
3 |
parcelable NeighboringCellInfo; |
b. 使用ITelephony.java接口
上面一步完成之后,你就会在你的gen目录下发现已经生成了ITelephony.java这个接口文件。这样,我们就可以使用它了..
这里的话,主要是利用反射机制来取得ITelephony对象,为什么要用反射呢?因为 ITelephony对象是以一个系统服务的形式存在系统中的,跟ams,wms等等一样。
一般通过ServiceManager来保存和获取。但是ServiceManager同样也是隐藏的,如:
02 |
public final class ServiceManager { |
07 |
* Returns a reference to a service with the given name. |
09 |
* @param name the name of the service to get |
12 |
public static IBinder getService(String name) { |
14 |
IBinder service = sCache.get(name); |
15 |
if (service != null ) { |
18 |
return getIServiceManager().getService(name); |
20 |
} catch (RemoteException e) { |
21 |
Log.e(TAG, "error in getService" , e); |
所以,我们首先要通过反射的机制拿到ServiceManager对象,然后调用ServiceManager.getService(String name)方法来取得ITelephony对象。这个name就是当时
addService()的时候使用的name...
ok... 那我们来看看反射出ServiceManager的代码怎么写。
1 |
Method method = Class.forName( "android.os.ServiceManager" ) |
2 |
.getMethod( "getService" , String. class ); |
3 |
IBinder binder = (IBinder) method.invoke( null , new Object[]{“phone”}); |
解释下上面的代码,Class.forName(String s)里面写的时ServiceManager类所在的完整包名和类型,这样就可以得到ServiceManager的Class对象。然后调用getMethod
方法,参数是getService,后面的String表示getService()方法的参数类型,也就是拿到了ServieManager的getService(String s)这个方法。
嗯...既然已经得到了getService(String name)方法,那么就调用它!把要传入的参数,也就是想得到的Service的名字传入,这里我们传入"phone"字符串,就可以返回一个
IBinder对象。
那,为什么要传入"phone"这个名字呢?
这是因为ITelephony.java 的实现类PhoneInterfaceManager.java在创建的时候,把自己添加进入了ServiceManager,然后使用的名字就是"phone"
如:
代码路径:
packages/apps/Phone/src/com/android/phone/PhoneInterfaceManager.java
代码:
02 |
* Implementation of the ITelephony interface. |
04 |
public class PhoneInterfaceManager extends ITelephony.Stub { |
07 |
private PhoneInterfaceManager(PhoneGlobals app, Phone phone) { |
10 |
mCM = PhoneGlobals.getInstance().mCM; |
11 |
mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE); |
12 |
mMainThreadHandler = new MainThreadHandler(); |
16 |
private void publish() { |
17 |
ServiceManager.addService( "phone" , this ); |
嗯,大家看到没有,PhoneInterfaceManager实现了ITelephony接口,然后在publish的时候,调用了ServicerManager.addService(xxx),
把自己添加进入了ServiceManager,起的名字时"phone"。所以,我们只要调用getService("phone"),就可以拿到ITelephony的对象,也就是PhoneInterfaceManager对象。
c. 调用ITelephony.java的answerRingingCall()方法接听电话
代码:
1 |
ITelephony telephony = ITelephony.Stub.asInterface(binder); |
2 |
telephony.answerRingingCall(); |
解释下上面两行代码:
第一行是把上面getService("phone")得到的IBinder对象binder转化成ITelephony对象,这是Binder机制的东西,就不讲了。大家只要记得Binder对象和具体对象和相互转换即可。
第二行是调用answerRingingCall()方法,这个方法调用之后,就会接通电话。
如果是挂断电话的话,就应该调用telephony.endCall()方法,这个相信大家也能理解的。
d. 配置应用程序权限
最后,我们还需要在AndroidManifest.xml里面配置下权限:
如下:
1 |
< uses-permission android:name = "android.permission.CALL_PHONE" /> |
2 |
< uses-permission android:name = "android.permission.MODIFY_PHONE_STATE" /> |
上面自动接听电话的代码在4.1以前版本运行是没有问题的,但是新版本的android对接听电话函数(挂断电话没问题),也就是answerRingingCall(),增加权限检查。只有系统进程才有权限执行这个方法,
其他应用程序调用的话,就抛出一个异常:
复制内容到剪贴板
代码:
D/Sandy ( 9058): java.lang.SecurityException: Neither user 10125 nor current process has android.permission.MODIFY_PHONE_STATE.
D/Sandy ( 9058): at android.os.Parcel.readException(Parcel.java:1327)
D/Sandy ( 9058): at android.os.Parcel.readException(Parcel.java:1281)
D/Sandy ( 9058): at com.android.internal.telephony.ITelephony$Stub$Proxy.answerRingingCall(ITelephony.java:1019)
D/Sandy ( 9058): at com.example.hillrestproject.service.PhonePickupService.onPickUpEvent(PhonePickupService.java:180)
D/Sandy ( 9058): at com.hcrest.gestures.pickup.PickUpDetector.onSensorData(PickUpDetector.java:150)
D/Sandy ( 9058): at com.hcrest.android.sensors.SensorManagerAdapter$ListenerDelegate.onSensorChanged(SensorManagerAdapter.java:373)
D/Sandy ( 9058): at android.hardware.SensorManager$ListenerDelegate$1.handleMessage(SensorManager.java:635)
D/Sandy ( 9058): at android.os.Handler.dispatchMessage(Handler.java:99)
D/Sandy ( 9058): at android.os.Looper.loop(Looper.java:137)
D/Sandy ( 9058): at android.app.ActivityThread.main(ActivityThread.java:4507)
D/Sandy ( 9058): at java.lang.reflect.Method.invokeNative(Native Method)
D/Sandy ( 9058): at java.lang.reflect.Method.invoke(Method.java:511)
D/Sandy ( 9058): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
D/Sandy ( 9058): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
D/Sandy ( 9058): at dalvik.system.NativeStart.main(Native Method)
所以,对于高版本的手机的话,我们要用另外的方法来处理,这也是下面要讨论的问题。
三 高版本自动接听电话
我们把整个代码贴出来,然后一行行解释
02 |
Method method = Class.forName( "android.os.ServiceManager" ) |
03 |
.getMethod( "getService" , String. class ); |
05 |
IBinder binder = (IBinder) method.invoke( null , new Object[]{TELEPHONY_SERVICE}); |
07 |
ITelephony telephony = ITelephony.Stub.asInterface(binder); |
09 |
telephony.answerRingingCall(); |
11 |
} catch (NoSuchMethodException e) { |
12 |
Log.d( "Sandy" , "" , e); |
13 |
} catch (ClassNotFoundException e) { |
14 |
Log.d( "Sandy" , "" , e); |
15 |
} catch (Exception e) { |
16 |
Log.d( "Sandy" , "" , e); |
18 |
Log.e( "Sandy" , "for version 4.1 or larger" ); |
19 |
Intent intent = new Intent( "android.intent.action.MEDIA_BUTTON" ); |
20 |
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); |
21 |
intent.putExtra( "android.intent.extra.KEY_EVENT" ,keyEvent); |
22 |
sendOrderedBroadcast(intent, "android.permission.CALL_PRIVILEGED" ); |
23 |
} catch (Exception e2) { |
24 |
Log.d( "Sandy" , "" , e2); |
25 |
Intent meidaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); |
26 |
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); |
27 |
meidaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT,keyEvent); |
28 |
sendOrderedBroadcast(meidaButtonIntent, null ); |
在上面的代码里面,最开始的代码和第二点讲解的时候,没有什么区别,主要看Exception发生之后,try{}里面的代码,也就是:
1 |
Log.e( "Sandy" , "for version 4.1 or larger" ); |
2 |
Intent intent = new Intent( "android.intent.action.MEDIA_BUTTON" ); |
3 |
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); |
4 |
intent.putExtra( "android.intent.extra.KEY_EVENT" ,keyEvent); |
5 |
sendOrderedBroadcast(intent, "android.permission.CALL_PRIVILEGED" ); |
这里其实就是发送了一个广播就完事了,这个广播的action是"android.intent.action.MEDIA_BUTTON",然后还有一个参数---keyEvent
那么,这个广播有什么用呢?为什么可以自动接听电话呢?关于这一点,我们要看看这个广播的接受者怎么处理这个广播的。
代码路径:
packages/apps/Phone/src/com/android/phone/PhoneGlobals.java
代码:
1 |
IntentFilter mediaButtonIntentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); |
2 |
mediaButtonIntentFilter.setPriority( 1 ); |
3 |
registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter); |
/
01 |
/ Broadcast receiver purely for ACTION_MEDIA_BUTTON broadcasts |
02 |
private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); |
05 |
protected class MediaButtonBroadcastReceiver extends BroadcastReceiver { |
07 |
public void onReceive(Context context, Intent intent) { |
08 |
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); |
11 |
&& (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) { |
13 |
boolean consumed = PhoneUtils.handleHeadsetHook(phone, event); |
1 |
static boolean handleHeadsetHook(Phone phone, KeyEvent event) { |
3 |
answerCall(phone.getRingingCall()); |
解释下上面贴的代码
1. PhoneGlobals注册一个广播接收器,action就是上面我们发送的广播“android.intent.action.MEDIA_BUTTON”
ok..这个广播接收器接受到广播之后,就会执行onReceive()方法,也就是MediaButtonBroadcastReceiver的onReceive()方法。
在onReceive()方法里面,它会判断按下的keyEvent,如果是KeyEvent.KEYCODE_HEADSETHOOK的话,就会执行PhoneUtils.handleHeadsetHook(xxx)方法
在handleHeadsetHook(xxx)里面,会调用answerCall(phone.getRingingCall)方法,也就是接听电话了...
那么,就有个疑问,为什么android会提供这个MediaButtonBroadcastReceiver广播接收器呢?
其实,这个广播接收器是为了监听耳机上接听电话那个按钮的,耳机上有个按钮,来电时只要按一下,就可以接听电话,也就是会调用我们这个MediaButtonBroadcastReceiver
广播接收器。
那,这就给我们提供了方便之门,做自动接听程序的时候,尽管google已经增加了权限检查,但是我们通过绕过去的方式,利用MediaButtonBroadcastReceiver,从而达到了
我们的目的。
这算不算android的一个漏洞呢...
- Android之——自己主动挂断电话的实现
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47072451 通过<Android之--AIDL小结>与<And ...
- Android 实现自动接听和挂断电话功能
添加权限 <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permis ...
- android 自动拨打电话 挂断电话代码
页面布局文件代码 ( res下面的layout下面的activity_main.xml代码 ) <RelativeLayout xmlns:android="http://sche ...
- C# Xamarin For Android自动升级项目实战
一.课程介绍 “明人不说暗话,跟着阿笨一起玩Xamarin”,本次分享课程阿笨将带来大家一起学习Xamarin For Android系列<C# Xamarin For Android自动升级项 ...
- Android 自动编译、打包生成apk文件 3 - 使用SDK Ant方式
相关文章列表: < Android 自动编译.打包生成apk文件 1 - 命令行方式> < Android 自动编译.打包生成apk文件 2 - 使用原生Ant方式> &l ...
- [原] Android自动打包之命令行打包
Android自动打包流程详细图: 总结为以下几个步骤: 1. 生成R文件 2. Java代码编译成class文件 3. class文件生成dex文件 4. 打包资源 5. 生成apk 6. 创建密匙 ...
- 关于Android模块化我有一些话不知当讲不当讲
关于Android模块化我有一些话不知当讲不当讲 最近公司一个项目使用了模块化设计,本人参与其中的一个小模块开发,但是整体的设计并不是我架构设计的,开发半年有余,在此记录下来我的想法. 关于Andro ...
- 2、Android自动测试之Monkey工具
Android自动测试之Monkey工具 APP测试工作中经常会听到领导说,APP压力测试做了吗?刚入行时,不知道什么是 APP压力测试,找了半天没找到自己想要的.过了几年,回头想这个问题,发现牵扯了 ...
- Android 自动取色并设置沉浸式状态栏
Android 自动取色并设置沉浸式状态栏 - Stars-One的杂货小窝 最近在进行产品的优化,也是研究了下沉浸式状态栏的实现方法及自动取色,记录一下笔记 设置沉浸式状态栏 1.添加依赖 这里,是 ...
随机推荐
- ACM题目————士兵杀敌(四)
描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的 人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上, ...
- Javascript中最常用的经典技巧
1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键<table border oncontextmenu= ...
- 山东理工大学第七届ACM校赛-经济节约 分类: 比赛 2015-06-26 10:34 19人阅读 评论(0) 收藏
经济节约 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 由于经济紧张,某国国王决定减少一部分多余的士兵,这些士兵在边界都有各自的 ...
- Windows上Python3.5安装Scrapy(lxml) 以及与twisted有关错误的解决
转载于:http://www.cnblogs.com/silverbullet11/p/4966608.html 常用网址: Python 3.5: https://www.python.org/do ...
- Less (一种动态样式语言)
Less (一种动态样式语言). LESS是一种由Alexis Sellier设计的动态层叠样式表语言,受Sass所影响,同时也影响了 Sass的新语法:SCSS. LESS是开源的,其第一个版本由R ...
- java配置自动任务,定期执行代码
任务调用类: package business.tools.service; import java.util.ArrayList; import java.util.Calendar; import ...
- UML实践详细经典教程----用例图、顺序图、状态图、类图、包图、协作图
面向对象的问题的处理的关键是建模问题.建模可以把在复杂世界的许多重要的细节给抽象出.许多建模工具封装了UML(也就是Unified Modeling Language™),这篇课程的目的是展示出UML ...
- java写的web服务器
经常用Tomcat,不知道的以为Tomcat很牛,其实Tomcat就是用java写的,Tomcat对jsp的支持做的很好,那么今天我们用java来写一个web服务器 //首先得到一个server, S ...
- [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)
快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...
- C语言使用fread和fwrite处理任何文件
1.文件必须以二进制形式打开 FILE* pfile1=fopen("fileone","rb"); FILE* pfile2=fopen("file ...