Как программно ответить / завершить вызов в Android 4.1?
Я пишу приложение для android, в котором мне нужно ответить на входящий вызов, выполнить некоторую работу, а затем завершить вызов.
После всего Googling я мог бы найти два разных способа добиться этого, оба из которых не работают с последними версиями Android, в частности, после 4.1, Jelly Bean.
I.) доступ " com.андроид.внутренний.телефония.ITelephony", используя механизм отражения Java в широковещательный приемник для "Андроид.намерение.действие.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 отвечать на вызов. Это делается путем трансляции " намерения.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, после чего android ограничил пользовательские приложения от трансляции "намерения.ACTION_MEDIA_BUTTON".
Итак, мой вывод заключается в том, что в настоящее время нет способа, как мы можем достичь этого в Android 4.1 или более поздней версии.
кто-нибудь еще нашел какое-либо другое решение или обходной путь к этой проблеме?
7 ответов:
это работает с Android 2.2 до 4.0 и теперь после добавления try catch в последнюю строку он работает для 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(); }
Мои приложения, используя следующий код, чтобы ответить на телефон в течение 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 вещания " намерение.ACTION_MEDIA_BUTTON " в моем тестировании N устройство 4.2.2, и я не видел отчетов о сбоях из Play Store, указывающих на такие исключения происходят.
Я скажу, что это не всегда работает. Он не работает на устройствах 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(); }кроме генерировать SecurityException и поймать его.
в других ответах, где работает код, это из-за Keyevent не.KEYCODE_HEADSETHOOK в эфир.
В заключение этой темы, вот код, который работает для меня для Android 4.2.2.
--> на вызов отвечает имитация нажатия крючка гарнитуры и сохранение трансляции в try-catch, как упоминалось @PravinDodia в потоке abouve. (Обратите внимание, что исключение генерируется и обрабатывается в catch, и на вызов все равно отвечают. Поэтому я думаю, что мы можем просто игнорировать это исключение и продолжать жить так, как будто ничего не произошло! )
--> вызов отключен с помощью Ителефония.
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); } } }по крайней мере, функция отключения работает, и мы знаем, как это работает. Таким образом, те, кто хочет разработать приложение для запрета вызовов, могут идти вперед. Для таких, как я, кто хочет ответить на звонок, я думаю, мы можем использовать это сейчас и только надеяться, что он не перестанет работать в следующей версии.
попробуй такое Ответ для завершения вызова с помощью про-грамматически. Он отлично работает для меня.
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); }
попробуйте это :
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"/>
метод ITelephony не работает на 4.4, и я нахожу, что метод headset/media button по-прежнему позволяет довольно долго звонить перед зависанием.
это сообщение в блоге chaps показывает новый метод, который я тестировал как работающий на 4.4.2 Galaxy s4 и HTC one mini, который зависает намного быстрее, и вы также не получаете запись пропущенного вызова.
http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-android-4.html
методика использует a runtime 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; } } }
отключение вызова с помощью IT-телефонии не работает на некоторых устройствах, таких как Samsung S Duos. Но вы все равно можете заставить звонок молчать:)
Comments