[sample translations]kim dae woo, android hardware service eng

Page 1

Sample Translations

Dae-woo Kim Android Hardware Service E ng l i s h

Book Information

Android Hardware Service (안드로이드 하드웨어 서비스) Development is a happy world Publishing corp. / 2013 / 49 p. For further information, please visit: http://library.klti.or.kr/node/772

This sample translation was produced with support from LTI Korea. Please contact the LTI Korea Library for further information. library@klti.or.kr


Android Hardware Service Written by Kim Dae-woo, Park Jae-young, Moon Byung-won

02 RIL: Radio Interface Layer A RIL (Radio Interface Layer) is a type of HAL (Hardware Abstraction Layer) that separates a modem from Android and provides Android with a standard interface that controls the modem. The RIL consists primarily of a RIL daemon and Vendor RIL library supplied by the modem vendor. The RIL daemon dynamically loads a Vendor RIL and controls the modem as it receives transmission responses from the modem. This chapter discusses Android’s RIL struct, RIL daemon initialization and RIL event scheduler’s RIL event processing mechanism.

An Android phone largely consists of a modem 1 that communicates with a mobile communication network and AP (Application Processor) that operates on Android. An Android application that operates on the AP can either make a phone call through the mobile communication network or receive help from the modem to access the Internet. In order to facilitate this process, a mechanism is needed for the AP to request services from the modem and process the response. The following must be taken into account in order for the AP to control the modem in Android. First, Android needs a HAL to implement a hardware-independent platform without relying on a particular AP and the modem. Second, Android must provide a common method for the AP to control the modem regardless 1

Other terms for modem are CP (Communication Processor) and Baseband Processor. In this

book, the term modem is used.

1


of the type of network—GSM or CDMA.

2.1 Introducing RIL (Radio Interface Layer) Android’s RIL is literally a HAL that provides a radio interface for the AP to control the modem. Android’s RIL provides a HAL between the telephony framework and the modem and divides the Android platform and the modem to enable easy porting to diverse types of modem.

2.1.1 Introducing RIL The RIL abstracts the modem and minimizes the changes in the Android platform caused by the modem. Android’s RIL, regardless of the modem, provides a common interface to Android. That is, it minimizes the dependence on the Android platform and the modem. For example, in Picture 2-1, even when different modems are used to develop mobile phones—as in the case of Qualcomm or Renaissance mobile modems—Android platforms are not affected by modem vendors if each vendor provides a Vendor RIL suitable for Android’s standard RIL. Moreover, Android’s RIL provides a common radio interface to Android regardless of the network that the modem devices support such as GSM, WCDMA, LTE and CDMA.

2.12 Interaction between AP and modem Interactions between the AP and modem include: 1) the Request and Response method, in which the AP makes a modem service request and receives a response from the modem service; and 2) the Notification method, in which the modem voluntarily notifies the AP. These are sometimes referred to as a solicited command and an unsolicited command. Picture 2-2 depicts solicited commands (RIL req and RIL resp) and an unsolicited one (RIL

2


ind). 1 Solicited commands (RIL req and RIL resp) ○ When the AP makes a service request to the modem it is called a solicited command. The modem sends a response to a solicited command to the AP and this is called a solicited response. In this book, RIL req is defined as the AP making a service request to the modem; RIL resp is defined as the modem responding to the service request. 2 Unsolicited command (RIL ind) ○ An unsolicited command or unsolicited response is defined as the modem voluntarily notifying the changes in modem and network conditions to the AP without a service request from the AP. In this book, RIL ind is defined as the modem voluntarily notifying the AP. As can be seen in Picture 2-3, Android’s execution by the AP implements communication related to the telephony stack and provides services to various applications using a cellular network. However, Android only defines the interface to modem control. The protocol stack for communicating with GSM, UMTS, and LTE networks is implemented within the modem. The actual modem communicates with the cellular network and performs Call control, SMS, various SS (Supplementary Services) and Data services. The modem receives various service requests from the AP and transmits the processed result to the AP’s Android telephony stack. The implementation details related to the modem’s protocol stack vary across modem vendors. They are property of the modem vendors and therefore not disclosed. This book focuses on implementation of the Android telephony stack excluding the Vendor RIL.

2.1.3 Communication between AP and modem: RIL commands Android’s RIL provides RIL commands so that the AP can control the modem. Android’s RIL

3


commands are defined in ril.h.2 ril.h defines a modem’s request and response in detail for each RIL command.

