The android source code directory hardware/RIL/libril contains a total of five C/CPP files: ril_commands.h, ril_unsol_commands.h, ril_event.h, ril_event.cpp, and rIL. cpp. This article mainly analyzes the code of ril_event.
Ril_event mainly processes the ports and modem events involved in the telephone module, organizes multiple events in chronological order, and stores them in the event team, three queues are used: watch_table [], timer_list, and pending_list. The code is implemented in C language. First, let's look at the header file ril_event.h:
// The maximum number of file descriptor handles monitored each time, you can modify <br/> # define max_fd_events 8 </P> <p> // callback function of ril_event <br/> typedef void (* ril_event_cb) (int fd, short events, void * userdata ); </P> <p> struct ril_event {<br/> // used to concatenate ril_event into forward and backward pointers of two-way linked lists <br/> struct ril_event * next; <br/> struct ril_event * Prev; </P> <p> // file descriptor handle related to RIL events (such as files, pipelines, and sockets) <br/> int FD; </P> <p> // index of the event in the monitoring list <br/> int index; </P> <p> // after an event is processed (that is, the event is moved from watch_table to pending_list to be moderately awaiting processing ), <br/> // The persist parameter determines whether the event persists in the monitoring list watch_table [] <br/> bool persist; </P> <p> // event timeout <br/> struct timeval timeout; </P> <p> // callback function and its input parameters <br/> ril_event_cb func; <br/> void * Param; <br/> }; </P> <p> // The following are RIL event-related operation functions <br/> // initialize the internal data structure <br/> void ril_event_init (); </P> <p> // initialize a RIL event <br/> void ril_event_set (struct ril_event * eV, int FD, bool persist, ril_event_cb func, void * PARAM ); </P> <p> // Add the event to the monitoring list watch_table [] <br/> void ril_event_add (struct ril_event * eV ); </P> <p> // Add a timer event to the timer_list linked list <br/> void ril_timer_add (struct ril_event * eV, struct timeval * TV ); </P> <p> // remove the specified event from the monitoring list watch_table [] <br/> void ril_event_del (struct ril_event * eV ); </P> <p> // event loop <br/> void ril_event_loop ();
Next, analyze the ril_event. cpp file:
# Define log_tag "rilc" </P> <p> # include <stdlib. h> <br/> # include <unistd. h> <br/> # include <errno. h> <br/> # include <fcntl. h> <br/> # include <utils/log. h> <br/> # include <ril_event.h> <br/> # include <string. h> <br/> # include <sys/time. h> <br/> # include <time. h> </P> <p> # include <pthread. h> </P> <p> // use mutex for thread synchronization. For more information, see <br/> static pthread_mutex_t listmutex in Linux program design; <br/> # define mutex_acquire () pthread _ Mutex_lock (& listmutex) <br/> # define mutex_release () pthread_mutex_unlock (& listmutex) <br/> # define mutex_init () pthread_mutex_init (& listmutex, null) <br/> # define mutex_destroy () pthread_mutex_destroy (& listmutex) </P> <p> // Add two values of the timeval type <br/> # ifndef timeradd <br/> # define timeradd (TVP, UVP, vvp) \ <br/> do {\ <br/> (vvp)-> TV _sec = (TVP)-> TV _sec + (UVP)-> TV _sec; \ <br/> (vvp)-> TV _usec = (TVP)-> TV _usec + (UVP)-> TV _usec; \ <br/> If (vvp)-> TV _usec >=1000000) {\< br/> (vvp)-> TV _sec ++; \ <br/> (vvp)-> TV _usec-= 1000000; \ <br/>}\< br/>} while (0) <br/> # endif </P> <p> // compare two values of the timeval type <br/> # ifndef timercmp <br/> # define timercmp (, b, OP) \ <br/> (a)-> TV _sec = (B)-> TV _sec \ <br/>? (A)-> TV _usec OP (B)-> TV _usec \ <br/>: (a)-> TV _sec OP (B)-> TV _sec) <br/> # endif </P> <p> // two values of the timeval type subtract <br/> # ifndef timersub <br/> # define timersub (A, B, res) \ <br/> do {\< br/> (RES)-> TV _sec = (a)-> TV _sec-(B)-> TV _sec; \ <br/> (RES)-> TV _usec = (a)-> TV _usec-(B)-> TV _usec; \ <br/> If (RES) -> TV _usec <0) {\< br/> (RES)-> TV _usec + = 1000000 ;\< br/> (RES)-> TV _sec-= 1; \ <br/>}\ <br/>} while (0); <br/> # endi </P> <p> // save all device file handles in rild, easy to use the select function to monitor events <br/> static fd_set readfds; <br/> // record the maximum FD value in readfds + 1 <br/> static int NFDs = 0; </P> <p> // to centrally manage RIL events, Android provides the following three Queues: <br/> // monitoring event list, all events to be detected must be saved to the list. <br/> static struct ril_event * watch_table [max_fd_events]; </P> <p> // timer event queue, after the event times out, it is moved into the pending_list queue <br/> static struct ril_event timer_list; </P> <p> // the queue of the event to be processed, that is, the event has been triggered, callback Function of the event to be called later <br/> S Tatic struct ril_event pending_list; </P> <p> # define debug 0 </P> <p> # If debug <br/> # define dlog (X ...) logd (x) <br/> static void dump_event (struct ril_event * eV) <br/>{< br/> dlog ("~~~~ Event % x ~~~~ ", (Unsigned INT) eV); <br/> dlog (" next = % x ", (unsigned INT) ev-> next ); <br/> dlog ("Prev = % x", (unsigned INT) ev-> PREV); <br/> dlog ("FD = % d ", ev-> FD); <br/> dlog ("pers = % d", ev-> persist ); <br/> dlog ("timeout = % DS + % DUS", (INT) ev-> timeout. TV _sec, (INT) ev-> timeout. TV _usec); <br/> dlog ("func = % x", (unsigned INT) ev-> func); <br/> dlog ("Param = % x ", (unsigned INT) ev-> param); <br/> dlog ("~~~~~~ ~~~~~~~~~~~~ "); <Br/>}< br/> # else <br/> # define dlog (X ...) do {} while (0) <br/> # define dump_event (x) do {} while (0) <br/> # endif </P> <p> // obtain the timeval value at the moment <br/> static void getnow (struct timeval * TV) <br/>{< br/> # ifdef have_posix_clocks <br/> struct timespec ts; <br/> clock_gettime (clock_monotonic, & TS ); <br/> TV-> TV _sec = ts. TV _sec; <br/> TV-> TV _usec = ts. TV _nsec/1000; <br/> # else <br/> gettimeofday (TV, null); <br/> # Endif <br/>}</P> <p> // initialize the specified ril_event linked list <br/> static void init_list (struct ril_event * List) <br/>{< br/> memset (list, 0, sizeof (struct ril_event); <br/> list-> next = List; <br/> list-> Prev = List; <br/> list-> FD =-1; <br/>}</P> <p> // Add an ril_event event to the ril_event queue header <br/> static void addtolist (struct ril_event * eV, struct ril_event * List) <br/>{< br/> ev-> next = List; <br/> ev-> Prev = List-> Prev; <B R/> ev-> Prev-> next = EV; <br/> list-> Prev = EV; <br/> dump_event (EV ); <br/>}</P> <p> // remove the specified ril_event from the ril_event queue <br/> static void removefromlist (struct ril_event * eV) <br/> {<br/> dlog ("~~~~ Removing event ~~~~ "); <Br/> dump_event (EV); </P> <p> ev-> next-> Prev = ev-> Prev; <br/> ev-> Prev-> next = ev-> next; <br/> ev-> next = NULL; <br/> ev-> Prev = NULL; <br/>}</P> <p> // remove the event of the specified index from watch_table [] <br/> static void removewatch (struct ril_event * eV, int index) <br/> {<br/> // The Event corresponding to the index is set to null, at the same time, the index of the event eV is set to an invalid value-1 <br/> watch_table [Index] = NULL; <br/> ev-> Index =-1; </P> <p> // clear the file descriptor handle corresponding to the event from readfds <br/> fd_clr (ev-> FD, & Readfds); </P> <p> If (ev-> FD + 1 = NFDs) {<br/> int n = 0; </P> <p> for (INT I = 0; I <max_fd_events; I ++) {<br/> struct ril_event * REV = watch_table [I]; </P> <p> If (Rev! = NULL) & (REV-> FD> N) {<br/> N = rev-> FD; <br/>}< br/> NFDs = n + 1; <br/> dlog ("~~~~ NFDs = % d ~~~~ ", NFDs); <br/>}</P> <p> // traverses events in the timer_list queue, when the event expires, <br/> // remove the event and add it to the pending_list queue <br/> static void processtimeouts () <br/> {<br/> dlog ("~~~~ + Processtimeouts ~~~~ "); <Br/> mutex_acquire (); <br/> struct timeval now; <br/> struct ril_event * TEV = timer_list.next; <br/> struct ril_event * next; </P> <p> getnow (& now); <br/> // walk list, see if now> = ev-> timeout for any events </P> <p> dlog ("~~~~ Looking for timers <= % DS + % DUS ~~~~ ", (INT) now. TV _sec, (INT) now. TV _usec); <br/> while (TEV! = & Timer_list) & (timercmp (& now, & TEV-> timeout,> ))) {<br/> // timer expired <br/> dlog ("~~~~ Firing timer ~~~~ "); <Br/> next = TEV-> next; <br/> removefromlist (TEV); <br/> addtolist (TEV, & pending_list ); <br/> TEV = next; <br/>}< br/> mutex_release (); <br/> dlog ("~~~~ -Processtimeouts ~~~~ "); <Br/>}</P> <p> // traverses events in the monitoring list watch_table, and add the event with readable data to the pending_list linked list, at the same time, if the persist of the event is not true <br/> //, the event will be removed from watch_table [] <br/> static void processreadies (fd_set * rfds, int N) <br/> {<br/> dlog ("~~~~ + Processreadreadies (% d )~~~~ ", N); <br/> mutex_acquire (); </P> <p> for (INT I = 0; (I <max_fd_events) & (n> 0 ); I ++) {<br/> struct ril_event * REV = watch_table [I]; <br/> If (Rev! = NULL & fd_isset (REV-> FD, rfds) {<br/> addtolist (Rev, & pending_list ); <br/> If (REV-> persist = false) {<br/> removewatch (Rev, I); <br/>}< br/> n --; <br/>}</P> <p> mutex_release (); <br/> dlog ("~~~~ -Processreadreadies (% d )~~~~ ", N); <br/>}</P> <p> // call the callback function of the events in the pending_list queue in sequence <br/> static void firepending () <br/> {<br/> dlog ("~~~~ + Firepending ~~~~ "); <Br/> struct ril_event * EV = pending_list.next; <br/> while (Ev! = & Pending_list) {<br/> struct ril_event * Next = ev-> next; <br/> removefromlist (EV ); <br/> ev-> func (ev-> FD, 0, ev-> param); <br/> EV = next; <br/>}< br/> dlog ("~~~~ -Firepending ~~~~ "); <Br/>}</P> <p> // calculate the new timeout value of the next event in the timer_list linked list <br/> static int calcnexttimeout (struct timeval * TV) <br/>{< br/> struct ril_event * TEV = timer_list.next; <br/> struct timeval now; </P> <p> getnow (& now ); </P> <p> // sorted list, so calc based on first node <br/> If (TEV = & timer_list) {<br/> // no pending timers <br/> return-1; <br/>}</P> <p> dlog ("~~~~ Now = % DS + % DUS ~~~~ ", (INT) now. TV _sec, (INT) now. TV _usec); <br/> dlog ("~~~~ Next = % DS + % DUS ~~~~ ", <Br/> (INT) TEV-> timeout. TV _sec, (INT) TEV-> timeout. TV _usec); <br/> If (timercmp (& TEV-> timeout, & now,>) {<br/> timersub (& TEV-> timeout, & now, TV); <br/>}else {<br/> // timer already expired. <br/> TV-> TV _sec = TV-> TV _usec = 0; <br/>}< br/> return 0; <br/>}</P> <p> // initialize the internal data structure (mutex, FD set, and three event queues) <br/> void ril_event_init () <br/>{< br/> mutex_init (); </P> <p> fd_zero (& readfds); <br/> init_list (& time R_list); <br/> init_list (& pending_list); <br/> memset (watch_table, 0, sizeof (watch_table )); <br/>}</P> <p> // initialize a RIL event <br/> void ril_event_set (struct ril_event * eV, int FD, bool persist, ril_event_cb func, void * PARAM) <br/>{< br/> dlog ("~~~~ Ril_event_set % x ~~~~ ", (Unsigned INT) eV); <br/> memset (EV, 0, sizeof (struct ril_event); <br/> ev-> FD = FD; <br/> ev-> Index =-1; <br/> ev-> persist = persist; <br/> ev-> func = func; <br/> ev-> param = Param; </P> <p> // file lock function of Linux, non-blocking file lock on file descriptor FD <br/> fcntl (FD, f_setfl, o_nonblock ); <br/>}</P> <p> // Add the event to the monitoring list watch_table [] <br/> void ril_event_add (struct ril_event * eV) <br/> {<br/> dlog ("~~~~ + Ril_event_add ~~~~ "); <Br/> mutex_acquire (); <br/> for (INT I = 0; I <max_fd_events; I ++) {<br/> If (watch_table [I] = NULL) {<br/> watch_table [I] = EV; <br/> ev-> Index = I; <br/> dlog ("~~~~ Added at % d ~~~~ ", I); <br/> dump_event (EV); <br/> fd_set (ev-> FD, & readfds ); <br/> If (ev-> FD> = NFDs) NFDs = ev-> FD + 1; <br/> dlog ("~~~~ NFDs = % d ~~~~ ", NFDs); <br/> break; <br/>}< br/> mutex_release (); <br/> dlog ("~~~~ -Ril_event_add ~~~~ "); <Br/>}</P> <p> // Add a timer event to the timer_list linked list <br/> void ril_timer_add (struct ril_event * eV, struct timeval * TV) <br/>{< br/> dlog ("~~~~ + Ril_timer_add ~~~~ "); <Br/> mutex_acquire (); </P> <p> struct ril_event * List; <br/> If (TV! = NULL) {<br/> // Add to timer list <br/> List = timer_list.next; <br/> ev-> FD =-1; // make sure FD is invalid </P> <p> struct timeval now; <br/> getnow (& now); <br/> timeradd (& now, TV, & ev-> timeout); </P> <p> // sort the values in the linked list from small to large. <br/> while (timercmp (& list-> timeout, & ev-> timeout, <) <br/> & (list! = & Timer_list) {<br/> List = List-> next; <br/>}< br/> // After the loop ends, list points to the first event with a timeout value greater than eV in the linked list. <br/> // Add the newly added event eV to the beginning of the event pointed to in the list. <br/> addtolist (EV, list); <br/>}</P> <p> mutex_release (); <br/> dlog ("~~~~ -Ril_timer_add ~~~~ "); <Br/>}</P> <p> // remove the event from watch_table [] <br/> void ril_event_del (struct ril_event * eV) <br/> {<br/> dlog ("~~~~ + Ril_event_del ~~~~ "); <Br/> mutex_acquire (); </P> <p> If (ev-> index <0 | ev-> index> = max_fd_events) {<br/> mutex_release (); <br/> return; <br/>}</P> <p> removewatch (EV, ev-> index ); </P> <p> mutex_release (); <br/> dlog ("~~~~ -Ril_event_del ~~~~ "); <Br/>}</P> <p> # If debug <br/> // print the events available in the monitoring list <br/> static void printreadies (fd_set * rfds) <br/> {<br/> for (INT I = 0; (I <max_fd_events); I ++) {<br/> struct ril_event * REV = watch_table [I]; <br/> If (Rev! = NULL & fd_isset (REV-> FD, rfds) {<br/> dlog ("Don: FD = % d is ready", rev-> FD ); <br/>}< br/> # else <br/> # define printreadies (rfds) Do {} while (0) <br/> # endif </P> <p> void ril_event_loop () <br/>{< br/> int N; <br/> fd_set rfds; <br/> struct timeval TV; <br/> struct timeval * PTV; </P> <p> (;;) {<br/> // make local copy of read fd_set <br/> memcpy (& rfds, & readfds, sizeof (fd_set); <br />/// Calculate the wait time of the select function based on timer_list <br/> // The Event timeout time has been sorted before timer_list <br/> If (-1 = calcnexttimeout (& TV )) {<br/> // no pending timers; block indefinitely <br/> dlog ("~~~~ No timers; blocking indefinitely ~~~~ "); <Br/> PTV = NULL; <br/>}else {<br/> dlog ("~~~~ Blocking for % DS + % DUS ~~~~ ", (INT) TV. TV _sec, (INT) TV. TV _usec); <br/> PTV = & TV; <br/>}< br/> printreadies (& rfds ); <br/> // use the select function to achieve multiplexing <br/> N = select (NFDs, & rfds, null, null, PTV ); <br/> printreadies (& rfds); <br/> dlog ("~~~~ % D events fired ~~~~ ", N); <br/> If (n <0) {<br/> If (errno = eintr) continue; </P> <p> LogE ("ril_event: Select error (% d)", errno); <br/> // bail? <Br/> return; <br/>}</P> <p> // check for timeouts <br/> processtimeouts (); <br/> // check for read-ready <br/> processreadies (& rfds, n); <br/> // fire away <br/> firepending (); <br/>}< br/>}