如何访问com.android.internal.telephony.CallManager?

7

我正在尝试从com.android.internal.telephony包访问CallManager类对象。

这是我的代码:

ClassLoader classLoader = TestActivity.class.getClassLoader();
final ClassLoader classLoader = this.getClass().getClassLoader();
try {
    final Class<?> classCallManager =
        classLoader.loadClass("com.android.internal.telephony.CallManager");
    Log.i("TestActivity", classCallManager);
} catch (final ClassNotFoundException e) {
    Log.e("TestActivity", e);
}

很遗憾,这会抛出一个“ClassNotFoundException”。同样的方法可以让我访问“PhoneFactory”,但显然我无法访问“CallManager”。
如果我能够访问该类,那么我想按照以下方式继续使用该类:
Method method_getInstance;
method_getInstance = classCallManager.getDeclaredMethod("getInstance");
method_getInstance.setAccessible(true);
Object callManagerInstance = method_getInstance.invoke(null);

有人能帮我解决这个问题吗?

提前感谢,
Harsha C


我尝试使用 ClassLoader.getSystemClassLoader(),但这也不起作用。 - Paul Lammertsma
7个回答

8

我成功地加载了CallManager及其方法。然而,当我调用getState()和getActiveFgCallState()时,即使应用程序从TelephonyManager接收到不同的呼叫状态,如TelephonyManager.CALL_STATE_IDLE、TelephonyManager.CALL_STATE_OFFHOOK、TelephonyManager.CALL_STATE_RINGING,它总是返回IDLE。

我使用以下代码来加载该类及其方法:

final Class<?> classCallManager = classLoader.loadClass("com.android.internal.telephony.CallManager");
Log.i(TAG, "Class loaded " + classCallManager.toString());

Method methodGetInstance = classCallManager.getDeclaredMethod("getInstance");
Log.i(TAG, "Method loaded " + methodGetInstance.getName());

Object objectCallManager = methodGetInstance.invoke(null);
Log.i(TAG, "Object loaded " + objectCallManager.getClass().getName());


Method methodGetState = classCallManager.getDeclaredMethod("getState");
Log.i(TAG, "Method loaded " + methodGetState.getName());

Log.i(TAG, "Phone state = " + methodGetState.invoke(objectCallManager));

顺便说一下,我想要做的是检测手机何时开始响铃。我在源代码中看到 ALERTING 是我应该监听的内部事件。因此,我尝试使用 CallManager 来获取 Call.State,而不是 Phone.State。我还尝试使用 CallManager 类的 registerForPreciseCallStateChanges() 方法,但迄今为止都没有成功。


2
我尝试访问准确的电话状态已经好几天了,但是没有任何进展。我已经发现 CallManager 本身不提供任何呼叫信息,除非有注册的 Phone 实例,也就是说,它从外部接收有关呼叫状态变化的通知。您正在从应用程序进程访问 CallManager 类,因此您会得到一个空实例,并且所有字段都具有默认值,而包含处理通话的 Phone 实例的 CallManager 的实际实例位于另一个进程中,可能在某个系统服务中,例如 Telephony Service。 - Vladimir Dmi
@VladimirDmi,你找到可能是哪个服务了吗? - Edw590

2

我曾经遇到过完全相同的问题 - 在通话挂断期间获取断开连接原因。我的解决方案是在无线电日志中查找相关行。看起来有效 - 希望能帮助到你。

private void startRadioLogListener() {
    new Thread(new Runnable() {
        public void run() {
            Process process = null;
            try {
                // Clear the log first
                String myCommandClear = "logcat -b radio -c";  
                Runtime.getRuntime().exec(myCommandClear);

                String myCommand = "logcat -b radio";  
                process = Runtime.getRuntime().exec(myCommand);
                Log.e(LogHelper.CAL_MON, "RadioLogProc: " + process);

                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                while (true) {
                    final String line = bufferedReader.readLine();
                    if (line != null) {
                        if (line.contains("disconnectCauseFromCode") || line.contains("LAST_CALL_FAIL_CAUSE")) {
                            Log.d(LogHelper.CAL_MON, "RadioLog: " + line);
                            radioLogHandler.post(new Runnable() {
                                public void run() {
                                    consolePrint("Radio: " + line + "\n");
                                }
                            });
                        }
                    }
                }
            } catch (IOException e) {
                Log.e(LogHelper.CAL_MON, "Can't get radio log", e);
            } finally {
                if (process != null) {
                    process.destroy();
                }
            }
        }
    }).start();
}

我正在尝试在API 8(Android版本2.2)上运行,但似乎无法工作,有什么想法吗? - Cemre Mengü
2
我认为自从Android 4.1以后,这个功能已经不再起作用了,因为应用程序日志只能被它们自己的应用程序读取... - galex

1

我写了一个程序,可以识别电话呼叫并将最近的所有通话记录在列表中。也许你可以看一下这段代码,我认为它可能会有所帮助。 你所需要的就是这个:

String state = bundle.getString(TelephonyManager.EXTRA_STATE);
if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING))

但是也请检查这个例子:

package org.java.sm222bt;

import org.java.sm222bt.R;

import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends ListActivity {
private CountryDbAdapter db;
private Cursor entrycursor;
private ListAdapter adapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //setContentView(R.layout.main);
    registerForContextMenu(getListView());
    db = new CountryDbAdapter(this);
   updateEntry();

}
public void updateEntry(){
    db.open();

    entrycursor = db.fetchAllEntries();

    adapter = new SimpleCursorAdapter(this, R.layout.row, entrycursor, new String[] {"phonenumber"}, new int[] {R.id.number});
    setListAdapter(adapter);
    db.close();
}
@Override
protected void onResume() {

    super.onResume();
    updateEntry();
}

