Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to end an incoming call programmatically on Android 8.0 Oreo

Up to Android 7.1 it was possible to end an incoming call by using the ITelephony.endCall() method and giving your app the permissions android.permission.CALL_PHONE and android.permission.READ_PHONE_STATE.

When doing the same on Android 8.0 Oreo (API 26), i get this error

12-09 18:11:25.195 16833-16833/li.doerf.leavemealone E/TelephonyServiceCallHangup: Missing permission MODIFY_PHONE_STATE, cannot hangup call

Since MODIFY_PHONE_STATE is a protected permission, my app cannot get it. Is there a way to programmatically end an incoming call on Android 8.0+?

like image 804
doerfli Avatar asked Dec 09 '17 18:12

doerfli


2 Answers

Changed App Target and Compile level to 28.

And following permissions.

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

Add the following code on onCallStateChanged method of MyPhoneStateListener class.

public void endCall() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
            TelecomManager tm = (TelecomManager) mcontext.getSystemService(Context.TELECOM_SERVICE);
            if (tm != null) {
                boolean success = tm.endCall();
            }
            // success == true if call was terminated.
        } else {
            if (mcontext != null) {
                TelephonyManager telephony = (TelephonyManager) mcontext
                        .getSystemService(Context.TELEPHONY_SERVICE);
                try {
                    Class c = Class.forName(telephony.getClass().getName());
                    Method m = c.getDeclaredMethod("getITelephony");
                    m.setAccessible(true);
                    telephonyService = (ITelephony) m.invoke(telephony);
                    // telephonyService.silenceRinger();
                    telephonyService.endCall();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
like image 53
Gaurav Agrawal Avatar answered Sep 18 '22 17:09

Gaurav Agrawal


There is a solution that I found. It requires registering as a NotificationListenerService, then parsing the actions and and sending the proper PendingIntents:

public class NotificationListener extends NotificationListenerService {
    private static final String TAG = "NotificationListener";
    private PendingIntent answerIntent;
    private PendingIntent hangupIntent;

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        String packageName = sbn.getPackageName();
        if ("com.google.android.dialer".equals(packageName)) {
            Notification notification = sbn.getNotification();
            for (Notification.Action action : notification.actions) {
                String title = String.valueOf(action.title);
                if ("hang up".equalsIgnoreCase(title) || "decline".equalsIgnoreCase(title)) {
                    hangupIntent = action.actionIntent;
                } else if ("answer".equalsIgnoreCase(title)) {
                    answerIntent = action.actionIntent;
                }
            }
        }
    }

    @Override
    public void onListenerConnected() {
        Log.i(TAG, "Listener connected");
        for (StatusBarNotification sbn : getActiveNotifications()) {
            onNotificationPosted(sbn);
        }
    }

    public void answerCall() {
        try {
            answerIntent.send();
        } catch (PendingIntent.CanceledException e) {
            e.printStackTrace();
        }
    }

    public void endCall() {
        try {
            hangupIntent.send();
        } catch (PendingIntent.CanceledException e) {
            e.printStackTrace();
        }
    }
}

Then declare the following in AndroidManifest.xml:

    <service
        android:name=".NotificationListener"
        android:label="@string/app_name"
        android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
        <intent-filter>
            <action android:name="android.service.notification.NotificationListenerService" />
        </intent-filter>
    </service>

There is one more step: The user has to manually enable your app as a notification listener. You can present a dialog with instructions, then send the user to the settings page using this code:

Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
startActivity(intent);
like image 45
jspenguin Avatar answered Sep 19 '22 17:09

jspenguin