【问题标题】:Detect when outgoing call is been answered on a rooted device检测何时在有根设备上接听了拨出电话
【发布时间】:2018-03-22 12:00:18
【问题描述】:

我不介意生根设备,因为该应用程序只会被私人使用,我正在做一个需要监控设备调用状态的项目,已阅读有关它的文档

https://developer.android.com/reference/android/telecom/Call.html

我一直在使用它,但我在知道何时选择呼叫时遇到问题,检查了文档和 stackoverflow,意识到这是 google 本身的一个已知问题。

Detecting outgoing call answered on Android

In rooted device detect if an outgoing call has been answered

以及我尝试过的许多其他人。 我知道没有记录的方法可以实现这一点,我相信这将是可能的,因为 android 本身会计算通话所花费的时间,还有一些应用程序也像 TRUE CALLER 和其他一些私人应用程序监控所花费的时间,这是基于接听电话和挂断电话时。 在决定发布之前,我自己尝试了很多, 关于如何在 ROOTED 设备上实现这一点的任何建议。

【问题讨论】:

标签: java android android-studio reflection phone-call


【解决方案1】:

这是一个监听语音呼叫的电话广播接收器示例。

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager;
import android.util.Log;

import com.nitesh.brill.saleslines.Common_Files.SaveData;
import com.nitesh.brill.saleslines.R;

public class MyPhoneReceiver extends BroadcastReceiver {

    private String phoneNumber;
    Context context;

    @Override
    public void onReceive(final Context context, Intent intent) {
        this.context = context;

        phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        String extraState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);


            try {

                if (extraState != null) {
                    if (extraState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {

                        Log.e("State","Offhook");

                    } else if (extraState
                            .equals(TelephonyManager.EXTRA_STATE_IDLE)) {

                        Log.e("State","Idle");


                    } else if (extraState
                            .equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                        if (phoneNumber == null)
                            phoneNumber = intent
                                    .getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

                        Log.e("State","Ringing");

                    }
                } else if (phoneNumber != null) {
                    Log.e("Outgoing call",""+phoneNumber);



                }
            } catch (Exception e) {
                Log.e(Constants.TAG, "Exception");
                e.printStackTrace();
            }
    }


}

将此代码添加到您的清单文件中

<receiver android:name=".MyPhoneReceiver">
            <intent-filter>

                <!-- Intent filters for broadcast receiver -->
                <action android:name="android.intent.action.PHONE_STATE" />
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />


            </intent-filter>
        </receiver>

【讨论】:

  • 上面的代码没有指定何时接听电话,扩展 PhoneStateListener 将给我所有这些,而无需扎根我的手机,我需要知道何时接听(接听)电话或不接听电话正在响铃。@Paras Watts
  • 摘机时调用额外的状态摘机
  • 我仍然不明白为什么人们将其标记为正确答案,通过在摘机状态添加 toast 来实现这一点,但没有任何反应。如果答案是正确的,您可以在接听电话进行澄清时发布敬酒消息的屏幕截图。 @帕拉斯瓦
  • 除了我的评论之外,在呼叫启动时立即触发摘机,如果这在您身边工作正常,您可以在此处发布您的设备详细信息,或者它是 root 或非 root 设备@Paras Watts
  • 我已经在 root 和非 root 的设备上进行了测试,我使用此代码在我的应用程序中进行通话录音。
【解决方案2】:

在调用过程中查看系统日志并查看源代码thisthis后,我发现PRECISE CALL STATE是可以用来在调用过程中监听精确变化的东西。

但正如您所见,大部分内容都使用 @hide 注释隐藏在文档中。

当应用于包、类、方法或字段时,@hide 会从文档中删除该节点及其所有子节点。

虽然方法和类是隐藏的,但可以使用Java Reflection API 访问它们,所以我想尝试一下。但是开发者社区是如此之大,以至于您想到的大部分内容都可以在 Google 上找到。

