RILD - Chapter 7 - RILProxy

RilProxy

For MTK platform, there is a RilProxy layer between RILC and RILJ.

One advantage of using proxy is that in the upper layer, not only RILJ and RILC are connected through Socket, but also other places can connect with RILC to communicate with the lower level Modem, such as sending commands directly to Modem through terminal ATCI interface.

Another advantage is that the lower layer of the proxy layer is free to switch its docking RIC layer, because for different projects, the RIC layer needs to be adapted differently, such as switching from default to C2K RIL.

Of course, the role of the proxy layer is not only to connect the upper and lower intermediaries, but also to filter some messages. For example, when Radio unavailable, it can filter some request s that need Radio to work.

As for the structure of RilProxy, it is similar to RILC. It is also divided into LibRIL layer and MtkRIL layer. In the LibRIL layer, they are quite similar, except that RilProxy listens directly to the socket interface of RILJ here. So the process of RilProxy monitoring from Socket handle, establishing stream sockets and processing stream sockets is not described here. The detailed process can be referred to Chapter 2.

7.1 RilProxy Layer Processing Flow

In RILProxy, unlike RILC, the process of calling MtkRIL callback function onRequest by LibRIL is different. Therefore, we start from this place to analyze its process.
Let's start with a general flow chart:

First, we review the onRequest function of RILC. It judges by different request codes. Finally, different functions are used to preprocess requests. Finally, requests are interpreted as AT commands and sent to AT channels.

Let's look at the onRequest function at the RilProxy layer in the following code:
1. First, request is sent directly to Rfx in ril_callbacks. Rfx is a module that handles different requests and sends them to different socket s. Its code is mainly in / ril/rilproxy/mtk-rilproxy/framework/core / directory.
2. One of Rfx is dispatch queue belonging to dispatch thread. Rfx first encapsulates the request as a Message object and puts it in the dispatch queue. In the dispatch thread, a looper reads the Mesage of the dispatch queue and distributes it to the main thread for the main thread to process.

//ril_callbacks.c
static void
onRequest (int request, void *data, size_t datalen, RIL_Token t){
    //Radio is not available, filter request
    if (radioState == RADIO_STATE_UNAVAILABLE &&
        request != RIL_REQUEST_OEM_HOOK_RAW && //This is for ATCI
        ...){

    }
    //Add to request queue
    rfx_enqueue_request_message(request, data, datalen, t, socket_id);
}

//Rfx.cpp
void rfx_enqueue_request_message(int request, void *data, size_t datalen, RIL_Token t,
        RIL_SOCKET_ID socket_id) {
    //Add to request queue
    dispatchThread->enqueueRequestMessage(request, data, datalen, t, socket_id);
}

//RfxDispatchThread.cpp
void RfxDispatchThread::enqueueRequestMessage(int request, void *data, size_t datalen,
        RIL_Token t, RIL_SOCKET_ID socket_id) {
    RequestInfo *requestInfo = (RequestInfo *)t;

    ...
    //Analysis of Message Objects Using Request Data Purchase R
    sp<RfxMessage> msg = RfxMessage::obtainRequest(socket_id, dest, request, requestInfo->token,
            parcel, t);
    //Building MessageObj
    MessageObj *obj = createMessageObj(msg);
    //Adding messages to dispatchRequestQueue
    dispatchRequestQueue.enqueue(obj);
}
// RfxDispatchThread.cpp
extern "C"
void *rfx_process_request_messages_loop(void *arg) {
    while (1) {
        RfxDispatchThread *dispatchThread = (RfxDispatchThread *) arg;
        dispatchThread->processRequestMessageLooper();
    }
    return NULL;
}
//RfxDispatchThread.cpp
void RfxDispatchThread::processRequestMessageLooper() {
    //Extract a MessageObj from dispatch Request Queueh
    MessageObj *obj = dispatchRequestQueue.dequeue();

    RfxMainThread::waitLooper();
    //Add MessageObj to the queue of MainThread
    RfxMainThread::enqueueMessage(obj->msg);
    //Delete from this queue
    delete(obj);
}

//RfxMainThread.cpp
void RfxMainThread::enqueueMessage(const sp<RfxMessage>& message) {
    ...
    //Using message to build RfxMessageHandler
    sp<MessageHandler> handler = new RfxMessageHandler(message);
    //Let Looper call handler processing
    s_self->m_looper->sendMessage(handler, s_self->m_dummy_msg);
}

The final Message processing above is in RfxMainThread, as shown in the following code.
There are two main steps:
1. Use Message to create a RfxRootController to process this message
2. In RfxRootController, the Hander corresponding to the Message is obtained first. If it is obtained, the Handler is used to process the Message, otherwise the default function is called.

