如何在Android 8.0 Oreo上通过编程方式结束来电

15
在Android 7.1及以前的版本中,可以使用ITelephony.endCall()方法来结束一个来电,并赋予你的应用程序android.permission.CALL_PHONEandroid.permission.READ_PHONE_STATE权限。
当在Android 8.0 Oreo (API 26)上执行相同操作时,会出现以下错误:
  

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

由于MODIFY_PHONE_STATE是受保护的权限,因此我的应用程序无法获取该权限。在Android 8.0+上是否有一种编程方式来结束一个来电呢?

请查看此答案,希望能对您有所帮助:结束 Android 来电 - Ashish Gupta
我尝试了那个解决方案,但它没有起作用。但我的问题不是与8.0奥利奥版本断开连接。 - Santosh
不幸的是,这在Android 8.0+(Oreo)中不再起作用。有人知道在Oreo+中可行的替代方案吗? - doerfli
有人找到解决方案了吗? - Krish
5个回答

3

将应用程序的目标和编译级别更改为28。

还需要以下权限。

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

在 MyPhoneStateListener 类的 onCallStateChanged 方法中添加以下代码。
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();
                }
            }
        }
    }

1
请查看下面两个链接,它们对你有帮助:

只有在清单文件中授予系统应用程序权限

https://source.android.com/devices/tech/config/perms-whitelist

上面的链接用于将您的 MODIFY_PHONE_STATE 权限加入白名单。 这是出于安全目的,因此如果您想要访问此类权限, 在那时需要将您的权限加入白名单,之后再调用操作。
我认为根据Android Oreo的最新更新,您可以使用内置方法实现在您的活动中接收电话,但这些方法不提供开发人员处理通话结束的功能。 同时,您也可以获取(读取)接收到的电话号码,但您需要定义相应的权限。 我认为我以上的描述已足够理解。
希望您能理解并关闭此问题。

这个解决方案仅适用于已经root或运行aosp的手机,而这对于普通市场应用来说并不是一个选择。 - doerfli
然后我们对此无能为力 @doerfli。 - Jyubin Patel
@jyubinpatel 请检查这个应用程序 https://play.google.com/store/apps/details?id=com.vladlee.easyblacklist&hl=en_IN。它可以在Android Oreo上运行。但是它的应用程序必须设置为默认电话应用程序。 - Krish

1
我找到了一个解决方案。它需要注册为NotificationListenerService,然后解析操作并发送适当的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();
        }
    }
}

然后在 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>

还有一步:用户必须手动启用您的应用作为通知监听器。您可以提供带有说明的对话框,然后使用此代码将用户发送到设置页面:

Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
startActivity(intent);

这不是一个可行的解决方案。它只有在挂断、拒绝或接听操作发生时才会做出反应。我正在寻找的解决方案必须能够执行这些操作。 - doerfli
它确实执行这些操作。相信我,我在我的呼叫拦截应用程序中使用过它。调用endCall()会拒绝来电或挂断正在进行的通话。 - jspenguin
这似乎不起作用。如果有办法让这个解决方案起作用,答案需要比当前给出的信息更多。 - Alan Kinnaman
我不知道你的代码出了什么问题。你能否调试它并验证通知服务已连接并且 onNotificationPosted 被调用了吗?我很快会提供一个示例应用程序。 - jspenguin
他只是忘了什么:如果你想拒绝或接听电话,你应该调用endCall()或answerCall()。好主意。恭喜,工作正常。 - ibrahim dao

0

尝试使用反射,就像答案这里提供的那样。

它适用于READ_PHONE_STATE和CALL_PHONE权限。根据Android文档,这些权限可用于非系统应用程序。


这个答案是不正确的。从Android Oreo开始,链接答案中描述的方法需要MODIFY_PHONE_STATE权限,而第三方开发者无法获得该权限。 - Alan Kinnaman

0

添加这些权限:

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

还要请求 ANSWER_PHONE_CALLS 和 READ_CALL_LOG 权限的运行时权限(因为它们是危险权限)。

对于 Android Oreo 及以下版本:

在您的项目中创建 com.android.internal.telephony 包,并将以下内容放入名为 "ITelephony.aidl" 的文件中:

package com.android.internal.telephony; 
interface ITelephony {      
    boolean endCall();     
}

针对Android Oreo及以上版本:

TelecomManager类提供了直接结束通话的方法(在代码中提供)。

在执行上述步骤后,添加以下提供的方法:(在此之前,请确保已初始化TelecomManager和TelephonyManager)。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
}

private boolean declinePhone() {
    if (telecomManager.isInCall()) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            if (telecomManager != null) {
                boolean success = telecomManager.endCall();
                return success;
            }
        } else {
            try {
                Class classTelephony = Class.forName(telephonyManager.getClass().getName());
                Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
                methodGetITelephony.setAccessible(true);
                ITelephony telephonyService = (ITelephony) methodGetITelephony.invoke(telephonyManager);
                if (telephonyService != null) {
                    return telephonyService.endCall();
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.d("LOG", "Cant disconnect call");
                return false;
            }
        }
    }
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接