Android 5.0 呼叫流程--呼出

原创 2014年12月01日 22:30:45

11

5.       Android 5.0 呼叫流程

5.1         MO call

我们先给出MO起呼过程的示例流程图,然后结合该图做代码流程的分析:



5.1.1   拨号请求(CallActivity

我们从CallActivity来开始看MO呼叫流程,在他的processIntent里处理收到的呼叫intent,目前支持3种类型的起呼呼叫,之后processOutgoingCallIntent给CallReceiver发送一个广播,完成本阶段的处理。

    private void processIntent(Intent intent) {

        // Ensure call intents are not processed on devices that are not capable of calling.

        if (!isVoiceCapable()) {

            return;

        }

 

        verifyCallAction(intent);

        String action = intent.getAction();

 

        if (Intent.ACTION_CALL.equals(action) ||

                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||

                Intent.ACTION_CALL_EMERGENCY.equals(action)) {

            processOutgoingCallIntent(intent);

        } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {

            processIncomingCallIntent(intent);

        }

    }         

 

 

5.1.2   拨号请求(CallReceiver

CallReceiver是一个广播接收器,处理所有的来电和去电广播,具体来讲,处理3个广播,来电、拨号盘去电、SIM卡去电,这里分析一下拨号盘呼叫的流程,收到拨号盘的拨号请求后,调用processOutgoingCallIntent进行后续处理。

    public void onReceive(Context context, Intent intent) {

        final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);

        final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);

        Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,

                isUnknownCall);

 

        if (isUnknownCall) {

            processUnknownCallIntent(intent);

        } else if (isIncomingCall) {

            processIncomingCallIntent(intent);

        } else {

            processOutgoingCallIntent(context, intent);

        }

    }         

 

processOutgoingCallIntent实现:

1)  获取到CallsManager的实例,并调用其方法startOutgoingCall,这里会创建一个Call实例,并通过addCall进行相关的状态维护;

2)  再新建一个NewOutgoingCallIntentBroadcaster实例,调用其方法processIntent,改变intent的一些参数,使用mCallsManager.placeOutgoingCall发起呼叫,再调用broadcastIntent发一个广播消息ACTION_NEW_OUTGOING_CALL出去。

 

呼叫发起的主要流程在placeOutgoingCall里面处理;

ACTION_NEW_OUTGOING_CALL广播的发送流程和接收器如下:

    private void broadcastIntent(

            Intent originalCallIntent,

            String number,

            boolean receiverRequired) {

        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);

        mContext.sendOrderedBroadcastAsUser(

                broadcastIntent,

                UserHandle.CURRENT,

                PERMISSION,

                receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,

                null,  // scheduler

                Activity.RESULT_OK,  // initialCode

                number,  // initialData: initial value for the result data (number to be modified)

                null);  // initialExtras

    }  

 

Intent.ACTION_NEW_OUTGOING_CALL的定义如下,

    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)

    public static final String ACTION_NEW_OUTGOING_CALL =

            "android.intent.action.NEW_OUTGOING_CALL";         

 

通过AndroidManifest.xml,我们可以看出有两个receiver接收这个intent,

ProcessOutgoingCallTest.java (packages\services\telephony\src\com\android\phone):

UndemoteOutgoingCallReceiver.java (packages\apps\dialer\src\com\android\dialer\interactions): 

 

继续分析呼叫的处理流程,在CallsManager.placeOutgoingCall里,使用之前创建的Call实例的方法call.startCreateConnection,

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {

        Preconditions.checkState(mCreateConnectionProcessor == null);

        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,

                phoneAccountRegistrar, mContext);

        mCreateConnectionProcessor.process();

    } 

这里会创建一个CreateConnectionProcessor实例,并调用其process,通过attemptNextPhoneAccount,调用到service.createConnection,其中service的类型是ConnectionServiceWrapper,它是IConnectionService的子类:

    private void attemptNextPhoneAccount() {…

            ConnectionServiceWrapper service =

                    mRepository.getService(

                            attempt.connectionManagerPhoneAccount.getComponentName());

 

                service.createConnection(mCall, new Response(service));

…}

 