○• Definition of RIL commands An integer is used to define each RIL command in the telephony framework and RIL. RIL commands must be defined the same way for both the telephony framework and RIL. If one wants to add a new RIL command, the telephony framework’s RIL.Contants.java and RIL’s ril.h must be added in the same way. Android’s RIL provides 108 solicited RIL commands and 36 unsolicited RIL commands in Jelly Bean Plus (version 4.2.

3

ril.h in Code 2-1 defines a RIL req (solicited) command as RIL_REQUEST_* and RIL ind (unsolicited) command as RIL_UNSOL_*. It assigns a unique ID to each RIL command.

When Android’s telephony framework makes a service request to the modem, it uses the ID above to distinguish each service. RIL commands can be classified into several detailed groups as shown in Table 2-1.

○• Relationship between RIL and AT commands If the AP wants to communicate with the modem, a communication protocol designed for modem control must be defined first. 3gpp defined AT (attention) commands to control the modem in 3gpp TS 27.004 documents in order to facilitate communication between the AP and modem. The modem that implements GSM, WCDMA and LTE protocol in 3gpp must support AT commands. 2

/hardware/ril/include/telephony/ril.h

3

The early Android Cupcake (version 1.5) had 103 solicited RIL commands and 29 unsolicited RIL

commands.

4


 Tip – What is AT command? AT command was the command system used to define the smart modem and compatible modems made by Hayes Microcomputer in the U.S. It has become the de facto standard used for almost all modem controls. The modem is designed to respond to all AT commands received. The format of the response is in ASCII alphanumeric characters and the response type may change according to the command.

From the point of view of the Android platform, AT commands are not suitable for modem control. This is because AT commands differ depending on whether the modem used is GSM or CDMA. Moreover, it is inconvenient for users, because AT commands are all expressed in abbreviations. In order to solve this problem, Android defines RIL commands for modem control. That is, Android, in order to implement modem control, does not directly use AT commands but defines RIL commands that respond to AT commands only. Then, each modem vendor provides a Vendor RIL that responds to Android RIL commands.4 Therefore, when Android defines the format for RIL commands and modem operation, request and response, there is no need to modify the Android platform to accommodate a modem change—even if the modem is changed physically or its implementation is modified—as long as relevant commands are either modified or added to the Vendor RIL. In order to understand RIL and AT commands, let us compare them to a menu in an Italian restaurant. Picture 2-4 depicts a menu of Italian food items. A customer who does not speak Italian and

4

In reality, implementation details of the modem services provided by each modem manufacturer

differ from one another. Also, in addition to RIL commands provided by RIL, additional RIL commands independently provided by each modem manufacturer are supplied.

5


wants to order “acini di pepe” may find it difficult to pronounce. For such customers, menu items map to numbers. Thus, a customer can order “acini di pepe” by asking the restaurant waiter for menu item number 8. In fact, the mapping relationship between RIL and AT commands in the Android platform is similar to what is shown in Picture 2-4. Let us look at the RIL command called RIL_REQUEST_DIAL that makes a call. Table 2-2 shows how—in terms of command language, parameter and response—the RIL command makes a call and AT commands respond to the call. The command that makes a call takes as parameters the calling number of the receiver and the result of CLIR activation, which corresponds to AT command <number> and <I> options. The response also corresponds to RIL_REQUEST_DIAL and AT command “OK”.

2.2 RIL structure Android’s RIL consists primarily of two modules: a RIL daemon and Vendor RIL. In the telephony framework that provides the telephony API to an application that requires network access, call receiving, SMS send/receive and data connection. The service request at the application level is ultimately transmitted to the modem and the modem transmits a response to the service request through the telephony framework to the application. The modem in fact implements GSM and CDMA protocol stacks and communicates with the network to exchange network requests and corresponding answers.

2.2.1 Android telephony stack How, then, does Android telephony communicate with the modem? In Android, the RIL that exists between the Android telephony framework and modem plays the bridging role. The RIL receives all the data relevant to all service requests coming from the telephony

6


framework and transmits them to the modem. Then, the RIL transmits to the telephony framework RIL ind voluntarily transmitted from the modem and RIL resp for the telephony framework’s RIL req. Picture 2-5 is a description of the structure of Android’s telephony stack. Android’s telephony stack consists of four layers—Application, Telephony Framework, RIL and Vendor RIL. Android’s telephony framework is a Java process and uses the Unix domain socket using IPC to communicate with the RIL daemon.5 The telephony framework transmits RIL commands to the RIL daemon and receives RIL responses from the RIL daemon. Table 2-3 Elements of Android telephony stack, by layer Layer/description Application A Java application accesses the modem using API provided by the telephony framework. PDK applications include mainly phone applications and MMS applications. Applications developed on SDK can access Android’s telephony framework through Telephony Manager. Telephony framework The telephony framework provides CALL, SMS, UICC and data service-related API to applications. When an application makes a service request to the telephony framework, the telephony framework transmits the RIL req defined by the RIL through a socket to the RIL daemon. The telephony framework processes the RIL resp and RIL ind transmitted from the RIL daemon and transmits the result to the application. RIL daemon The RIL is an Android defined modem HAL. The RIL daemon connects Android with modem services. The RIL daemon includes the RIL event scheduler so that it can monitor and

5

Every mention of socket hereafter refers to the Unix domain socket, even when not explicitly

specified.

7


process RIL commands and debugging requests that are sent and received. The RIL daemon uses a Vendor RIL to control the modem. Vendor RIL A Vendor RIL is software supplied by the modem vendor to control the modem. The Vendor RIL uses a vendor-dependent IPC (Inter-Process Communication) method and is responsible for actually communicating with the modem. The Vendor RIL implements a standard modem control interface supplied by Android.

○• A transmission route for RIL commands Picture 2-6 shows how RIL commands defined in ril.h are transmitted from the AP’s RIL daemon to the modem in Code 2-1. The telephony framework, in order to make a call to the modem, transmits a RIL_REQUEST_DIAL(ID=10) command that maps to telephone calling to the RIL daemon. The RIL daemon transmits the RIL_REQUEST_DIAL to the Vendor RIL. The Vendor RIL uses a vendor-dependent IPC to transmit a modem control command that corresponds to RIL_REQUEST_DIAL. The modem then processes the modem control command that maps to RIL_REQUEST_DIAL and transmits the result to the Vendor RIL through

the

IPC.

The

Vendor

RIL

then,

again,

transmits

the

response

to

RIL_REQUEST_DIAL to the RIL daemon, which in turn transmits the response to RIL_REQUEST_DIAL received from the Vendor RIL back to the telephony framework. The Vendor RIL, after receiving signal strength from the modem, transmits to the RIL daemon the RIL_UNSOL_SIGNAL_STRENGTH (ID=1008) command that maps to the signal strength. Then, in order to inform the network of the signal strength, the RIL daemon transmits RIL_UNSOL_SIGNAL_STRENGTH to the telephony framework.

2.2.2 Android RIL ‘s modem control

8


The RIL daemon receives a RIL req from the telephony framework and controls the Vendor RIL. It assumes the ultimate responsibility for controlling the modem. In order for the RIL daemon to control the modem:  It must be able to process modem control requests from multiple processes.  It must support modems from diverse vendors and provide a common interface regardless of specific vendor implementation.  It must guarantee stable communication between the AP and modem. Android, in order to reflect such requirements, implemented a daemon program—ie, the RIL daemon6— to control the modem. The RIL daemon receives RIL events created by the telephony framework and Vendor RIL. A RIL event is a unit of work processing for the RIL daemon. When it becomes ready to be processed, it is processed by the event scheduler. The following explains RIL daemon implementation. 1. Structure of daemon The RIL daemon must receive and process service requests from applications that make such requests. The RIL daemon runs in the background and receives service requests from applications and controls the Vendor RIL to request services from the modem. Moreover, the RIL daemon receives services from the modem and transmits them to the applications that requested modem services. 2. Communication between RIL daemon and application Applications make modem service requests to the RIL daemon. Here, how do applications communicate with the RIL daemon? The RIL daemon and applications are a different process and therefore must use an IPC method appropriate for inter-process communication.

6

/system/bin/rild

9


Android provides an IPC mechanism that is similar to the binder. However, the RIL daemon adopted the Unix Domain Socket supplied by Linux as its IPC method. The Unix Domain Socket is a classical IPC method used for inter-process communication. 3. Modem control using Vendor RIL The Vendor RIL exists in the form of *.so in the common library. The RIL daemon is executed and becomes a daemon process when Android is booted. The operation that is executed first when the RIL daemon is executed is dynamically loading and initializing the Vendor RIL. The dynamically loaded Vendor RIL can control the modem to facilitate communication with the modem. Android’s RIL defines the radio interface between the RIL daemon and Vendor RIL as well as the radio response interface. 4. RIL event scheduling The RIL daemon processes modem service requests from applications in units of event and defines them as RIL events. Types of RIL events include a RIL req needed to make service requests, a time-out event that keeps the AP active when a RIL ind is transmitted and puts it to sleep after a specified time period, etc. Since the RIL daemon is processed at a different time for each RIL event, it is necessary to schedule RIL EVENTS. The RIL daemon defines a scheduling policy in order to process RIL events. 5. Simultaneous handling of service request and response The modem can simultaneously process a RIL req and RIL resp. The RIL daemon must support simultaneous processing of RIL req and RIL resp commands and create threads for independent execution. The RIL daemon and Vendor RIL must satisfy diverse requirements for AP-modem communication. Below, we discuss the RIL daemon and detailed implementation of the RIL.

10


2.3 Structure of RIL daemon The responsibility of the RIL daemon is to monitor RIL events and schedule their execution when they occur so that they can be processed at suitable times. The RIL daemon generally performs the following four operations:  First, when the telephony framework makes a RIL req to the RIL daemon, the latter changes the RIL req to a RIL event, the unit with which scheduling can be done. The RIL daemon then immediately verifies the type of RIL event and starts RIL event scheduling.  Second, the daemon estimates the time when the RIL event transmitted by the telephony framework can be processed and stores it in the RIL event queue.  Third, when it is time to process the RIL event, the RIL daemon takes the RIL event stored in RIL event queue, calls the RIL’s radio control function, and sends the relevant RIL req to the modem.  Fourth, the Vendor RIL transmits to the RIL daemon a RIL resp, a response to a RIL request from the modem, and a RIL ind, which is voluntarily transmitted through the RIL daemon’s radio response function. The RIL daemon transmits the RIL resp and RIL ind to the telephony framework received from the Vendor RIL. The following section explains the RIL’s basic structure and actions of basic elements in addition to RIL event, the unit of scheduling for a RIL req.

2.3.1 Elements of RIL daemon The RIL daemon, in order to perform the four major tasks described above, is composed of five elements (Picture 2-7). 1 RIL event scheduler ○ The RIL daemon changes the RIL req received to a RIL event, a scheduling unit. The RIL

11


event is processed by the RIL event scheduler within the RIL daemon. When a RIL event occurs, the RIL event scheduler either immediately processes the RIL event or suspends it until a time-out occurs before processing. 2 Dispatch function ○ The RIL req event calls the radio control function supplied by the Vendor RIL and is then transmitted to the Vendor RIL. The RIL req event dispatch function processes the RIL req event scheduled by the RIL event scheduler in the form of appropriate data according to a RIL req ID. Then, it calls the radio control function to transmit it to the Vendor RIL. 3 Radio response function ○ The RIL daemon provides a radio response function to the Vendor RIL during the initialization of the latter. When the Vendor RIL receives a RIL resp or RIL ind from the modem, it calls the RIL daemon’s radio response function in order to transmit the result to the RIL daemon. 4 sendResponse() function ○ RIL resp and RIL ind commands received from the Vendor RIL must be transmitted to the telephony framework. The radio response function of the RIL daemon calls the sendResponse() function of ril.cpp7 and transmits the RIL resp and RIP ind to the telephony framework through the Unix Domain Socket. 5 Wake-up event trigger ○ When a time-out event is added to the Timer_List or RIL event processing call-back function is registered in the watch_table, the RIL event scheduler cannot recognize this by itself. Wake-up event trigger a wake-up event in order to force scheduling by the RIL event scheduler.

7

Hardware/ril/libril/ril.cpp

12


2.3.2 RIL event A RIL event is the smallest unit by which the RIL event scheduler can schedule an event. All events requested of the RIL daemon from outside and the event needed by the RIL for running the RIL event scheduler are scheduled to be processed at an appropriate time. RIL events scheduled by the RIL daemon are classified into two categories—IO and time-out events. Picture 2-8 shows the types of events.

○• I/O events I/O events consist of wake-up events, listen events, RIL req events and debugging events. Additional I/O events, if necessary, can be added. Table 2-4 is a summary of I/O events in RIL events. The ril_event structure of an I/O event that describes how to process the IO event is registered in the watch_table as the RIL event scheduler begins. When an I/O event begins, it is scheduled by the RIL event scheduler and the I/O event is processed by an I/O event callback function defined in ril_event struct.

Table 2-4 Types of I/O events Event/description wake_up event: the event used to trigger the RIL event scheduler to start scheduling listen event: the event that occurs on the arrival of an initial connection from the telephony framework RIL req event: the event that occurs on the arrival of a RIL req command to the RIL daemon from the telephony framework through the socket debug event: the debugging event activated by the radiooptions program

○• Time-out event

13


Unlike I/O events, time-out events are not registered in the watch_table. Whenever a time-out event occurs, it is added to the timer_list. A time-out event is timed out after a specified period of time and the call back function of the time-out ril_event struct is called by the RIL event scheduler. This event is usually used to activate a certain action after a certain period of time. Table 2-5 is a summary of the wakelock release event among time-out events. 14 Table 2-5 Types of time-out event Event/description Wakelock release event: When the RIL daemon receives a RIL ind from the Vendor RIL, it holds partial Wake Lock so that power can be supplied to the AP while the RIL ind is being processed. For partial Wake Lock, the AP can enter the sleep state only when the RIL ind is processed and released. When the RIL ind is transmitted, a wakelock release event is added to the timer_list in order to process the partial Wake Lock. After 1 second, the wakelock release event is scheduled by the RIL event scheduler and the partial Wake Lock is released by a wakelock release event processing a call back function.

2.3.3 RIL event scheduler The RIL daemon must process RIL events created internally and externally. RIL events, due to the nature of the telephony service, occur at unpredictable times and can occur simultaneously as a result of two different trigger events. Taking these characteristics into account, Android has implemented the RIL event scheduler in the RIL daemon in order to process RIL events appropriately. The RIL event scheduler has the following properties. ď Ź RIL event processing using dedicated thread (eventLoop thread) The RIL daemon implements the RIL event scheduler so that RIL events can be processed


simultaneously. The RIL event scheduler is a process flow that is separate and independent from the RIL daemon and consists of an eventLoop thread that is dedicated to processing RIL events. The eventLoop thread receives RIL events and schedules the time at which RIL event must be processed.  RIL event scheduling 15 The RIL event scheduler must simultaneously process I/O events and wake-up events. The RIL event scheduler is a single eventLoop thread and must use I/O multiplexing in order to process several RIL events simultaneously. For this purpose, the RIL event scheduler uses a select() system function to implement I/O multiplexing.8

○• Data structure of RIL event scheduler The RIL event scheduler schedules in units of RIL events. For this purpose, it defines the ril_event struct, which is a data structure that can define I/O events and time-out events. In addition, when RIL events occur, the RIL event scheduler needs a list that stores RIL events until they are processed by the RIL event scheduler. Picture 2-9 shows the data structure of the RIL event scheduler. The data structure of the RIL event scheduler is defined in the form of ril_event struct of hardware/ril/libril/ril_event.h.

Table 2-6 Parameters in ril_event struct Parameter/description next: a next pointer for a linked-list prev: a prev pointer for a linked-list 8

Use of the select() function is explained in detail in the I/O multiplexing sections of Chapters 1

and 3.


fd: a file descriptor that receives I/O events. The RIL event scheduler uses this value to discover from which file descriptor an input/output has occurred. index: a watch_table array index stored in an I/O event persist: defined for the RIL event that must be processed permanently 1. When persist=false: used for an event that is removed from the watch_table after being processed once by the RIL event scheduler (ex: a listen event) 2. When persist=true: used for an event that must be continually processed without being removed from the watch_table (ex. a RIL req event)

If the ril_event struct was defined in order to store I/O events and time-out events, the RIL event scheduler needs a data structure that must wait until the relevant RIL events are processed after it is received and scheduled. The data structure used by the RIL event scheduler, as in Code 2-2, is declared in hardaware/ril/libril/ril_event.c

Table 2-7 is an explanation of the watch_table, timer_list and pending_list data structures for the RIL event scheduler.

Table 2-7 Data structure of RIL event scheduler Data structure of RIL event scheduler/ description watch_table: a look_up table used during I/O event processing timer_list: the list that stores time-out events that wait for a time-out pending_list: the list that stores RIL events that are pending processing by the RIL event scheduler

16


○• RIL event scheduler’s RIL event processing function The RIL event scheduler uses the RIL event handling helper function as shown in Code 2-3 in order to manage RIL events created by the RIL event scheduler.

① ril_event_init() 17 The ril_event_init() function is called when the eventLoop thread begins and initializes the watch_table, timer_list and pending_list. Code 2-4 is a description of how ril_event_init() initializes the data structure inside the RIL event scheduler. 1-1 File descriptor information to be monitored by the RIL event is initialized to zero. 1-2 init_list() function is used to initialize the timer_list and pending_list 1-3 watch_table is initialized to NULL. watch_table can store up to eight ril_events.

② ril_event_set() The ril_event_set() function initializes ril_event struct. Code 2-5 is how the ril_event_set() function initializes ril_event struct. The RIL event scheduler uses ril_event struct to define how RIL events are processed. The ril_event_set() function passes the pointer ril_event struct as the first parameter, and the rest of the parameters for the ril_event_set()—fd, persist, cb, param—are stored in the ril_event struct. Picture 2-10 shows how the RIL event scheduler uses the ril_event_set() function to initialize the wake_up ril_event struct (s_wakeupfd_event). When a wake-up event occurs, it is scheduled by the RIL event scheduler and processed by the processWakeupCallback() function—a wake-up event handling call back function—at an appropriate time.


③ ril_event_add() During the initialization of the RIL event scheduler, the ril_event struct, which handles the I/O event, must be registered in the watch_table. The watch_table() plays the role of a lookup table that tells the RIL event scheduler how to handle I/O events when they occur. That is, watch_table predefines which RIL event handling call back function is called when an I/O event is received. The RIL event scheduler uses the ril_event_add() function in order to register the ril_event struct with the I/O defined in the watch_table. Code 2-6 describes how the ril_event_add() function registers the ril_event struct in the watch_table for handling I/O events. The ril_event_add() function can register the ril_event struct in an empty slot in the watch_table for handling I/O events.

3-1 The value of MAX_FD_EVENTS is 8 which means the watch_table can store up to 8 I/O event handling ril_event structs. The Ril_event_add() function loops around number slots 0 to 7to find an empty slot. 3-2 The I/O handling ril_event struct passed as a parameter is registered in an empty slot in the watch_table. 3-3 The I/O event file descriptor passed is defined in readFds of the fd_set type so that the RIL event scheduler can monitor the relevant file descriptor. 3-4 The maximum value in I/O RIL event file descriptors registered in the watch_table plus 1 is stored in the nfds variable. The nfds variable is used in the RIL event scheduler’s select() function.

18


Picture 2-11 shows how the ril_event_add() function is used to register in the watch_table the wake up ril_event struct (s_wakeupfd_event) initialized by the ril_event_set() function shown in Picture 2-10.

④ ril_timer_add() 19 When the RIL event scheduler receives a RIL ind from the Vendor RIL, a time-out event is created and then the ril_timer_add() function is used to register it in the timer_list.

4-1 Refer to the first RIL event in the timer_list. 4-2 The value of the RIL event’s file descriptor value is -1. That is, when handling a time-out event, unlike for an I/O event, a different file descriptor is not used. 4-3 The timeradd() function adds 1 second to the current time and stores it in the ril_event struct’s time-out parameter. That is, the time-out event is scheduled 1 second after the current time and then processed. 4-4 In the timer_list, time-out events are put in ascending order. Therefore, whenever a timeout event is newly added to the timer-list, the time-out is reordered in ascending sequence.

Picture 2-12 displays how the wake up ril_event struct is added to the RIL event scheduler’s timer_list. The triggerEvLoop() function is called to inform the RIL event scheduler that the time-out event is added to the timer_list.

2.4 RIL daemon initialization

Android’s init process reads the RIL daemon section in the Service section of INIT.RC during


booting and executes the RIL daemon. When the RIL daemon is performed, the RIL is initialized and becomes a daemon process. When the RIL daemon process starts, it first dynamically loads the Vendor RIL and the Vendor RIL obtains from the RIL daemon a pointer to the radio control function that can handle the RIL resp and RIL ind commands. When this process ends, the RIL daemon finally uses the Vendor RIL to control the modem. 20 2.4.1 Broadly speaking, there are two ways to start the RIL daemon—a static method and a dynamic one. The static method uses the init process to start the RIL daemon. In the dynamic method, the RIL daemon starts in Android’s Linux shell during the run-time.

○• Static execution of RIL daemon The init process reads the the initialization script init.rc during booting and starts the RIL daemon. The initialization script init.rc is shown in Code 2-8.

When the init process is executed, it reads the service list in init.rc and executes /system/bin/rild. In addition to the RIL daemon, the init process creates a socket that will be used to communicate with the telephony framework (/dev/socket/rild) and RIL debugging socket (/dev/socket/rild-debug).9 The RIL daemon can use this to communicate with the telephony framework and radiooptions program. The RIL daemon can use a ps command as a background daemon process to verify whether the RIL daemon process is in a sleep condition. Picture 2-14 show how the ps command is used to verify which process state the RIL daemon is in.

9

How the init process creates a Unix domain socket for the daemon process is described in detail

in Chapter 1.


The system property rild.libpath designates the location of the Vendor RIL that must be dynamically loaded. The static method of executing the RIL daemon searches the path for the RIL vendor designated in the rild.libpath to dynamically load the Vendor RIL. In fact, the rild.libpath of the Nexus One can be searched using the command as adb shell getpopr, as shown in Picture 2-15. If one uses descriptor file search machine to search for the Nexus One’s Vendor RIL, one can find /system/lib/libhtc_ril.so, as shown in Picture 2-16.

○• Dynamic execution of RIL daemon The dynamic method executes the RIL daemon during run-time. The following shows how the dynamic method is used to execute the RIL daemon. The executable file for the RIL daemon is named rild and the file is located in /system/bin/rild. When the RIL daemon is dynamically loaded, it is mainly used to test the Vendor RIL. That is, when various versions of the Vendor RIL are being tested, they can enter parameters according to command type and dynamically restart the RIL daemon. Table 2-8 is a summary of options needed to execute rild in the adb shell window dynamically.

Table 2-8 Command type options for rild Option/description Designate library path for Vendor RIL (ex. –l/system/lib/libhtc_ril.so. The path for the Vendor RIL is /system/lib/libhtc_ril.so Designate parameters for Vendor RIL (ex. –d/dev/smd0, the device path (/dev/smd0) is designated behind “-d” option)

○• RIL daemon build option

21


Earlier, it was mentioned that the RIL daemon can dynamically load the Vendor RIL. Build options determine if the Vendor RIL should be dynamically loaded or the RIL daemon should be combined with the Vendor RIL statically. Code 2-9 uses an example of the reference Vendor RIL’s Android.mk to show how the Vendor RIL is built in the form of a common library. If LOCAL_CFLAGS := -DRILL_SHLIB is configured, the reference Vendor RIL is built in the form of a common library format. On the other hand, if RIL_SHLIB is not configured, the reference Vendor RIL will be built in the form of an executable file. In general, the Vendor RIL is built in the form of a common library and the method whereby the RIL daemon dynamically loads Vendor RIL when necessary is usually used.

★Tip – Stop, start and restart methods for RIL daemon One can use the setprop ctl, start, stop and restart commands to stop and start the Android daemon service. One can use them to disconnect communication with the modem temporarily and restart the RIL daemon.

2.4.2 Initialization process for RIL daemon When the RIL daemon executes, it performs RIL initialization. RIL initialization is completed through the process shown in Picture 2-17.

1 Dynamic loading of Vendor RIL ○ The Vendor RIL is needed in order for the RIL daemon to control the modem. In general, the Vendor RIL exists in the form of a common library (*.so) and the RIL daemon dynamically loads the Vendor RIL to control the modem. 2 Creating RIL event scheduler ○

22


The RIL daemon needs the RIL event scheduler in order to schedule various RIL events. The RIL daemon creates eventLoop threads in order to schedule RIL events. The eventLoop threads created this way take the role of the RIL event scheduler. 3 Obtaining a symbol for the RIL_Init() function to initialize the Vendor RIL ○

1 is in an uninitialized state. The Vendor RIL The Vendor RIL dynamically loaded in ○ provides the RIL_Init() function that can initialize itself. The RIL daemon uses a dlsym() function to obtain a function pointer to the RIL_init() function.

10

4 Initializing Vendor RIL ○ 3 , the RIL_Init() function Using the function pointer for the RIL_Init() function obtained in ○ is called to initialize the Vendor RIL. The Vendor RIL receives from the RIL daemon a pointer to the radio response function to handle the RIL resp and RIL ind commands and registers them in the Vendor RIL domain. After initialization, the Vendor RIL returns the radio control function so that RIL req and RIL ind can be returned to the RIL daemon. 5 Initializing RIL daemon ○ When the Vendor RIL initialization completes, the RIL event scheduler is initialized and the call back function of the Vendor RIL is registered in the RIL daemon domain. Also, the RIL daemon becomes a daemon process and runs in the background while handling RIL commands.

2.4.3 Dynamically loading Vendor RIL library Vendor library is needed for the RIL daemon to execute and control the modem. As the Vendor library is stored in a specific path, the RIL daemon first checks if vendor library is

10

For explanations about dynamically loading the common library, refer to Android Anatomy:

system service (The Happy Developer World, 2011)

23


exists in a specific path and, if so, dynamically loads Vendor RIL. Code 2-10 describes how the RIL daemon dynamically loads the Vendor RIL. 1 Search for RIL library path ○ The RIL daemon, in order to load the Vendor RIL library dynamically, searches the path for the Vendor RIL library. This path uses the property_get() function to read the value stored in rild.libpath from the system property. If the RIL daemon finds the value stored in rild.libpath, it stores the path for the RIL library stored in rild.libpath in the libPath array and stores the address for the libPath array in rilLibPath.

However, if the RIL daemon does not find the

path for the Vendor RIL in rild.libpath, it finishes the RIL initialization process, exits to sleep condition via a goto statement and remains inactive. 2 Dynamically loading Vendor RIL ○ Dynamic loading of the Vendor RIL is executed by the dlopen() function. RIL daemon uses the dlopn() function to load the Vendor RIL library dynamically in the path designated by rild.libpath. The value designated in rild.libpath, as shown in Picture 2-15, executes the same command to verify the path for the Vendor RIL library. If the dlopen() function succeeds in loading the Vendor RIL library, it returns the the Vendor RIL library’s handle.

2.4.4 Creating RIL event scheduler The RIL daemon creates the RIL event scheduler in order to handle RIL events efficiently. The RIL event scheduler receives I/O and time-out events as inputs, schedules execution points and calls the relevant RIL event handling call back function. Code 2-11 describes how the RIL daemon creates the RIL event scheduler. The RIL daemon calls the RIL_startEventLoop() function in ril.cpp and creates event Loop threads.

3 Calling RIL daemon’s RIL_startEventLoop() function ○

24


The actual role of the RIL event scheduler is performed by the eventLoop thread. The eventLoop thread initializes the RIL event scheduler and performs the important role of scheduling RIL events. Code 2-12 shows how the eventLoop thread is created by pthread_create() of the POSIX pthread. Once an eventLoop thread is created, it initializes the RIL event scheduler (as shown in Code 2-13) and begins RIL event scheduling.

3-1-1 Initializing RIL event scheduler data structure Once the RIL event scheduler starts, it first initializes the table, timer_list and pending_list, the data structure used for RIL event scheduling. 3-1-2 Create pipes for wake-up event If necessary, the RIL event scheduler requires an action to force-trigger RIL event scheduling. For example, when a wake-up event occurs, the RIL event scheduler must schedule wake-up events by force. The RIL event scheduler uses the select() function for I/O multiplexing. When one of the file descriptors (fd_set) enters the I/O ready condition, the select() function returns and performs I/O operations for the file descriptor that has entered the I/O ready state. Therefore, in order for the RIL event scheduler to begin the scheduling operation, it is important to designate a file descriptor that can force-trigger the RIL event scheduler on the file descriptor set. The RIL event scheduler creates pipes in order to support such functions. Picture 2-18 shows the write file descriptor (s_fdWakeupWrtie) and read file descriptor (s_fdWakeupRead) after the creation of a pipe. When the RIL event scheduler creates a pipe, two file descriptors are created—a write file descriptor

and

read

file

s_fdWakejupRead variables.

descriptor—and

stored

in

the

s_fdWakeupWrite

and

25


The write file descriptor and read file descriptor have the following purposes.

○• Write file descriptor (s_fdWakeupWrite) s_fdWakeupWrite is used to activate the wakeup event literally designed to wake up the RIL event scheduler. Code 2-14 describes how the triggerEvLoop() function triggers the RIL event scheduler. The triggerEvLoop() function writes a 1-byte space character (‘ ‘) on s_fdWakeupWrite to return the select() function that is waiting for the RIL event and leads the RIL event scheduler to start scheduling by force. The triggerEvLoop() function is called when the RIL event call-back struct is registered in the watch_list and when the RIL ind command is received to schedule a wake-up event. Table 2-9 portrays how the triggerEvLoop() function is called to create a wake-up event.

Table 2-9 Two cases in which wake-up event is created by triggerEvLoop() function When a wake-up event is needed/description Registering the ril_event struct in the watch_table: When the RIL daemon initializes the RIL, it registers the ril_event struct in the RIL event scheduler’s watch_table. The ril_event struct defines a call back function to handle the I/O event. Here, the RIL daemon needs a method to wake up the RIL event scheduler by force in order to notify it that the ril_event has been registered in the watch_table. For this purpose, the RIL event scheduler provides a wake-up event and, as necessary, allows the RIL daemon to schedule the RIL event. Adding a time-out event to the timer_list: This plays the role of notifying that the RIL ind was received by the RIL event scheduler and the time-out list was added to the timer_list. In order to notify the RIL event scheduler that the time-out event has been added to the timer_list, a wake-up event is created to wake up the RIL event scheduler by force so that it can schedule a time-out event.

26


○• Read file descriptor (s_fdWakeupRead) When a wake-up event that triggers the RIL event scheduler is created and received, the RIL event

scheduler

executes

the

processWakeupCallback()

function.

The

processWakeupCallback() function is used to read a 1-byte space character (‘ ‘) used to trigger the RIL event scheduler from s_fdWakeupRead. File descriptors with the following purposes will be added to the file script list to be monitored. 3-1-3 Change read file descriptor properties Use the fcntl() function to set up the pipe read file descriptor (s_fdWakeupRead) in nonblocking mode. 3-1-4 Initialize wake up ril_event struct Use the ril_event_set() function to register the processWakeupCalbackI() function in the wake up ril_event struct (s_wakeupfd_event) in the watch_table. The file descriptor that the wake-up event scheduler must monitor is s_fdWakeupRead and the event handler that is called to handle the wake-up event is the processWakeupCallback() function.

Table 2-10 describes the specific member variables of the wake up event struct (s_wakeupfd_event) that are initialized after the ril_event_set() function is executed.

Table 2-10 Status of ril_event struct’s parameters after initializing wake up ril_event_set() function Parameter/ Value / Description fd/ s_fdWakeupRead: The file descriptor for the read-pipe. When a space character (‘ ‘) is written on the read-pipe’s file descriptor (s_fdWakeupWrite) using the triggerEvLoop()

27


function, the RIL event scheduler’s read-pipe file descriptor (s_fdWakeupRead) enters the Ready state. Index/ 0: This represents the index of the wake up ril_event structure within the watch_table Persist/ true: Since persist=true, the wake-up RIL event is not removed from the watch_table and scheduled. Time-out/ 0: By setting the time-out value to 0, it allows the wake-up RIL event to be scheduled immediately. Func/ processWakeupCallback(): This is a call-back function used to handle a space character (‘ ‘) written by the triggerEvLoop() function when s_fdWaekupRead enters the Ready state and the RIL event scheduler wakes up. It has no function besides handling the space character as shown in Code 2-15. Param/ NULL: The parameter for the user is not designated.

3-1-5 Registering wake up ril_event struct The wake up ril_event struct initialized in 3-1-4 must be registered in the watch_table to be able to handle wake-up events. The RIL event scheduler calls the rilEventAddWakeup() function to register the wake up event ril_event struct in the watch_table and call the triggerEvLoop() function to notify the RIL event scheduler of new file descriptors to be monitored (s_fd_WakeupRead).

Code 2-16 is a function that implements rilEventAddWakeup()

3-1-5-1 Register wake up ril_event struct in watch_table Use the ril_event_add() function to add a wake up ril_event struct in the watch_table. From then on, the RIL event scheduler is ready to monitor wake-up events. Therefore, the RIL

28


event scheduler must be notified so that it can monitor the file descriptor (s_fdWakeupRead_Read) for wake-up events.

3-1-5-2 Executes wake-up event’s call back function. If the triggerEvLoop() function is called to perform a write operation on s_fdWAkeupWrite, the I/O status of the file descriptor of s_fdWakeupRead is changed to the Ready state. The file descriptor of the wake-up event is s_fdWakeupRead. Then, the RIL event scheduler that was monitoring s_fdWakeupRead begins scheduling wake-up events.

Picture 2-19 is how wake event handlers are executed.

1 The triggerEvLoop() function writes a 1-byte space character (‘ ‘) on the pipe’s write file ○ descriptor. Then, it becomes a read file descriptor (s_fdWakeupRead) being monitored by RIL event scheduler, enters the Ready state and therefore wakes up. 2 RIL event scheduler, in order to handle a wake-up event, copies the wake up ril_event ○ struct registered in the watch_table to the pending_list. The wake up ril_event struct copied to the pending_list is handled by the RIL event scheduler at some point. 3 The RIL event scheduler calls the wake-up event handling call back function ○ (processWakeupCallback()) of the wake up ril_event struct in order to handle the wake-up event waiting in the pending_list. The processWakeupCallback() function simply writes a 1byte space character on the pipe.

★ TIP – Why is wake-up event registered first when eventLoop thread is created? Why is a wake-up event initialized before other RL events? It is because the wake-up event

29


wakes up the RIL event scheduler by force and triggers RIL event scheduling. In order to handle the RIL event, the ril_event struct of each RIL event must be registered in the watch_table and the triggerEvLoop() function must be called to wake-up the RIL event scheduler in waiting. This is because when the ril_event struct is registered in the watch-table, the FD_SET() macro adds a file descriptor to be monitored and thus the select() function must be executed again to be able to schedule RIL events in the file descriptor of the ril_event struct added. If the triggerEvLoop() function is not executed after registering the ril_event struct in the watch_table, even if a new ril_event struct is registered in the watch_table, the RIL event scheduler cannot realize that new file descriptors to be monitored have been added to the readFDs set.

3-1-6 Executing RIL event scheduler Execute RIL event scheduler. The ril_event_loop() function is examined in detail in section 2.5.

2.4.5 Initializing Vendor RIL After the RIL event has been created, the RIL daemon initializes the dynamically loaded Vendor RIL. In order to perform this, the Vendor RIL must provide the RIL_Init() function. Each modem manufacturer has a different way of implementing the Vendor RIL and therefore Vendor RIL initialization also differs according to each Vendor RIL. In general, the Vendor RIL initialization process is explained based on a reference Vendor RIL (reference-ril)11 that Android provides as a reference design for the Vendor RIL. ď Ź Set up the RIL daemon’s call back function as the Vendor RIL: 11

/hardware/ril/reference-ril/reference-ril.c

30


 Execute the mainLoop thread and readerLoop thread  Return the Vendor RIL’s call back function to the RIL daemon Code 2-17 describes how the RIL daemon dynamically loads the Vendor RIL library and calls the RIL_Init() function provided by the Vendor RIL library. 31 4 Dynamically loading Vendor RIL library ○ The RIL daemon uses the dlopen() function to dynamically load the Vendor RIL library from the path designated in the rilLibPath parameter. The dynamically loaded Vendor RIL library is located in the same process address as the RIL daemon. 5 Search Vendor RIL’s “RIL_Init” ○ To initialize the Vendor RIL, the RIL daemon uses the dlsym() function to search for the RIL_Init() function. The Dlsym() function uses the Vendor RIL library handle returned by the dlopen() function to find the “RIL_Init()” symbol and return its function pointer. 6 Call Vendor RIL’s RIL_Init() function ○ The RIL daemon calls the function pointer of the returned RIL_Init() function to initialize the Vendor RIL.

Code 2-18 describes how the RIL_Init() function of reference-ril.cpp initializes the reference Vendor RIL.

6-1 Setting up RIL daemon call back function The RIL_Env struct pointer passed from the RIL daemon is stored in the s_rilenv static variable. Radio response functions passed from the RIL daemon to the Vendor RIL are stored in the form of function pointers in the RIL_Evn struct as shown in Table 2-11. In order to


pass the RIL resp and RIL ind commands received from the modem to the RIL daemon, the Vendor RIL refers to s_rilenv and calls the RIL_onRequestComplete() function and RIL_onUnsolictedResponse() function of the RIL daemon.

Picture 2-20 illustrates how the vendor radio response functions of the RIL daemon are passed to the Vendor RIL when calling the RIL_Init() function of the Vendor RIL.

Table 2-11 displays the parameters of the RIL_Env struct. The RIL_Env struct includes a pointer to a call back function for the Vendor RIL to pass the RIL resp and RIL ind commands to the RIL daemon.

Table 2-11 Function pointer for RIL_Env struct Call back function /description This is a function pointer used to designate the call back function to pass the RIL resp to the RIL daemon.  t: token information for the RIL resp used to match the RIL req  e: types of error (If the RIL req is normally processed, RIL_E_SUCCESS is returned)  response: RIL resp data to be passed to the RIL daemon  responselen: the data size of the RIL ind to be passed to the RIL daemon This is a function pointer to designate a call back function used to pass the RIL ind to the RIL daemon.  unsolResponse: ID of RIL ind  data: RIL ind data to be passed to the RIL daemon

32


ď Ź datalen: size of RIL ind data to be passed to the RIL daemon This is a function pointer to designate a call back function RIL_TimedCallback, which is to be executed after a time-out. If relativeTime was specified, it specifies the relative time in which call back is to occur. If relativeTime is NULL or a pointer to a struct with 0 values, call back occurs in the fastest time.

6-2 Creating a mainLoop thread of reference Vendor RIL Use the pthread_creat() function to create mainLoop threads for the reference Vendor RIL.

Picture 2-21 describes the role of mainLoop threads and the way the readeLoop threads that are to receive a response to AT commands are created.

When a mainLoop thread is created, a readerLoop thread is created to open the modem device and transmit AT commands. When the AT channel is disconnected or the AT response does not arrive after the rAT command is received, a mainLoop thread will assume the radio status has changed to RADIO_STATE_UNAVAILABLE and initialize. The readerLoop thread will go around an infinite loop to wait for a response to the AT command from the AT channel. It will decide how to handle the AT response depending on whether it is of the solicited or unsolicited type.

6-3 Returning Vendor RIL call back function to RIL daemon The Vendor RIL returns to the RIL daemon a pointer to the radio control function (RIL_RadioFunctios). The RIL daemon can use the radio control function to control the modem.

33


Table 2-12 explains RIL_RadioFunctions structure variables. The RIL_RadioFunctions structure includes a RIL_RequestFunc function pointer that can send the RIL req to the Vendor RIL and RIL_RadioStateRequest function pointer that can request the radio status of the Vendor RIL.

Table 2-12 Function pointer for RIL_RadioFunctions struct Return value/call back function The RIL_RequestFunc function pointer is a RIL entry point for a solicited command. This function must be able to handle RIL solicited commands with RIL_REQUEST defined in ril.h.  request: RIL_REQUEST_*  data: pointer to data that defines RIL_REQUEST_*  datalen: data length  t: a pointer to the RequestInfo struct that stores a token value  The RIL_RadioStateRequest function pointer synchronously returns the current radio state  The RIL_Support function pointer returns 1 if it supports a given RIL_Request command, or 0 otherwise.  The RIL_Cancel function pointer is immediately returned and does not wait until it is canceled.  Returns Vendor RIL’s version.

34


2.4.6 Registering Vendor RIL radio control function and creating I/O event socket The RIL daemon uses radio control functions provided by the Vendor RIL to control the modem. Once Vendor RIL initialization completes, the Vendor RIL returns to the RIL daemon radio control function of the RIL_RadioFunctions type explained in section 2.4.5. The RIL daemon stores in the RIL daemon domain the radio control function returned by the Vendor RIL. From then on, the RIL daemon can use the Vendor RIL’s radio control function to send a RIL req command to the Vendor RIL. Code 2-19 shows how the RIL daemon receives the Vendor RIL’s radio control function and registers in the RIL daemon domain.

The RIL daemon, in order to register the Vendor RIL’s radio control daemon, calls the RIL_register() function defined in ril.cpp. Code 2-20 explains how the RIL_register() function is implemented in practice.

7-1 Storing Vendor RIL’s call back function Picture 2-22 shows how the RIL daemon calls the RIL_register() function to store the Vendor RIL radio control function in the RIL daemon. The pointer to RIL_RadioFunction struct returned from the Vendor RIL is copied in the RIL daemon’s static variable, s_callbacks. The Vendor RIL provides the RIL daemon with the radio control function in the form of a function pointer. As shown in Code 2-21, the RIL daemon calls the onRequest function pointer of s_callbacks to call the Vendor RIL’s radio control function. The related dispatch function is explained in detail in Section 2.5.

7-2 Obtaining “rild” socket file descriptor The init process creates a socket described in the service section of init.rc and passes the file

35


descriptor list of the socket to the environment variables of the created process. When the init process creates the RIL daemon, it creates Unix domain sockets

named “rild” and ”rild-

debug” and adds the file descriptor to the RIL daemon’s environment variables. For the RIL daemon to use this socket, it must use the android_get_control_socket() helper function to obtain the socket’s file descriptors. Here, it obtains the “rild” socket’s file descriptors via communication with the telephony framework.

7-3 Calling listen() function of the telephony framework The server must define a socket so that it can wait for the client’s connection request. The RIL daemon calls the listen() function to make the socket in the Linux kernel accept the client’s connection request. The socket must maintain Listen status until the connection request arrives from the telephony framework. The backlog parameter must be set at 4 to make four the maximum number of client connections.

7-4 Initializing listen ril_event struct Use the ril_event_set() function to initialize the listen ril_event struct (s_listen_event). The listen event occurs when the RIL req is requested by the telephony framework for the first time via the s_fdListen socket.

Table 2-13 describes values for variables of the listen ril_event struct (s_listen_event) created. The file descriptor being monitored by the RIL event scheduler is s_fdListen and the call back function that handles listen events is the listenCallback() function. Unlike a wake-up event, the listen event is scheduled only once by the RIL event scheduler.

Table 2-13 listen ril_event struct

36


Member variable/parameter/description File descriptor used when the first RIL req request arrives from the telephony framework. Index for listing the ril_event struct in the watch_table The listen event is scheduled only once and removed from the watch_table Set the time-out value to 0 so that the listen RIL event can be scheduled immediately If the RIL event scheduler receives a listen event, the listenCallback() function is called to handle listen event. Does not designate a parameter for user.

7-5 Register listen ril_event struct in watch_table (call rilEventAddWakeup() function) The RIL event scheduler, in the same manner that the wake ril_event struct is registered, calls the rilEventAdWakeup() function to register the listen ril_event struct in the watch_table. In addition, the rilEventAddWakeup() function executes the triggerEvLoop() function so that the RIL event scheduler can monitor the newly added listen event. From then on, the RIL event scheduler can receive RIL req commands from the telephony framework to handle the RIL req.

7-6 Obtaining “rild-debug” socket file descriptor Use android_get_control_socket() function to obtain the file descriptor of the debugging socket (“rild-debug”). Call the listen() function and wait for the radiooptions program to connect to the s_fdDebug socket used for debugging. The debugging socket is used in the radiooptions program during RIL daemon debugging.

7-7 Listening to radiooptions program connection request for RIL debugging (calling listen() function)

37


The RIL daemon calls the listen() function and sets up the kernel to accept the radiooptions program connection request via the “rild-debug� socket.

7-8 Initializing debug ril_event struct Use the ril_event_set() function to initialize the debug ril_event struct (s_debug_event). When the radiooptions program is used to debug the RIL daemon, the debug event provides necessary information via the s_fdDebug socket.

Table 2-14 is a description of member variables of the debug ril_event struct created. The file descriptor monitored by the RIL event scheduler is s_fdDebug. The debug event handling call back function is the debugCallback() function.

Table 2-14 Debug ril_event struct Member variable/parameter/description The file descriptor used when the debug request arrives from the radiooptions program Indicates the debug ril_event struct index within the watch_table The debug event is not removed from the watch_table and is continually scheduled. The time-out value is set at zero and the debug event is scheduled for immediately. When the RIL event scheduler receives the debug event, the debugCallback() function is used to handle it. Does not designate a parameter for user.

7-9 Register debug ril_event struct in watch_table. The RIL event scheduler calls the rilEventAddWakeup() function to register the debug ril_event struct in the watch_table. In addition, the rilEventAddWakeup() function executes

38


the triggerEvLoop() function so that the RIL event scheduler can monitor the newly added debug event. From then on, the RIL event scheduler can receive debug events from the radiooptions program for handling.

2.5 RIL event handling mechanism A RIL event is the basic unit of scheduling handled by the RIL event scheduler. The RIL event scheduler is triggered by the RIL event and executes the RIL event handling call back function at an appropriate time according to the I/O event and time-out event. This section introduces the RIL event handling mechanism.

2.5.1 Principles behind working of RIL event scheduler In 2.3.1, basic elements that make up the RIL daemon were described. Picture 2-23 shows the RIL daemon’s main elements and functions. The RIL event scheduler is one of the main elements that makes up the RIL daemon and performs a core role in handling RIL events. The goal of scheduling an algorithm for the RIL event scheduler is to: 1) determine when the I/O and time-out events created by an application or the RIL event scheduler need to be executed; and 2) call the relevant RIL event handling call back function to execute the RIL events at the times determined. Now, we look at the RIL event scheduler’s RIL event scheduling algorithm.

