【问题标题】:How to end an incoming call programmatically on Android 8.0 Oreo如何在 Android 8.0 Oreo 上以编程方式结束来电
【发布时间】:2018-05-23 17:23:25
【问题描述】:

在 Android 7.1 之前,可以通过使用 ITelephony.endCall() 方法并为您的应用授予权限 android.permission.CALL_PHONE@ 来结束来电987654324@

Android 8.0 Oreo (API 26) 上执行相同操作时,我收到此错误

12-09 18:11:25.195 16833-16833/li.doerf.leavemealone E/TelephonyServiceCallHangup:缺少权限 MODIFY_PHONE_STATE, 无法挂断电话

由于 MODIFY_PHONE_STATE 是受保护的权限,我的应用无法获得它。有没有办法以编程方式结束Android 8.0+ 的来电?

【问题讨论】:

  • 看到这个答案,希望这会有所帮助:End Incoming Call Android
  • 我尝试了那个解决方案。不工作。但我的问题不是与 8.0 Oreo 版本断开连接
  • 不幸的是,这不再适用于 Android 8.0+ (Oreo)。有谁知道在 Oreo+ 中可以使用的替代方法?
  • 有人找到解决方案了吗?

标签: android android-permissions telephony android-8.0-oreo


【解决方案1】:

请查看以下对您有帮助的2个链接:

Permission is only granted to system app, in Manifest

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

以上链接用于将您的 MODIFY_PHONE_STATE 权限列入白名单。 这是出于安全目的,所以如果您想访问这种权限 届时在您致电您的运营部门后将您的权限列入白名单。

我认为根据 android oreo 最新更新,您可以使用内置方法实现在您的活动中接听电话,但这些不提供开发人员处理通话结束。 还获取(读取)接听电话的电话号码,但您需要定义它的权限。 我觉得我上面的描述已经足够理解了

希望您理解并结束这个问题。

【讨论】:

  • 此解决方案仅适用于已root或运行aosp的手机,这不是常规市场应用程序的选项。
  • 那么我们不能对它做任何事情@doerfli。
  • @jyubinpatel 请检查此应用程序play.google.com/store/apps/…。它在 Android Oreo 中工作。但它的应用程序必须设置为默认电话应用程序。
【解决方案2】:

尝试像答案here 中那样使用反射。

它适用于 READ_PHONE_STATE 和 CALL_PHONE 权限。根据android docs,这些权限对非系统应用程序可用

【讨论】:

  • 这个答案不正确。从 Android Oreo 开始,链接答案中描述的方法需要 MODIFY_PHONE_STATE 权限,第三方开发人员无法使用该权限。
【解决方案3】:

我找到了一个解决方案。它需要注册为 NotificationListenerService,然后解析操作并发送正确的 PendingIntent:

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

【讨论】:

  • 这不是一个可行的解决方案。它仅在挂断、拒绝或接听动作发生时做出反应。我正在寻找的解决方案必须能够执行这些操作。
  • 它实际上执行了这些操作。相信我,我已经在我的呼叫阻止应用程序中使用了它。调用 endCall() 会拒绝来电或挂断正在进行的通话。
  • 这似乎不起作用。如果有办法让这个解决方案发挥作用,那么答案需要比目前提供的更多信息。
  • 我不知道你的代码有什么问题。您是否能够对其进行调试并验证通知服务是否已连接并且是否正在调用 onNotificationPosted?我会尽快提供一个示例应用程序。
【解决方案4】:

将应用目标和编译级别更改为 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();
                }
            }
        }
    }

【讨论】:

    【解决方案5】:

    添加这些权限:

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

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

    适用于安卓奥利奥及以下:

    在您的项目中创建包 com.android.internal.telephony,并将其放入名为“ITelephony.aidl”的文件中:

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

    以上安卓奥利奥:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-24
      • 2018-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      相关资源
      最近更新 更多