2012年7月21日 星期六

android RIL event


ril event loop運作的示意圖
6,10的persist屬性為true





RIL event list是環狀的linked list
起始狀態的 list
加入一個event後




關於ril_event structure
struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;
    int fd;
    int index;
    bool persist;
    struct timeval timeout;
    ril_event_cb func;
    void *param;
};

linking list的資料結構
fd: file descriptor
index: unknown
persist: 是否要從watch_table中移除
timeout: unknown
func: callback function
param: 傳入callback的data pointer 


總共有timer list,pending list兩種list.(watch table是array的資料結構,不是linked list)
有以下幾種操作function
1.ril_event_set:
    設置event,填入callback function,persist...等
2.ril_event_add:
    將event加入watch table之中,並且將event的fd存入readFds全域變數之內與更新nfds(如果event的fd大於目前的value)
3.ril_event_del:
    將event從watch table中刪除,並且將event中的fd從readFds中刪除
4.ril_timer_add:
    將timer event加入timer list之中,下有程式碼說明
5.addToList:
    將event加入list之中,下有程式碼說明

ril_timer_add
將timer event加入timer list之中, event的排序由timeout的時間從小到大





























timeradd 
將目前的系統時間加上timeout的間隔
addToList
將ev插入目前list指的後面那個位置

ril event loop
整個RILD的運作核心



select()說明可參考 http://blog.roodo.com/rocksaying/archives/2333878.html

以下是event loop內function的說明
1.calNextTimeout
如果list是空的回傳-1,反之取出第一個event的timer減去目前的時間




























2.processTimeouts
如果event內的timeout時間小於目前取得的系統時間,time is expired ....






















3.processReadReadies
檢查watch table內的event是否需要移到pending list等待被處裡






















4.firePending
執行pending list中的event
















2012年6月18日 星期一

system properties


可參考以下的網址:
http://rxwen.blogspot.tw/2010/01/android-property-system.html
http://favoritepili.blogspot.tw/
http://blog.csdn.net/thl789/article/details/7014300

存放system properties的build.prop, default.prop是build image後動態產生的檔案
其Makefile放在 build/core/

搜尋system.prop字串

