阻止来电

时间:2017-12-21 02:06:11

标签: android

我试图阻止来自特定号码的来电,我设法检测到来电及其号码。 但是,我无法阻止通话。我查看了我在本网站和其他网站上找到的所有代码,我开始认为,由于新版本的android或每个设备的核心差异(我拥有LG4),代码可能无法正常工作)。

还有一个人认为,任何人都可以向我解释在这个软件包中创建ITelephony的重要性:

package com.android.internal.telephony;

这是我的代码:

package com.darkmoonapps.telephony;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;

import com.android.internal.telephony.ITelephony;

import java.lang.reflect.Method;

/**
 * Created by Shai on 19/12/2017.
 */

public class incomingCalls extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        ITelephony telephonyService;
        try {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)){

                    TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
                    try {
                        Method m = tm.getClass().getDeclaredMethod("getITelephony");

                        m.setAccessible(true);
                        telephonyService = (ITelephony) m.invoke(tm);

                        if ((number != null)) {
                            telephonyService.endCall();
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                Toast.makeText(context, "Ring " + number, Toast.LENGTH_SHORT).show();

            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK)){
                Toast.makeText(context, "Answered " + number, Toast.LENGTH_SHORT).show();
            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)){
                Toast.makeText(context, "Idle "+ number, Toast.LENGTH_SHORT).show();
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

这是我的电话:

package com.android.internal.telephony;

/**
 * Created by Shai on 19/12/2017.
 */

public interface ITelephony {
    boolean endCall();
    void answerRingingCall();
    void silenceRinger();
}

谢谢!

1 个答案:

答案 0 :(得分:1)

我遇到了和你一样的问题。我找到了解决方案并在my blog上逐步记录了它。

这是我所缺少的最重要的部分:在运行时请求权限。这是妨碍我成功实现这一目标的一件事!

即,在Android 6.0及之后,即使您在AndroidManifest.xml文件中设置了权限,如果他们属于危险权限类别,您仍然必须明确询问用户。

要在此处申请此类权限,您可以使用的代码(我在MainActivity.java方法的onCreate中使用了该代码):

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED || checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED) {
        String[] permissions = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE};
        requestPermissions(permissions, PERMISSION_REQUEST_READ_PHONE_STATE);
    }
}

这里有完整内容的帖子:

TL; DR

在这篇文章中,我将逐步向您展示如何制作可以阻止某些号码呼叫您的原生Android应用。

源代码为on Github

我希望我将在此向您展示的分步指南将帮助您,使您免于进行其他研究。

当然,由于我在日常工作中不是本地Android开发人员,所以我也这样做,因为它可以作为我提醒我的好时机。我需要再次处理类似的情况。向你们其他人大喊#jackOfAllTrades out

另外,鉴于上述声明;我将不胜感激有关此代码的任何反馈。

TL;!DR

我花了很多时间浏览StackOverflow和博客文章以寻找此解决方案。在所有这些中,这些都很有帮助:

但遗憾的是,这些都不是直截了当的初学者教程。所以,经过大量的额外研究后,我才开始工作,这是我解释方法的最佳尝试。

  

作为旁注:在测试时,how to simulate an incoming call or SMS to an emulator in Android Studio的发现也非常有用。

开始新项目

在Android Studio中,转到File->New->New Project,为其指定名称和位置,然后点击Next

保留最低API级别的默认选项:

选择Empty Activity模板:

保留活动名称:

的AndroidManifest.xml

uses-permission文件中设置权限(两个receiver代码)和AndroidManifest.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nikola.callblockingtestdemo">

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver  android:name=".IncomingCallReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

使用READ_PHONE_STATE权限,我们可以获得此权限(如official docs中所定义):

  

允许对电话状态进行只读访问,包括设备的电话号码,当前的蜂窝网络信息,任何正在进行的呼叫的状态以及设备上注册的任何PhoneAccounts列表。

使用CALL_PHONE权限,我们可以获得此权限(如official docs中所定义):

  

允许应用程序在不通过拨号器用户界面的情况下发起电话呼叫,以便用户确认呼叫。

⚠️我发现虽然这里没有说明,但我需要此权限才能以编程方式结束通话。

receiver标记用于定义将处理android.intent.action.PHONE_STATE的广播操作的类。当顾名思义,电话呼叫状态发生变化(我们接听电话,拒绝接听电话,正在通话等)时,Android操作系统将广播此操作。

