难懂的书之--android Call(MO)流程介绍

网上有很多的资料,我这里整理一份属于自己的。当然其中的很多内容还是来自网络,无所不能的网络啊~~~


很多人的文章写Call的流程的时候,都是从OutgoingCallBroadcaster.java开始到RIL.java结束,java部分的确是这样。但为了更完美我将补充前面的TwelveKeyDialer.java,
及后续的RIL层的AT指令,这样便一条路走到尽头(其实GSM/CDMA模块中还有,这部分暂时不提)。
全部的代码在四个部分:package/../Contacts ,package/../Phone,frameworks/base/telephony/java/com/android/internal/telephony 及hardware/ril/reference-ril

文章中粘贴的代码,由于从2.0到2.2的移植,所以有些代码并不是和2.2源码中一致。
1、界面部分的TwelveKeyDialer.java,extends Activity implements View.OnClickListener...
这一部分在Contacts这个APP中,在你一个个按下按键时,可以从onClick()函数中看到会响按键音、接着afterTextChanged()会匹配联系人中包含你按下数字的联系人
当你按下dial键的时候则会进入Call的流程中。
    void dialButtonPressed() {
        final String number = mDigits.getText().toString();
        boolean sendEmptyFlash = false;
        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
		...
		intent.setData(Uri.fromParts("tel", number, null));
        ...
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
		...
	}

2、进入OutgoingCallBroadcaster.java,extends Activity
上面start intent时就进入OutgoingCallBroadcaster的onCreate(),首先判断电话号码是否为emergencyNumber
        if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
            action = emergencyNumber
                    ? Intent.ACTION_CALL_EMERGENCY
                    : Intent.ACTION_CALL;
            intent.setAction(action);
        }

如果是紧急号码,将callNow变量赋值为true,立即启动InCallScreen。如不是则通过发送广播进入OutgoingCallReceiver.java
该类是一个内部类,作用是接收OutgoingCallBroadcaster发送的广播,判断是否已经启动InCallScreen。没有启动的话就进行一些初始化,如:对OTA进行初始化。
接收到广播之后,从Intent里面取出电话号码及其URi。然后设置Intent为ACTION_CALL,并带上号码和uri。启动InCallScreen。关闭该OutgoingCallBroadcaster。
            Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
            newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
			...
            newIntent.setClass(context, InCallScreen.class);
            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//注意这个flag保证通话界面这个activity同时只能显示一个
			context.startActivity(newIntent);

3 启动InCallScreen.java , extends Activity implements View.OnClickListener, View.OnTouchListener 
它主要类主要是负责通话的界面,包括通话时间的显示、联系人头像、电话号码、通话状态、DTMFTwelveKeyDialer及菜单等的现实
在Call流程中所起到的作用简单概述如下:得到action为ACTION_CALL、或ACTION_CALL_EMERGENCY,直接placeCall(intent),然后将状态返回.
        } else if (action.equals(Intent.ACTION_CALL)
                || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
			...
            InCallInitStatus status = placeCall(intent);
            if (status == InCallInitStatus.SUCCESS) {
                // Notify the phone app that a call is beginning so it can
                // enable the proximity sensor
                app.setBeginningCall(true);
            }
            return status;

而在 placeCall(intent)中直接是调用PhoneUtils的接口
           callStatus = PhoneUtils.placeCall(mPhone, number, contactUri);
在接下来就只 Connection cn = phone.dial(number),这个就是调用Phone interface。
you cannot assume the audio path is connected until PhoneStateChanged notification has occurred.

4、接下来就进入到了framework中的code,我们已一个cdma 电话为例,总结一下相关的几个类
Phone 、CDMAPhone、PhoneBase、CdmaCallTracker、CdmaConnection、CallTracker、CommandsInterface、RIL、BaseCommands共8个类,我们先整理一下这些类的关系
本人较懒,就不画UML图了,直接文字表达一下,呵呵~~
public interface Phone
PhoneBase extends Handler implements Phone
CDMAPhone extends PhoneBase

所以可以看到CDMAPhone中的dial()函数如下    
public Connection dial (String dialString) throws CallStateException {
        // Need to make sure dialString gets parsed properly
        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
        return mCT.dial(newDialString);
    }

mCT的定义也在CDMAPhone中 CdmaCallTracker mCT;
在mCT的dial()函数中有
		...
		pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall);
		...
            // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
                cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());
            } else {
		...
            }
        }
        updatePhoneState();
        phone.notifyPreciseCallStateChanged();