//RfxMainThread.cpp
virtual void onHandleMessage(const Message& message) {
    //fault tolerant
    ...
    // dispatch msg to root controller, it will
    // do further dispatch
    //Create a RfxRootController object
    RfxRootController *root = RFX_OBJ_GET_INSTANCE(RfxRootController);
    //Use RfxRootController to process messages, where m_msg is the message passed in by the parameter
    root->processMessage(m_msg);
}
//RfxRootController.cpp
bool RfxRootController::processMessage(const sp<RfxMessage>& message) {
    RfxController *handler = NULL;

    ...
    //handler for Getting Different Types of Message s
    if (message->getType() == REQUEST) {
        handler = findMsgHandler(message->getId(), message->getSlotId(), m_request_list);
    } else if (message->getType() == URC) {
        handler = findMsgHandler(message->getId(), message->getSlotId(), m_urc_list);
    } else if (message->getType() == RESPONSE || message->getType() == REQUEST_ACK) {
        handler = findMsgHandler(message->getId(), message->getSlotId(), m_request_list);
    }

    bool ret = false;

    //If the Handler is not obtained, different types of Message call different default function processing
    if (handler == NULL) {
        if (REQUEST == message->getType()) {
           //Send the request to GSM RILC
            requestToRild(message);
            ret = true;
        } else if (REQUEST_ACK == message->getType()) {
            requestAckToRilj(message);
            ret = true;
        } else {
            ret = responseToRilj(message);
        }
    } else {
        //If you get the Handler, handle it with the Handler
        ret = handler->processMessage(message);
    }

    if (message->getType() == RESPONSE || message->getType() == URC) {
        handleSendResponseAck(message);
    }

    return ret;
}

The request above can be processed in a variety of ways. Here, take the request ToRild (message) as an example:

The following code first sends the size of the data to Socket, and then sends the content of the data to the past.

//RfxRilAdapter.cpp
void RfxRilAdapter::requestToRild(const sp<RfxMessage>& message) {
    //fault tolerant

   requestToRildX(message);
}


bool RfxRilAdapter::requestToRildX(const sp<RfxMessage>& message) {
    //fault tolerant

    // fd check
    int slotId = message->getSlotId();
    int dest = message->getDest();
    int targetFd = socket_fds[slotId][dest];


    Parcel *parcel = NULL;
    parcel = message->getParcel();
    //Get data size
    int dataSize = parcel->dataSize();
    //get data
    const uint8_t* data = parcel->data();

    // parcel length in big endian
    dataLength[0] = dataLength[1] = 0;
    dataLength[2] = ((dataSize >> 8) & 0xff);
    dataLength[3] = ((dataSize) & 0xff);

    //Send data size first
    sent = send(targetFd , dataLength , 4, 0);
    //Re-send data
    sent += send(targetFd , data , dataSize, 0);
}

The above process just sends the data size and data to each other.

In general, the RilProxy layer uses Message and Looper mechanisms to package our requests into Messages, then create corresponding Handler objects with Messages, and finally send Handler objects to Looper for processing.

Notable is the socket_fds[slotId][dest] array, which specifies the target we are sending, so let's see what values it initializes. So you can know what targets it specifically represents.

7.2 socket_fds initialization

Start tracing from the assignment of socket_fds:

//RfxRilAdapter.cpp
bool RfxRilAdapter::setSocket(int slotId, RILD_RadioTechnology_Group group, int fd) {
    ...

    socket_fds[slotId][group] = fd;

    return true;
}

//RfxSocketStateManager.cpp
void RfxSocketStateManager::setSocketState(RILD_RadioTechnology_Group groupId,
            bool isConnected, int slotId, int socFd) {
    RfxRilAdapter::getInstance()->setSocket(slotId, groupId, socFd);
}

//RfxSocketStateManager.cpp
void RfxSocketStateManager::processMessage(const sp<RfxSocketStateMessage>& message) {
    setSocketState(message->getGroupId(), message->getIsConnected(), message->getSlotId(),
        message->getSocketFd());
}

//RfxSocketStateManager.cpp
class RfxSocketMessageHandler : public RfxMainHandler {
public:
    explicit RfxSocketMessageHandler(
            const sp<RfxSocketStateMessage>& msg) : m_msg(msg) {}

protected:
    /**
     * Handles a message.
     */
    virtual void onHandleMessage(const Message& message) {
        ...
        mgr->processMessage(m_msg);
    }
}

//RfxSocketStateManager.cpp
void RfxSocketStateManager::notifySocketState(
        RILD_RadioTechnology_Group group, int slotId, int fd, bool isConnected) {
    RfxSocketState::SOCKET_ID socId;
    Message dummyMsg;
    sp<RfxSocketStateMessage> new_msg = new RfxSocketStateMessage(group, isConnected, slotId, fd);
    //Create a corresponding RfxSocketMessageHandler object using Message
    sp<MessageHandler> handler = new RfxSocketMessageHandler(new_msg);
    //Send this Message to MainLooper for processing
    RfxMainThread::waitLooper()->sendMessageAtTime(0, handler, dummyMsg);
}