BUILDINFO_SH := build/tools/buildinfo.sh
$(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
...
$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
         cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
       fi

它會去system.prop內撈properties出來放到build.prop裡面

build.prop ( out/target/product/generic/system/ )
default.prop (  out/target/product/generic/root/ )

Exercise:
build/target/board/generic/system.prop中亂加一參數 TEST="testing"
make後再去build.prop中檢查是否有此參數, 或者進入shell
> adb shell
> getprop
> ...
> [TEST]: ["testing"]
> ...

End!


2012年6月7日 星期四

Android Telephony framework - SMS


SmsManager.java內部定義了給AP使用的interface,

AP要使用前必須先getdefault()取得SmsManager這個class的instance

看看sendTextMessage()裡頭做的事情
iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
這邊要去取得名稱為isms這個service的instance
因此 iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
會一路跑到定義在RIL.java的sendSMS()

這個service內的interface定義在ISms.aidl這個class裡頭
而這些interface會在IccSmsInterfaceManagerProxy被實現(implementation)

    public void sendText(String destAddr, String scAddr,
            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
        mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
    }


注意一下class IccSmsInterfaceManagerProxy這個constructor裡頭所做的事情

if(ServiceManager.getService("isms") == null) {
                ServiceManager.addService("isms", this);
            }

會把IccSmsInterfaceManagerProxy這個instance塞進去serviceManager裡頭, 名稱為isms


所以當你要使用SMS的API時候
要先去getDefault()取得SmsManager的intstance
然後就可以使用它所提供的API


MT SMS:

RIL.java內會收到RIL_UNSOL_RESPONSE_NEW_SMS這個event

                mGsmSmsRegistrant
                        .notifyRegistrant(new AsyncResult(null, sms, null));

這會發出notify給SMS dispatcher

當我們去搜尋mGsmSmsRegistrant可以找到

    public void setOnNewGsmSms(Handler h, int what, Object obj) {
        mGsmSmsRegistrant = new Registrant (h, what, obj);
    }

setOnNewGsmSms這個function會去取得一個Registrant instance
而這個instance的what是甚麼呢?

再去搜尋setOnNewGsmSms

public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
...
mCm.setOnNewGsmSms(this, EVENT_NEW_SMS, null);
...

所以what就是EVENT_NEW_SMS

在RIL.java內notify後, 這個event會被送到

    public void handleMessage(Message msg) {
        AsyncResult ar;


        switch (msg.what) {
        case EVENT_NEW_SMS:
        ...
        try {
                int result = dispatchMessage(sms.mWrappedSmsMessage);
                if (result != Activity.RESULT_OK) {
         
此dispatchMessage在GsmSMSDispatcher.java中實現

public abstract class SMSDispatcher extends Handler
{
   ...
   // 宣告dispatchMessage()
   public abstract int dispatchMessage(SmsMessageBase sms);
   ...
}


public final class GsmSMSDispatcher extends SMSDispatcher
{
    ...
    @Override
    public int dispatchMessage(SmsMessageBase smsb) {
      ...
      dispatchNormalMessage()
      ...
    }
}


往下的路:
dispatchNormalMessage()  --> dispatchPdus()

protected void dispatchPdus(byte[][] pdus, String address) {
           // new intent to AP
           Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
           intent.putExtra("pdus", pdus);
           intent.putExtra("format", getFormat());





Concatenated SMS 

http://en.wikipedia.org/wiki/Concatenated_SMS


    protected int dispatchNormalMessage(SmsMessageBase sms) {
        SmsHeader smsHeader = sms.getUserDataHeader();
     ....
     if ((smsHeader == null) || (smsHeader.concatRef == null)) {

                 // 單封 MT SMS
                ... 
                dispatchPdus()
     } else {
                 // 組合的MT SMS
                 ...
                processMessagePart()
     }
     ... 

下面的程式碼會去parse SMS user data header
public static SmsHeader fromByteArray(byte[] data) {
            ... 

            case ELT_ID_CONCATENATED_8_BIT_REFERENCE:
                concatRef = new ConcatRef();
                concatRef.refNumber = inStream.read();
                concatRef.msgCount = inStream.read();
                concatRef.seqNumber = inStream.read();
                concatRef.isEightBits = true;
                if (concatRef.msgCount != 0 && concatRef.seqNumber != 0 &&
                        concatRef.seqNumber <= concatRef.msgCount) {
                    smsHeader.concatRef = concatRef;
                }
                break;

同一封SMS的user data header會有相同的reference number, 但sequence number會不一樣,這代表封包的排列順序

reference:
PDU foramate : http://www.ie.ksu.edu.tw/data/GPRS%20Modem%E8%B3%87%E6%96%99/sms.pdf


end !



2012年5月24日 星期四

add_service and PERMISSION DENIED

在Radio中新增一service時發生

add_service('YourService',0x78) uid=1001 - PERMISSION DENIED

擋的地方是

int do_add_service(struct binder_state *bs,
                   uint16_t *s, unsigned len,
                   void *ptr, unsigned uid)
{
    struct svcinfo *si;
//    LOGI("add_service('%s',%p) uid=%d\n", str8(s), ptr, uid);

    if (!ptr || (len == 0) || (len > 127))
        return -1;

    if (!svc_can_register(uid, s)) {
        LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n",
             str8(s), ptr, uid);
        return -1;
    }




我的解法:

在service_manage.c中的新增一欄位


static struct {
    unsigned uid;
    const char *name;
} allowed[] = {
#ifdef LVMX
    { AID_MEDIA, "com.lifevibes.mx.ipc" },
#endif
    { AID_MEDIA, "media.audio_flinger" },
    { AID_MEDIA, "media.player" },
    { AID_RADIO, "YourService" },

這樣的話在svc_can_register()中的檢查就可以過了



詳細情形可參閱
http://www.jollen.org/blog/2010/02/android-uid-system.html











2012年5月5日 星期六

RIL.Java

Telephony framework的其中一環


RILJ定義以及實作telephony相關操作的interface,並且提供給framework來使用
圖中,RILJ與RILD之間透過socket溝通,在RILJ裡面定義了兩個thread來access rild socket,分別是RILReceiver與RILSender.
RIL constructor中create這兩個thread
        ....

        mSenderThread = new HandlerThread("RILSender");
        mSenderThread.start();
        ...
        Looper looper = mSenderThread.getLooper();
        mSender = new RILSender(looper);


            riljLog("Starting RILReceiver");
            mReceiver = new RILReceiver();
            mReceiverThread = new Thread(mReceiver, "RILReceiver");
            mReceiverThread.start();


RILSender
RILSender這個class裡面的行為
透過RILJ interface如getIMSI()所送出的RIL Request會被包成message送到這個handler處裡,其message的type為EVENT_SEND
        handleMessage(Message msg) {
            RILRequest rr = (RILRequest)(msg.obj);
            RILRequest req = null;


            switch (msg.what) {
                case EVENT_SEND:

然後RIL Request(rr)會被加入mRequestsList這個queue
                        synchronized (mRequestsList) {
                            mRequestsList.add(rr);
                            mRequestMessagesWaiting++;
                        }


接著,從rr裡面把request所需要的data拿出來
                        data = rr.mp.marshall();
                        rr.mp.recycle();
                        rr.mp = null;

計算data長度
                        // parcel length in big endian
                        dataLength[0] = dataLength[1] = 0;
                        dataLength[2] = (byte)((data.length >> 8) & 0xff);
                        dataLength[3] = (byte)((data.length) & 0xff);

寫到socket裡面
                        s.getOutputStream().write(dataLength);
                        s.getOutputStream().write(data);

RILReceiver
get socket
                    s = new LocalSocket();
                    l = new LocalSocketAddress(SOCKET_NAME_RIL,
                            LocalSocketAddress.Namespace.RESERVED);
                    s.connect(l);


read data from rild soecket and write to buffer
                        length = readRilMessage(is, buffer);

create new parcel and put data in
                        p = Parcel.obtain();
                        p.unmarshall(buffer, 0, length);
                        p.setDataPosition(0);

dispatch it to response function to parse
                         processResponse(p);

以下是整個RIL request到RILD以及回來的示意圖
dial()為例



2012年5月3日 星期四

RIL daemon

RILD
rild.c, ril.cpp, ril_event.cpp, reference-ril.c以上是相關的.c or .cpp檔
RILD上承RILJ,也就是ril.java,往下是將AT command送給modem
所以簡單來說,RILD的功能就是將RIL request轉成AT command

這個daemon在開機的時候就會init起來,其參數的定義放在init.rc裡面
class main
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root

首先,先看rild.c中的main()
RIL_startEventLoop()這個function的實作放在ril.cpp中
RIL_startEventLoop(void) {
    ....
    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
    ...
}


他會create一個thread, 入口是eventLoop(),同樣實作在ril.cpp
static void *
eventLoop(void *param) {
    ...
    ril_event_init();
    ....
    ril_event_loop();
    ...
}

RILD裡頭有一套event的機制來處理上層送下來的request.
ril_event_init()用來init所需的event queue,總共有三種: timer list, pending list, watch_table

main()中的rilInit(&s_rilEnv, argc, rilArgv)
這個function會參照到reference-ril.c中的RIL_Init()
這裡頭又會create "mainLoop"這個thread
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

看一下mainLoop裡頭做的事情
有一部分的事情是要透過socket跟qemud溝通?? 這一部分還沒看熟
另外
ret = at_open(fd, onUnsolicited);
這個function內部會create "readLoop"這個thread,它用來讀從channel送上來的AT command,並且註冊用來處理unsolicited AT command的function

還有
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0)這個function會將會新增一event,並且填入callback function: initializeCallback(),看一下此callback做的事情

    at_send_command("ATE0Q0V1", NULL);
    ... 
    /*  No auto-answer */
    at_send_command("ATS0=0", NULL);
    ... 
    /*  Extended errors */
    at_send_command("AT+CMEE=1", NULL);
    ... 

看樣子是要intialize AT command channel

rilInit()所回傳的pointer是指向s_callbacks,定義在reference-ril.c



static const RIL_RadioFunctions s_callbacks = {
    RIL_VERSION,
    onRequest,
    currentState,
    onSupports,
    onCancel,
    getVersion
};


此pointer接著會被丟到RIL_register(),定義在ril.cpp中
看一下RIL_register()中所做的事情

s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
...
ret = listen(s_fdListen, 4);
...
ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);
...
rilEventAddWakeup (&s_listen_event);

android_get_control_socket()取得rild socket的file descriptor,這邊的rild也就是之前提到定義在init.rc內的rild

接著,透過ril_event_set()新增一event,填入此rild的fd與callback function 

    ril_event_set (&s_listen_event, s_fdListen, false,
                listenCallback, NULL);

rilEventAddWakeup()會把event放入watch_table裡面

下圖是關於以上說明的簡單示意圖






































所以會有3個thread被create出來,mainLoop, readLoop, eventLoop
藍色部分是會被set的RIL event. 到時候會被排入ril event loop中執行

event說明
1. ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL);
    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
    // accept a connection並且取得new socket的file descriptor,此s_fd_Command接著會被傳入
    // s_commands_event裡面,用來read從RIL.java送下來的ril request 

    ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);
    // 設定s_fdCommand的屬性 (??)

     p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
     // read data from s_fdCommand 

2. ril_event_set (&s_commands_event, s_fdCommand, 1,
        processCommandsCallback, p_rs);
for (;;) {
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);

        // read data from s_fdCommand
       ...
        processCommandBuffer(p_record, recordlen);
        // data 在此function裡面會被打包成RequestInfo,也就是pRI
}

看一下processCommandBuffer()內所做的事情,它定義在ril.cpp中
先介紹兩種資料結構,Parcel與RequestInfo
1. Parcel: http://developer.android.com/reference/android/os/Parcel.html 
用來放data的容器(資料結構),RILJ要往RILD送的data都會先封裝在Parcel裡面再透過socket傳送

2. RequestInfo:
    typedef struct RequestInfo {
        int32_t token;      //this is not RIL_Token
        CommandInfo *pCI;
        struct RequestInfo *p_next;
        char cancelled;
        char local;         // responses to local commands do not go back to command process
    } RequestInfo;

token: ril request (from RILJ)的sequence number 
pCI: callback function for this ril request 

    typedef struct {
        int requestNumber;
        void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
        int(*responseFunction) (Parcel &p, void *response, size_t responselen);
    } CommandInfo;

requestNumber: ril request ID
dipspatchFunction: callback function to handle request
responseFunction: callback function to handle response

首先,拿ril request ID與sequence number出來

    status = p.readInt32(&request);
    status = p.readInt32 (&token);

將token與查表取得callback function填入

    pRI->token = token;
    pRI->pCI = &(s_commands[request]);

s_commands內的值定義在ril_commands.h
example:
    {RIL_REQUEST_OPERATOR, dispatchVoid, responseStrings},
    {RIL_REQUEST_RADIO_POWER, dispatchInts, responseVoid},
    {RIL_REQUEST_DTMF, dispatchString, responseVoid},

接下來便會去執行dispatch function

    pRI->pCI->dispatchFunction(p, pRI);

以send SMS為例
{RIL_REQUEST_SEND_SMS, dispatchStrings, responseSMS}

ril request ID: RIL_REQUEST_SEND_SMS
dispatch function: dispatchStrings
response function: responseSMS

進入dispatchStrings後Parcel內的data會被取出,最後會呼叫
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);

onRequest定義在reference-ril.c,此時就會轉成AT command往modem送

** ril request的interface定義在ril.h中