final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {…}    

 

这里又涉及到binder通信,ConnectionServiceWrapper是客户端,ConnectionService是服务端,MO起呼使用了createConnection这个接口方法。

 

在ConnectionService端,onCreateOutgoingConnection会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionService是ConnectionService的子类和最终要实例化的类,所以ConnectionService实例的onCreateOutgoingConnection方法在TelephonyConnectionService执行,这个方法代码较多,最终它会调用placeOutgoingConnection(),

placeOutgoingConnection代码如下,它通过Phone.dial进行拨号,这个就是我们熟悉的流程了,在Android2.3、4.0、4.4都有这个拨号过程,之后将连接信息存放起来。

    private void placeOutgoingConnection(

            TelephonyConnection connection, Phone phone, ConnectionRequest request) {…

            originalConnection = phone.dial(number, request.getVideoState());

        if (originalConnection == null) {…

        } else {

            connection.setOriginalConnection(originalConnection);

        }

    }

 

 

5.1.3         拨号请求(Phone

 

Phone.dial是一个接口,实现的原型在basePhone.dial(dialString),basePhone是GSMPhone的父类,实际上是一个实例是GSMPhone,所以最终调用GSMPhone.dial。代码中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。

    public Connection

    dial (String dialString, UUSInfo uusInfo) throws CallStateException {

        GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());

        if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'...");

        //MTK-END [mtk04070][111118][ALPS00093395]Add Cclog

 

        if (mmi == null) {

            return mCT.dial(newDialString, uusInfo);

        } else if (mmi.isTemporaryModeCLIR()) {

            return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);

        } else {

            mPendingMMIs.add(mmi);

            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));

            mmi.processCode();

 

            // FIXME should this return null or something else?

            return null;

        }

    }     

 

在GsmCallTracker的dial方法中,会先将音频通道mute,再通过mCi.dial进行拨号,之后再将状态信息更新到应用。

   synchronized Connection

    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {

        clearDisconnected();

            setMute(false);

                if (PhoneNumberUtils.isEmergencyNumber(dialString)

                    cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                } else {

                    cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                }

        updatePhoneState();

        phone.notifyPreciseCallStateChanged();

    }     

 

 

其中cm是RIL.java的实例,它在PhoneFactory.makeDefaultPhone里被实例化,然后在GsmPhone实例创建的时候,引用被传递给GsmPhone,在GsmPhone的构造函数里,调用其父类PhoneBase的构造方法,给mCi赋值为RIL的引用值,之后GsmPhone就可以使用父类的mCi了,所以mCi.dial即RIL.dial()。

    public static void makeDefaultPhone(Context context) {

                int numPhones = TelephonyManager.getDefault().getPhoneCount();

                sCommandsInterfaces = new RIL[numPhones];

                for (int i = 0; i < numPhones; i++) {

                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],

                            cdmaSubscription, i);

                }

 

                for (int i = 0; i < numPhones; i++) {

                    PhoneBase phone = null;

                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);

                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {

                        phone = new GSMPhone(context,

                                sCommandsInterfaces[i], sPhoneNotifier, i);

                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {

                        phone = new CDMALTEPhone(context,

                                sCommandsInterfaces[i], sPhoneNotifier, i);

                    }

                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);

 

                    sProxyPhones[i] = new PhoneProxy(phone);

                }

…}

 

 

    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {

        super("GSM", notifier, context, ci, unitTestMode);

        mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);

}

 

 

    protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,

            boolean unitTestMode, int phoneId) {

        mCi = ci;

}

 

 

 

RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD,

如果是高通平台,RILD会通过QCRIL,以及QMI,和modem进行通信,完成呼叫起呼过程;如果是MTK平台,RILD使用AT指令向modem发送呼叫请求。