○• RIL event scheduling algorithm Picture 2-24 is an explanation of the principles behind the RIL event scheduler algorithm. The RIL event scheduler was implemented by the eventLoop thread and the eventLoop thread goes into an infinite loop to execute RIL events. 1. The calcNextTimeout() function, if a time-out event exists in the timer_list, calculates the

39


time for the RIL event scheduler to wake up to handle the time-out event. 2. The select() function either waits for an I/O event to occur or returns after the time calculated by the calcNextTimeout() to handle time-out events. 3. The processTimeouts() function checks the time-out event in the timer_list, removes from the timer_list ril_event the time-out event struct that exceeds the time-out time and moves it to the pending_list. 4. The processReadReadies() function adds the ril_event struct from the watch_list to the pending_list that handles the I/O event when an I/O event is received. 5. The filePending() function checks the pending_list and calls the RIL event handling call back function of the ril_event struct to process the RIL event.

Code 2-22 shows the main codes of the ril_event_loop() function, the core of the RIL event scheduler.

1. Copy the readFds of the fd_set type in the local variable rfds. Use the memcpy() function to copy the value of the readFds to the local variable rfds. The reason the value of the readFds is copied to the local variable rfds is because the select() function does not remember the prior state of the fd_set. That is, when the select() function is called in repetition, it is marked as being ready to read the file descriptor and returned. 2. Calculate the time-out period of a time-out event in the timer_list. The RIL event scheduler calculates the time difference between the current time and the timeout time.

