代码之家  ›  专栏  ›  技术社区  ›  Shashank Agrawal

检测拨出电话何时开始播放铃声

  •  10
  • Shashank Agrawal  · 技术社区  · 6 年前

    我正在尝试检测一个拨出的电话开始播放铃声时的状态。我试过各种方法来检测这种状态。以下是其中一些:

    一。使用 PhoneStateListener :

    ( Cannot detect when outgoing call is answered in Android )

    import android.telephony.PhoneStateListener;
    import android.telephony.TelephonyManager;
    import android.util.Log;
    
    public class CustomPhoneStateListener extends PhoneStateListener {
    
        public void onCallStateChanged(int state, String num) {
            Log.d(CallStatusPlugin.TAG, ">>>state changed" + state);
        }
    }
    

    但是州政府 TelephonyManager.CALL_STATE_IDLE , TelephonyManager.CALL_STATE_OFFHOOK 不会给我们这些状态。

    2。使用 READ_PRECISE_PHONE_STATE :

    将方法添加到上述电话状态侦听器

    public void onPreciseCallStateChanged() {
        Log.d(CallStatusPlugin.TAG, "onPreciseCallStateChanged");
    }
    

    但是根据我的研究,阅读精确的状态需要应用程序必须是一个系统应用程序。

    三。使用 NotificationListener :

    public class CustomNotificationListener extends NotificationListenerService {
    
        public static final String TAG = "CallStatusPlugin";
    
        public CustomNotificationListener() {
            Log.v(TAG, ">>> CustomNotificationListener");
        }
    
        public void onNotificationPosted(StatusBarNotification sbn) {
            Log.i(TAG, "New Notification");
    
            Bundle extras = sbn.getNotification().extras;
    
            if ("Ongoing call".equals(extras.getString(Notification.EXTRA_TEXT))) {
                Log.v(TAG, "outgoing call");
            } else if ("Dialing".equals(extras.getString(Notification.EXTRA_TEXT))) {
                Log.v(TAG, "dialling call");
            }
        }
    }
    

    但这没有帮助,因为操作系统不会在拨出电话的铃声开始播放时更改通知。

    四。使用 BroadcastReceiver :

    public class CallBroadcastReceiver extends BroadcastReceiver {
    
        public static final String TAG = "CallStatusPlugin";
    
        public void onReceive(Context context, Intent intent) {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
    
            Log.i(TAG, "CallBroadcastReceiver state: " + state);
    
            // TelephonyManager.EXTRA_FOREGROUND_CALL_STATE = "foreground_state"
            Log.d(TAG, "new state >>>>" + intent.getIntExtra("foreground_state", -2));
        }
    }
    

    但也没有帮助。

    5个。使用反射:

    我还尝试使用反射获取默认拨号程序的实例,但没有成功:

        //com.samsung.android.incallui
        Reflections reflections = new Reflections("com.samsung.android.contacts", new SubTypesScanner(false));
    
        final ClassLoader classLoader = this.getClass().getClassLoader();
        ClassLoader[] loaders = { classLoader };
    
        ConfigurationBuilder configurationBuilder = (ConfigurationBuilder) reflections.getConfiguration();
        configurationBuilder.setClassLoaders(loaders);
    
        Log.d(TAG, "cl" + classLoader.toString());
        Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(Object.class);
        Log.d(TAG, "allclasses" + allClasses.toString());
    

    我不能上任何课(也许我没有正确地使用反射)

    6。使用 InCallService :

    通过将默认拨号程序(我不想使用)替换为自定义拨号程序来获取 Call 国家。

    import android.os.Bundle;
    import android.telecom.Call;
    import android.telecom.InCallService;
    import android.util.Log;
    
    public class CustomInCallService extends InCallService {
    
        public static final String TAG = "CallStatusPlugin";
    
        @Override
        public void onCallAdded(Call call) {
            Log.d(TAG, "onCallAdded: " + call.getState());
    
            call.registerCallback(
                    new Call.Callback() {
                        @Override
                        public void onConnectionEvent (Call call, String event, Bundle extras) {
                            Log.d(TAG, "Call.Callback.onConnectionEvent: " + event + ", " + call.getState());
                        }
    
                        @Override
                        public void onStateChanged (Call call, int state) {
                            Log.d(TAG, "Call.Callback.onStateChanged: " + state + ", " + call.getState());
                        }
                    }
            );
        }
    }
    

    这个 onStateChanged 最后给了我一些东西:

    呼叫状态 ->9级( STATE_CONNECTING )->1个( STATE_DIALING )->4个( STATE_ACTIVE )(回答时)->7( STATE_DISCONNECTING )

    但呼叫状态也更改为 状态拨号 当拨出的电话有问题时,例如无法接通已拨号码或电话已关机。所以这意味着我们不能说 DIALING 状态是传出呼叫开始播放铃声的状态。

    7号。使用 CallManager 从反思:

    ( 2018年8月3日增加 )

    public class OutCallLogger extends BroadcastReceiver {
    
        public static final String TAG = "CallStatusPlugin";
    
        public OutCallLogger() {
            Log.e(TAG, "\n\n\nOutCallLogger Instance Created\n\n\n");
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            Log.i(TAG, "OutCallLogger state: " + state);
    
            String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Log.i(TAG, "Outgoing Number: " + number);
    
            // TelephonyManager.EXTRA_FOREGROUND_CALL_STATE = "foreground_state"
            Log.d(TAG, "new state >>>>" + intent.getIntExtra("foreground_state", -2));
            Log.d(TAG, "new state >>>>" + intent.getIntExtra("ringing_state", -2));
            Log.d(TAG, "new state >>>>" + intent.getIntExtra("background_state", -2));
            Log.d(TAG, "new state >>>>" + intent.getIntExtra("disconnect_cause", -2));
    
            final ClassLoader classLoader = this.getClass().getClassLoader();
    
            try {
                Class<?> callManagerClass = classLoader.loadClass("com.android.internal.telephony.CallManager");
                Log.e(TAG, "CallManager: Class loaded " + callManagerClass.toString());
    
                Method[] methods = callManagerClass.getDeclaredMethods();
                for (Method m : methods) {
                    Log.e(TAG, "Methods: " + m.getName());
                }
    
                Method getInstanceMethod = callManagerClass.getDeclaredMethod("getInstance");
                getInstanceMethod.setAccessible(true);
                Log.e(TAG, "CallManager: Method loaded " + getInstanceMethod.getName());
    
                Object callManagerObject = getInstanceMethod.invoke(null);
                Log.e(TAG, "CallManager: Object loaded " + callManagerObject.getClass().getName());
    
                Method getAllPhonesMethod = callManagerClass.getDeclaredMethod("getAllPhones");
                Log.e(TAG, "CallManager: Method loaded " + getAllPhonesMethod.getName());
    
                Method getForegroundCallsMethod = callManagerClass.getDeclaredMethod("getForegroundCalls");
                Log.e(TAG, "CallManager: Method loaded " + getForegroundCallsMethod.getName());
                List foregroundCalls = (List) getForegroundCallsMethod.invoke(callManagerObject);
                Log.e(TAG, "Foreground calls: " + foregroundCalls + ", " + foregroundCalls.size());
    
                Method getBackgroundCallsMethod = callManagerClass.getDeclaredMethod("getBackgroundCalls");
                Log.e(TAG, "CallManager: Method loaded " + getForegroundCallsMethod.getName());
                List backgroundCalls = (List) getBackgroundCallsMethod.invoke(callManagerObject);
                Log.e(TAG, "Background calls: " + backgroundCalls + ", " + backgroundCalls.size());
    
                Timer timer = new Timer();
    
                // keep printing all the for 20 seconds to check if we got one
                TimerTask doAsynchronousTask = new TimerTask() {
                    long t0 = System.currentTimeMillis();
    
                    @Override
                    public void run() {
                        // cancel the timer after 20 seconds
                        if (System.currentTimeMillis() - t0 > 20 * 1000) {
                            cancel();
                            return;
                        }
    
                        try {
                            List phonesObject = (List) getAllPhonesMethod.invoke(callManagerObject);
                            Log.e(TAG, "All phones " + phonesObject + ", " + phonesObject.size());
    
                            List foregroundCalls = (List) getForegroundCallsMethod.invoke(callManagerObject);
                            Log.e(TAG, "Foreground calls: " + foregroundCalls + ", " + foregroundCalls.size());
    
                            Object backgroundCalls = getBackgroundCallsMethod.invoke(callManagerObject);
                            Log.e(TAG, "Background calls: " + backgroundCalls);
    
                            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
                            Log.i(TAG, "New state: " + state);
                        } catch (Exception e) {
                            Log.e(TAG, ">>>1. " + e.getMessage());
                        }
                    }
                };
    
                timer.schedule(doAsynchronousTask, 0, 1000); //execute in every 1000 ms
            } catch (ClassNotFoundException e) {
                Log.e(TAG, ">>>2. " + e.getMessage());
            } catch (NoSuchMethodException e) {
                Log.e(TAG, ">>>3. " + e.getMessage());
            } catch (InvocationTargetException e) {
                Log.e(TAG, ">>>4. " + e.getMessage());
            } catch (IllegalAccessException e) {
                Log.e(TAG, ">>>5. " + e.getMessage());
            }
        }
    }
    

    但我得到了所有的背景/前景电话和emty结果:

    08-03 15:19:22.638  2586  4636 E CallStatusPlugin: All phones [], 0
    08-03 15:19:22.639  2586  4636 E CallStatusPlugin: Foreground calls: [], 0
    08-03 15:19:22.639  2586  4636 E CallStatusPlugin: Background calls: []
    

    不确定OS是否正在使用 呼叫管理器 或者不。

    经过2-3周的研究,我了解到:

    1. 隐藏的API为我们提供了与OS更深入的集成(但是 Google may remove access to hidden API in Android P )
    2. Ringing sound of an outgoing call == Ringback tone
    3. State dialing !== State of playing ringback tone
    4. 但是 我没有得出一个结论,我可以说,这是一种方式,我们可以得到的状态,一个传出的电话开始播放铃声

    我通过读取所有操作系统日志 adb logcat '*:V' 当我听到铃声的时候看到一个日志正在打印( adb logcat -b system '*:V' ):

    07-31 13:34:13.487  3738 29960 I Telephony: AsyncConnectTonePlayer : play
    07-31 13:34:13.784  3273  7999 D SSRM:p  : SIOP:: AP = 330, PST = 313 (W:26), BAT = 294, USB = 0, CHG = 0
    07-31 13:34:13.902  3738 29960 I Telephony: AsyncConnectTonePlayer : onCompletion
    07-31 13:34:14.304  3273 15438 D CustomFrequencyManagerService: releaseDVFSLockLocked : Getting Lock type frm List : DVFS_MIN_LIMIT  frequency : 1352000  uid : 1000  pid : 3273  tag : com.samsung.android.incallui@2
    07-31 13:34:14.639  3273  7999 D AudioService: getStreamVolume 0 index 10
    07-31 13:34:16.449  4175  4175 D io_stats: !@ 179,0 r 264349 9232882 w 465999 10111332 d 42634 3418440 f 235485 235449 iot 468730 459107 th 51200 0 0 pt 0 inp 0 0 104823.814
    

    然后我开始用谷歌搜索与 android io统计事件 &安培; gsm连接事件 但我想不出任何方法来获得拨出电话的特定状态,即电话开始播放铃声。

    我查看了各种Android代码,发现了一些提示:

    1. TelephonyConnection
    2. GsmConnection

    有什么提示或方向可以帮助我实现这个目标吗?像GSM连接, ConnectionService , ConnectivityManager , ConnectionService ConnectionRequest 有什么事吗?

    编辑1:

    我读了更多的日志 SipManager 我读到了 SIP Basic Call Flow Examples 我想,Android是用来打电话或接电话的。

    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:TRANSACTION | TuSelector.cxx:131 | Start add(alm)
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:TRANSACTION | TuSelector.cxx:138 | AlarmMsg is not null
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:TRANSACTION | TuSelector.cxx:140 | Sending AlarmMessage 0 to TUs
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:TRANSACTION | TuSelector.cxx:145 | End add(alm)
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:DNS | DnsResult.cxx:240 | Whitelisting 2405:200:380:1581::42(28): 2405:200:380:1581::42
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:DNS | RRVip.cxx:128 | updating an existing vip: 2405:200:380:1581::42 with 2405:200:380:1581::42
    07-31 16:29:55.335  4076  5081 I reSIProcate: INFO | RESIP:TRANSACTION | TuSelector.cxx:71 | Send to TU: TU: CALL-SESSION(8) size=0 
    07-31 16:29:55.335  4076  5081 I reSIProcate: 
    07-31 16:29:55.335  4076  5081 I reSIProcate: SipResp: 180 tid=935e2afa889bdcad cseq=1 INVITE contact=xxxx@10.56.68.219:5070 / 1 from(wire)
    07-31 16:29:55.336  4076  5081 D StackIF : readMessage: messageType 2 tid 0 pduLength 920
    07-31 16:29:55.336  4076  5081 D SECIMSJ[0]: [UNSL]< NOTIFY_SIP_MESSAGE
    07-31 16:29:55.336  4076  5081 D StackIF[0]: processNotify: id NOTIFY_SIP_MESSAGE
    07-31 16:29:55.340  4076  5081 D SIPMSG[0]: [<--] SIP/2.0 180 Ringing [CSeq: 1 INVITE]
    07-31 16:29:55.340  4076  4896 D ResipRawSipHandler: handleMessage: event: 100
    07-31 16:29:55.340  4076  5082 D OpenApiServiceModule: handleMessage: what 100
    07-31 16:29:55.341  4076  4896 D ResipVolteHandler: handleMessage: evt 114
    07-31 16:29:55.341  4076  5081 D CpAudioEngineClient: SAE_NotiInfo: SAE_NotiInfo string: 16:29:55.341<180 Ringing:1 INVITE
    07-31 16:29:55.341  4076  5081 D VoIpEngineProxy: NotiInfo:Sending IPC_IMS_INFO Noti
    

    那有用吗?

    0 回复  |  直到 6 年前