对于RILD之后的处理我们暂不进行分析了。(AT\QMI及modem这部分是feature phone很常规的呼叫流程过程,不是android特色的代码。)

   public void

    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {

        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

 

        rr.mp.writeString(address);

        rr.mp.writeInt(clirMode);

        rr.mp.writeInt(0); // UUS information is absent

 

        if (uusInfo == null) {

            rr.mp.writeInt(0); // UUS information is absent

        } else {

            rr.mp.writeInt(1); // UUS information is present

            rr.mp.writeInt(uusInfo.getType());

            rr.mp.writeInt(uusInfo.getDcs());

            rr.mp.writeByteArray(uusInfo.getUserData());

        }

        send(rr);

    }     

 

 

在GsmCallTracker的dial方法中,MTK平台还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()和canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。)

5.       Android 5.0 呼叫流程

5.1         MO call

我们先给出MO起呼过程的示例流程图,然后结合该图做代码流程的分析:

5.1.1   拨号请求(CallActivity

我们从CallActivity来开始看MO呼叫流程,在他的processIntent里处理收到的呼叫intent,目前支持3种类型的起呼呼叫,之后processOutgoingCallIntentCallReceiver发送一个广播,完成本阶段的处理。

    private void processIntent(Intent intent) {

        // Ensure call intents are not processed on devices that are not capable of calling.

        if (!isVoiceCapable()) {

            return;

        }

 

        verifyCallAction(intent);

        String action = intent.getAction();

 

        if (Intent.ACTION_CALL.equals(action) ||

                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||

                Intent.ACTION_CALL_EMERGENCY.equals(action)) {

            processOutgoingCallIntent(intent);

        } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {

            processIncomingCallIntent(intent);

        }

    }         

 

 

5.1.2   拨号请求(CallReceiver

CallReceiver是一个广播接收器,处理所有的来电和去电广播,具体来讲,处理3个广播,来电、拨号盘去电、SIM卡去电,这里分析一下拨号盘呼叫的流程,收到拨号盘的拨号请求后,调用processOutgoingCallIntent进行后续处理。

    public void onReceive(Context context, Intent intent) {

        final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);

        final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);

        Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,

                isUnknownCall);

 

        if (isUnknownCall) {

            processUnknownCallIntent(intent);

        } else if (isIncomingCall) {

            processIncomingCallIntent(intent);

        } else {

            processOutgoingCallIntent(context, intent);

        }

    }         

 

processOutgoingCallIntent实现:

1)  获取到CallsManager的实例,并调用其方法startOutgoingCall,这里会创建一个Call实例,并通过addCall进行相关的状态维护;

2)  再新建一个NewOutgoingCallIntentBroadcaster实例,调用其方法processIntent,改变intent的一些参数,使用mCallsManager.placeOutgoingCall发起呼叫,再调用broadcastIntent发一个广播消息ACTION_NEW_OUTGOING_CALL出去。

 

呼叫发起的主要流程在placeOutgoingCall里面处理;

ACTION_NEW_OUTGOING_CALL广播的发送流程和接收器如下:

    private void broadcastIntent(

            Intent originalCallIntent,

            String number,

            boolean receiverRequired) {

        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);

        mContext.sendOrderedBroadcastAsUser(

                broadcastIntent,

                UserHandle.CURRENT,

                PERMISSION,

                receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,

                null,  // scheduler

                Activity.RESULT_OK,  // initialCode

                number,  // initialData: initial value for the result data (number to be modified)

                null);  // initialExtras

    }  

 

Intent.ACTION_NEW_OUTGOING_CALL的定义如下,

    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)

    public static final String ACTION_NEW_OUTGOING_CALL =

            "android.intent.action.NEW_OUTGOING_CALL";         

 

通过AndroidManifest.xml,我们可以看出有两个receiver接收这个intent

ProcessOutgoingCallTest.java (packages\services\telephony\src\com\android\phone):

