• 在高通Fastmmi模式中增强交互方式


    在高通Fastmmi模式中增强交互方式

    背景

    由于之前工厂抱怨 FCT模式不好用。

    之前的FCT测试是这样子的:PCBA上夹具,连接USB。

    同时,使用上位机程序(ATE)发送指令,人工判断结果以后,发送结果;以及下一条测试指令的情况。

    可见,测试一条指令所需的交互次数很多。

    现在要求减少AT指令的交互测试,思路有2种:

    1、做成自动化的,不再人工发送指令

    2、通过现有的功能(例如按键)实现模拟点击确认交互方式的指令

    大概是这样子的:

    机器 放上 夹具,连接USB
    
    进入 Fastmmi  模式以后 ,自动执行每一项测试项,
    - 如果指令能够自动返回,则继续下一项
    - 如果指令需要人工判断,则通过定义的按键来 显示 成功或者失败;如果 没有按键,则一直等待,否则就进入下一项
    - 循环,直到结束测试。
    

    行动目标

    1、即使PC不再发送AT指令,机器自己也能运行自动测试(满足某些情况下)。

    2、按下指定的按键,能够直接判断测试项的结果。

    行动思路

    框架了解

    整个MMI的框架如下:

    • MMI Core: Core manages all MMI modules and is responsible for UI control, also responses Diag request from DIAG service.

    MMI core is on: /dev/socket/mmi, running as server role. Agent and Diag will connect server
    socket

    • MMI Agent: Agent loads each MMI module (mmi_xxx.so) in a single process and communicates with MMI Core via socket.
    • MMI Diag: Diag handles diag command from Host PC and communicates with MMI core via socket.
    • MMI UI: UI is part of MMI core, MMI core responsible for drawing UI components.

    源码树如下:

    The FASTMMI code is in the folder vendor/qcom/proprietary/fastmmi. Check the source file structure below. 
    .
    ├── Android.mk
    ├── configure.ac
    ├── libmmi
    ...
    ├── Makefile.am
    ├── mmi
    ...
    ├── mmi.mk
    ├── module
    │   ├── cpu
    │   │   ├── Android.mk
    │   │   ├── cpu.cpp
    │   │   └── Makefile.am
    ...
    │   └── wifi
    │       ├── Android.mk
    │       ├── Makefile.am
    │       ├── wifi.cpp
    │       └── wifi_uav.cpp
    └── res
    ...
        ├── layout
        │   ├── ...
        │   ├── layout_wifi.xml
        │   ├── main_wear.xml
        │   └── main.xml
        ├── raw
        │   ├── Android.mk
        │   ├── DroidSansFallback.ttf
        │   ├── LICENSE
        │   ├── NOTICE
        │   └── qualsound.wav
        ├── values
        │   ├── Android.mk
        │   ├── path_config_android.xml
        │   ├── path_config_le.xml
        │   ├── strings.xml
        │   └── strings-zh-rCN.xml
        └── wifi_config
            ├── Android.mk
            └── wpa_supplicant_test.conf
    
    • libmmi: is the UI controller library, like button, window, text ...
    • mmi :is the main application.
    • module: is the individual mmi test cases.
    • res: is the config and layout resource, and so on.

    流程分析

    fastmmi的资料比较少,因此,只能基于现有的代码来进行分析推断。

    流程很简单,fastmmi初始化,创建了包括ate_test_thread (默认的处理串口数据线程)。at_costomer_thread(新增的FCT专用指令函数)在内的各种线程。

    从发送AT指令的线程入手:

    路径:vendor/qcom/proprietary/fastmmi/mmi/mmi.cpp

    实现了从各路串口中接收数据到buff,根据buff内容的不同而进行处理,我们修改FASTMMI的交互方式的关键就在于:

    1、判断现在的layout(图层)是否存在

    通过 get_main_module找到主界面(最后需要release_cur_layout释放互斥资源)

    通过lay = g_layout_map[mod->config_list[KEY_LAYOUT]];判断当前的lay是否存在(子界面)

    2、找到我们需要执行的按钮(例如,passfailed

    通过layout.find_button_by_name方法找到对应的按钮

    3、通过fastmmi的流程来“模拟”点击。

    获取按钮的功能:r->cb = btn->get_cb();

    执行按钮的功能:r->cb(r->module);

    即:

        /* step one: Get layout */
        module_info *mod = get_main_module();
        if(mod == NULL) {
            ALOGE("%s Not main screen OR Null point",__FUNCTION__);
            break;
        }
        layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];
        if(lay == NULL || lay->m_listview == NULL) {
            ALOGE("%s No Main layout",__FUNCTION__);
            break;
        }
    
        /* step two: Find button */	
        layout *curlay = acquire_cur_layout();
        button *btn = curlay->find_button_by_name("xxx");
    	if(btn==NULL) 
        {
            ALOGE("btn xxx not found");
            break;
        }
    
    	/* step three: exec button click */
    	runnable_t *r = new runnable_t;
    
        r->cb = btn->get_cb();
    	release_cur_layout(); 
        if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
            module_info *rmod = (module_info *)(r->module);
            r->cb(r->module);
        }else {
            ALOGE("btn function not found");
            break;
        }
    

    操作记录

    使用什么软件,做了什么操作,改动了什么代码

    根据上面的思路,发现FCT自动进入了GPS。不知道怎么回事(恢复了默认版本也一样),后面我使用r54分支进行调试。没有这个问题。

    好像是因为我的判断逻辑有错误导致的

    实现按键交互

    路径:vendor/qcom/proprietary/fastmmi/mmi/input.cpp

    确保按键能够向下传递

    我们最终的目的是为了能够在key_callback中添加我们需要的特殊处理。首先,需要关心按键事件能否向下传递。

    下列的2个函数实现了 按键事件处理以及是否向下传递分发(类似QT的事件发放):

    int input_callback(int fd, uint32_t revents, void *data) {
        struct input_event ev;
        int retval;
    
        retval = ev_get_input(fd, revents, &ev);
        if(retval < 0) {
            MMI_ALOGE("input event get fail
    ");
            return -1;
        }
    
        /**Adjust the value to match LCD resolution*/
        adjust_ev(&ev);
    
       /**Convert virtual key to KEY code*/
        hook_vkey(&ev, &g_key_map);
    
        /**Call listener, if return False mean stop here,
      * if return true mean continue process event.
      */
        if(!invoke_listener(ev)) // 如果 invoke_listener 处理了,则不再向下传递
            return 0;
    
        if(ev.type == EV_KEY) {
            key_callback(ev.type, ev.code, ev.value);
        } else if(ev.type == EV_SW) {
            sw_callback(ev.type, ev.code, ev.value);
        } else if(ev.type == EV_ABS || ev.type == EV_SYN) {
            touch_callback(&ev);
        }
    
        return 0;
    }
    
    static bool invoke_listener(input_event ev) {
        bool ret = true;
    
        pthread_mutex_lock(&g_listener_mutex);
        if(g_input_listener != NULL)
            ret = g_input_listener->dispatch_event(ev);
        pthread_mutex_unlock(&g_listener_mutex);
    
        return ret;
    }
    

    由于invoke_listener调用了dispatch_event,我们继续看dispatch_event是如何对某些按键进行特殊处理的。

    路径:vendor/qcom/proprietary/fastmmi/mmi/input_listener_key.cpp

    bool input_listener_key::dispatch_event(input_event ev) {
    
        layout *lay = this->get_lay();
        char btn_name[64] = { 0 };
    
        __u16 type = ev.type;
        __u16 code = ev.code;
        __u32 value = ev.value;
        mod_ev_t modev;
    
        modev.mod = this->get_module();
    
        int down = ! !value;
        
        if(type == EV_KEY) {
            switch (code) {
            case KEY_BACK: // 在 fastmmi中, KEY_BACK 对应的按键应该是 fail
                strlcpy(btn_name, KEY_FAIL, sizeof(btn_name));
                break;
    
            case KEY_HOMEPAGE:
                ev.code = KEY_HOME; //change the code to KEY_HOMEPAGE
    
    		// ...
            default:
                break;
            }
        }
        // ...
        
        button *btn = lay->find_button_by_name(btn_name);
    
        if(btn != NULL) {
            if(down) {
                MMI_ALOGI("button(%s) press down, code=%d", btn->get_name(), code);
                btn->set_color(255, 0, 0, 125);
                cb_t cb = this->get_cb();
    
                modev.ev = &ev;
                if(cb != NULL)
                    cb(&modev);
            } else {
                MMI_ALOGI("button(%s) release, code=%d", btn->get_name(), code);
                btn->set_color(255, 0, 0, 255);
            }
            MMI_ALOGW("Button Pushed");
    
            invalidate();
        } else {
            MMI_ALOGW("Not find button(%s) in layout(%s)", btn_name, lay->get_layout_path());
        }
    
    #if 0 // 解除对 back 键的屏蔽。
        // 关闭这2行,以使得返回值为真,则最后能够将事件传递下去
        if(code == KEY_BACK)
            return false;
    #endif
    
        return true;
    }
    
    

    处理按键

    现在,按下的按键能够调用key_callback了。我们只需要在这里进行我们要的特殊处理即可。

    路径:vendor/qcom/proprietary/fastmmi/mmi/input.cpp

    省略了针对 有关值的定义以及函数声明。

    static int key_callback(int type, int code, int value) {
        int down = ! !value;
    
        if(type != EV_KEY) {
            return 0;
        }
    
        if(!down) {
            return 0;
        }
        
        MMI_ALOGI("key:%d release", code);
        
        switch (code)
        {
                
    #if 1
        // 用于确认结果
        case KEY_BACK :
            mark_this_test_module_result(module_failed); // 新增的函数
        break; case KEY_ENTER:
            mark_this_test_module_result(module_success);
        break; default:
                return 0;
            break;
        }
    #endif
    
        return 0;
    }
    

    按照之前说的“流程分析”,我是这么写的:

    很简单,还是按照3步走。

    // vendor/qcom/proprietary/fastmmi/mmi/mmi.cpp
    
    // 用于手动标记测试结果成功或者失败
    void mark_this_test_module_result(bool result)
    {
        int key_test_result = 0;
        
        /* step one: Get layout */
        module_info *mod = get_main_module();
        if(mod == NULL) {
            MMI_ALOGE("%s Not main screen OR Null point",__FUNCTION__);
            return;
        }
    
        layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
        if(lay == NULL || lay->m_listview == NULL) {
            MMI_ALOGE("%s No Main layout",__FUNCTION__);
            return;
        }
        
        /* step two: Find button */
        layout *curlay = acquire_cur_layout();
        button *btn_pass = curlay->find_button_by_name("pass");
        button *btn_fail = curlay->find_button_by_name("fail");
    
        if(btn_pass == NULL && btn_fail == NULL){
            MMI_ALOGE("[%s] FCT Confirm Result Button is NULL", __FUNCTION__);
            release_cur_layout();
            return ;
        }
        
        /* step three: exec button click */
        runnable_t *r = new runnable_t;
    
        button *btn = NULL;
        r->cb = NULL;
        r->module = NULL;
    
        if(result == module_success) {
            MMI_ALOGI("[%s] FCT Confirm Result Button is PASS", __FUNCTION__);
            btn = btn_pass;
            btn->set_disabled(true);
            r->cb = btn->get_cb();
        }else  if( result == module_failed) {
            MMI_ALOGI("[%s] FCT Confirm Result Button is FAIL", __FUNCTION__);
            btn = btn_fail;
            btn->set_disabled(true);
            r->cb = btn->get_cb();
        }
    
        r->module = curlay->module;
        MMI_ALOGE("[%s]:[%s] Receive Terminal Signal:(E)", __FUNCTION__, curlay->module->module);
        release_cur_layout();
    
        if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
            module_info *rmod = (module_info *)(r->module);
            ALOGI("[%s] Callback Activated Module:%s", __FUNCTION__, rmod->module);
            r->cb(r->module);
        }
    
        if(btn != NULL && btn->get_disabled()){
            ALOGI("[%s] Btn Set Enable", __FUNCTION__);
            btn->set_disabled(false);
            btn = NULL;
        }
    
        return;
    }
    

    这样子就完成了。

    新建线程用于自动交互

    目的:实现在某个条件下,定期轮询获取需要执行的条目即可。

    做法:

    1、实现对应的功能,并实现每次调用某个函数则得到不同的结果。

    2、按着fastmmi的规范实现模拟按键功能点击(参考如上)

    3、在某个时候启动这条线程

    // 自动测试 功能 添加
    struct test_item_for_keypad_ui {
        char * module_name; // 对应的界面
        char * item_name;  // 测试项名称
        //int need_comfirm; // 如果这一项需要人工确认,则为1,能够自动测试,则为0
    };
    #define TEST_TIEM_ARRAY_SIZE(obj) (sizeof((obj))/sizeof(struct test_item_for_keypad_ui))
    
    struct test_item_for_keypad_ui  fct_test_item_array[] =
    {
        {"KEY",              "Key_Start",               1 }, // 需要处理对应的按键测试内容
        {"LCD",              "Lcd_Start",               1 },
        {"LCM_BACKLIGHT",    "Lcd_Backlight_Start",     1 },
        {"LED",              "Led_Start",               1 },
        {"BUTTON_BACKLIGHT", "Button_Backlight_Start",  1 },
        {"BLUETOOTH",        "BT_Start",                0 },
        {"WIFI",             "WIFI_Start",              0 },
        {"GPS",              "GPS_Start",               0 },
        {"NETSIGNAL",        "Netsignal_Start",         0 },
        {"SIMCARD1",         "Sim_Start",               0 },
        {"SDCARD",           "Sd_Start",                0 },
        {"BATTERY",          "Battery_Start",           0 },
        {"PRIMARY MIC",      "Primary_Mic_Start",       0 },
        {"SPEAKER",          "Speaker_Start" ,          0 },
        {"HANDSET PLAY",     "Headset_Start",           0 },
        {"HEADSET MIC",      "Headset_Mic_Start" ,      0 },
        {"GSENSOR",          "Gsensor_Start",           1 }, // 由于这一项会在内部提前结束,因此必须放在最后(调试这块的功能不再本文关心的范围内)
        // 以下部分 算是 保留项目
        //{"VERSION",          "Version_Start" },
    };
    
    static int item_index = 0;
    
    int get_next_test_item(char*buff)
    {
        if(!buff) return -1;
        if((item_index ) >= TEST_TIEM_ARRAY_SIZE(fct_test_item_array))
        {
            ALOGE("full
    ");
            sleep(5);
            return -1;
        }
        ALOGE("get cmd buff from array : [%s]
    ", fct_test_item_array[item_index].item_name);
        ALOGE("item_index / Max : %d/%ld
    ", item_index+1, TEST_TIEM_ARRAY_SIZE(fct_test_item_array));
        sprintf(buff, "%s", fct_test_item_array[item_index].item_name);
        item_index ++;
    
        return 1;
    }
    
    void reset_test_item_index(void)
    {
        item_index = 0;
    }
    
    void get_back_this_test_item(void)
    {
        if(item_index)
            item_index--;
    }
    
    int is_buff_in_test_array(char *buff)
    {
        int i;
        for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
        {
            if(!strcmp(buff, fct_test_item_array[i].item_name))
                return 1;
        }
        return 0;    
    }
    
    int is_mod_in_test_array(char *mod_name, char*buff)
    {
        int i;
        if(!mod_name || !buff) return -1;
        for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
        {
            // 找到对应的模块
            if(!strcmp(mod_name, fct_test_item_array[i].module_name))
            {
                // 判断此时的命令是否匹配
                if(!strcmp(buff, fct_test_item_array[i].item_name))
                return 1;
            }
                
        }
        return 0;    
    }
    
    /*! enum test_thread_loop_type
     *
     *  判断auto_loop_for_each_cmd_thread进入了哪个if
     */
    enum test_thread_loop_type {
        loop_no_set, // 默认状态
        loop_for_end, // 命令结束
        loop_for_build_in_auto_test, // 内置的自动测试命令
        loop_for_page_up_down, // 翻页命令
        loop_for_get_result, // 获取总结果的命令
        loop_for_test_single_item, // 匹配测试的每一项
    };
    
    static void *auto_loop_for_each_cmd_thread(void *)
    {
        int loop_type = loop_no_set;
        char buff[255];
        void *ate_module = NULL;
        bool ate_test = false;
        button *btn = NULL;
        int ret;
    
        ALOGE("Schips create auto_loop_for_each_cmd_thread 
    ");
    
        signal(SIGUSR1, signal_handler);
    	
        while(1)
        {
            sleep(1);
            memset(buff,0,255);
            // wait_for_auto_test(); 实现这个接口的阻塞等待与唤醒即可完成在某个时候自动执行
    loop_while:
            while((get_next_test_item(buff)==1))
            {
                ALOGE("this cmd is [%s] 
    ",buff);
    
                button *btn = NULL;
                ate_test = false;
    
                /*step one:Get the main layout */
                module_info *mod = get_main_module();
                if(mod == NULL) {
                    ALOGE("%s Not main screen",__FUNCTION__);
                    get_back_this_test_item();
                    break;
                }
                layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
                if(lay == NULL || lay->m_listview == NULL) {
                    ALOGE("%s No Main layout",__FUNCTION__);
                    get_back_this_test_item();
                    break;
                } else
                {
                    // 检查 当前是否 有 正在运行的模块
                    list < item_t * >*items = lay->m_listview->get_items();
                    list < item_t * >::iterator iter;
                    for(iter = items->begin(); iter != items->end(); iter++)
                    {
                        item_t *item = (item_t *) (*iter);
                        module_info *tmod = item->mod;
                        if(tmod->running_state == MODULE_RUNNING)
                        {
                            ALOGI("[%s] FCT module [%s] is in running,please waiting", __FUNCTION__, tmod->module);
                            ate_test = true;
                            get_back_this_test_item();
                            sleep(1);
                            goto loop_while;
                        }
                    }
                }
    
                runnable_t *r = new runnable_t;
                r->cb = NULL;
                r->module = NULL;
    			if(is_buff_in_test_array(buff)) // !strcmp(buff,"Key_Start") || !strcmp(buff,"Lcd_Start") || ...
                {
                    ALOGI("[%s][%d] Receive ATE Test Command:%s", __FUNCTION__, __LINE__, buff);
                    /*step two:check modules running state */ // 由于 这里还需要进行界面切换,因此需要先判断这一步。
                    if(lay != NULL && lay->m_listview != NULL)
                    {
                        list < item_t * >*items = lay->m_listview->get_items();
                        list < item_t * >::iterator iter;
                        for(iter = items->begin(); iter != items->end(); iter++)
                        {
                            item_t *item = (item_t *) (*iter);
                            module_info *tmod = item->mod;
                            if(tmod->running_state == MODULE_RUNNING){
                                ALOGI("[%s] FCT module %s is in running,please waiting", __FUNCTION__, tmod->module);
                                ate_test = true;
                            }
    
                            if(!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start"))
                            {
                                clear_all_pushed_keys();
                            }
    #if 0
                            if((!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start")) ||
                               (!strcmp(tmod->module,"LCD") && !strcmp(buff,"Lcd_Start")) ||
                               // ...
    #else
                               if(is_mod_in_test_array(tmod->module, buff))
    #endif
                            {
                                ALOGI("[%s] : [%s]-module, cmd is [%s]", __FUNCTION__, tmod->module, buff);
                                ate_module = tmod;
                                r->module = ate_module;
                                r->cb = lay->m_listview->get_cb();
                                //set_mmi_response(resp_buf_ok);
                                loop_type = loop_for_test_single_item;
                            }
                        }
                    }
                }
    
                if(ate_test)
                {
                    ate_module = NULL;
                    r->module = NULL;
                    r->cb = NULL;
                    get_back_this_test_item();
                    break;
                }
    
                // 执行动作
                if(loop_type == loop_no_set)
                {
                    ALOGE("[%s][%d] Nothing to do,Start Next Loop", __FUNCTION__, __LINE__);
                }else if((r != NULL) && (r->cb != NULL) && (r->module != NULL))
                {
                    module_info *rmod = (module_info *)(r->module);
                    ALOGI("[%s] Callback Activated Module:%s Command:%s", __FUNCTION__, rmod->module, buff);
                    ALOGE("[%s][%d] cmd is [%s]", __FUNCTION__, __LINE__, buff);
    
                    // 只关心单项测试
                    if(loop_type == loop_for_test_single_item)
                    {
                        r->cb(r->module);
                    }
                }
    
                if(btn != NULL && btn->get_disabled())
                {
                    ALOGI("[%s] Btn Set Enable", __FUNCTION__);
                    btn->set_disabled(false);
                    btn = NULL;
                }
                sleep(2);
    
            }
        }
    
        return NULL;
    }
    

    附录:分析r->cb(r->module);

    fastmmi是如何实现r->cb(r->module);的,其实我也很好奇,因为没有找到具体的按键功能实现。

    所以特意翻了一下代码,看了一下,大概知道是,如果“有添加布局的需求”,那么可以好好研究一下:

    1、标记按钮对应的按键功能。

    2、实现对应的方法,并绑定功能与对应的组件。(通过C++中STL的map的方式)

    其中涉及到 xml 的解析就不说了,纯应用层的东西,很多途径可以实现。

    mmi/config.cpp:166: } else if(!xmlStrcmp(attr->name, (const xmlChar *) "onclick")) {

    in vendor/qcom/proprietary/fastmmi
    ===============================
    # res/layout/layout_xxx.xml (任意一个)
    <layout>
    <!--
        ....
    -->
        <include layout="footer.xml"/>
    </layout>
    ===============================
    # res/layout/footer.xml
      <button
            name="pass"
            onclick="do_pass"
            text="btn_pass"
            h_rel="16"
            w_rel="49"
            x_rel="0"
            y_rel="84"
            color="0x007D7Dff" />
    ===============================
    # mmi/func_map.cpp:
    void process_exit(void *m) {
        if(m == NULL) return;
    
        module_info *mod = (module_info *) m;
        mod->running_state = MODULE_IDLE;
        flush_result();
        module_cleanup(mod);
        ALOGI("[%s]  Test finished with result =%d ", mod->module, mod->result);
        launch_main();
        usleep(100);
        sem_post(&g_sem_mod_complete);
    }
    
    void process_exit(void *m, int result) {
    
        if(m == NULL) {
            MMI_ALOGE("Invalid parameter");
            return;
        }
    
        module_info *mod = (module_info *) m;
    
        time(&mod->last_time);
        mod->duration = difftime(mod->last_time, mod->start_time);
        mod->result = result;
        MMI_ALOGI("[%s] Test finished with result=%s, test duration=%f seconds",
                  mod->module, MMI_TEST_RESULT(result), mod->duration);
        process_exit(m);
    }
    
    static void do_pass(void *m) {
        process_exit(m, SUCCESS);
    }
    
    static void do_fail(void *m) {
        sem_post(&g_sem_confirm);
        process_exit(m, FAILED);
    }
    
    static func_map_t func_list[] = {
        {"do_cancel", do_cancel},
        {"do_extra_cmd", do_extra_cmd},
        {"do_fail", do_fail},
        {"do_ok", do_ok},
        {"do_report", do_report},
        {"do_page_down", do_page_down},
        {"do_page_up", do_page_up},
        {"do_pass", do_pass},
        {"switch_module", switch_module},
        {"do_reboot", do_reboot},
        {"do_run_all", do_run_all},
        {"do_reset", do_reset},
        {"do_show_fail", do_show_fail},
        {"do_show_all", do_show_all},
    #ifdef ANDROID
        {"do_next", do_next},
    #endif
        {"do_exit", do_exit},
        {"onchange_poweroff", onchange_poweroff},
        {"onchange_reboot_ffbm", onchange_reboot_ffbm},
        {"onchange_reboot_android", onchange_reboot_android},
    };
    
    static unordered_map < string, cb_t > func_map;
    void create_func_map() {
        uint32_t i = 0;
    
        for(i = 0; i < sizeof(func_list) / sizeof(func_map_t); i++) {
            func_map[(string) func_list[i].name] = func_list[i].cb;
        }
    }
    
    cb_t get_cb(string func_name) {
        return func_map[func_name];
    }
    ===============================
    # libmmi/common.h
    typedef void (*cb_t) (void *);
    class module_info {
      public:
        char module[64];
        int socket_fd;
        int result;
        pid_t pid;
        int mode;
        int running_state;
        extra_cmd_t extracmd;
        time_t start_time;          //start test time
        double duration;            //test duration
        time_t last_time;           //last time to modify test result data
        char data[SIZE_512];        //module test data
    
          unordered_map < string, string > config_list;
          module_info(char *mod) {
            if(mod != NULL)
                strlcpy(module, mod, sizeof(module));
    
            memset(data, 0, sizeof(data));
            result = INT_MAX;
            pid = -1;
            socket_fd = -1;
            extracmd.is_valid = false;
            running_state = MODULE_IDLE;
        }
    };
    
    typedef struct {
        char name[32];
        cb_t cb;
    } func_map_t;
    ===============================
    # mmi/config.cpp
    static void parse_button(xmlNodePtr node, button * btn) {
        xmlAttrPtr attr;
        rect_t rect;
    
        attr = node->properties;
        while(attr != NULL) {
            char *value = (char *) xmlGetProp(node, (const xmlChar *) attr->name);
    
            if(value != NULL) {
                if(!xmlStrcmp(attr->name, (const xmlChar *) "name")) {
                    btn->set_name(value);
                } else if(!xmlStrcmp(attr->name, (const xmlChar *) "text"))
                    btn->set_text(get_string(value));
                else if(!xmlStrcmp(attr->name, (const xmlChar *) "image")) {
                    btn->set_image(value);
                } else if(!xmlStrcmp(attr->name, (const xmlChar *) "onclick")) { // 这里对应的是值是 do_pass、do_failed 等
                    btn->set_cb(get_cb(value));
                } else if // ...
                } else if(!xmlStrcmp(attr->name, (const xmlChar *) "visibility")) {
                    if(!strcmp("invisible", value))
                        btn->set_visibility(false);
                    else
                        btn->set_visibility(true);
                }
    
                xmlFree(value);
            }
    
            attr = attr->next;
        }
        btn->set_rect(&rect);
    }
    

    注:fastmmi 的代码看着还是可圈可点的,供学习的地方也很多。这里提到的事件分发和功能组件化映射只是其中的一小部分。

    附录:机器上的按键值

    getevent -l
    ===============================================
    add device 5: /dev/input/event3
      name:     "gpio-keys"
      # .
    /dev/input/event3: EV_KEY       KEY_F2               DOWN
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    /dev/input/event3: EV_KEY       KEY_F2               UP
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
     #  PTT
    /dev/input/event3: EV_KEY       KEY_F1               DOWN
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    /dev/input/event3: EV_KEY       KEY_F1               UP
     #  ..
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    /dev/input/event3: EV_KEY       KEY_F3               DOWN
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    /dev/input/event3: EV_KEY       KEY_F3               UP
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
     # emergency
    /dev/input/event3: EV_KEY       KEY_F4               DOWN
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    /dev/input/event3: EV_KEY       KEY_F4               UP
    /dev/input/event3: EV_SYN       SYN_REPORT           00000000
    ===============================================
    add device 3: /dev/input/event2
      name:     "qpnp_pon"
      # trick-power
      旋钮 的 按键电源键(POWER, OK)
    /dev/input/event2: EV_KEY       KEY_POWER            DOWN
    /dev/input/event2: EV_SYN       SYN_REPORT           00000000
    /dev/input/event2: EV_KEY       KEY_POWER            UP
    /dev/input/event2: EV_SYN       SYN_REPORT           00000000
    ===============================================
    add device 4: /dev/input/event1
      name:     "gpiokey-pulley"
      旋钮 相关 : 音量 上下(OK)
      # anticlockwise
    /dev/input/event1: EV_KEY       KEY_VOLUMEUP         DOWN
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_KEY       KEY_VOLUMEUP         UP
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
      # clockwise
    /dev/input/event1: EV_KEY       KEY_VOLUMEDOWN       DOWN
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_KEY       KEY_VOLUMEDOWN       UP
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    ===============================================
    add device 6: /dev/input/event0
      name:     "aw9523-keys"
          
    ..  ↑  ..
       ← →
    拨号键 电源键
    1   2   3
    4   5   6
    7   8   9
    *   0   #
          
    	# ..
    /dev/input/event0: EV_KEY       KEY_ENTER            DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_ENTER            UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
     	#  ↑
    /dev/input/event0: EV_KEY       KEY_UP               DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_UP               UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
         # ..
    /dev/input/event0: EV_KEY       KEY_BACK             DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_BACK             UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # ←
    /dev/input/event0: EV_KEY       KEY_LEFT             DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_LEFT             UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # →
    /dev/input/event0: EV_KEY       KEY_RIGHT            DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_RIGHT            UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 拨号键
    /dev/input/event0: EV_KEY       KEY_SEND             DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_SEND             UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # ↓
    /dev/input/event0: EV_KEY       KEY_DOWN             DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_DOWN             UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 电源键(挂断键)
    /dev/input/event0: EV_KEY       KEY_ESC              DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_ESC              UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 1
    /dev/input/event0: EV_KEY       KEY_1                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_1                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 2
    /dev/input/event0: EV_KEY       KEY_2                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_2                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 3
    /dev/input/event0: EV_KEY       KEY_3                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_3                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 4
    /dev/input/event0: EV_KEY       KEY_4                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_4                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 5
    /dev/input/event0: EV_KEY       KEY_5                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_5                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 6
    /dev/input/event0: EV_KEY       KEY_6                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_6                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 7
    /dev/input/event0: EV_KEY       KEY_7                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_7                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 8
    /dev/input/event0: EV_KEY       KEY_8                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_8                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 9
    /dev/input/event0: EV_KEY       KEY_9                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_9                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # *
    /dev/input/event0: EV_KEY       KEY_NUMERIC_STAR     DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_NUMERIC_STAR     UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # 0
    /dev/input/event0: EV_KEY       KEY_0                DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_0                UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
        # #
    /dev/input/event0: EV_KEY       KEY_NUMERIC_POUND    DOWN
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    /dev/input/event0: EV_KEY       KEY_NUMERIC_POUND    UP
    /dev/input/event0: EV_SYN       SYN_REPORT           00000000
    
    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    3.Golang运算符
    下载nodejs和vue
    快速排序
    apache部署react刷新路由导致错误The requested URL * was not found on this server.
    关于数据可水平拆分和垂直拆分的学习记录
    javascript 类流程图控件
    Jenkins 安装和配置
    【前端开发】跨域请求如何携带cookie
    【前端开发】基于vue+elementui架构的图片上传实时预览demo
    【前端开发】Promise几种常见用法
  • 原文地址:https://www.cnblogs.com/schips/p/enhance_the_way_of_ui_in_qualcomm_fastmmi.html
Copyright © 2020-2023  润新知