android系统学习笔记七

来源:互联网 发布:如何打开usb端口 编辑:程序博客网 时间:2024/05/21 12:44

  Android 电话部分综述 

主要部分:

   呼叫 

   短信

   数据连接

   SIM

   电话本     

电话部分分为以下几层:

   Modem驱动 

   RIL(radio interface  layer)

   电话服务框架

   应用层

 Modem通信模块

 Chip-on-board
Modem硬件上一般使用两个渠道:  

         一个是用于AT命令一般使用UARTUSB方式, AT命令由Hayes公司发明

                            以AT开头,用于完成调制解调器之间的交互

         另一个用于数据传输通过usb方式传输数据

                             如果基带与应用处理器集成一般通过共享内存方式传输

本地的RIL代码
   本地代码的路径:\hardware\ril

   部分文件;

    include:  RIL头文件

Libril:   RIL,最终生成libril.so  是一个辅助库

Rild:    守护进程  安装在system/bin目录下

Rdference-ril:  参考库, 生成动态库 libreference-ril.so

    Include 目录中ril.h头文件是RIL框架结构和接口

   

  Rild守护进程

  Rild是一个可执行程序 获取参数------->r打开功能库------->建立事件循环----------->执行RIL-Init------>register

 

ril.c

static struct RIL_Env s_rilEnv = {

    RIL_onRequestComplete,  //RIL请求完成

    RIL_onUnsolicitedResponse,  //主动上报的响应

    RIL_requestTimedCallback   //用户定义响应的回调函数

};

  

     主函数部分代码如下:

      int main(int argc, char **argv)

      { 

    ...

           //获取参数并解析

           dlHandle = dlopen(rilLibPath, RTLD_NOW);

           //启动线程进入事件循环

           RIL_startEventLoop();

rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");

      //处理参数

      funcs = rilInit(&s_rilEnv, argc, rilArgv);

       RIL_register(funcs);

            ...    

      }

      通过select多路复用机制,读取来自上层的Socket接口的具体操作命令

       在init.rc中启动这个守护进程如果使用-l 则可以指定所使用的功能库

 

  Libril

 Libril.so 主要提供了用于注册的RIL_register(),

几个回调函数

RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {}

void RIL_onUnsolicitedResponse(int unsolResponse, void *data,size_t datalen){}

static UserCallbackInfo *internalRequestTimedCallback (RIL_TimedCallback callback, void *param,

                                const struct timeval *relativeTime){}

字符串转换函数

extern "C" const char * requestToString(int request);

extern "C" const char * failCauseToString(RIL_Errno);

extern "C" const char * callStateToString(RIL_CallState);

extern "C" const char * radioStateToString(RIL_RadioState);

RIL的实现库Reference RIL

 libreference-ril.so 功能库的一个参考实现,完成从具体电话服务命令到实际的AT命令之间的转换

 Request请求流程

 首先由java层通过socket将命令发送到RIL层的RILD守护进程,负责监听的ril_event_loop消息循环中

RILD Socket有了请求链接信号,会建立起一个record_system, 打通与上层的数据通道开始接收请求数据

数据通道的回调函数processCommandsCallback()会保证收到一个完整的Request(Request包的完整性由record_stream的机制保证) ,将其送达processCommandBuffer()函数,

static int

processCommandBuffer(void *buffer, size_t buflen) {

    Parcel p;

    status_t status;

    int32_t request;

    int32_t token;

    RequestInfo *pRI;

    int ret;

    p.setData((uint8_t *) buffer, buflen);

    // status checked at end

    status = p.readInt32(&request);

    status = p.readInt32 (&token);

    if (status != NO_ERROR) {

        LOGE("invalid request block");

        return 0;

    }

    if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) {

        LOGE("unsupported request code %d token %d", request, token);

        // FIXME this should perhaps return a response

        return 0;

    }

    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));

    pRI->token = token;

    pRI->pCI = &(s_commands[request]);

    

    //互斥量上锁

    ret = pthread_mutex_lock(&s_pendingRequestsMutex);

    assert (ret == 0);

    pRI->p_next = s_pendingRequests;

    s_pendingRequests = pRI;

    

    //互斥量解锁

    ret = pthread_mutex_unlock(&s_pendingRequestsMutex);

    assert (ret == 0);

/*    sLastDispatchedToken = token; */

   //调用分派函数

    pRI->pCI->dispatchFunction(p, pRI);

    return 0;

}

 processCommandBuffer()函数正式进入命令的解析流程  它从socket中序列化的数据流里还原信息,将其组织到RequestInfo,每个命令将以下形式存在

typedef struct RequestInfo {

    int32_t token;      //this is not RIL_Token

    CommandInfo *pCI;

    struct RequestInfo *p_next;

    char cancelled;

    char local;         // responses to local commands do not go back to command process

} RequestInfo;

在还原出来的信息中,最重要的是request号 它在RIL层和上层之间ril_commands.h中定义

/** Index == requestNumber */

static CommandInfo s_commands[] = {

#include "ril_commands.h"

};

RIL层中采用表驱动方式分派请求,分派的基础是ruquest,头文件按以下方式被包含 

commandInfo结枸表示命令信息关联了request号和实际的请求函数,及响应函数之间的关系

typedef struct {

    int requestNumber;

    void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);

    int(*responseFunction) (Parcel &p, void *response, size_t responselen);

} CommandInfo;

 

 接下来的分发流程,

  由s_callbacks.onReauest()完成这一操作,s-callbacks是获取libreference-rilRILRadioFunctions的结构指针,请求在这里转入底层libreference-ril处理,handlerreferece-ril.cRequest

例如:RIL——REQUEST——DIAL请求 流程如下