//Rfx.cpp
void rfx_set_socket(RILD_RadioTechnology_Group group, int slotId, int fd) {
    RfxSocketStateManager::notifySocketState(group, slotId, fd, true);
}

//socket_channel.cpp
void RilpSocket::connectSocket(void)
{
    RLOGI ("connectSocket to %d %d socket_name %s\n", group, id, name);
    while (1) {
        //Try using Socket Name to get Socket handle
        socketFd = socket_local_client(name,
                ANDROID_SOCKET_NAMESPACE_RESERVED,
                SOCK_STREAM);
        if(socketFd > 0) {
            RLOGI ("connectSocket to %d\n", socketFd);
            rfx_set_socket(group, id, socketFd);
            return;
        } else {
            RLOGD ("connectSocket fail, try again %d\n", socketFd);
            sleep(1);
        }
    }
}

//socket_channel.cpp
void RilpSocket::initSocket(const char *socketName, RIL_SOCKET_ID socketid, RILD_RadioTechnology_Group groupId) {
    pthread_attr_t attr;
    PthreadPtr pptr = ril_socket_reader_looper;
    int result;

    RilpSocket* socket = new RilpSocket(socketName, socketid, groupId);
    socket->connectSocket();

    ...
}

//socket_channel.cpp
extern "C"
void ril_socket_init() {
    int i=0;
    char *socketName;
    //Initialization of Socket in GSM RIL
    for(i=0; i<SIM_COUNT; i++) {
        socketName = RilpSocketUtil::getSocketName(RADIO_TECH_GROUP_GSM, RIL_SOCKET_ID(i));
        RilpSocket::initSocket(socketName, RIL_SOCKET_ID(i), RADIO_TECH_GROUP_GSM);
    }

    socketName = RilpSocketUtil::getSocketName(RADIO_TECH_GROUP_C2K, RIL_SOCKET_1);
    if(RpFeatureOptionUtils::isC2kSupport()) {
        RilpSocket::initSocket(socketName, RIL_SOCKET_1, RADIO_TECH_GROUP_C2K);
    }

    for (i=0; i<SIM_COUNT; i++) {
        setRadioState(RADIO_STATE_OFF, RIL_SOCKET_ID(i));
    }
    //Initialize the Socket of ATCI
    socketName = RilpSocketUtil::getSocketName(RADIO_TECH_GROUP_ATCI, RIL_SOCKET_1);
    RilpAtciSocket::initSocket(socketName);

    ...
}

For the above step-by-step reverse tracking, a flow chart is summarized.

Firstly, SocketName is obtained through the Socket type and ID in ril_socket_init(), and then the static function of RilpSocket is invoked to initialize the SocketName.

In the initialization process, a RilpSocket object is created using SocketName, and then the RilpSocket object is called connectSocket(). The connectSocket function creates a Message in the RfxSocketStateManager to handle the Connect operation of the Socket. Finally, the Socket handle is added to the two-dimensional array of socket_fds.

The ril_socket_init() function is called in the mainloop() function of the Mtk-RIL layer of RipProxy. So far, the Socket initialization operation has been completed. Through the following Log, we can see which Sockets have been initialized:

//Socket of SIM slot 1
09:47:54.193526  1148  1174 I         : connectSocket to 0 0 socket_name mrild
09:47:54.195757  1148  1174 I         : connectSocket to 24
//Socket of SIM slot 2
09:47:54.205873  1148  1174 I         : connectSocket to 0 1 socket_name mrild2
09:47:54.208010  1148  1174 I         : connectSocket to 29
//Socket of ATCI Port
09:47:54.209648  1148  1174 I         : connectSocket to 0 socket_name rilproxy-atci
09:47:54.209725  1148  1174 I         : connectSocket to 15
09:47:54.212312  1148  1174 I         : connectSocket to 0 socket_name msap_uim_socket1
09:47:54.214344  1148  1174 I         : connectSocket to 30

Attachment: RilProxy, RILC corresponding file LOG TAG, easy to locate code according to Log
RILC:

rild.c           #define LOG_TAG "RILD"
ril.cpp          #define LOG_TAG "RILC"
ril_event.c      #define LOG_TAG "RILC"
ril_callbacks.c   #define LOG_TAG "RIL"
atchannel.c     #define LOG_TAG "AT"

RilProxy:

rild.c           #define LOG_TAG "RILP"
ril.cpp          #define LOG_TAG "RILC-RP"
ril_event.cpp    #define LOG_TAG "RILC-RP"
ril_callbacks.c  #define LOG_TAG "RIL-RP"
atchannel.c      #define LOG_TAG "AT"

Keywords: socket

Added by achintha on Sun, 02 Jun 2019 01:13:35 +0300