After working with Android telephony for a while and learning about it mostly by looking at the code, I realized that there was not enough documentation about it besides the code itself. This post will provide detailed walk through about the java telephony internals.
The android telephony architecture is split between java and native code. As of today there is a clear documentation about the native layer (http://pdk.android.com/online-pdk/guide/telephony.html), however there is no documentation about the java layer architecture itself.
The android framework classes interact with the Phone API (com.android.internal.telephony.Phone) through two basic method types. Both are based on asynchronous message exchanges.
public void get[…](Message response);
The first type provides a way to get radio and/or network related information asynchronously by passing an android.os.Message class instance. The message will be delivered to the message’s handler when the response from the underlying radio interface layers becomes available.
public void registerFor[…](Handler h, int what, Object obj);
The second type provides a way to get radio/network state updates by registering to receive response messages of the specified type (what parameter) to the specified handler (h parameter) and an optional user object (obj parameter) within the message instance.
The first type of method will call the underlying com.android.internal.telephony.RIL class directly, passing down the message object to be dispatched when the response is available from the underlying layers. The second type may have one or more android.os.Handler instances registered for status updates, they are wrapped on android.os.Registrant objects as weak references in order to allow them to be garbage collected. Because they are not referenced anywhere else, it does not worth sending updates or keeping track of them. The registrants for each register method are stored on android.os.RegistrantList instances so they can be referenced for the future updates.
The communication between the java and native layers is done through a Linux local socket. Every request to the native layer is wrapped into an instance of the com.android.internal.telephony.RILRequest class in order to keep the request information stored until the response is returned the bottom layers. When the response arrives the RILRequest object for the original request will be retrieved from the pending requests list in order to resolve the destination handler to dispatch the response to. The RILRequest class has the following attributes in order to keep the request information:
- int mSerial; // The request sequence number
- int mRequest; // The request code
- Message mResult; // The result message to be dispatched upon response
- Parcel mp; // The parcel where the raw data will be written and sent though the socket
Once the request object is retrieved or created from the requests pool, every public void get[…](Message response); method will write its parameters (if any) into the request object’s Parcel instance field(mp) and send it to the com.android.internal.telephony.RIL.RILSender class. The RILSender class is a handler that runs on its own looper thread, waiting for new RIL requests to be sent to the native layers through the socket connected to the RIL daemon as shown on the diagram above. The sender main responsibilities are to store the request object instance into the pending requests list, marshall the parcel into a raw byte array and send it through the socket. The raw request format is specified bellow:
Right now you already know how the requests are sent down to the native layers, but how the request handlers get the response messages back? The com.android.internal.telephony.RIL.RILReceiver class runs on its own thread listening on the RIL daemon socket for asynchronous responses. Its main responsibilities in opposition to the RILSender are to unmarshall the raw data into an android.os.Parcel, process the response and dispatch it inside a message object to its target handler.
There are two kinds of responses that come from the native RIL, solicited and unsolicited commands (see details at http://pdk.android.com/online-pdk/guide/telephony.html). The raw response format for the response types is specified bellow:
In conclusion, the java and native layers communicate with each other asynchronously through a socket passing requests/responses up and down the stack.
NOTE: All this article information is based on the Android Open Source Project source code. For details see http://source.android.com/.
Hope you all the best,