在Android 5.0(Lollipop)中如何以编程方式回答传入呼叫?
正如我试图创build一个自定义屏幕来电,我试图编程方式应答来电。 我正在使用下面的代码,但它不适用于Android 5.0。
// Simulate a press of the headset button to pick up the call Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED"); // froyo and beyond trigger on buttonUp instead of buttonDown Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Android O更新
即使这个问题最初是针对Android L支持问题的,但人们似乎还是碰到了这个问题和答案,所以值得描述一下Android O的最新改进。向后兼容的方法仍然在下面描述。
什么改变了?
从Android O开始, PHONE权限组还包含ANSWER_PHONE_CALLS权限 。 正如许可的名称所暗示的,持有它可以让你的应用程序通过适当的API调用以编程方式接受来电,而不会使用reflection或模拟用户对系统进行任何黑客攻击。
我们如何利用这一变化?
如果您支持较旧的Android版本,则应该在运行时检查系统版本,以便您可以封装这个新的API调用,同时保持对那些较旧的Android版本的支持。 您应该在运行时按照请求的权限在运行时获得新的权限,就像在较新的Android版本上一样。
获得许可后,您的应用程序只需要调用TelecomManager的acceptRingingCall方法。 一个基本的调用看起来如下:
TelecomManager tm = (TelecomManager) mContext .getSystemService(Context.TELECOM_SERVICE); if (tm == null) { // whether you want to handle this is up to you really throw new NullPointerException("tm == null"); } tm.acceptRingingCall();`
方法1:TelephonyManager.answerRingingCall()
当你有无限的控制设备。
这是什么?
TelephonyManager.answerRingingCall()是一个隐藏的内部方法。 它作为ITelephony.answerRingingCall()的桥梁,已经在互联网上讨论过,并且在开始的时候似乎很有前途。 它在4.4.2_r1上不可用,因为它仅在针对Android 4.4 KitKat的提交83da75d ( 4.4.3_r1的行1537 )中引入,并且稍后在针对棒棒糖的提交f1e1e77 ( 5.0.0_r1上的行3138 )中被重新引入, Git树是结构化的。 这意味着,除非你只支持Lollipop的设备,根据目前它的微小市场份额,这可能是一个糟糕的决定,如果沿着这条路线走,你还是需要提供备用方法。
我们将如何使用这个?
由于所讨论的方法对SDK应用程序的使用是隐藏的,因此您需要使用reflection来在运行时dynamic检查和使用该方法。 如果你不熟悉reflection,可以快速阅读什么是reflection,为什么它是有用的? 。 如果您对此感兴趣,您还可以深入了解Trail:Reflection API的具体内容。
那么代码如何呢?
// set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("answerRingingCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e); }
这真是太好了!
其实,有一个小问题。 这个方法应该是完全可用的,但是安全pipe理者希望调用者保存android.permission.MODIFY_PHONE_STATE 。 这个权限只是系统中部分logging的function,因为第三方不会触及它(正如你可以从文档中看到的那样)。 你可以尝试为它添加一个<uses-permission>
,但是这样做并不好,因为这个权限的保护级别是signature | system ( 请参阅5.0.0_r1上core / AndroidManifest的第1201行 )。
您可以阅读Issue 34785:更新 2012年创build的android:protectionLevel文档 ,以查看我们是否缺less关于特定“pipe道语法”的详细信息,但是从实验来看,它似乎必须作为“AND”指定的标志必须满足授予的许可。 在这个假设下工作,这将意味着你必须有你的申请:
-
作为系统应用程序安装。
这应该没问题,可以通过要求用户在恢复中使用ZIP进行安装来完成,例如,在没有打包的自定义ROM上生成或安装Google应用程序。
-
与框架/基础(也就是ROM)签署相同的签名。
这是问题出现的地方。 为此,您需要掌握用于签署框架/基础的密钥。 您不仅需要访问Google的Nexus工厂映像的密钥,还需要访问所有其他OEM和ROM开发人员的密钥。 这看起来似乎并不合理,所以你可以让你的应用程序使用系统密钥进行签名,方法是制作自定义ROM并要求你的用户切换到它(这可能很困难),或者find一个可以绕过权限保护级别的漏洞利用(这可能很难)。
此外,此行为似乎与问题34792相关:Android Jelly Bean / 4.1:android.permission.READ_LOGS不再起作用 ,它使用相同的保护级别以及未logging的开发标志。
使用TelephonyManager听起来不错,但是除非获得适当的权限,否则在实践中并不容易。
以其他方式使用TelephonyManager?
不幸的是,它似乎要求你保持android.permission.MODIFY_PHONE_STATE使用酷的工具,这反过来意味着你将很难获得这些方法的访问。
方法2:服务呼叫SERVICE CODE
对于何时可以testing设备上运行的构build将使用指定的代码。
如果不能与TelephonyManager进行交互,还可以通过service
可执行文件与服务进行交互。
这个怎么用?
这是相当简单的,但有关这条路线比其他文件更less。 我们知道可执行文件需要两个参数 – 服务名称和代码。
-
我们要使用的服务名称是电话 。
这可以通过运行
service list
来看到。 -
我们想要使用的代码似乎是6,但现在似乎是5 。
它看起来像现在已经基于IBinder.FIRST_CALL_TRANSACTION + 5(很多版本从1.5_r4到4.4.4_r1 ),但在本地testing中,代码5工作来接听来电。 由于Lollipo是一个全面的更新,所以内部变化也是可以理解的。
这是service call phone 5
的命令的结果。
我们如何利用这个编程?
Java的
下面的代码是一个粗略的实现,作为概念certificate。 如果你真的想继续使用这种方法,你可能想要检查su的使用问题的准则,并可能切换到Chainfire更充分开发的libsuperuser 。
try { Process proc = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(proc.getOutputStream()); os.writeBytes("service call phone 5\n"); os.flush(); os.writeBytes("exit\n"); os.flush(); if (proc.waitFor() == 255) { // TODO handle being declined root access // 255 is the standard code for being declined root for SU } } catch (IOException e) { // TODO handle I/O going wrong // this probably means that the device isn't rooted } catch (InterruptedException e) { // don't swallow interruptions Thread.currentThread().interrupt(); }
performance
<!-- Inform the user we want them root accesses. --> <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
这真的需要root权限吗?
可悲的是,似乎是这样。 你可以尝试使用Runtime.exec ,但是我没有得到任何运气与该路线。
这有多稳定?
我很高兴你问。 由于没有被logging,这可以打破各种版本,如上述看似代码差异所示。 服务名称应该可以保留在不同版本的手机上,但是对于我们所知道的,代码值可以在同一版本的多个版本之间进行更改(例如,通过OEM的皮肤进行内部修改),从而打破所使用的方法。 因此值得一提的是testing发生在Nexus 4(mako / occam)上。 我个人build议你不要使用这种方法,但由于我无法find更稳定的方法,我相信这是最好的select。
原始方法:耳机键码意图
有时你必须解决。
以下部分受到Riley C的 回答的强烈影响。
在原始问题中发布的模拟耳机意图方法似乎是像预期的那样广播,但是它似乎并没有完成应答呼叫的目标。 虽然似乎有代码应该处理这些意图,但它们根本就不被关心,这意味着必须采取某种新的对付这种方法的对策。 这个日志也没有显示任何感兴趣的东西,而且我个人也不认为通过Android源代码挖掘这个代码是值得的,因为Google可能会引入一个轻微的变化,轻易地破坏了使用的方法。
现在有什么我们可以做的吗?
行为可以使用input的可执行文件一致地复制。 它需要一个keycode参数,我们只需传入KeyEvent.KEYCODE_HEADSETHOOK 。 该方法甚至不需要root访问权限,使其适用于普通公众的常见使用情况,但是该方法有一个小缺点 – 耳机button按下事件不能被指定为需要许可,意味着它像真实button按下并在整个链上冒泡,这意味着您必须谨慎模拟button按下的时间,例如,如果没有其他更高优先级的人准备好处理,则触发音乐播放器开始播放事件。
码?
new Thread(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { // Runtime.exec(String) had an I/O problem, try to fall back String enforcedPerm = "android.permission.CALL_PRIVILEGED"; Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); mContext.sendOrderedBroadcast(btnDown, enforcedPerm); mContext.sendOrderedBroadcast(btnUp, enforcedPerm); } } }).start();
tl; dr,在Android O之前
- 没有公共的API。
- 内部的API是禁止的或根本没有文档。
- 你应该谨慎行事。
完全可行的解决scheme基于@Valter Strods代码。
为了使它工作,你必须在执行代码的locking屏幕上显示一个(不可见的)活动。
AndroidManifest.xml中
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <activity android:name="com.mysms.android.lib.activity.AcceptCallActivity" android:launchMode="singleTop" android:excludeFromRecents="true" android:taskAffinity="" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@style/Mysms.Invisible"> </activity>
呼叫接受活动
package com.mysms.android.lib.activity; import android.app.Activity; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.telephony.TelephonyManager; import android.view.KeyEvent; import android.view.WindowManager; import org.apache.log4j.Logger; import java.io.IOException; public class AcceptCallActivity extends Activity { private static Logger logger = Logger.getLogger(AcceptCallActivity.class); private static final String MANUFACTURER_HTC = "HTC"; private KeyguardManager keyguardManager; private AudioManager audioManager; private CallStateReceiver callStateReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); } @Override protected void onResume() { super.onResume(); registerCallStateReceiver(); updateWindowFlags(); acceptCall(); } @Override protected void onPause() { super.onPause(); if (callStateReceiver != null) { unregisterReceiver(callStateReceiver); callStateReceiver = null; } } private void registerCallStateReceiver() { callStateReceiver = new CallStateReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); registerReceiver(callStateReceiver, intentFilter); } private void updateWindowFlags() { if (keyguardManager.inKeyguardRestrictedInputMode()) { getWindow().addFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } else { getWindow().clearFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } } private void acceptCall() { // for HTC devices we need to broadcast a connected headset boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER) && !audioManager.isWiredHeadsetOn(); if (broadcastConnected) { broadcastHeadsetConnected(false); } try { try { logger.debug("execute input keycode headset hook"); Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { // Runtime.exec(String) had an I/O problem, try to fall back logger.debug("send keycode headset hook intents"); String enforcedPerm = "android.permission.CALL_PRIVILEGED"; Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); sendOrderedBroadcast(btnDown, enforcedPerm); sendOrderedBroadcast(btnUp, enforcedPerm); } } finally { if (broadcastConnected) { broadcastHeadsetConnected(false); } } } private void broadcastHeadsetConnected(boolean connected) { Intent i = new Intent(Intent.ACTION_HEADSET_PLUG); i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); i.putExtra("state", connected ? 1 : 0); i.putExtra("name", "mysms"); try { sendOrderedBroadcast(i, null); } catch (Exception e) { } } private class CallStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { finish(); } } }
样式
<style name="Mysms.Invisible"> <item name="android:windowFrame">@null</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@null</item> </style>
最后称之为魔法!
Intent intent = new Intent(context, AcceptCallActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(intent);
为了详细解释@Muzikant的答案,并稍微修改一下,以便在我的设备上工作input keyevent 79
干净,请尝试input keyevent 79
( KeyEvent.KEYCODE_HEADSETHOOK的常量)。 非常大致:
new Thread(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec( "input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK ); } catch (Throwable t) { // do something proper here. } } }).start();
原谅相当不好的编码惯例,我不太熟练的Runtime.exec()调用。 请注意,我的设备没有root权限,也不需要root权限。
这种方法的麻烦是,它只能在一定的条件下工作(对我来说)。 也就是说,如果我从菜单选项中运行上面的线程,用户在呼叫响起时select,则呼叫应答就好了。 如果我从监听来电状态的接收机运行它,它会被完全忽略。
所以在我的Nexus 5上,它对于用户驱动的回答很有效,并且适合自定义的呼叫屏幕的用途。 它不适用于任何types的自动呼叫控制types的应用程序。
另外值得注意的是,所有可能的警告,包括这也可能会停止工作在更新或两个。
以下是为我工作的另一种方法。 它直接使用MediaController API将关键事件发送到电信服务器。 这要求应用程序具有BIND_NOTIFICATION_LISTENER_SERVICE权限, 并且明确授予来自用户的通知访问权限:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) void sendHeadsetHookLollipop() { MediaSessionManager mediaSessionManager = (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE); try { List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions (new ComponentName(getApplicationContext(), NotificationReceiverService.class)); for (MediaController m : mediaControllerList) { if ("com.android.server.telecom".equals(m.getPackageName())) { m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); log.info("HEADSETHOOK sent to telecom server"); break; } } } catch (SecurityException e) { log.error("Permission error. Access to notification not granted to the app."); } }
上面的代码中的NotificationReceiverService.class
可能只是一个空的类。
import android.service.notification.NotificationListenerService; public class NotificationReceiverService extends NotificationListenerService{ public NotificationReceiverService() { } }
与清单中的相应部分:
<service android:name=".NotificationReceiverService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter>
由于事件的目标是明确的,这应该可能避免触发媒体播放器的任何副作用。
注意:振铃事件后,电信服务器可能不会立即生效。 为了可靠地工作,在发送事件之前,应用程序实现MediaSessionManager.OnActiveSessionsChangedListener监视电信服务器何时变为活动状态可能是有用的。
更新:
在Android O中 ,需要在ACTION_UP
之前模拟ACTION_DOWN
,否则上述操作无效。 即需要以下内容:
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
但是自从Android O以来,一个正式的接听电话呼叫是可用的(见最佳答案),可能不再需要这个黑客,除非在Android O之前有一个旧的编译API级别。
感谢@notz在Lolillop上为我工作的答案。 为了保持这个代码与旧的android SDK的工作,你可以做这个代码:
if (Build.VERSION.SDK_INT >= 21) { Intent answerCalintent = new Intent(context, AcceptCallActivity.class); answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(answerCalintent); } else { if (telephonyService != null) { try { telephonyService.answerRingingCall(); } catch (Exception e) { answerPhoneHeadsethook(); } } }
如何在自动接听电话后打开免提电话。
我用setSpeakerphoneOn解决了我的问题。 我认为它值得在这里发布,因为自动接听电话的用例通常也需要扬声器才有用。 再次感谢这个主题上的每个人,这是一个了不起的工作。
这对我的Android 5.1.1在我的Nexus 4没有ROOT的作品。 ;)
所需权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Java代码:
// this means the phone has answered if(state==TelephonyManager.CALL_STATE_OFFHOOK) { // try and turn on speaker phone final Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { AudioManager audioManager = (AudioManager) localContext.getSystemService(Context.AUDIO_SERVICE); // this doesnt work without android.permission.MODIFY_PHONE_STATE // audioManager.setMode(AudioManager.MODE_IN_CALL); // weirdly this works audioManager.setMode(AudioManager.MODE_NORMAL); // this is important audioManager.setSpeakerphoneOn(true); // note the phone interface won't show speaker phone is enabled // but the phone speaker will be on // remember to turn it back off when your done ;) } }, 500); // half a second delay is important or it might fail }
通过adb命令如何通过adb接听电话
请记住,Android是前端具有大量JVM的Linux。 你可以下载一个命令行应用程序,并根据手机,现在你有一个普通的Linux电脑和命令行,所有正常的事情。 运行脚本,你甚至可以SSH(OpenVPN技巧)
以root身份运行以下命令:
input keyevent 5
更多关于在这里模拟keyevents的细节。
您可以使用我创build的这个基类来以应用程序的根用户身份运行命令。
testing这个:首先添加权限,然后使用killCall()挂断使用answerCall()来接听电话
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission> public void killCall() { try { TelephonyManager telephonyManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); Class classTelephony = Class.forName(telephonyManager.getClass().getName()); Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony"); methodGetITelephony.setAccessible(true); Object telephonyInterface = methodGetITelephony.invoke(telephonyManager); Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName()); Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall"); methodEndCall.invoke(telephonyInterface); } catch (Exception ex) { Log.d(TAG, "PhoneStateReceiver **" + ex.toString()); } } public void answerCall() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { answerRingingCallWithIntent(); } } public void answerRingingCallWithIntent() { try { Intent localIntent1 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent1.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent1.putExtra("state", 1); localIntent1.putExtra("microphone", 1); localIntent1.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent1, "android.permission.CALL_PRIVILEGED"); Intent localIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK); localIntent2.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent1); getContext().sendOrderedBroadcast(localIntent2, "android.permission.CALL_PRIVILEGED"); Intent localIntent3 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); localIntent3.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent2); getContext().sendOrderedBroadcast(localIntent3, "android.permission.CALL_PRIVILEGED"); Intent localIntent4 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent4.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent4.putExtra("state", 0); localIntent4.putExtra("microphone", 1); localIntent4.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent4, "android.permission.CALL_PRIVILEGED"); } catch (Exception e2) { e2.printStackTrace(); } }
仅供参考如果您有兴趣了解如何结束Android O上正在进行的调用,则Valter的Method 1: TelephonyManager.answerRingingCall()
将在更改您调用的方法为endCall
。
它只需要android.permission.CALL_PHONE
权限。
代码如下:
// set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; public void endCall() { TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("endCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e); } }