UndemoteOutgoingCallReceiver.java (packages\apps\dialer\src\com\android\dialer\interactions): 

 

继续分析呼叫的处理流程,在CallsManager.placeOutgoingCall里,使用之前创建的Call实例的方法call.startCreateConnection

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {

        Preconditions.checkState(mCreateConnectionProcessor == null);

        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,

                phoneAccountRegistrar, mContext);

        mCreateConnectionProcessor.process();

    } 

这里会创建一个CreateConnectionProcessor实例,并调用其process,通过attemptNextPhoneAccount,调用到service.createConnection,其中service的类型是ConnectionServiceWrapper,它是IConnectionService的子类:

    private void attemptNextPhoneAccount() {…

            ConnectionServiceWrapper service =

                    mRepository.getService(

                            attempt.connectionManagerPhoneAccount.getComponentName());

 

                service.createConnection(mCall, new Response(service));

…}

 

final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {…}    

 

这里又涉及到binder通信,ConnectionServiceWrapper是客户端,ConnectionService是服务端,MO起呼使用了createConnection这个接口方法。

 

ConnectionService端,onCreateOutgoingConnection会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionServiceConnectionService的子类和最终要实例化的类,所以ConnectionService实例的onCreateOutgoingConnection方法在TelephonyConnectionService执行,这个方法代码较多,最终它会调用placeOutgoingConnection(),

placeOutgoingConnection代码如下,它通过Phone.dial进行拨号,这个就是我们熟悉的流程了,在Android2.34.04.4都有这个拨号过程,之后将连接信息存放起来。

    private void placeOutgoingConnection(

            TelephonyConnection connection, Phone phone, ConnectionRequest request) {…

            originalConnection = phone.dial(number, request.getVideoState());

        if (originalConnection == null) {…

        } else {

            connection.setOriginalConnection(originalConnection);

        }

    }

 

 

5.1.3         拨号请求(Phone

 

Phone.dial是一个接口,实现的原型在basePhone.dial(dialString)basePhoneGSMPhone的父类,实际上是一个实例是GSMPhone,所以最终调用GSMPhone.dial代码中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。

    public Connection

    dial (String dialString, UUSInfo uusInfo) throws CallStateException {

        GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());

        if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'...");

        //MTK-END [mtk04070][111118][ALPS00093395]Add Cclog

 

        if (mmi == null) {

            return mCT.dial(newDialString, uusInfo);

        } else if (mmi.isTemporaryModeCLIR()) {

            return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);

        } else {

            mPendingMMIs.add(mmi);

            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));

            mmi.processCode();

 

            // FIXME should this return null or something else?

            return null;

        }

    }     

 

GsmCallTrackerdial方法中,会先将音频通道mute,再通过mCi.dial进行拨号,之后再将状态信息更新到应用。

   synchronized Connection

    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {

        clearDisconnected();

            setMute(false);

                if (PhoneNumberUtils.isEmergencyNumber(dialString)

                    cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                } else {

                    cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                }

        updatePhoneState();

        phone.notifyPreciseCallStateChanged();

    }     

 

 

其中cmRIL.java的实例,它在PhoneFactory.makeDefaultPhone里被实例化,然后在GsmPhone实例创建的时候,引用被传递给GsmPhone,在GsmPhone的构造函数里,调用其父类PhoneBase的构造方法,给mCi赋值为RIL的引用值,之后GsmPhone就可以使用父类的mCi了,所以mCi.dialRIL.dial()

    public static void makeDefaultPhone(Context context) {

                int numPhones = TelephonyManager.getDefault().getPhoneCount();

                sCommandsInterfaces = new RIL[numPhones];

                for (int i = 0; i < numPhones; i++) {

                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],

                            cdmaSubscription, i);

                }

 

                for (int i = 0; i < numPhones; i++) {

                    PhoneBase phone = null;

                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);

                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {

                        phone = new GSMPhone(context,

                                sCommandsInterfaces[i], sPhoneNotifier, i);

                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {

                        phone = new CDMALTEPhone(context,

                                sCommandsInterfaces[i], sPhoneNotifier, i);

                    }

                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);

 

                    sProxyPhones[i] = new PhoneProxy(phone);

                }