40


Code 2-23 explains the calcNextTimeout() function that determines the time-out period for a time-out event that waits in the timer_list. The time period calculated by the calcNextTimeout() function is used as a time-out parameter for the select()j function. The select() function in Code 2-22 is returned if the I/O event does not occur before the time-out period passes. 41 The calcNextTimeout() function, if there is no time-out event to be scheduled by the timer_list, returns -1, and 0 if otherwise.

2-1 Obtain the current time The getNow() function returns the current time to now in the timerval struct. 2-2 Compare the current time (now()) and the time-out time of the time-out event waiting in the timer_list If there is a time-out event waiting in the timer_list, the current time (now()) and the time-out time stored in the time-out variable of the time-out event are compared. If the current time is greater than the time-out time of the time-out event, the timercmp macro returns 1, or 0 otherwise. 2-3 Calculate the difference between the current time and the time-out event time. If the time-out of the time-out event is greater than the current time, the timersub() macro calculates the difference and returns it to tv. The tv is then used to set the time-out time in the select() function and determine its waiting period by that value. 2-4 Set tv_sec and tv_usec in tv as 0 If the current time is greater than the time-out time of the time-out event, the calcNextTimeout() function sets the tv_sec and tv_usec in tv as 0 and returns them. The value of tv is set to 0 because it is no longer necessary to schedule a time-out event in the timer_list.