public static final int Call = 0;
public static final int SendSMS = 1;
public static final int Share = 2;
public static final int Delete = 3;
public static final int ClearList = 4;



public void onCreateContextMenu(ContextMenu menu, View v, 
ContextMenu.ContextMenuInfo menuInfo) { 
menu.setHeaderTitle("Select:"); 
menu.add(0, Call, 0, "Call");
menu.add(0, SendSMS, 0, "Send SMS");
menu.add(0, Share, 0, "Share");
menu.add(0, Delete, 0, "Delete");
menu.add(0, ClearList, 0, "Clear all contacts");


}

@Override
public boolean onContextItemSelected(MenuItem item) { 
switch (item.getItemId()) {
case Call:
//System.out.println(entrycursor.getString(1));
//EditText number=(EditText)findViewById(R.id.number);
String toDial="tel:"+entrycursor.getString(1);
//start activity for ACTION_DIAL or ACTION_CALL intent
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(toDial)));
    //update(entryCursor.getLong(0));
return true;
case SendSMS:


    //EditText number=(EditText)findViewById(R.id.number);
    //EditText msg=(EditText)findViewById(R.id.msg);
    String sendUri="smsto:"+entrycursor.getString(1);
    Intent sms=new Intent(Intent.ACTION_SENDTO,
                            Uri.parse(sendUri));
    //sms.putExtra("sms_body", msg.getText().toString());
    startActivity(sms);

return true;
case Share:

    Intent i=new Intent(Intent.ACTION_SEND);
    i.setType("text/plain");
    i.putExtra(Intent.EXTRA_SUBJECT, "Hello");
    i.putExtra(Intent.EXTRA_TEXT, "Sharing this number"+entrycursor.getString(1));
    startActivity(Intent.createChooser(i,
                                  entrycursor.getString(1)));
    return true;

case Delete:

    db.open();
    db.deleteEntry(entrycursor.getLong(0));
    db.close();
    updateEntry();

return true;

case ClearList:

    db.open();
    db.deleteEntry();
    db.close();
    updateEntry();

    return true;

default:
return super.onContextItemSelected(item);
}
}
}

这里是来电接收器:

package org.java.sm222bt;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;

public class IncomingCallReceiver extends BroadcastReceiver {
CountryDbAdapter db;
@Override
public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    Bundle bundle = intent.getExtras();
    if(null == bundle)
            return;
    String state = bundle.getString(TelephonyManager.EXTRA_STATE);
    if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING))
    {
        String phonenumber = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        System.out.println(phonenumber);

        db = new CountryDbAdapter(context);
        db.open();
        db.insertEntry(phonenumber);
        db.fetchAllEntries();
        db.close();

}
}} 

1

你尝试过在这个博客上找到的方法吗?

通过加载Phone.apk可能是绕过ClassNotFound异常错误的线索...


-1

我了解您想要检测通话何时断开。您可以使用PhoneStateListener.LISTEN_CALL_STATE。例如:

final TelephonyManager telephony = (TelephonyManager)
        getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
    public void onCallStateChanged(final int state,
            final String incomingNumber) {
        switch (state) {
        case TelephonyManager.CALL_STATE_IDLE:
            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:
            Log.d("TestActivity", "Call disconnected");
            break;
        case TelephonyManager.CALL_STATE_RINGING:
            break;
        default:
            break;
        }
    }
}, PhoneStateListener.LISTEN_CALL_STATE);

3
不是。我的要求是找出通话掉线的原因。是由于网络故障、呼叫被禁止等等。 - Harsha
好的。我实际上正在尝试完成非常类似的事情(发送DTMF音调),这也需要访问CallManager。让我们一起看看能否解决它。 - Paul Lammertsma
你尝试过访问CallManager了吗?我正在这里尝试做同样的事情.... :D 还请参见我的上面的答案... :D - t0mm13b
1
@tommieb75 不好意思,不行。我研究了一下 Phone.apk 的方式,看起来它有特殊的权限。我不记得确切的错误,但唯一的解决方法就是要对手机进行 root。真可惜! - Paul Lammertsma

-1
请使用这个:
    telephone = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

    private PhoneStateListener psl = new PhoneStateListener() {

    @Override
    public  void onCallStateChanged (int state, String incomingNumber)
    {
        state = telephone.getCallState();

        switch(state) {
        case android.telephony.TelephonyManager.CALL_STATE_IDLE:    
            if (callsucces ) act();

            break;
        case android.telephony.TelephonyManager.CALL_STATE_RINGING:
            callsucces = true; 
            break;
        case android.telephony.TelephonyManager.CALL_STATE_OFFHOOK:
            callsucces = true; 
            break;
        }
    }

};


   private void call(String pnum) {
    try {

        callIntent = new Intent(Intent.ACTION_CALL);

        callsucces = false;
        if (telephone.getNetworkType() != 0) {
        if (telephone.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
        callIntent.setData(Uri.parse("tel:"+pnum)); 
        startActivity(callIntent);
        telephone.listen(psl,PhoneStateListener.LISTEN_CALL_STATE);
        }
        } else act();
        //callIntent.ACTION_NEW_OUTGOING_CALL
    } catch (ActivityNotFoundException activityException) {
         Toast.makeText(getBaseContext(), "Call failed",Toast.LENGTH_SHORT).show();
         act();

    }
}

-2

你在 AndroidManifest.xml 中添加了 READ_PHONE_STATE 吗?

我觉得你漏掉了这个。


不行。。。:( 我想访问 CallManager 实例,以便可以解析该类并访问 GSMConnection 以获取呼叫断开状态。由于所有这些都是内部 API,因此我正在尝试使用 Java 反射。 - Harsha

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