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中