Accordingly, the ptv in the select() function becomes NULL and will be on an infinite waiting status until an RIL event comes.

Picture 2-25 explains how to determine the select() function’s return time according to the time-out time of a time-out event. 42 3. I/O multiplexing using select() function The select() function allows the RIL event schedule to use I/O multiplexing using various RIL events coming from various routes.

Table 2-15 is a description of parameters returned by the select() function. The select() function returns the count of file descriptors that are I/O ready.

Table 2-15 Parameters for select() function Parameter/description nfds: the file descriptor with the highest value among managed file descriptors + 1 rfds: copied value of readFds copies in 1 (2-22) ptv: the time remaining until the time-out of the first time-out event waiting in the timer_list ‘ If there is no time-out event waiting in the timer_list, the calcNextTimeout() function returns -1. Therefore, the select() function’s time-out parameter ptv = NULL and it will be blocked infinitely until another RIL event is received. If there is a time-out event in the timer-list that times out after 0.5 seconds, ptv = {0,500000} according to the calcNextTimeout() function.

Code 2-24 is an example of the select() function that automatically returns after 0.5 seconds if


there is no I/O event in the rfds, which is the read-fd_set.

4. Handle the time-out event waiting in the timer_list based on the status of the time-out. The processTimeouts() function compares the time-out time of a time-out event waiting in the timer_list to the current time and adds it to the pending_list if it is less than the current time.

