Поскольку я пытаюсь создать собственный экран для входящих вызовов, я пытаюсь программно отвечать на входящий вызов. Я использую следующий код, но он не работает в 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 8.0 Oreo
Несмотря на то, что изначально вопрос задавался о поддержке Android L, люди, похоже, все еще задают этот вопрос и ответ, поэтому стоит описать улучшения, представленные в Android 8.0 Oreo. Методы обратной совместимости все еще описаны ниже.
Что изменилось?
Начиная с Android 8.0 Oreo , группа разрешений PHONE также содержит разрешение ANSWER_PHONE_CALLS . Как следует из названия разрешения, его удержание позволяет вашему приложению программно принимать входящие вызовы через соответствующий вызов API без какого-либо взлома системы с использованием отражения или имитации пользователя.
Как мы используем это изменение?
Вам следует проверять версию системы во время выполнения, если вы поддерживаете более старые версии Android, чтобы вы могли инкапсулировать этот новый вызов API, сохраняя поддержку этих старых версий Android. Вам следует выполнить запрос разрешений во время выполнения, чтобы получить это новое разрешение во время выполнения, как это стандартно для новых версий Android.
После получения разрешения ваше приложение просто должно вызвать метод acceptRingingCall TelecomManager . Тогда базовый вызов выглядит следующим образом:
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 , как она была введена только в фиксации 83da75d для Android 4.4 KitKat ( строка 1537 на 4.4.3_r1 ) , а затем «повторно» в фиксации f1e1e77 для Lollipop ( линии 3138 на 5.0.0_r1 ) из - за того , как Структурировано дерево Git. Это означает, что если вы не поддерживаете только устройства с Lollipop, что, вероятно, является плохим решением, учитывая крошечную долю рынка на данный момент, вам все равно необходимо предоставить запасные методы, если вы пойдете по этому пути.
Как бы мы это использовали?
Поскольку рассматриваемый метод скрыт от использования приложениями SDK, вам необходимо использовать отражение для динамического изучения и использования метода во время выполнения. Если вы не знакомы с отражением, вы можете быстро прочитать Что такое отражение и почему оно полезно? . Вы также можете более подробно изучить особенности в Trail: The 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); }
Это слишком хорошо, чтобы быть правдой!
Собственно, есть одна небольшая проблема. Этот метод должен быть полностью функциональным, но менеджер безопасности хочет, чтобы вызывающие абоненты сохраняли android.permission.MODIFY_PHONE_STATE . Это разрешение относится только к частично задокументированным функциям системы, поскольку третьи стороны не должны трогать его (как вы можете видеть из документации к нему). Вы можете попробовать добавить
<uses-permission>
для него, но это не поможет, поскольку уровень защиты для этого разрешения - signature | system ( см. Строку 1201 core / AndroidManifest на 5.0.0_r1 ).Вы можете прочитать Документацию о проблеме 34785: Обновление android: protectionLevel, которая была создана еще в 2012 году, чтобы увидеть, что нам не хватает деталей о конкретном «синтаксисе канала», но, поэкспериментировав, выяснилось, что он должен работать как «И», что означает все указанные флаги должны быть выполнены для предоставления разрешения. При таком предположении это будет означать, что у вас должно быть приложение:
Устанавливается как системное приложение.
Это должно быть нормально и может быть выполнено, попросив пользователей установить с помощью ZIP-архива при восстановлении, например, при рутировании или установке приложений Google на пользовательские ПЗУ, которые еще не упакованы.
Подписан той же подписью, что и frameworks / base, также известный как система, он же ROM.
Вот тут-то и всплывают проблемы. Для этого вам нужно иметь в руках ключи, используемые для подписи frameworks / base. Вам потребуется не только получить доступ к ключам Google для заводских образов Nexus, но и получить доступ ко всем остальным OEM-производителям и ключам разработчиков ROM. Это не кажется правдоподобным, поэтому вы можете подписать свое приложение с помощью системных ключей, либо создав пользовательское ПЗУ и попросив пользователей переключиться на него (что может быть сложно), либо найдя эксплойт, с помощью которого можно обойти уровень защиты разрешений. (что тоже может быть сложно).
Кроме того, это поведение, похоже, связано с проблемой 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS больше не работает, который использует тот же уровень защиты вместе с недокументированным флагом разработки.
Работа с TelephonyManager звучит неплохо, но не будет работать, если вы не получите соответствующее разрешение, что на практике не так просто.
А как насчет использования TelephonyManager другими способами?
К сожалению, для использования классных инструментов , похоже, требуется, чтобы вы держали android.permission.MODIFY_PHONE_STATE, что, в свою очередь, означает, что вам будет сложно получить доступ к этим методам.
Метод 2: вызов службы СЕРВИСНЫЙ КОД
Когда вы можете проверить, что сборка, запущенная на устройстве, будет работать с указанным кодом.
Не имея возможности взаимодействовать с TelephonyManager, существует также возможность взаимодействия со службой через
service
исполняемый файл.Как это работает?
Это довольно просто, но документации по этому маршруту даже меньше, чем по другим. Мы точно знаем, что исполняемый файл принимает два аргумента - имя службы и код.
Имя службы, которое мы хотим использовать, - телефон .
В этом можно убедиться, запустив
service list
.Код мы хотим использовать , как представляется , было 6 , но , кажется, теперь будет 5 .
Похоже, что он был основан на IBinder.FIRST_CALL_TRANSACTION + 5 для многих версий сейчас (от 1.5_r4 до 4.4.4_r1 ), но во время локального тестирования код 5 работал, чтобы ответить на входящий звонок. Поскольку Lollipo представляет собой массовое обновление, понятно, что здесь также изменились внутренние компоненты.
Это приводит к команде
service call phone 5
.Как это использовать программно?
Ява
Следующий код является приблизительной реализацией, призванной служить доказательством концепции. Если вы действительно хотите продолжить и использовать этот метод, вы, вероятно, захотите ознакомиться с инструкциями по беспроблемному использованию su и, возможно, переключиться на более полностью разработанный libsuperuser от Chainfire .
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(); }
Манифест
<!-- Inform the user we want them root accesses. --> <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
Для этого действительно нужен root-доступ?
К сожалению, это так. Вы можете попробовать использовать на нем Runtime.exec , но мне не повезло с этим маршрутом.
Насколько это стабильно?
Я рад, что ты спросил. Из-за того, что это не задокументировано, это может нарушаться в разных версиях, как показано на кажущейся разнице в коде выше. Имя службы, вероятно, должно оставаться телефонным в разных сборках, но, насколько нам известно, значение кода может изменяться в нескольких сборках одной и той же версии (внутренние модификации, например, оболочкой OEM), в свою очередь, нарушая используемый метод. Поэтому стоит упомянуть, что тестирование проводилось на Nexus 4 (mako / occam). Я бы лично посоветовал вам не использовать этот метод, но, поскольку я не могу найти более стабильного метода, я считаю, что это лучший вариант.
Исходный метод: назначение кода гарнитуры
Для времен, когда вам нужно успокоиться.
В следующем разделе под сильным влиянием этого ответа по Райли C .
Метод моделирования намерения гарнитуры, опубликованный в исходном вопросе, кажется, транслируется так же, как и следовало ожидать, но, похоже, он не достигает цели ответа на звонок. Хотя кажется, что существует код, который должен обрабатывать эти намерения, о них просто не заботятся, что должно означать, что должны быть какие-то новые контрмеры против этого метода. Журнал также не показывает ничего интересного, и я лично не верю, что копаться в исходном коде Android будет целесообразным только из-за возможности того, что Google внесет небольшое изменение, которое в любом случае легко нарушает используемый метод.
Что мы можем сделать прямо сейчас?
Поведение можно последовательно воспроизвести с помощью входного исполняемого файла. Он принимает аргумент кода ключа, для которого мы просто передаем KeyEvent.KEYCODE_HEADSETHOOK . Для этого метода даже не требуется root-доступ, что делает его подходящим для распространенных случаев использования среди широкой публики, но в этом методе есть небольшой недостаток - событие нажатия кнопки гарнитуры не может быть указано для запроса разрешения, что означает, что он работает как настоящий нажатие кнопки и всплывает вверх по всей цепочке, что, в свою очередь, означает, что вы должны быть осторожны с тем, когда имитировать нажатие кнопки, так как это может, например, запустить музыкальный проигрыватель для начала воспроизведения, если никто другой с более высоким приоритетом не готов обработать событие.
Код?
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
Существует хороший общедоступный API для Android 8.0 Oreo и новее.
До Android 8.0 Oreo не было общедоступного API. Внутренние API запрещены или просто без документации. Вы должны действовать осторожно.
источник
Полностью рабочее решение основано на коде @Valter Strods.
Чтобы заставить его работать, вы должны отобразить (невидимое) действие на экране блокировки, где выполняется код.
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);
источник
Ниже приводится альтернативный подход, который сработал для меня. Он отправляет ключевое событие на телекоммуникационный сервер напрямую с помощью API MediaController. Для этого необходимо, чтобы приложение имело разрешение 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_DOWN
раньшеACTION_UP
, иначе сказанное выше не действует. т.е. необходимо следующее:m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
Но поскольку официальный вызов для ответа на вызов доступен с Android O (см. Верхний ответ), в этом хакерстве может больше не быть необходимости, если только вы не застряли на старом уровне API компиляции до Android O.
источник
Чтобы немного уточнить ответ @Muzikant и немного изменить его, чтобы он работал немного чище на моем устройстве, попробуйте
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-права.
Проблема с этим подходом в том, что он работает только при определенных условиях (для меня). То есть, если я запустил вышеуказанный поток из пункта меню, который пользователь выбирает во время звонка вызова, вызов отвечает нормально. Если я запускаю его с приемника, который отслеживает статус входящего вызова, он полностью игнорируется.
Таким образом, на моем Nexus 5 он хорошо работает для ответа, управляемого пользователем, и должен соответствовать назначению настраиваемого экрана вызова. Он просто не будет работать ни с какими приложениями автоматического управления вызовами.
Также следует отметить все возможные предостережения, в том числе то, что это тоже, вероятно, перестанет работать через одно или два обновления.
источник
input keyevent 79
отлично работает на Sony Xperia 5.0. Работает при звонке с занятия или с приемника вещания.через команды adb Как ответить на звонок по adb
Имейте в виду, что Android - это Linux с массивной JVM на передней панели. Вы можете загрузить приложение командной строки и получить root-права на телефоне, и теперь у вас есть обычный компьютер с Linux и командная строка, которая выполняет все обычные действия. Запускать скрипты, можно даже по ssh (трюк с OpenVPN)
источник
Спасибо @notz, у меня работает ответ на Lolillop. Чтобы этот код продолжал работать со старым SDK для Android, вы можете сделать этот код:
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 }
источник
Выполните следующую команду от имени пользователя root:
input keyevent 5
Подробнее о моделировании ключевых событий здесь .
Вы можете использовать этот базовый класс, который я создал, для запуска команд из вашего приложения с правами root.
источник
проверьте это: сначала добавьте разрешения, затем используйте 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); } }
источник