This paper mainly analyzes the command interaction process between Android 4.4 RIL's telephone and modem, but this paper does not focus on telephone.
Telephone involves many specific business logic contents, including sim, dail, sms, network, etc., which will be studied and analyzed in the future.
RIL's position in Android system:
(A) the application layer initiates a request to access the modem
(B) RILD process
(A) the application layer initiates a request to access the modem
RIL class in ﹣ frameworks / opt / telephone / SRC / Java / COM / Android / internal / telephone / ril.java provides a series of
The interface of the column is called to the upper application to access the modem. Of course, these interfaces are not directly used by APP, but by sim, dail,
sms, network and other related service calls.
For example, to query the SIM card status geticc cardstatus(), the API is called by the uiccontroller module:
Complete SIM card request log:
10-11 12:21:43.630 D/RILJ ( 1833): [3653]> GET_SIM_STATUS
10-11 12:21:43.630 D/RILC ( 1286): [0005]> GET_SIM_STATUS
10-11 12:21:43.630 D/RILC ( 1286): onRequest: GET_SIM_STATUS
10-11 12:21:43.630 D/ATC ( 1286): AT> AT+CPIN?
10-11 12:21:43.640 D/ATC ( 1286): AT< +CPIN: READY
10-11 12:21:43.640 D/ATC ( 1286): AT< OK
10-11 12:21:43.640 D/RILC ( 1286): [0005]< GET_SIM_STATUS {[app_type=1,app_state=5,perso_substate=2,aid_ptr=(null),app_label_ptr=(null),pin1_replaced=0,pin1=0,pin2=0],}
10-11 12:21:43.640 D/RILJ ( 1833): [3653]< GET_SIM_STATUS IccCardState {CARDSTATE_PRESENT,PINSTATE_UNKNOWN,num_apps=1,gsm_id=0{APPTYPE_SIM,APPSTATE_READY},cdma_id=8,ims_id=8}
Initiate request:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
@Override public void getIccCardStatus(Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); /* Get a RILRequest */ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); /* Refer to log: [3653] > get SIM status */ send(rr); /* Send request */ }
RILRequest.obtain is to obtain a RILRequest instance from the memory pool and initialize:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
class RILRequest { static final String LOG_TAG = "RilRequest"; ... ... static RILRequest obtain(int request, Message result) { RILRequest rr = null; synchronized(sPoolSync) { /* Remove a RILRequest object from the memory pool*/ if (sPool != null) { rr = sPool; sPool = rr.mNext; rr.mNext = null; sPoolSize--; } } if (rr == null) { /* If not in the memory pool, instantiate a */ rr = new RILRequest(); } rr.mSerial = sNextSerial.getAndIncrement(); /* Get the serial number and + 1 */ rr.mRequest = request; /* Corresponding to RIL "request" */ rr.mResult = result; rr.mParcel = Parcel.obtain(); /* Initialize a Parcel to send to the underlying RILD */ if (result != null && result.getTarget() == null) { throw new NullPointerException("Message target must not be null"); } // first elements in any RIL Parcel rr.mParcel.writeInt(request); /* The first and most basic two elements of each RIL Parcel */ rr.mParcel.writeInt(rr.mSerial); return rr; /* Returns the instance obtained from the memory pool */ } ... ... String serialString() { //Cheesy way to do %04d StringBuilder sb = new StringBuilder(8); /* Create a StringBuilder instance for manipulating strings */ String sn; long adjustedSerial = (((long)mSerial) - Integer.MIN_VALUE)%10000; sn = Long.toString(adjustedSerial); /* Convert values to strings */ //sb.append("J["); sb.append('['); for (int i = 0, s = sn.length() ; i < 4 - s; i++) { sb.append('0'); } sb.append(sn); sb.append(']'); return sb.toString(); /* Converted string format: [xxxx] */ } ... ... }
send(rr) sends the request to the server:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private void send(RILRequest rr) { Message msg; ... ... msg = mSender.obtainMessage(EVENT_SEND, rr); /* Send event send time, time parameter is RILRequest */ acquireWakeLock(); /* Get wakelock, disable entering sleep */ msg.sendToTarget(); /* message Obtained from the handler class so that messages can be sent directly to the handler object. target is the handler that creates the message */ }
In fact, telephone can't communicate with modem directly. Because modem of every manufacturer is different, the way modem exists in the system
It is also different. For example, some CPU chip manufacturers' modem s are integrated on baseband in the way of a CP core (Qualcomm, Spreadtrum, etc.), and some CPU cores
Chips (Exynos 4412, etc.) need to be connected with modem module through serial port / USB, such as BC72 LTE module, etc.
send(rr) sends a request to RILD, which involves an inter process communication problem, and the Java side communicates with the C + + side. Of course, there is no
Bond, which is familiar to all Android developers, is socket.
The processing of RIL command interaction on the phone / Java side is temporarily called RILJ.
RILJ is the client of socket, and RILD (RILD process) is the server. The RILD process will be analyzed later.
Create socket client:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { ... ... mSenderThread = new HandlerThread("RILSender"); /* Create RILSender thread */ mSenderThread.start(); Looper looper = mSenderThread.getLooper(); mSender = new RILSender(looper); ConnectivityManager cm = (ConnectivityManager)context.getSystemService( Context.CONNECTIVITY_SERVICE); if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) { riljLog("Not starting RILReceiver: wifi-only"); } else { riljLog("Starting RILReceiver"); mReceiver = new RILReceiver(); mReceiverThread = new Thread(mReceiver, "RILReceiver"); /* Create RILReceiver thread */ mReceiverThread.start(); ... ... } ... ... } class RILReceiver implements Runnable { byte[] buffer; RILReceiver() { /* Assign an array when constructing */ buffer = new byte[RIL_MAX_COMMAND_BYTES]; } @Override public void run() { /* Loop read data returned from RILD or actively reported */ int retryCount = 0; try {for (;;) { LocalSocket s = null; LocalSocketAddress l; try { s = new LocalSocket(); /* Create a socket client */ l = new LocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED); s.connect(l); /* Connect to server */ } ... ... } ... ... try { InputStream is = mSocket.getInputStream(); /* Loop read socket data */ for (;;) { Parcel p; length = readRilMessage(is, buffer); /* Parsing socket data */ if (length < 0) { // End-of-stream reached break; } p = Parcel.obtain(); /* Get a Parcel */ p.unmarshall(buffer, 0, length); /* The read out is the byte array serialized before, so a deserialization operation is required. */ p.setDataPosition(0); /* After switching from buffer to Parcel, you need to manually point the pointer to the original position */ //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes"); processResponse(p); p.recycle(); /* After data processing, the memory of Parcel needs to be recycled. */ } } ... ... }
The RILReceiver thread creates a socket client, connects to the server, and then enters the processResponse message processing cycle waiting for the server, RILJ
The response received from RILD returns the initiator of RIL request. Take geticc cardstatus (message result) for example, processResponse(p)
Will return the response of drold to uiccontroller
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private static int readRilMessage(InputStream is, byte[] buffer) throws IOException { int countRead; int offset; int remaining; int messageLength; // First, read in the length of the message offset = 0; remaining = 4; do { countRead = is.read(buffer, offset, remaining); /* Read 4 bytes of message */ if (countRead < 0 ) { Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message length"); return -1; } offset += countRead; remaining -= countRead; } while (remaining > 0); messageLength = ((buffer[0] & 0xff) << 24) /* Get length */ | ((buffer[1] & 0xff) << 16) | ((buffer[2] & 0xff) << 8) | (buffer[3] & 0xff); // Then, re-use the buffer and read in the message itself offset = 0; remaining = messageLength; do { countRead = is.read(buffer, offset, remaining); /* Read remaining data */ if (countRead < 0 ) { Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message. messageLength=" + messageLength + " remaining=" + remaining); return -1; } offset += countRead; remaining -= countRead; } while (remaining > 0); return messageLength; } private void processResponse (Parcel p) { int type; type = p.readInt(); /* The first byte of data returned from RILD indicates the return type of the request: response ﹣ unsolicited / response ﹣ unsolicited */ if (type == RESPONSE_UNSOLICITED) { processUnsolicited (p); /* Voluntary reporting */ } else if (type == RESPONSE_SOLICITED) { RILRequest rr = processSolicited (p); /* Synchronous submission corresponding to common request */ if (rr != null) { rr.release(); /* Release the corresponding RILRequest memory and wakelock */ decrementWakeLock(); } } }
Generally, there are two kinds of response of RILD: one is the RILJ general request, the other is the response of RILD to the RILJ request (response socialized), and the other is reported by RILD actively.
Response (response unsolicited) and process response (parcel P) respectively handle the response of the two situations.
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
private RILRequest processSolicited (Parcel p) { int serial, error; boolean found = false; serial = p.readInt(); /* Serial number, that is, token */ error = p.readInt(); /* Error code */ RILRequest rr; rr = findAndRemoveRequestFromList(serial); /* Take out the corresponding RILRequest according to the token */ ... ... */ case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break; ... ... if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, ret, null); /* Save rr.mResult to AsyncResult.userObj, and convert rr.mResult.obj to AsyncResult */ rr.mResult.sendToTarget(); /* msg Send to corresponding target(Handler) */ } ... ... } private Object responseIccCardStatus(Parcel p) { IccCardApplicationStatus appStatus; ... ... appStatus = new IccCardApplicationStatus(); ... ... return cardStatus; }
Return to send(rr). send(rr) is not directly sent to the socket server RILD, but sent to the RILSender thread through a Message.
In handleMessage, send the request to the socket server RILD.
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
class RILSender extends Handler implements Runnable { /* Inherit Handler and implement Runnable */ public RILSender(Looper looper) { super(looper); } ... ... //***** Handler implementation @Override public void handleMessage(Message msg) { /* Inherit handleMessage of Handler */ RILRequest rr = (RILRequest)(msg.obj); /* Maessage RILRequest object carried in */ RILRequest req = null; switch (msg.what) { case EVENT_SEND: /* Send RIL request event */ try { LocalSocket s; s = mSocket; /* RILReceiver socket for communication with RILD created in */ ... ... synchronized (mRequestList) { /* Multithread protection operation mRequestList */ mRequestList.append(rr.mSerial, rr); /* Save the received RILRequest and the corresponding serial number to the mRequestList data. */ } byte[] data; data = rr.mParcel.marshall(); /* Convert data in Parcel to byte data */ rr.mParcel.recycle(); /* Parcel Memory recycling for */ rr.mParcel = null; ... ... // parcel length in big endian dataLength[0] = dataLength[1] = 0; /* RIL The size of the request package is 4 bytes */ dataLength[2] = (byte)((data.length >> 8) & 0xff); dataLength[3] = (byte)((data.length) & 0xff); //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes"); s.getOutputStream().write(dataLength); /* Send out the packet size and packet data */ s.getOutputStream().write(data); } catch (IOException ex) { Rlog.e(RILJ_LOG_TAG, "IOException", ex); req = findAndRemoveRequestFromList(rr.mSerial); /* If an exception occurs, remove the RILRequest corresponding to the serial number from the mRequestList */ // make sure this request has not already been handled, // eg, if RILReceiver cleared the list. if (req != null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); decrementWakeLock(); } } ... ... } } }
(B) RILD process
As an independent process, RILD is the communication channel between telephone and modem. Abstract some interfaces to adapt to different modem manufacturers, and do not need to care about specific
Hardware operation, or in which form is stored in the system (modem is integrated into CPU as CP or CPU is connected through serial port / USB, such as BC72 LTE module). Because of these interfaces
Manufacturers will implement specific hardware operation details. These interfaces are in libreference RIL, and BC72 LTE module is used in Android, as long as they are ported.
Libreference RIL is fine.
1. Start of RILD
RILD has init process to start directly. After starting, it will listen to RILJ client and wait for RILJ connection request.
device/samsung/smdk4x12/conf/init.smdk4x12.rc
service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root
hardware/ril/rild/rild.c is the RILD process entry:
hardware/ril/rild/rild.c
int main(int argc, char **argv) { ... ... dlHandle = dlopen(rilLibPath, RTLD_NOW); /* Open / system/lib/libreference-ril.so */ if (dlHandle == NULL) { RLOGE("dlopen failed: %s", dlerror()); exit(-1); } RIL_startEventLoop(); /* Create event loop thread and listen for multiple IO events in ril_event_loop(), such as active wake-up event (pipe), RILJ request, etc. */ /* Get RIL init function pointer in / system/lib/libreference-ril.so */ rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init"); if (rilInit == NULL) { RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath); exit(-1); } if (hasLibArgs) { rilArgv = argv + i - 1; argc = argc -i + 1; } else { static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv; property_get(LIB_ARGS_PROPERTY, args, ""); argc = make_argv(args, rilArgv); } // Make sure there's a reasonable argv[0] rilArgv[0] = argv[0]; funcs = rilInit(&s_rilEnv, argc, rilArgv); /* Initialize Vender RIL */ RIL_register(funcs); /* Registered RIL */ ... ... }
hardware/ril/libril/ril.cpp
extern "C" void RIL_startEventLoop(void) { ... ... ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL); /* Create eventLoop thread */ ... ... } static void * eventLoop(void *param) { int ret; int filedes[2]; ril_event_init(); /* Initializing event list, timer list, pending list, watch table */ pthread_mutex_lock(&s_startupMutex); s_started = 1; pthread_cond_broadcast(&s_startupCond); pthread_mutex_unlock(&s_startupMutex); ret = pipe(filedes); /* Create a pipe to wake up the selet() to return each time a new event is added, update the fd_set to make select listen to the new event */ if (ret < 0) { RLOGE("Error in pipe() errno:%d", errno); return NULL; } s_fdWakeupRead = filedes[0]; /* filedes[0]For reading pipe, files [1] for writing pipe */ s_fdWakeupWrite = filedes[1]; fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); /* Read pipe in a non blocking way */ ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, /* Read the pipe descriptor and bind it to the s'wakeupfd'event event, and specify the callback processWakeupCallback. */ processWakeupCallback, NULL); rilEventAddWakeup (&s_wakeupfd_event); /* Add the s ﹣ wakeupfd ﹣ event event to the watch ﹣ table, update the readFds set, make select listen to the event, and trigger the event */ // Only returns on error ril_event_loop(); /* Enter multiple IO event monitoring cycle */ RLOGE ("error in event_loop_base errno:%d", errno); // kill self to restart on error kill(0, SIGKILL); return NULL; }
The main function starts the event loop thread and listens for multiple IO events in RIL event loop(), such as the active wake-up event (pipe), RILJ request, etc.
Register vendor RIL interface (libreference RIL)
Note that the main function of pipe here is to wake up select return, because every time an event is added dynamically, the readFds set must be updated to facilitate select listening.
New IO in collection.
select return will be triggered when rilEventAddWakeup() adds a new event
hardware/ril/libril/ril.cpp
static void rilEventAddWakeup(struct ril_event *ev) { ril_event_add(ev); /* Add event */ triggerEvLoop(); /* Trigger event. Every time an event is added, wake up select by writing a pipe to update the multi-channel IO set, enabling listening to the event */ }
hardware/ril/libril/ril_event.cpp
void ril_event_add(struct ril_event * ev) { dlog("~~~~ +ril_event_add ~~~~"); MUTEX_ACQUIRE(); for (int i = 0; i < MAX_FD_EVENTS; i++) { if (watch_table[i] == NULL) { watch_table[i] = ev; /* Add a new event to the watch table */ ev->index = i; dlog("~~~~ added at %d ~~~~", i); dump_event(ev); FD_SET(ev->fd, &readFds); /* Update readFds set */ if (ev->fd >= nfds) nfds = ev->fd+1; /* Update nfds */ dlog("~~~~ nfds = %d ~~~~", nfds); break; } } MUTEX_RELEASE(); dlog("~~~~ -ril_event_add ~~~~"); }
hardware/ril/libril/ril.cpp
static void triggerEvLoop() { int ret; if (!pthread_equal(pthread_self(), s_tid_dispatch)) { /* trigger event loop to wakeup. No reason to do this, * if we're in the event loop thread */ do { ret = write (s_fdWakeupWrite, " ", 1); /* Write a '' to pipe to wake up select */ } while (ret < 0 && errno == EINTR); } }
After RIL event loop() receives the event or the request from RILJ, firePending() calls the corresponding processing function according to the event request.
hardware/ril/libril/ril_event.cpp
void ril_event_loop() { int n; fd_set rfds; struct timeval tv; struct timeval * ptv; for (;;) { // make local copy of read fd_set memcpy(&rfds, &readFds, sizeof(fd_set)); if (-1 == calcNextTimeout(&tv)) { /* Calculate the timeout corresponding to each event in the timer list */ // no pending timers; block indefinitely dlog("~~~~ no timers; blocking indefinitely ~~~~"); ptv = NULL; } else { dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec); ptv = &tv; } printReadies(&rfds); n = select(nfds, &rfds, NULL, NULL, ptv); /* Waiting for events in readFds set to wake up */ printReadies(&rfds); dlog("~~~~ %d events fired ~~~~", n); if (n < 0) { if (errno == EINTR) continue; RLOGE("ril_event: select error (%d)", errno); // bail? return; } // Check for timeouts processTimeouts(); /* Check whether there are events in the timer list list that have timed out */ // Check for read-ready processReadReadies(&rfds, n); /* Take the monitored events from the watch table and add them to the pending list */ // Fire away firePending(); /* Take the event out of the pending list, and execute the callback for the event */ } } static void processTimeouts() { dlog("~~~~ +processTimeouts ~~~~"); MUTEX_ACQUIRE(); struct timeval now; struct ril_event * tev = timer_list.next; struct ril_event * next; getNow(&now); // walk list, see if now >= ev->timeout for any events /* Check whether there is an event in the timer list list that has timed out */ dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec); while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) { // Timer expired dlog("~~~~ firing timer ~~~~"); next = tev->next; removeFromList(tev); /* Remove the timeout from the list */ addToList(tev, &pending_list); /* And add the timeout to the pending list */ tev = next; /* Pointer to next timeout */ } MUTEX_RELEASE(); dlog("~~~~ -processTimeouts ~~~~"); } static void processReadReadies(fd_set * rfds, int n) { dlog("~~~~ +processReadReadies (%d) ~~~~", n); MUTEX_ACQUIRE(); for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) { struct ril_event * rev = watch_table[i]; if (rev != NULL && FD_ISSET(rev->fd, rfds)) { /* Remove the monitored event from the watch table */ addToList(rev, &pending_list); /* Add the event to the pending list */ if (rev->persist == false) { /* If the event does not need to be processed, remove the removeWatch */ removeWatch(rev, i); } n--; } } MUTEX_RELEASE(); dlog("~~~~ -processReadReadies (%d) ~~~~", n); } static void firePending() { dlog("~~~~ +firePending ~~~~"); struct ril_event * ev = pending_list.next; while (ev != &pending_list) { /* Take events from the pending list */ struct ril_event * next = ev->next; removeFromList(ev); ev->func(ev->fd, 0, ev->param); /* And execute the callback of the event */ ev = next; } dlog("~~~~ -firePending ~~~~"); }
Analyze the event flow of RIL Φ starteventloop(). A simple summary is to call the event handler according to the event.
So far, we haven't talked about how to create the socket server. Go back to Mia (), FuncS = rilinit (& s'rilenv, argc, rilargv);
It initializes libreference RIL and RIL register (FuncS), registers the relevant interfaces that the manufacturer must implement, and creates a socket server.
And listen to the client connection, once connected, start waiting to read the request sent by the client.
hardware/ril/libril/ril.cpp
extern "C" void RIL_register (const RIL_RadioFunctions *callbacks) { ... ... s_fdListen = android_get_control_socket(SOCKET_NAME_RIL); /* Create socket server for communication with RILJ */ if (s_fdListen < 0) { RLOGE("Failed to get socket '" SOCKET_NAME_RIL "'"); exit(-1); } ret = listen(s_fdListen, 4); /* Monitoring RILJ */ if (ret < 0) { RLOGE("Failed to listen on control socket '%d': %s", s_fdListen, strerror(errno)); exit(-1); } /* note: non-persistent so we can accept only one connection at a time */ ril_event_set (&s_listen_event, s_fdListen, false, /* Set a listening event, once the connection with RILJ is established */ listenCallback, NULL); /* Then enter listenCallback and wait for reading the data sent by RILJ */ rilEventAddWakeup (&s_listen_event); /* Add s listen event to watch table, wake up select */ ... ... } static void listenCallback (int fd, short flags, void *param) { ... ... s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen); /* Accept connections from RILJ clients */ ... ... err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK); /* Non blocking read write socket */ if (ret < 0) { RLOGE ("Error setting O_NONBLOCK errno:%d", errno); } RLOGI("libril: new connection"); p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES); /* Create a stream to cache read socket data */ ril_event_set (&s_commands_event, s_fdCommand, 1, /* Set s ﹣ commands ﹣ event, and process commands callback loop reads socket data */ processCommandsCallback, p_rs); rilEventAddWakeup (&s_commands_event); /* Add s ﹣ commands ﹣ event event to wake up select */ onNewCommandConnect(); /* Notify RILJ that a connection has been established */ } static void processCommandsCallback(int fd, short flags, void *param) { ... ... for (;;) { /* loop until EAGAIN/EINTR, end of stream, or other error */ ret = record_stream_get_next(p_rs, &p_record, &recordlen); /* Loop to read socket data from data stream */ if (ret == 0 && p_record == NULL) { /* end-of-stream */ break; } else if (ret < 0) { break; } else if (ret == 0) { /* && p_record != NULL */ processCommandBuffer(p_record, recordlen); /* Package the received data and send it to vender ril, i.e. libreference-ril.so */ } } }
Processcommandbuffer (P \ record, recordlen) packs the received data and distributes it to vender ril, i.e. libreference-ril.so.
Then it is out of the control of RILD. libreference-ril.so is mainly the implementation of RILD control model interface by manufacturers.
hardware/ril/libril/ril.cpp
static int processCommandBuffer(void *buffer, size_t buflen) { ... ... p.setData((uint8_t *) buffer, buflen); /* Fill in the received data to parcel */ // status checked at end status = p.readInt32(&request); /* Parse request */ status = p.readInt32 (&token); /* Analysis of serial in token and RILJ */ ... ... pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); /* Assign a RequestInfo to send the request to vendor ril */ pRI->token = token; /* Set token */ pRI->pCI = &(s_commands[request]); /* Setup request */ ret = pthread_mutex_lock(&s_pendingRequestsMutex); assert (ret == 0); pRI->p_next = s_pendingRequests; /* Add to the "s pendingrequests" request list */ s_pendingRequests = pRI; ret = pthread_mutex_unlock(&s_pendingRequestsMutex); assert (ret == 0); /* sLastDispatchedToken = token; */ pRI->pCI->dispatchFunction(p, pRI); /* Execute the event callback, and start to enter vender ril here. */ return 0; }
We still take getting the SIM card status as an example. Pri - > PCI - > dispatchfunction (P, PRI) calls dispatchVoid()
hardware/ril/libril/ril.cpp
static void dispatchVoid (Parcel& p, RequestInfo *pRI) { clearPrintBuf; printRequest(pRI->token, pRI->pCI->requestNumber); s_callbacks.onRequest(pRI->pCI->requestNumber, NULL, 0, pRI); }
The onrequest function in libreference-ril.c is called.
The above analysis shows the processing flow of requests sent by RILD to RILJ. Next, it analyzes the flow of response returned by RILD to RILJ. There are two situations, one is the response to the request.
The other is to report actively.
After processing the request, call RIL [onrequestcomplete] to reply to the processing result of the request.
hardware/ril/libril/ril.cpp
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) { ... ... p.writeInt32 (RESPONSE_SOLICITED); p.writeInt32 (pRI->token); errorOffset = p.dataPosition(); p.writeInt32 (e); if (response != NULL) { // there is a response payload, no matter success or not. ret = pRI->pCI->responseFunction(p, response, responselen); ... ... } ... ... sendResponse(p); ... ... } static int sendResponse (Parcel &p) { printResponse; return sendResponseRaw(p.data(), p.dataSize()); } static int sendResponseRaw (const void *data, size_t dataSize) { ... ... ret = blockingWrite(fd, (void *)&header, sizeof(header)); /* Write 4 bytes data length first */ if (ret < 0) { pthread_mutex_unlock(&s_writeMutex); return ret; } ret = blockingWrite(fd, data, dataSize); /* Write data again */ ... ... }
Finally, through sendResponseRaw(), reply to RILJ directly by writing socket. The processing of active escalation is similar, and also through sendResponseRaw()
Report to RILJ. You can refer to the RIL ﹣ unsolicitedresponse() function.
So far, the communication process between RILJ and RILD has been analyzed, and libreference RIL will be analyzed later. In libreference RIL, the implementation of the first pass interface is different from each modem manufacturer.
BC72 is controlled by sending AT through serial port / USB to realize the functions of call, SMS, Internet, etc.
Thank you!