Code 2-25 describes how the processTimeouts() function moves a time-out event from the timer_list to the pending_list.

4-1 Obtain the current time The getNow() function returns the current time to now, which is of timerval type. 4-2 Loop timer_list to check for a time-out event that passed the time-out time. It loops the timer_list and executes a while statement until the time-out time of the time-out event is smaller than the current time. 4-3 Remove from the timer_list the time-out event that passed the time-out time. Compare the time-out time of the time-out event waiting in the timer_list to the current time and remove time-out events from the time_list if the current time is greater than their timeout times. 4-4 Add to the pending-list in order to remove time-out events that pass the time-out time. The time-out event that has passed the time-out time is handled by the RIL event scheduler and therefore added to the pending_list.

5 File descriptor finds a Ready state I/O event from watch_table and adds to pending_list The select() function of the RIL event scheduler is returned either when I/O event arrives and is returned from the waiting state or is automatically returned as the time-out event times out.

43


The processReadReadies() function in Code 2-26 must check if an I/O event has arrived and perform the process of adding the ril_event struct registered to handle the I/O event from the watch_list to the pending_list.

5-1 Loop ril_event struct of I/O event registered in watch_table The ril_event struct that can be registered in the watch_table is at maximum MAX_FD_EVENTS(=8). Therefore, it loops through up to eight ril_event struct and can execute for statement if the value of n returned by the select() function is greater than 1, for it means there exists an I/O event in the Ready state. 5-2 Check if the status of file descriptors by I/O event is Ready Use the FD_ISSET() macro while looping the watch_table to check if the I/O event’s file descriptor is I/O Ready. 5-3 Add to pending_list ril_event struct of I/O event whose file descriptor is in ready state 5-4 If the file descriptor is I/O ready, add I/O event’s ril_event struct to the pending_list so that it can be handled by the RIL event scheduler. 5-5 If persist, a variable of the ril_event struct, is false, remove the ril_event struct from the watch_table. As a same example, the persist variable in the listen ril_event struct (s_listen_event) is false and therefore removed from the watch_table after performing the role of creating the RIL req ril_event struct (s_commands_event).

