Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检)
来源:互联网 发布:淘宝1元秒杀 编辑:程序博客网 时间:2024/05/16 11:17
http://bbs.51cto.com/thread-1078059-1.html
一 前言
这两天要研究类似白名单黑名单以及手势自动接听的一些功能,所以呢,自然而然的涉及到怎么自动接听/挂断电话的功能了。
对于自动接听这一块,android4.1版本及其以上的版本和之前的版本处理逻辑不太一样,因为google增加了权限检查...所以,按照以前的方法可能不能实现自动接听了.
二 android低版本自动接听/挂断实现
1. copy android源代码的ITelephony.aidl文件到自己的项目
为什么要copy这个文件到自己的项目中呢?这是因为接听/挂断电话的方法在接口ITelephony.java里面,而这个接口时隐藏的,也就是sdk开发是看不到这个接口的。
比如:
01
package
com.android.internal.telephony;
02
/**
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.
06
*
07
* {@hide}
08
*/
09
public
interface
ITelephony
extends
android.os.IInterface
10
{
11
...
12
}
然后把系统的ITelephony.aidl拷贝过来.
由于ITelephony.aidl关联了NeighboringCellInfo.aidl,所以也一并拷贝过来。
不过要注意的是,NeighboringCellInfo.aidl所在的的包名是android.telephony;所以,你要新建一个包android.telephony,然后把NeighboringCellInfo.aidl放到
包android.telephony里面。
NeighboringCellInfo.aidl的定义:
1
package
android.telephony;
2
3
parcelable NeighboringCellInfo;
上面一步完成之后,你就会在你的gen目录下发现已经生成了ITelephony.java这个接口文件。这样,我们就可以使用它了..
这里的话,主要是利用反射机制来取得ITelephony对象,为什么要用反射呢?因为 ITelephony对象是以一个系统服务的形式存在系统中的,跟ams,wms等等一样。
一般通过ServiceManager来保存和获取。但是ServiceManager同样也是隐藏的,如:
01
/** <a href="http://home.51cto.com/index.php?s=/space/126010" target="_blank">@hide</a> */
02
public
final
class
ServiceManager {
03
...
04
}
05
06
/**
07
* Returns a reference to a service with the given name.
08
*
09
* @param name the name of the service to get
10
* <a href="http://home.51cto.com/index.php?s=/space/34010" target="_blank">@return</a> a reference to the service, or <code>null</code> if the service doesn't exist
11
*/
12
public
static
IBinder getService(String name) {
13
try
{
14
IBinder service = sCache.get(name);
15
if
(service !=
null
) {
16
return
service;
17
}
else
{
18
return
getIServiceManager().getService(name);
19
}
20
}
catch
(RemoteException e) {
21
Log.e(TAG,
"error in getService"
, e);
22
}
23
return
null
;
24
}
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”});
方法,参数是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
代码:
01
/**
02
* Implementation of the ITelephony interface.
03
*/
04
public
class
PhoneInterfaceManager
extends
ITelephony.Stub {
05
....
06
07
private
PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
08
mApp = app;
09
mPhone = phone;
10
mCM = PhoneGlobals.getInstance().mCM;
11
mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
12
mMainThreadHandler =
new
MainThreadHandler();
13
publish();
14
}
15
16
private
void
publish() {
17
ServiceManager.addService(
"phone"
,
this
);
18
}
19
}
把自己添加进入了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"
/>
其他应用程序调用的话,就抛出一个异常:
复制内容到剪贴板
所以,对于高版本的手机的话,我们要用另外的方法来处理,这也是下面要讨论的问题。代码:
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)
三 高版本自动接听电话
我们把整个代码贴出来,然后一行行解释
01
try
{
02
Method method = Class.forName(
"android.os.ServiceManager"
)
03
.getMethod(
"getService"
, String.
class
);
04
05
IBinder binder = (IBinder) method.invoke(
null
,
new
Object[]{TELEPHONY_SERVICE});
06
07
ITelephony telephony = ITelephony.Stub.asInterface(binder);
08
09
telephony.answerRingingCall();
10
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);
17
try
{
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
);
29
}
30
}
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"
);
那么,这个广播有什么用呢?为什么可以自动接听电话呢?关于这一点,我们要看看这个广播的接受者怎么处理这个广播的。
代码路径:
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();
03
04
05
protected
class
MediaButtonBroadcastReceiver
extends
BroadcastReceiver {
06
<a href=
"http://home.51cto.com/index.php?s=/space/5017954"
target=
"_blank"
>
@Override
</a>
07
public
void
onReceive(Context context, Intent intent) {
08
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
09
10
if
((event !=
null
)
11
&& (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
12
13
boolean
consumed = PhoneUtils.handleHeadsetHook(phone, event);
14
.....
15
}
else
{
16
....
17
}
18
}
19
}
1
static
boolean
handleHeadsetHook(Phone phone, KeyEvent event) {
2
...
3
answerCall(phone.getRingingCall());
4
...
5
}
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的一个漏洞呢...
0 0
- Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检)
- Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检查)
- android电话自动接听/挂断
- android自动接听和挂断电话
- Android 电话自动接听和挂断详解
- Android-实现电话自动接听/电话自动挂断功能
- android低版本自动接听/挂断实现
- android 来电自动接听和自动挂断(2.3以上)
- Android 实现自动接听和挂断电话功能
- Android自动接听和挂断电话实现原理
- Android自动接听和挂断电话实现原理
- ANDROID 自动接听 支持2.3以上版本
- Android拨打、接听、挂断电话操作
- Android通过程序接听或者挂断电话
- Android通过程序接听或者挂断电话
- Android电话响铃、接听、挂断状态
- android电话相关功能整合(读取通话记录、自动拨号、自动挂断、自动接听)
- android 来电自动接听和自动挂断
- 本博客停止更新,请访问新博客
- Hash算法
- css知识笔记(四)——代码简写、颜色值、长度值
- 基于流量的协议分析
- ffmpeg常用命令
- Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检)
- 第10题
- function handle in MATLAB
- 第7周-项目3-分数类中的运算符重载-拓展分数的加减乘除(分数与整型 )
- 第11题
- HTML、XML和XHTML的联系与区别
- JAVAIO流小节
- 【转自mos文章】在linux 下正确分辨用的是哪种cpu
- 友情提醒:欲开发android5.0以上应用,请全部更新开发工具至最新