这段代码主要注意3点
首先是pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall),这个是创建一个Call的connection实例,然后将其加入到
一个connections的list中,因为每个网络支持好几通电话。
其次是obtainCompleteMessage() { return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);},这个是要处理返回信息的

最后还更新了Phone的状态,phone call的状态一共就3个

enum State {
        IDLE, RINGING, OFFHOOK;
    };

再往下看cm.dial()
public final class CdmaCallTracker extends CallTracker
CallTracker中定义public CommandsInterface cm;
CommandsInterface为接口类 public interface CommandsInterface
public abstract class BaseCommands implements CommandsInterface
public final class RIL extends BaseCommands implements CommandsInterface
由于以上的这些关系,最后走到RIL.java的dial部分,这段代码不多说,就是往socket中写东西。这里先记住RIL_REQUEST_DIAL
    public void
    dial (String address, int clirMode, 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 (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

        send(rr);
    }

其实到了这,我们暂时不去分析RIL层。先假设如果这通电话成功播出的话,应该是一个ATD<dial string>下去,然后返回一个OK。这个在上层是怎么处理的呢?
在RIL.java中有RILSender extends Handler implements Runnable 和RILReceiver implements Runnable,其实发送就是通过RILSender往socket中写data。
而接收AT就是就是从socket中读取data,然后处理。这里返回的OK会在processResponse()->processSolicited()中处理。如果返回没有错误最终是
走rr.mResult.sendToTarget(),然后出发CdmaCallTracker.java中的msg(EVENT_OPERATION_COMPLETE),可以看到
CdmaCallTracker.java中有handlemessage()
		...
	        case EVENT_OPERATION_COMPLETE:
                operationComplete();
            break;

在operationComplete()中其实是将pendingOperations计数减1,然后获取当前的calls(这个一般对应AT+CLCC).这样便知道此时该网络的通话情况。
	 pendingOperations--;
			...
        if (pendingOperations == 0 && needsPoll) {
            lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            cm.getCurrentCalls(lastRelevantPoll);
        } else if (pendingOperations < 0) {
			...
        }

5、接下来,步入正轨。进入RIL层。
这里涉及的几个文件为
Ril_commands.h 中一个映射结构{RIL_REQUEST_DIAL, dispatchDial, responseVoid}
Ril.h 主要定义一个宏 #define RIL_REQUEST_DIAL 10 ,其实这个必须和java层的RILConstants.java中定义的int RIL_REQUEST_DIAL = 10;一致,并且在
Ril_commands.h 中的顺序也应从上数第10行,不然的话会出错。这一点,设计的确实有些呆板。
Ril.cpp case RIL_REQUEST_DIAL: return "DIAL",这个是要是一个请求转换为string的操作
Reference-ril.c 中的
static void
onRequest (int request, void *data, size_t datalen, RIL_Token t)
		...
        case RIL_REQUEST_DIAL:
            requestDial(data, datalen, t);
            break;
		...

在requestDial()中
static void requestDial(void *data, size_t datalen, RIL_Token t)
{
	...
    p_dial = (RIL_Dial *)data;
	...
    asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);


    ret = at_send_command(cmd, NULL);
    free(cmd);
	...
    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}

这样一个ATD通过at_send_command()写向串口(最终调用的是系统函数writeline()),剩下的就交给CDMA模块了。呵呵~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值