6 Call the call back function of RIL event in pending list Code 2-27 describes how the filePending() function handles all RIL events in the pending_list. The firePending() function calls the RIL event handling call back function for each RIL event and handles it.

44


6-1 Remove from the pending_list the ril_event struct. 6-2 Execute a call back function designated for each RIL event’s ril_event struct and handle the RIL event.

All RIL events waiting in the pending_list are removed and at the same time each relevant call back function is called. After the call back functions finish their tasks, they go into the next RIL event handling and so on.

2.5.2 RIL event handling call back function The RIL event scheduler, in order to handle RIL events in the pending_list, calls the RIL event handling call back function in the ril_event struct. When all RIL events have been handled, it goes into a monitoring state until the fd_set enters the Ready state. Let us take a look in detail at how the RIL event handling call back function works in practice.

Table 2-16 is a summary of types of RIL event handling call back function The RIL event handling call back function is responsible for handling RIL events received. Event handling call back functions in Table 2-16 all have common parameters. Code 2-28 is a declaration for the ril_event_cb function pointer to handle RIL events. In order to use the RIL event handling call back functions as a function pointer type, typedef is used to define a function pointer ril_evetn_cb that can point to each call back function. The RIL event handling call back function requires three parameters. Table 2-17 is a summary of the ril_event_cb function pointer in Code 2-28.

