How to hang up outgoing call in Android?

2019-01-01 05:24发布

问题:

I am developing an application where one of the things we need is to control the outgoing call, at least to be able to stop it from our application.

I\'ve tried using Intent.ACTION_CALL from an existing activity:

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

But stopping the call seems to be disallowed through the API.

Can you suggest some workaround?

For example: enabling airplane mode during the call? Just an example; this hack didn\'t work for me.

回答1:

Capturing the outgoing call in a BroadcastReceiver has been mentioned and is definitely the best way to do it if you want to end the call before dialing.

Once dialing or in-call, however, that technique no longer works. The only way to hang up that I\'ve encountered so far, is to do so through Java Reflection. As it is not part of the public API, you should be careful to use it, and not rely upon it. Any change to the internal composition of Android will effectively break your application.

Prasanta Paul\'s blog demonstrates how it can be accomplished, which I have summarized below.

Obtaining the ITelephony object:

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

Ending the call:

telephonyService.endCall();


回答2:

Try this:

(I used Reflection to access advanced telephony features and modify somethings)

// 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);
}


回答3:

  1. Create a BroadcastReceiver with a priority of 0.
  2. In the BC intercept the ACTION_NEW_OUTGOING_CALL intent in its onReceive method
  3. call setResultData(null) in the same method

This will prevent the call from initiating (as long as your receiver is the last to process the intent I think)



回答4:

You can try enabling then disabling airplane mode:

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);


回答5:

For Ilana:

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

In addition in Manifest put in package section:

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

That is all.



回答6:

Considering the potential for wonderful mischief I would be surprised if this is allowed.

This thread says flatly that the API cannot end a call. Others have tried.



回答7:

Here\'s the most updated code, which will work for Android P too, because it has an official API for it (here) :

in manifest, add this:

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

In code, use this:

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

or in 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
    }
}


回答8:

According to the documentation on ACTION_NEW_OUTGOING_CALL

The Intent will have the following extra value:

EXTRA_PHONE_NUMBER - the phone number originally intended to be dialed.

Once the broadcast is finished, the resultData is used as the actual number to call. If null, no call will be placed.