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

Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检查)

2014-09-22 14:28 459 查看
转自:http://bbs.51cto.com/thread-1078059-1.html

一前言

这两天要研究类似白名单黑名单以及手势自动接听的一些功能,所以呢,自然而然的涉及到怎么自动接听/挂断电话的功能了。

对于自动接听这一块,android4.1版本及其以上的版本和之前的版本处理逻辑不太一样,因为google增加了权限检查...所以,按照以前的方法可能不能实现自动接听了.

二android低版本自动接听/挂断实现

1.copyandroid源代码的ITelephony.aidl文件到自己的项目

为什么要copy这个文件到自己的项目中呢?这是因为接听/挂断电话的方法在接口ITelephony.java里面,而这个接口时隐藏的,也就是sdk开发是看不到这个接口的。

比如:


01
package

com.android.internal.telephony;
02
/**
03
*
Interfaceusedtointeractwiththephone.Mostlythisisusedbythe
04
*
TelephonyManagerclass.Afewplacesarestillusingthisdirectly.
05
*
PleasecleanthemupifpossibleanduseTelephonyManagerinsteadl.
06
*
07
*{@hide}
08
*/
09
public

interface

ITelephony
extends

android.os.IInterface
10
{
11

...
12
}
正如上面所说,这个接口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;
2
3
parcelable
NeighboringCellInfo;
b.使用ITelephony.java接口

上面一步完成之后,你就会在你的gen目录下发现已经生成了ITelephony.java这个接口文件。这样,我们就可以使用它了..

这里的话,主要是利用反射机制来取得ITelephony对象,为什么要用反射呢?因为ITelephony对象是以一个系统服务的形式存在系统中的,跟ams,wms等等一样。

一般通过ServiceManager来保存和获取。但是ServiceManager同样也是隐藏的,如:


01
/**
<ahref="http://home.51cto.com/index.php?s=/space/126010"target="_blank">@hide</a>*/
02
public

final
class

ServiceManager{
03

...
04
}
05
06
/**
07
*
Returnsareferencetoaservicewiththegivenname.
08
*
09
*
@paramnamethenameoftheservicetoget
10
*
<ahref="http://home.51cto.com/index.php?s=/space/34010"target="_blank">@return</a>areferencetotheservice,or<code>null</code>iftheservicedoesn'texist
11
*/
12
public

static

IBindergetService(Stringname){
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


(RemoteExceptione){
21

Log.e(TAG,
"error
ingetService"
,e);
22

}
23

return

null
;
24

}
所以,我们首先要通过反射的机制拿到ServiceManager对象,然后调用ServiceManager.getService(Stringname)方法来取得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(Strings)里面写的时ServiceManager类所在的完整包名和类型,这样就可以得到ServiceManager的Class对象。然后调用getMethod

方法,参数是getService,后面的String表示getService()方法的参数类型,也就是拿到了ServieManager的getService(Strings)这个方法。

嗯...既然已经得到了getService(Stringname)方法,那么就调用它!把要传入的参数,也就是想得到的Service的名字传入,这里我们传入"phone"字符串,就可以返回一个

IBinder对象。

那,为什么要传入"phone"这个名字呢?

这是因为ITelephony.java的实现类PhoneInterfaceManager.java在创建的时候,把自己添加进入了ServiceManager,然后使用的名字就是"phone"

如:

代码路径:

packages/apps/Phone/src/com/android/phone/PhoneInterfaceManager.java

代码:


01
/**
02
*
ImplementationoftheITelephonyinterface.
03
*/
04
public

class

PhoneInterfaceManager
extends


ITelephony.Stub{
05

....
06

07

private


PhoneInterfaceManager(PhoneGlobalsapp,Phonephone){
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
}
嗯,大家看到没有,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:Neitheruser10125norcurrentprocesshasandroid.permission.MODIFY_PHONE_STATE.

D/Sandy(9058):atandroid.os.Parcel.readException(Parcel.java:1327)

D/Sandy(9058):atandroid.os.Parcel.readException(Parcel.java:1281)

D/Sandy(9058):atcom.android.internal.telephony.ITelephony$Stub$Proxy.answerRingingCall(ITelephony.java:1019)

D/Sandy(9058):atcom.example.hillrestproject.service.PhonePickupService.onPickUpEvent(PhonePickupService.java:180)

D/Sandy(9058):atcom.hcrest.gestures.pickup.PickUpDetector.onSensorData(PickUpDetector.java:150)

D/Sandy(9058):atcom.hcrest.android.sensors.SensorManagerAdapter$ListenerDelegate.onSensorChanged(SensorManagerAdapter.java:373)

D/Sandy(9058):atandroid.hardware.SensorManager$ListenerDelegate$1.handleMessage(SensorManager.java:635)

D/Sandy(9058):atandroid.os.Handler.dispatchMessage(Handler.java:99)

D/Sandy(9058):atandroid.os.Looper.loop(Looper.java:137)

D/Sandy(9058):atandroid.app.ActivityThread.main(ActivityThread.java:4507)

D/Sandy(9058):atjava.lang.reflect.Method.invokeNative(NativeMethod)

D/Sandy(9058):atjava.lang.reflect.Method.invoke(Method.java:511)

D/Sandy(9058):atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)

D/Sandy(9058):atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)

D/Sandy(9058):atdalvik.system.NativeStart.main(NativeMethod)

所以,对于高版本的手机的话,我们要用另外的方法来处理,这也是下面要讨论的问题。

三高版本自动接听电话

我们把整个代码贴出来,然后一行行解释


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


(NoSuchMethodExceptione){
12

Log.d(
"Sandy"
,
""
,e);
13

}
catch


(ClassNotFoundExceptione){
14

Log.d(
"Sandy"
,
""
,e);
15

}
catch


(Exceptione){
16

Log.d(
"Sandy"
,
""
,e);
17

try
{
18

Log.e(
"Sandy"
,
"for
version4.1orlarger"
);
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


(Exceptione2){
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

}
在上面的代码里面,最开始的代码和第二点讲解的时候,没有什么区别,主要看Exception发生之后,try{}里面的代码,也就是:


1
Log.e(
"Sandy"
,
"for
version4.1orlarger"
);
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
/
Broadcastreceiverpurely
for


ACTION_MEDIA_BUTTONbroadcasts
02
private

final

BroadcastReceivermMediaButtonReceiver=
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(Contextcontext,Intentintent){
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(Phonephone,KeyEventevent){
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的一个漏洞呢...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: