Как программно ответить/завершить вызов в Android 4.1?
Я пишу приложение для Android, в котором мне нужно ответить на входящий звонок, выполнить некоторую работу и затем завершить вызов.
После всего Googling я мог найти два разных способа достижения этого, оба из которых не работают с последними версиями Android, особенно после 4.1, Jelly Bean.
I.) Доступ к "com.android.internal.telephony.ITelephony" с использованием Java Reflection в приемнике Broadcast для "android.intent.action.PHONE_STATE". Ниже пример кода можно найти в сотнях связанных сообщений:
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
private ITelephony telephonyService;
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
Log.v(TAG, "Receving....");
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Log.v(TAG, "Get getTeleService...");
Class c = Class.forName(telephony.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
telephonyService = (ITelephony) m.invoke(telephony);
telephonyService.silenceRinger();
Log.v(TAG, "Answering Call now...");
telephonyService.answerRingingCall();
Log.v(TAG, "Call answered...");
//telephonyService.endCall();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG,
"FATAL ERROR: could not connect to telephony subsystem");
Log.e(TAG, "Exception object: " + e);
}
}
}
Проблема с этим кодом заключается в том, что
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
требуется, чтобы этот метод работал, и это разрешение было определено как "только для системных приложений" от android v 2.3. Короче говоря, обычные пользовательские приложения больше не могут определять это разрешение в файле манифеста.
II.) Другой способ - имитировать нажатие на крючок гарнитуры, что заставляет Android отвечать на вызов. Это делается путем трансляции "Intent.ACTION_MEDIA_BUTTON", как показано ниже.
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON);
answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(answer, null);
Log.d(TAG, "Answered incoming call from: " + number);
}
return;
}
}
Этот метод работает до Android 4.1, после которого андроид ограничил пользовательские приложения от трансляции "Intent.ACTION_MEDIA_BUTTON".
Итак, я пришел к выводу, что в настоящее время мы не можем добиться этого в Android 4.1 или новее.
Кто-нибудь еще нашел какое-либо другое решение или обходное решение этой проблемы?
Ответы
Ответ 1
Это работает с Android 2.2 до 4.0, и теперь, после добавления try try в последнюю строку, это работает для 4.1.2 и 4.2. Честно говоря, не знаю, как это работает, но это работает для меня.
Log.d(tag, "InSecond Method Ans Call");
// 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));
sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) {
e.printStackTrace();
}
Это работает для меня в Android 4.1.2, а также я тестировал на 4.2 Это все еще дает исключение, которое обрабатывается.
Редактировать для завершения вызова
Надеюсь, что это поможет всем людям, которые ищут полное решение для ответа и завершения вызова.
/**
* Reject button click listener will reject the incoming call.
*/
private class RejectCallOnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
Log.d(tag, "OnRejectButton: " + "Reject OnClick");
ignoreCall();
exitCleanly();
}
}
/**
* ignore incoming calls
*/
private void ignoreCall() {
if (USE_ITELEPHONY)
ignoreCallAidl();
else
ignoreCallPackageRestart();
}
/**
* AIDL/ITelephony technique for ignoring calls
*/
private void ignoreCallAidl() {
try {
// telephonyService.silenceRinger();
telephonyService.endCall();
} catch (RemoteException e) {
e.printStackTrace();
Log.d(tag, "ignoreCall: " + "Error: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
Log.d(tag, "ignoreCall" + "Error: " + e.getMessage());
}
}
/**
* package restart technique for ignoring calls
*/
private void ignoreCallPackageRestart() {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.restartPackage("com.android.providers.telephony");
am.restartPackage("com.android.phone");
}
/**
* cleanup and exit routine
*/
private void exitCleanly() {
unHookReceiver();
this.finish();
}
Ответ 2
Мое приложение использовало следующий код для ответа на телефон в течение 6 месяцев:
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(i, null);
Я тестировал это в версиях Android от 2.2 до 4.2.2. Я не видел трансляцию SecurityException "Intent.ACTION_MEDIA_BUTTON" в моем тестировании n 4.2.2-устройства, и я не видел отчетов о сбоях из Play Маркета, указывающих на такие исключения.
Я скажу, что это не всегда работает. Он не работает на устройствах HTC из-за того, что у устройств HTC есть HeadsetObeserver, которые прослушивают фактическое подключение проводной гарнитуры. Без этого события, которое в настоящее время является исключением SecurityException для стороннего приложения для трансляции, HeadsetHook KeyEvent игнорируется.
Предыдущие ответы вводят в заблуждение. Следующий блок кода ничего не делает:
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
за исключением генерации исключения безопасности и его уловления.
В других ответах, где работает код, это происходит из-за того, что передается KeyEvent.KEYCODE_HEADSETHOOK.
Ответ 3
В качестве вывода к этой теме, вот код, который работает для меня для Android 4.2.2.
- > На вызов отвечает симуляция нажатия на крючок гарнитуры и сохранение трансляции в try-catch, как упоминалось в @PravinDodia в abouve thread. (Обратите внимание, что исключение бросается и обрабатывается в catch, и на вызов все равно ответят. Поэтому, я думаю, мы можем просто игнорировать это исключение и продолжать жизнь, как будто ничего не произошло!)
- > Вызов отключен с помощью ITelephony.
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
else {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
answerPhoneHeadsethook(context, intent);
return;
}
else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
Log.d(TAG, "CALL ANSWERED NOW!!");
try {
synchronized(this) {
Log.d(TAG, "Waiting for 10 sec ");
this.wait(10000);
}
}
catch(Exception e) {
Log.d(TAG, "Exception while waiting !!");
e.printStackTrace();
}
disconnectPhoneItelephony(context);
return;
}
else {
Log.d(TAG, "ALL DONE ...... !!");
}
}
}
public void answerPhoneHeadsethook(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(TAG, "Incoming call from: " + number);
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
try {
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Log.d(TAG, "ACTION_MEDIA_BUTTON broadcasted...");
}
catch (Exception e) {
Log.d(TAG, "Catch block of ACTION_MEDIA_BUTTON broadcast !");
}
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone
headSetUnPluggedintent.putExtra("name", "Headset");
// TODO: Should we require a permission?
try {
context.sendOrderedBroadcast(headSetUnPluggedintent, null);
Log.d(TAG, "ACTION_HEADSET_PLUG broadcasted ...");
}
catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
Log.d(TAG, "Catch block of ACTION_HEADSET_PLUG broadcast");
Log.d(TAG, "Call Answered From Catch Block !!");
}
Log.d(TAG, "Answered incoming call from: " + number);
}
Log.d(TAG, "Call Answered using headsethook");
}
public static void disconnectPhoneItelephony(Context context) {
ITelephony telephonyService;
Log.v(TAG, "Now disconnecting using ITelephony....");
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Log.v(TAG, "Get getTeleService...");
Class c = Class.forName(telephony.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
telephonyService = (ITelephony) m.invoke(telephony);
//telephonyService.silenceRinger();
Log.v(TAG, "Disconnecting Call now...");
//telephonyService.answerRingingCall();
//telephonyService.endcall();
Log.v(TAG, "Call disconnected...");
telephonyService.endCall();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG,
"FATAL ERROR: could not connect to telephony subsystem");
Log.e(TAG, "Exception object: " + e);
}
}
}
По крайней мере, работает функция разъединения, и мы знаем, как она работает. Поэтому те, кто хочет разработать приложение для запрета вызовов, могут продолжать работу.
Для таких, как я, кто хочет ответить на звонок, я думаю, мы можем использовать это сейчас, и только надеемся, что он не перестанет работать в следующей версии.
Ответ 4
Попробуйте
Ответ для завершения вызова с использованием программы. Его работа прекрасна для меня.
try {
String serviceManagerName = "android.os.ServiceManager";
String serviceManagerNativeName = "android.os.ServiceManagerNative";
String telephonyName = "com.android.internal.telephony.ITelephony";
Class telephonyClass;
Class telephonyStubClass;
Class serviceManagerClass;
Class serviceManagerStubClass;
Class serviceManagerNativeClass;
Class serviceManagerNativeStubClass;
Method telephonyCall;
Method telephonyEndCall;
Method telephonyAnswerCall;
Method getDefault;
Method[] temps;
Constructor[] serviceManagerConstructor;
// Method getService;
Object telephonyObject;
Object serviceManagerObject;
telephonyClass = Class.forName(telephonyName);
telephonyStubClass = telephonyClass.getClasses()[0];
serviceManagerClass = Class.forName(serviceManagerName);
serviceManagerNativeClass = Class.forName(serviceManagerNativeName);
Method getService = // getDefaults[29];
serviceManagerClass.getMethod("getService", String.class);
Method tempInterfaceMethod = serviceManagerNativeClass.getMethod(
"asInterface", IBinder.class);
Binder tmpBinder = new Binder();
tmpBinder.attachInterface(null, "fake");
serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder);
IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone");
Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class);
telephonyObject = serviceMethod.invoke(null, retbinder);
//telephonyCall = telephonyClass.getMethod("call", String.class);
telephonyEndCall = telephonyClass.getMethod("endCall");
//telephonyAnswerCall = telephonyClass.getMethod("answerRingingCall");
telephonyEndCall.invoke(telephonyObject);
} catch (Exception e) {
e.printStackTrace();
Log.error(DialerActivity.this,
"FATAL ERROR: could not connect to telephony subsystem");
Log.error(DialerActivity.this, "Exception object: " + e);
}
Ответ 5
Попробуйте следующее:
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");
Добавить разрешения в файл AndroidManifest.xml как
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
Ответ 6
Метод ITelephony не работает на 4.4, и я нахожу, что метод кнопки гарнитуры/медиапотока все еще позволяет довольно длинное кольцо перед зависанием.
Этот заголовок блога в блогах показывает новый метод, который я тестировал как работающий на 4.4.2 Galaxy s4 и HTC one mini, который зависает намного быстрее, и вы также не получаете пропущенную запись вызова.
http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-android-4.html
В методе используется исполняемый файл exec, как показано ниже, возможно, для некоторых устройств вам может понадобиться другое число.
public class HangupPhoneCallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
Executor eS = Executors.newSingleThreadExecutor();
eS.execute(new Runnable() {
@Override
public void run() {
Runtime runtime = Runtime.getRuntime();
try {
Log.d(TAG, "service call phone 5 \n");
runtime.exec("service call phone 5 \n");
} catch (Exception exc) {
Log.e(TAG, exc.getMessage());
}
}
});
return;
}
}
}
Ответ 7
Отключение вызова с помощью IT-телефонии не работает на некоторых устройствах, таких как Samsung S Duos. Но вы все равно можете сделать звонок беззвучным:)
Ответ 8
To end call in older version then 9.0
use this:
TelephonyManager tm = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);
Method m1 = null;
try {
m1 = tm.getClass().getDeclaredMethod("getITelephony");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
m1.setAccessible(true);
Object iTelephony = null;
try {
iTelephony = m1.invoke(tm);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Method m3 = null;
try {
m3 = iTelephony.getClass().getDeclaredMethod("endCall");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
m3.invoke(iTelephony);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
& for pie
TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
return telecomManager.endCall();
}
Make sure your compile SDK version is 28