Как повесить исходящий звонок в Android?

Я разрабатываю приложение, в котором одна из вещей, которые нам нужны, - это управлять исходящим вызовом, по крайней мере, чтобы иметь возможность остановить его из нашего приложения.

Я пробовал использовать Intent.ACTION_CALL из существующего действия:

Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); 
startActivity(callIntent); 

Но похоже, что остановка вызова через API запрещена.

Можете ли вы предложить обходной путь?

Например: включение авиарежима во время разговора? Просто пример; этот хакер у меня не сработал.


person Tilek    schedule 01.03.2009    source источник
comment
Прекращение разговора возможно. TextMe4Callback на рынке Android делает это.   -  person    schedule 24.05.2010
comment
Сработало ли использование BroadcastReceiver? Не могли бы вы изменить этот вопрос и / или принять ответ?   -  person Paul Lammertsma    schedule 22.11.2010


Ответы (8)


Захват исходящего вызова в BroadcastReceiver уже упоминался, и это определенно лучший способ сделать это, если вы хотите завершить вызов перед набором номера.

Однако после набора номера или разговора этот метод больше не работает. Единственный способ повесить трубку, с которым я сталкивался до сих пор, - это сделать это с помощью Java Reflection. Поскольку он не является частью общедоступного API, вы должны использовать его с осторожностью, а не полагаться на него. Любое изменение внутренней структуры Android приведет к поломке вашего приложения.

Блог Прасанты Пола демонстрирует, как этого можно достичь, что я резюмировал ниже.

Получение объекта ITelephony:

TelephonyManager tm = (TelephonyManager) context
        .getSystemService(Context.TELEPHONY_SERVICE);
try {
    // Java reflection to gain access to TelephonyManager's
    // ITelephony getter
    Log.v(TAG, "Get getTeleService...");
    Class c = Class.forName(tm.getClass().getName());
    Method m = c.getDeclaredMethod("getITelephony");
    m.setAccessible(true);
    com.android.internal.telephony.ITelephony telephonyService =
            (ITelephony) m.invoke(tm);
} catch (Exception e) {
    e.printStackTrace();
    Log.e(TAG,
            "FATAL ERROR: could not connect to telephony subsystem");
    Log.e(TAG, "Exception object: " + e);
}

Завершение разговора:

telephonyService.endCall();
person Paul Lammertsma    schedule 15.03.2011
comment
+1 Это не очень хорошая практика, но OP конкретно запрашивает обходной путь для чего-то, что не разрешено API, поэтому это хороший ответ. - person Bjarke Freund-Hansen; 09.06.2011
comment
Если это единственное решение, то я думаю, что это отличный ответ! - person TacB0sS; 12.01.2012
comment
эй, я не могу заблокировать частный вызов с помощью этого кода, который я проверил - person Ronak Mehta; 06.04.2012
comment
@Rstar Что это за исключение? Какая линия или метод не работают? На каком устройстве вы работаете? Какая версия Android? - person Paul Lammertsma; 06.04.2012
comment
у меня нет никаких исключений, см. этот код, и я также написал несколько примечаний в pastebin.com/3TW0ieVZ - person Ronak Mehta; 06.04.2012
comment
Это гораздо лучший ответ, чем у Эша. OP, очевидно, заявляет, что он хочет завершить звонок, а не мешать ему отвечать. - person you786; 11.04.2012
comment
@Rstar Я бы посоветовал добавить несколько операторов отладки, чтобы убедиться, что ваш BroadcastReceiver правильно зарегистрирован из манифеста. - person Paul Lammertsma; 20.05.2012
comment
работал над 4.2 в эмуляторе (с измененным .jar, чтобы избежать сокрытия API Eclipse-ADT) - person kagali-san; 24.01.2014
comment
Как вы импортируете com.android.internal.telephony.ITelephony? Выдает сообщение «Невозможно устранить ошибку символа». - person Buddy; 14.09.2015
comment
@EnesBattal Вам необходимо включить ITelephony.aidl в свой проект, чтобы обеспечить интерфейс для класса. - person Paul Lammertsma; 14.09.2015

РЕДАКТИРОВАТЬ: для Android P или новее см. https://stackoverflow.com/a/51121175/450148