所以在谷歌搜索之后,我找到了this blog,它解释了如何使用 Java Reflection API 来监听精确的调用状态。 所以我把这段代码的原始形式取为source

将此添加到AndroidManifest.xml 文件中以声明广播接收器。

<receiver
    android:name=".OutCallLogger"
    android:enabled="true"
    android:exported="true" >
        <intent-filter>
            <action android:name="android.intent.action.PRECISE_CALL_STATE" />
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
</receiver>

所需权限:

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

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

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

还要将此行添加到清单以使用功能android.hardware.telephony

 <uses-feature android:name="android.hardware.telephony"></uses-feature>

这是您的广播接收器类,将用于获取拨出呼叫的精确呼叫状态。

public class OutCallLogger extends BroadcastReceiver {

public OutCallLogger() {

}

TelephonyManager Tm;
ITelephony telephonyService;
Class c = null;
Method methodGetInstance = null;
Method methodGetActiveFgCallState=null;
String TAG="Tag";
Object objectCallManager=null;
Context context1;
Class<?> classCallManager;

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;
private Timer timer= null;

@Override
public void onReceive(Context context, Intent intent) {
    // TODO: This method is called when the BroadcastReceiver is receiving
    // an Intent broadcast.



    this.context1= context;
    Tm=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);

    final ClassLoader classLoader = this.getClass().getClassLoader();
    try {
        classCallManager = classLoader.loadClass("com.android.internal.telephony.CallManager");
        Log.e(TAG, "CallManager: Class loaded " + classCallManager.toString());
        methodGetInstance = classCallManager.getDeclaredMethod("getInstance");
        methodGetInstance.setAccessible(true);
        Log.e(TAG, "CallManager: Method loaded " + methodGetInstance.getName());
        objectCallManager = methodGetInstance.invoke(null);
        Log.e(TAG, "CallManager: Object loaded " + objectCallManager.getClass().getName());
        Method[] aClassMethods = classCallManager.getDeclaredMethods();
        for(Method m : aClassMethods)
        {
            Log.e("MEthods", m.getName());
        }
        methodGetActiveFgCallState = classCallManager.getDeclaredMethod("getActiveFgCallState");
        Log.e(TAG, "CallManager: Method loaded " + methodGetActiveFgCallState.getName());

        Log.e(TAG, "CallManager: What is the Call state = " + methodGetActiveFgCallState.invoke(objectCallManager));
    }
    catch (ClassNotFoundException e) {
        Log.e(TAG, e.getClass().getName() + e.toString());
    }
    catch (NoSuchMethodException e) {
        Log.e(TAG, e.getClass().getName() + e.toString());
    }
    catch (InvocationTargetException e) {
        Log.e(TAG, e.getClass().getName() + e.toString());
    }
    catch (IllegalAccessException e) {
        Log.e(TAG, e.getClass().getName() + e.toString());
    }
    Tm.listen(new PhoneStateListener(){
        public void  onCallStateChanged(int state,String number) {
            super.onCallStateChanged(state, number);

            try {
                if (methodGetActiveFgCallState.invoke(objectCallManager).toString().toLowerCase() .equals("idle"))
                {
                    //Toast.makeText(context1, "I am in idle state", Toast.LENGTH_LONG).show();            }
                    if (methodGetActiveFgCallState.invoke(objectCallManager).toString().toLowerCase() .equals("active"))
                    {
                        //Toast.makeText(context1, "I am in active state", Toast.LENGTH_LONG).show();            }

                        Toast.makeText(context1, " "+methodGetActiveFgCallState.invoke(objectCallManager).toString(), Toast.LENGTH_LONG).show();


                    } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block            e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block            e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block            e.printStackTrace();
                }

                }

            }, PhoneStateListener.LISTEN_CALL_STATE);

        }

会出现一个 Toast,告诉您通话状态。

既然你已经指出你不介意root你的设备,你必须将生成的apk安装为系统应用程序。只需将生成的 apk 复制到 /system/app 目录并重启设备即可。

免责声明:我没有测试上面的代码,因为我目前没有根设备。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多