Table 2-17 Parameters for call back function Parameter/description

45


fd: a socket or file descriptor of pipe events: a flag parameter, which is not used in practice. Userdata: a parameter used by the call back function (of void type and therefore type must be changed before using).

The ril_event struct’s func parameter determines that the RIL event handling call back function handles RIL events. The RIL event scheduler calls the RIL event handling call back function registered in the func parameter to handle RIL events. As shown in Table 2-16, there are five RIL events that can be handled by the RIL event scheduler and a call back function can be designated for each. Now, let us look at how call back functions described in Table 2-16 can be handled.

○• Listen event handling call back function: listenCallback() function During initialization, the RIL daemon registers the listen ril_event struct (s_listen_event) in the watch_table when calling the RIL_register() function. The func variable in the listen ril_event_event struct stores the listenCallback() function, a listen event handling call back function, and therefore the RIL event scheduler can call the listenCallback() call back function when a listen event occurs. The RIL in the telephony framework makes a connection request to the RIL daemon’s UNIX domain socket when a RILReceiver thread is created. When the RILReceiver thead’s connection request is received by the RIL daemon’s file descriptor s_fd_listen, it creates a listen event. The RIL event scheduler executes the listenCallback() function in order to handle the listen event created.

Code 2-29 explains how the listenCallback() function works. The listen event uses the accept() function to permit a connection request from client program and creates a new socket for the

46


client program. The listenCallback() function is executed only once when the telephony framework attempt to make a connection request. When the RIL daemon accepts the connection request, it no longer needs to be performed. Therefore, the value of the persist value in the listen ril_event struct is set to False and removed from the watch_table after being executed once. 47 1. Connection request to the telephony framework permitted (accept() function) The listenCallback() function calls the accept() function to accept a connection request from the telephony framework coming to the file descriptor’s s_fdListen. The accept() function creates and returns a client UNIX domain socket (s_fdCommand) to communicate with clients and therefore communication between the telephony framework and the RIL daemon becomes possible through the s_fdCommand. Picture 2-26 shows how the RIL daemon accepts a connection request from the telephony framework and uses the UNIX domain socket to communicate. 2. Obtain a pointer to a new record_stream struct Calls the record_stream_new() function to create a buffer to store data transmitted by the s_fdCommand and returns the record_stream struct’s pointer. The RIL event scheduler uses the returned record_stream struct’s pointer and receive data packet from the telephony framework transmitted via the s_fdCommand. 3. Initialize RIL req ril_event struct Calls ril_event_set() function to initialize the RIL req ril_event struct (s_commands_event). Assigns the s_fdCommand to the file descriptor to receive data and set the processCommandCallback() function as the RIL req event handling call back function. 4. Call the rilEventAddWakeup() function


The RIL event scheduler calls the rilEventAddWakeup() function to register the RIL req ril_event struct (s_commands_event) in the watch_table and trigger the RIL event scheduler. The triggered RIL event scheduler starts monitoring to see if a RIL req event occurs. 5. RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED occurs. Finally, transmit RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, a RIL ind, and notify the change in RIL Radio status to the RIL’s RILReceiver thread.

Code

2-30

describes

how

the

onNewCommandConnect()

function

calls

the

RIL_onUnsolicitedResponse() function to notify the telephony framework of a change in the RIL’s radio status.

○• RIL req event handling call-back function: processCommandsCallback() function The RIL event scheduler, in order to handle the listen event, calls the listenCallback() function and creates a client UNIX domain socket (s_fdCommand). From then, communication between the telephony framework and the RIL daemon becomes possible. The S_fdCommand in the file descriptor is registred in the fd_set by the listenCallback() function. Therefore, if the RIL’S RILsender thread sends a RIL req to the RIL daemon, the s_fdCommand field in the fd_set enters the Ready state. The RIL event scheduler detects that the s_fdCommand field in the fd_set has entered the Ready state by the select() function and starts RIL event scheduling to handle RIL req events in the RILSender thread. During RIL event scheduler initialization, the RIL req ril_event struct registered in the watch_table has the processCommandCallback() function designated as a RIL req event handling call back function for handling RIL req events.

Code 2-31 describes how processCommandCallback() function works

48


1. Stores recordStream’s pointer When the processCommandCallback() function is called, a record stream created by the listenCallback()

function

is

passed

to

a

void

type

pointer.

Then,

the

processCommandCallback() function’s parameter param is cast in the RecordStream type pointer and then stored in p_rs, which is also a RecordStream type pointer. 2. Use RecordStream’s pointer to receive RIL req from RIL’s RILSender thread The processCommandCallback() function calls the record_stream_get_next() function and reads the RIL req data packet sent by the RIL’s RILSender thread. Then it reads the data pointer (p_record) and length (recordlen) received in the RecordStream buffer. 3. Send RIL req to Vendor RIL If there is no problem with RIL req received by the record_stream_get_next() function, the processCommandsCallback() function calls the processCommandBuffer() function and sends a RIL req in the telephony framework to the Vendor RIL area.

How the processCommandBuffer function works is explained in detail in section 2.5.3.

49


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.