onRequest()---->requestDial()-这里将命令和参数转换成对应的AT命令----------->at_send_connand------->at_command_full()------>at_send_command_full_nolock()---->writeline()

Response 流程

 

RIL的移植工作

 当宏RIL——SHLIB被定义时,将使用库的行式,

 没有被定义时,将使用守护进程的方式

移植需要考虑的问题:

  RIL设备所使用的不同端口

  在RIL_RadioFunctionsonRequest函数中需要处理的不同命令(差异部分在数据连接和呼叫状态查询等方面)

电话部分Java框加及应用

 代码路径为:\frameworks\base\telephony\java

 Java层与RIL本地代码的接口是一个名为rildsocket接口

 RIL.java中的几个类

 RILRequest代表一个电话服务命令请求

 RILSender 负责命令的发送

 RILReceiver处理信命令响应和主动上报信息的响应

RILConstants.java定义了电话报务的具体命令

 

 class RILRequest {    

    static RILRequest obtain(int request, Message result) { }      

    void release() {}  

Static void resetSerial() {}

    String  serialString() {}

void onError(int error, Object ret) {}

}

RIL  RIL.RIL_Sender   RIL.RILReceiver几个类的关系

public final class RIL extends BaseCommands implements CommandsInterface {}

Connamds 为命令响应或主动上报提供回调函数的注册机制

commandInterface 提供具体的电话服务接口

RILJava中一个命令的发送标准流程是:

 RILRequest.obtain--->复制参数----->通过send()函数发送EVENT_SEND----->RILSender线程中处理EVENT_SEND------>将命令写到out stream(socket)

Socket的取得 ,是在RILReceiver中建立

static final String SOCKET_NAME_RIL = "rild";

 s = new LocalSocket();

                    l = new LocalSocketAddress(SOCKET_NAME_RIL,

                            LocalSocketAddress.Namespace.RESERVED);

                    s.connect(l);

响应和主动上报消息的流程:

 RILReceiver线程监视mSocket input------>readRilMessage(读取完整响应)---------->processResponse---------->分别处理RESPONSE_UNSOLICITEDRESPONSE_SOLICITED(前者为主动上报,后者为命令响应)

注:sendprocessResponse 是异步的,电话服务的发送和响应是异步的

 RIL直接同RIL本地代码打交道,对上层应用来说他并不是直接的接口,GSMPhone.java 他继承了PhoneBase,phoneBase实现了phone接口, GSMPhoneRIL进行了一层封装,通过phone接口来实现功能

 

Phone应用程式序中:

通过PhoneFactory来获取GSMPhone

流程是:

PhoneFactory.makeDefaultPhones------>PhoenFactory.useNewRIL------>PhoneFactory.registerPhone

然后Phone就可以获取GSMPhone的实例(Phone接口)

拔打电话和获取网络状态,是由telephonyManager来完成

TelephoneManager通过两个IBander接口ITelephonyITelephonyRegistry来完成

ITelephony是电话服务的用户主动进行RIL访问的路径(如拔打电话), 它的服务实现类不在框架代码中,而在phone应用中 phoneInterfaceManager.java

ITelephonyRegistry提供一个通知机制,将底层状态或变更通知给电话服务 如网络状态/信号强弱

底层通知的来源是GSMPhone通过PhoneNotifier的实现者DefaultPhoneNotifier.java

telephonyRegistry通过两种方式通知用户

 一种是Broadcast

 另一种是在TelephoneRegistry.java中注册的IPhoneStateListener接口实现回调机制

interface ITelephonyRegistry {

    void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);

}

传递的是IPhoneStateListener callback参数

呼叫

ITelephony接口实现在Phone应用中的phone服务,通过TelephonyManager提供访问接口

 服务内部通过PhoneFactory获取GSMPhone来访问URI提供如拔号/接通/挂断

 在呼叫部分中,GSMPhoneRIL的路径中用到的几个数据结构如下:

 GSMCall  基本的呼叫控制结构,每个接通的GSMCall 都拥有一个GSMConnection结构

 GSMConnection  该结构用于存放呼叫时长等信息

 CallTracker  是呼叫模块的核心  它提供与呼叫相关的接口

             GSMPhone拥有CallTracker的实例,通过调用CommandsInterface实现

维护当前的GSMCall,实现追踪的方法为:pollCallsWhenSafe

getCurrentCalls取得当前活动的呼叫列表

短信部分

SMSManager实现短信的发送与sim卡短信相关的操作 通过ISms接口实现对应操作

 ISms的服务端实现是:simsmsInterfaceManager

SMSDispatacher是短信的核心部分,负责发送接收短信,同样也集成在GSMPhone

发送方面

        提供了text pdu两种方式.SmsTracker跟踪短信的发送过程

        发送结果 根据PendingIntent 回传

接收方面

        SMSDispatcher启动时会通过CommandInterface注册新短信和返回报告的回调接口

        mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);

        mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);

        mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);

        mCm.registerForOn(this, EVENT_RADIO_ON, null);

 

      有新消息上来时,相应的消息会被发送到SMSDispatcher的消息处理函数

      dispatchMessage会读取SMSHeader分析是否需要拼接,完在后由dispatchPdus通过

           Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);

            intent.putExtra("result", result);

            mWakeLock.acquire(WAKE_LOCK_TIMEOUT);

            mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");

   广播出去

数据联接

 Android 数据联接是通过ppp方式实现的

     数据联接分两个步骤:

            首先是通过AT命令激活PDP联接

            pppd通过数据端口完成拔号联接

            GSMPhone 拥有其实例基入口点是:

  DataConnectionTrackr.trySetupData------>setupData------->pdpConnection.connect------>commandsInterface.setupDefaultPDP

数据连接部分的结构

注: pppd是一个单独的进程,需要在init.rc里注册此服务

原创粉丝点击