…}

 

 

    GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {

        super("GSM", notifier, context, ci, unitTestMode);

        mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);

}

 

 

    protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,

            boolean unitTestMode, int phoneId) {

        mCi = ci;

}

 

 

 

RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD

如果是高通平台,RILD会通过QCRIL,以及QMI,和modem进行通信,完成呼叫起呼过程;如果是MTK平台,RILD使用AT指令向modem发送呼叫请求。

对于RILD之后的处理我们暂不进行分析了。(AT\QMImodem这部分是feature phone很常规的呼叫流程过程,不是android特色的代码。)

   public void

    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {

        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

 

        rr.mp.writeString(address);

        rr.mp.writeInt(clirMode);

        rr.mp.writeInt(0); // UUS information is absent

 

        if (uusInfo == null) {

            rr.mp.writeInt(0); // UUS information is absent

        } else {

            rr.mp.writeInt(1); // UUS information is present

            rr.mp.writeInt(uusInfo.getType());

            rr.mp.writeInt(uusInfo.getDcs());

            rr.mp.writeByteArray(uusInfo.getUserData());

        }

        send(rr);

    }     

 

 

GsmCallTrackerdial方法中,MTK平台还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。)






如果觉得我的文章对您有用,请打赏。您的支持是对我莫大的认可




版权声明:本文为博主原创文章,未经博主允许不得转载。

通过Intent.ACTION_NEW_OUTGOING_CALL拦截电话拨号

通过Intent.ACTION_NEW_OUTGOING_CALL拦截电话拨号

Android5.0 InCall 通话界面显示流程

收到底层发送的通话状态的消息后到UI处理 RIL.java      processResponse() -- processUnsolicited (p)  -- notifyRegist...

Android5.0 呼叫流程--挂断

5.2         MO terminate MO方主动挂断电话有不同的方式,常规的是在通话界面点击挂断按钮挂断电话,还有一种是通过硬件挂断,如POWER键或其他物理按键挂断,这个要看厂家自行的设...

Android5.0 来电流程图

1.从Framework开始: RIL.java接受到状态改变信号开始:

Android 5.0 呼叫流程--SIP呼出

4.2.1.  5.0 SIP呼叫MO流程   SIP MO呼叫流程如下图所示(为了便于看清,分两段截图):       3.6.2.1.    SIP呼叫App层处理流程   ...

Android5.1中Contacts模块拨号加载联系人信息流程

基于5.1代码Contacts模块拨号流程   之前的总结介绍过联系人界面的快速拨号流程以及显示界面的接收数据过程,现在着重讲中间是怎么从OutgoingBroadcast过来拨号界面,中间的联系人信...

Android入门:广播接收者应用(电话拦截器)

一、电话拦截器应用说明 在我们输入完电话号码并拨打电话时,系统会发出一个有序广播(action="android.intent.action.NEW_OUTGOING_CALL"),并且预计...
  • xiazdong
  • xiazdong
  • 2012年07月21日 21:41
  • 10441

Android实现通话呼叫转移与监听通话录音功能

一、呼叫转移功能。 呼叫转移CF(Call Forwarding,呼叫转移),是电信业一项传统通信业务,又称呼叫前转、呼入转移。如果您的电话无法接听或您不愿接电话,可以将来电转移到其它电话号码上。 我...

【Android】Audio音频输出通道切换 - 蓝牙、外放

手机音频的输出有外放(Speaker)、听筒(Telephone Receiver)、有线耳机(WiredHeadset)、蓝牙音箱(Bluetooth A2DP)等输出设备。在平时,电话免提、插拔耳...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 5.0 呼叫流程--呼出
举报原因:
原因补充:

(最多只允许输入30个字)