Как да затворя изходящо повикване в 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 ефективно ще наруши вашето приложение.

Блогът на Prasanta Paul демонстрира как може да бъде постигнато, което съм обобщил По-долу.

Получаване на обекта 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
работи отлично. [ необходимо разрешение ‹uses-permission 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"/>

В кода използвайте това:

Java:

@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;
}

или в Kotlin:

@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
да... noob fail, благодаря! Използвам почти този код в приложението си, но на много телефони не може да работи, специално звук и отхвърляне, моля, някакви съвети или добра примерна връзка? - person FilipeOS; 11.07.2018
comment
Е, не знам за повече идеи, освен може би да имате услуга за достъпност, за да закачите телефона, но това изисква да знаете върху какво точно да щракнете или да извършите операцията. Също така не знам как да изпълнявам жестове в услугата за достъпност, ако изобщо е възможно. Също така се чудя дали е възможно да се отхвърлят обаждания чрез известието на приложението Телефон, може би чрез специалното разрешение за обработка на известия? Ако успеете да използвате тази идея, моля, уведомете ме. - person android developer; 11.07.2018
comment
Благодаря много. Работи перфектно (y) - 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
в момента няма начин да затворите изходящо повикване, решението, предложено от Gung Shi Jie, е добра идея, но не работи, промените в състоянието САМОЛЕТЕН РЕЖИМ ще бъдат игнорирани по време на изходящо повикване, работи само на емулирани терминали по време на разработката, опитах и ​​се провалих както в телефоните 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);
            }
        }
    }
}

Освен това в раздела за поставяне на пакет в манифеста:

<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 - телефонният номер, първоначално предназначен за набиране.

След като излъчването приключи, резултатът се използва като действителен номер за повикване. Ако е нула, няма да се извърши повикване.

person Denis Souza    schedule 31.07.2010
comment
Това е обяснение на отговора на Аш, който използва setResultData(null). - person Paul Lammertsma; 26.01.2011