IncomingCallReceiver.java

创建一个新类(File->New->Java Class),将其命名为IncomingCallReceiver并粘贴此代码(注意:您的package名称将与我的名称不同! ):

package com.example.nikola.callblockingtestdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;
import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;

public class IncomingCallReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        ITelephony telephonyService;
        try {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)){
                TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
                try {
                    Method m = tm.getClass().getDeclaredMethod("getITelephony");

                    m.setAccessible(true);
                    telephonyService = (ITelephony) m.invoke(tm);

                    if ((number != null)) {
                        telephonyService.endCall();
                        Toast.makeText(context, "Ending the call from: " + number, Toast.LENGTH_SHORT).show();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

                Toast.makeText(context, "Ring " + number, Toast.LENGTH_SHORT).show();

            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK)){
                Toast.makeText(context, "Answered " + number, Toast.LENGTH_SHORT).show();
            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)){
                Toast.makeText(context, "Idle "+ number, Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在Android中,如果我们想要获得&#39;来自BroadcastReceiver的数据,我们需要继承BroadcastReceiver类,我们需要覆盖onReceive方法。在此方法中,我们使用TelephonyManager来获取通话状态,并且我们正在使用ITelephony界面结束通话。

老实说,这是一个有点“奇怪”的地方,至于获取此ITelephony界面,您需要创建ITelephony界面。

ITelephony.java

为此,请创建一个新类(File->New->Java Class),将其命名为ITelephony并粘贴此代码(注意:使用以下内容覆盖所有内容;是的,即使是奇怪的包名称的):

package com.android.internal.telephony;

public interface ITelephony {
    boolean endCall();
    void answerRingingCall();
    void silenceRinger();
}

Android Studio会抱怨package com.android.internal.telephony;(此软件包名称下的红色波浪点),但是必须如何设置才能使其工作。我没有找到确切的解释,为什么必须包含它,所以如果你知道,请在评论中分享。

在运行时请求权限

这是妨碍我成功实现这一目标的一件事!

即使在Android 6.0及更高版本之后,即使您在AndroidManifest.xml文件中设置了权限,如果属于危险类别,您仍需要明确询问用户权限。这是此类权限的列表:

  • ACCESS_COARSE_LOCATION
  • ACCESS_FINE_LOCATION
  • ADD_VOICEMAIL
  • BODY_SENSORS
  • CALL_PHONE
  • CAMERA
  • GET_ACCOUNTS
  • PROCESS_OUTGOING_CALLS
  • 功能(6)
  • READ_CALL_LOG
  • READ_CELL_BROADCASTS
  • READ_CONTACTS
  • READ_EXTERNAL_STORAGE
  • READ_PHONE_STATE
  • READ_SMS
  • RECEIVE_MMS
  • RECEIVE_SMS
  • RECEIVE_WAP_PUSH
  • RECORD_AUDIO
  • SEND_SMS
  • USE_SIP
  • WRITE_CALENDAR
  • WRITE_CALL_LOG
  • WRITE_CONTACTS
  • WRITE_EXTERNAL_STORAGE

要在此处请求此类权限,您可以使用的代码(我在MainActivity.java方法的onCreate中使用了该代码):

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED || checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED) {
        String[] permissions = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE};
        requestPermissions(permissions, PERMISSION_REQUEST_READ_PHONE_STATE);
    }
}

PERMISSION_REQUEST_READ_PHONE_STATE变量用于确定onRequestPermissionsResult方法中要求的权限。当然,如果您不需要执行任何逻辑,具体取决于用户是否批准了该权限,您可以省略此方法:

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_READ_PHONE_STATE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission granted: " + PERMISSION_REQUEST_READ_PHONE_STATE, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Permission NOT granted: " + PERMISSION_REQUEST_READ_PHONE_STATE, Toast.LENGTH_SHORT).show();
            }

            return;
        }
    }
}

App in action

这就是应用在运行中的样子,在模拟器上测试以及在Android Studio中使用Android Device Monitor触发的调用:

结论

在这篇文章中,我向您展示了如何制作可以阻止某些号码呼叫您的原生Android应用。我指出了我所面对的拦截器,我还在寻找一个解决方案来隐藏一个原生的来电弹出窗口,这个弹出窗口有时会在呼叫被拒绝之前显示一小段时间。

所以,如果您有任何想法,我可以接受建议