Попробуй это:

(Я использовал Reflection, чтобы получить доступ к расширенным функциям телефонии и что-то изменить)

// required permission <uses-permission android:name="android.permission.CALL_PHONE"/>

try {
    //String serviceManagerName = "android.os.IServiceManager";
    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);
}
person Felipe    schedule 05.12.2011
comment
спасибо, все работает отлично, чтобы положить трубку, бросить приложение - person shereifhawary; 12.04.2012
comment
работает отлично. [необходимое разрешение ‹использует-разрешение android: name = android.permission.CALL_PHONE /›] - person VISHAL VIRADIA; 23.10.2012
comment
действительно хорошо работает. знаете ли вы о других полезных действиях вызова, таких как включение динамика, ответ, ...? - person android developer; 02.05.2013
comment
Извините, @androiddeveloper, я не знаю. Собственно, я делаю этот код на основе множества других советов, найденных на StackOverflow. После множества попыток я построил его. - person Felipe; 26.05.2013
comment
@FelipeMicaroniLalli, спасибо. Интересно, почему иногда так сложно понять, как что-то делать на Android. - person android developer; 26.05.2013
comment
@Felipe Это больше не работает на Android P. Вот обновленный код: stackoverflow.com/a/51121175/878126 - person android developer; 01.07.2018

  1. Создайте BroadcastReceiver с приоритетом 0.
  2. В BC перехватить ACTION_NEW_OUTGOING_CALL намерение в его onReceive методе
  3. вызовите setResultData(null) тем же методом

Это предотвратит инициирование вызова (если ваш получатель будет последним, чтобы обработать намерение, я думаю)

person Community    schedule 12.03.2009
comment
Есть мысли о том, почему это работает на Android 2.3.3 и более ранних версиях, но не на Android 4.0 и более поздних версиях? Я не могу ответить на ACTION_NEW_OUTGOING_CALL с помощью своего BroadcastReceiver. Я не заинтересован в отмене звонка, я просто не могу работать, даже зная о звонке. Любые мысли будут оценены, спасибо! - person DonnaLea; 10.09.2012

Вот самый обновленный код, который будет работать и для Android P, потому что для него есть официальный API ( здесь):

в манифесте добавьте это:

<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/>

В коде используйте это:

Ява:

@SuppressLint("PrivateApi")
public static boolean endCall(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        final TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
        if (telecomManager != null && ContextCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_GRANTED) {
            telecomManager.endCall();
            return true;
        }
        return false;
    }
    //use unofficial API for older Android versions, as written here: https://stackoverflow.com/a/8380418/878126
    try {
        final Class<?> telephonyClass = Class.forName("com.android.internal.telephony.ITelephony");
        final Class<?> telephonyStubClass = telephonyClass.getClasses()[0];
        final Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
        final Class<?> serviceManagerNativeClass = Class.forName("android.os.ServiceManagerNative");
        final Method getService = serviceManagerClass.getMethod("getService", String.class);
        final Method tempInterfaceMethod = serviceManagerNativeClass.getMethod("asInterface", IBinder.class);
        final Binder tmpBinder = new Binder();
        tmpBinder.attachInterface(null, "fake");
        final Object serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder);
        final IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone");
        final Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class);
        final Object telephonyObject = serviceMethod.invoke(null, retbinder);
        final Method telephonyEndCall = telephonyClass.getMethod("endCall");
        telephonyEndCall.invoke(telephonyObject);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        LogManager.e(e);
    }
    return false;
}

или в Котлине:

@SuppressLint("PrivateApi")
fun endCall(context: Context): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_GRANTED) {
            telecomManager.endCall()
            return true
        }
        return false
    }
    //use unofficial API for older Android versions, as written here: https://stackoverflow.com/a/8380418/878126
    try {
        val telephonyClass = Class.forName("com.android.internal.telephony.ITelephony")
        val telephonyStubClass = telephonyClass.classes[0]
        val serviceManagerClass = Class.forName("android.os.ServiceManager")
        val serviceManagerNativeClass = Class.forName("android.os.ServiceManagerNative")
        val getService = serviceManagerClass.getMethod("getService", String::class.java)
        val tempInterfaceMethod = serviceManagerNativeClass.getMethod("asInterface", IBinder::class.java)
        val tmpBinder = Binder()
        tmpBinder.attachInterface(null, "fake")
        val serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder)
        val retbinder = getService.invoke(serviceManagerObject, "phone") as IBinder
        val serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder::class.java)
        val telephonyObject = serviceMethod.invoke(null, retbinder)
        val telephonyEndCall = telephonyClass.getMethod("endCall")
        telephonyEndCall.invoke(telephonyObject)
        return true
    } catch (e: Exception) {
        e.printStackTrace()
        return false
    }
}
person android developer    schedule 01.07.2018
comment
какая-либо причина, по которой telecomManager.endCall () не может быть разрешен? - person FilipeOS; 10.07.2018
comment
@FilipeOS Это только из Android P. Вам нужно изменить compileSdkVersion в файле gradle как минимум на 28, чтобы использовать его в коде. - person android developer; 10.07.2018
comment
да ... нуб провалился, спасибо! Я использую почти этот код в своем приложении, но на многих телефонах не работает, особенно звук и отклонение, какие-либо рекомендации или ссылки на хороший пример, пожалуйста? - person FilipeOS; 11.07.2018
comment
Что ж, я не знаю других идей, кроме, возможно, наличия службы специальных возможностей для зависания телефона, но для этого нужно знать, что именно нужно щелкнуть или выполнить операцию. Я также не знаю, как выполнять жесты на сервисе доступности, если это вообще возможно. Мне также интересно, можно ли отклонять вызовы через уведомление в приложении «Телефон», возможно, с помощью специального разрешения на обработку уведомлений? Если вам удастся использовать эту идею, дайте мне знать. - person android developer; 11.07.2018
comment
Большое спасибо. Работает идеально (у) - person Muhammad Babar; 05.12.2019
comment
Обратите внимание, что developer.android.com/reference/android/telecom/ устарел на уровне API 29 :-( - person Felipe; 31.12.2019

Вы можете попробовать включить, а затем отключить режим полета:

android.provider.Settings.System.putInt(getContentResolver(),
        android.provider.Settings.System.AIRPLANE_MODE_ON, 1);

Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", 1);
sendBroadcast(new Intent("android.intent.action.AIRPLANE_MODE"));
sendBroadcast(intent);
android.provider.Settings.System.putInt(getContentResolver(),
        android.provider.Settings.System.AIRPLANE_MODE_ON, 0);

intent.putExtra("state", 0);
sendBroadcast(new Intent("android.intent.action.AIRPLANE_MODE"));
sendBroadcast(intent);
person Gung Shi Jie    schedule 18.06.2010
comment
в настоящее время нет возможности положить конец исходящему вызову, само решение, предложенное Гунг Ши Цзе, является хорошей идеей, но не работает, изменения состояния РЕЖИМ САМОЛЕТА будут игнорироваться во время исходящего вызова, работает только на emulaterd терминалы во время разработки, я пробовал и потерпел неудачу в телефонах HTC Desire и Acer Liquid. - person ; 30.11.2010

Для Иланы:

public class ilanasReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
            if (getResultData()!=null) {
                String number = "123456";
                setResultData(number);
            }
        }
    }
}

Дополнительно в Manifest поместите в раздел пакета:

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

Вот и все.

person Tony    schedule 31.08.2010

Учитывая возможность чудесного вреда, я был бы удивлен, если бы это было разрешено.

В этой ветке прямо говорится, что API не может завершить вызов. Другие пытались.

person Community    schedule 01.03.2009
comment
Тогда как они это сделали в tCallBlocking Lite? - person an0; 03.08.2009
comment
Конечно, это возможно. См. Ответ выше. - person Felipe; 05.12.2011

Согласно документации по ACTION_NEW_OUTGOING_CALL

<Цитата> Намерение будет иметь следующее дополнительное значение:

EXTRA_PHONE_NUMBER - номер телефона, который изначально предполагалось набрать.

После завершения трансляции resultData используется как фактический номер для вызова. Если значение равно null, вызов не будет выполняться.

person Denis Souza    schedule 31.07.2010
comment
Это объяснение ответа Эша, в котором используется setResultData(null). - person Paul Lammertsma; 26.01.2011