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

背景

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

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

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

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

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

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

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

大概是这样子的:

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

行动目标

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.

源码树如下:

  1. The FASTMMI code is in the folder vendor/qcom/proprietary/fastmmi. Check the source file structure below.
  2. .
  3. ├── Android.mk
  4. ├── configure.ac
  5. ├── libmmi
  6. ...
  7. ├── Makefile.am
  8. ├── mmi
  9. ...
  10. ├── mmi.mk
  11. ├── module
  12.    ├── cpu
  13.       ├── Android.mk
  14.       ├── cpu.cpp
  15.       └── Makefile.am
  16. ...
  17.    └── wifi
  18.    ├── Android.mk
  19.    ├── Makefile.am
  20.    ├── wifi.cpp
  21.    └── wifi_uav.cpp
  22. └── res
  23. ...
  24. ├── layout
  25.    ├── ...
  26.    ├── layout_wifi.xml
  27.    ├── main_wear.xml
  28.    └── main.xml
  29. ├── raw
  30.    ├── Android.mk
  31.    ├── DroidSansFallback.ttf
  32.    ├── LICENSE
  33.    ├── NOTICE
  34.    └── qualsound.wav
  35. ├── values
  36.    ├── Android.mk
  37.    ├── path_config_android.xml
  38.    ├── path_config_le.xml
  39.    ├── strings.xml
  40.    └── strings-zh-rCN.xml
  41. └── wifi_config
  42. ├── Android.mk
  43. └── 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);

即:

  1. /* step one: Get layout */
  2. module_info *mod = get_main_module();
  3. if(mod == NULL) {
  4. ALOGE("%s Not main screen OR Null point",__FUNCTION__);
  5. break;
  6. }
  7. layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];
  8. if(lay == NULL || lay->m_listview == NULL) {
  9. ALOGE("%s No Main layout",__FUNCTION__);
  10. break;
  11. }
  12. /* step two: Find button */
  13. layout *curlay = acquire_cur_layout();
  14. button *btn = curlay->find_button_by_name("xxx");
  15. if(btn==NULL)
  16. {
  17. ALOGE("btn xxx not found");
  18. break;
  19. }
  20. /* step three: exec button click */
  21. runnable_t *r = new runnable_t;
  22. r->cb = btn->get_cb();
  23. release_cur_layout();
  24. if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
  25. module_info *rmod = (module_info *)(r->module);
  26. r->cb(r->module);
  27. }else {
  28. ALOGE("btn function not found");
  29. break;
  30. }

操作记录

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

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

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

实现按键交互

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

确保按键能够向下传递

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

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

  1. int input_callback(int fd, uint32_t revents, void *data) {
  2. struct input_event ev;
  3. int retval;
  4. retval = ev_get_input(fd, revents, &ev);
  5. if(retval < 0) {
  6. MMI_ALOGE("input event get fail\n");
  7. return -1;
  8. }
  9. /**Adjust the value to match LCD resolution*/
  10. adjust_ev(&ev);
  11. /**Convert virtual key to KEY code*/
  12. hook_vkey(&ev, &g_key_map);
  13. /**Call listener, if return False mean stop here,
  14. * if return true mean continue process event.
  15. */
  16. if(!invoke_listener(ev)) // 如果 invoke_listener 处理了,则不再向下传递
  17. return 0;
  18. if(ev.type == EV_KEY) {
  19. key_callback(ev.type, ev.code, ev.value);
  20. } else if(ev.type == EV_SW) {
  21. sw_callback(ev.type, ev.code, ev.value);
  22. } else if(ev.type == EV_ABS || ev.type == EV_SYN) {
  23. touch_callback(&ev);
  24. }
  25. return 0;
  26. }
  27. static bool invoke_listener(input_event ev) {
  28. bool ret = true;
  29. pthread_mutex_lock(&g_listener_mutex);
  30. if(g_input_listener != NULL)
  31. ret = g_input_listener->dispatch_event(ev);
  32. pthread_mutex_unlock(&g_listener_mutex);
  33. return ret;
  34. }

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

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

  1. bool input_listener_key::dispatch_event(input_event ev) {
  2. layout *lay = this->get_lay();
  3. char btn_name[64] = { 0 };
  4. __u16 type = ev.type;
  5. __u16 code = ev.code;
  6. __u32 value = ev.value;
  7. mod_ev_t modev;
  8. modev.mod = this->get_module();
  9. int down = ! !value;
  10. if(type == EV_KEY) {
  11. switch (code) {
  12. case KEY_BACK: // 在 fastmmi中, KEY_BACK 对应的按键应该是 fail
  13. strlcpy(btn_name, KEY_FAIL, sizeof(btn_name));
  14. break;
  15. case KEY_HOMEPAGE:
  16. ev.code = KEY_HOME; //change the code to KEY_HOMEPAGE
  17. // ...
  18. default:
  19. break;
  20. }
  21. }
  22. // ...
  23. button *btn = lay->find_button_by_name(btn_name);
  24. if(btn != NULL) {
  25. if(down) {
  26. MMI_ALOGI("button(%s) press down, code=%d", btn->get_name(), code);
  27. btn->set_color(255, 0, 0, 125);
  28. cb_t cb = this->get_cb();
  29. modev.ev = &ev;
  30. if(cb != NULL)
  31. cb(&modev);
  32. } else {
  33. MMI_ALOGI("button(%s) release, code=%d", btn->get_name(), code);
  34. btn->set_color(255, 0, 0, 255);
  35. }
  36. MMI_ALOGW("Button Pushed");
  37. invalidate();
  38. } else {
  39. MMI_ALOGW("Not find button(%s) in layout(%s)", btn_name, lay->get_layout_path());
  40. }
  41. #if 0 // 解除对 back 键的屏蔽。
  42. // 关闭这2行,以使得返回值为真,则最后能够将事件传递下去
  43. if(code == KEY_BACK)
  44. return false;
  45. #endif
  46. return true;
  47. }

处理按键

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

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

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

  1. static int key_callback(int type, int code, int value) {
  2. int down = ! !value;
  3. if(type != EV_KEY) {
  4. return 0;
  5. }
  6. if(!down) {
  7. return 0;
  8. }
  9. MMI_ALOGI("key:%d release", code);
  10. switch (code)
  11. {
  12. #if 1
  13. // 用于确认结果
  14. case KEY_BACK :
  15. mark_this_test_module_result(module_failed); // 新增的函数
  16. break; case KEY_ENTER:
  17. mark_this_test_module_result(module_success);
  18. break; default:
  19. return 0;
  20. break;
  21. }
  22. #endif
  23. return 0;
  24. }

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

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

  1. // vendor/qcom/proprietary/fastmmi/mmi/mmi.cpp
  2. // 用于手动标记测试结果成功或者失败
  3. void mark_this_test_module_result(bool result)
  4. {
  5. int key_test_result = 0;
  6. /* step one: Get layout */
  7. module_info *mod = get_main_module();
  8. if(mod == NULL) {
  9. MMI_ALOGE("%s Not main screen OR Null point",__FUNCTION__);
  10. return;
  11. }
  12. layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
  13. if(lay == NULL || lay->m_listview == NULL) {
  14. MMI_ALOGE("%s No Main layout",__FUNCTION__);
  15. return;
  16. }
  17. /* step two: Find button */
  18. layout *curlay = acquire_cur_layout();
  19. button *btn_pass = curlay->find_button_by_name("pass");
  20. button *btn_fail = curlay->find_button_by_name("fail");
  21. if(btn_pass == NULL && btn_fail == NULL){
  22. MMI_ALOGE("[%s] FCT Confirm Result Button is NULL", __FUNCTION__);
  23. release_cur_layout();
  24. return ;
  25. }
  26. /* step three: exec button click */
  27. runnable_t *r = new runnable_t;
  28. button *btn = NULL;
  29. r->cb = NULL;
  30. r->module = NULL;
  31. if(result == module_success) {
  32. MMI_ALOGI("[%s] FCT Confirm Result Button is PASS", __FUNCTION__);
  33. btn = btn_pass;
  34. btn->set_disabled(true);
  35. r->cb = btn->get_cb();
  36. }else if( result == module_failed) {
  37. MMI_ALOGI("[%s] FCT Confirm Result Button is FAIL", __FUNCTION__);
  38. btn = btn_fail;
  39. btn->set_disabled(true);
  40. r->cb = btn->get_cb();
  41. }
  42. r->module = curlay->module;
  43. MMI_ALOGE("[%s]:[%s] Receive Terminal Signal:(E)", __FUNCTION__, curlay->module->module);
  44. release_cur_layout();
  45. if((r != NULL) && (r->cb != NULL) && (r->module != NULL)) {
  46. module_info *rmod = (module_info *)(r->module);
  47. ALOGI("[%s] Callback Activated Module:%s", __FUNCTION__, rmod->module);
  48. r->cb(r->module);
  49. }
  50. if(btn != NULL && btn->get_disabled()){
  51. ALOGI("[%s] Btn Set Enable", __FUNCTION__);
  52. btn->set_disabled(false);
  53. btn = NULL;
  54. }
  55. return;
  56. }

这样子就完成了。

新建线程用于自动交互

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

做法:

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

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

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

  1. // 自动测试 功能 添加
  2. struct test_item_for_keypad_ui {
  3. char * module_name; // 对应的界面
  4. char * item_name; // 测试项名称
  5. //int need_comfirm; // 如果这一项需要人工确认,则为1,能够自动测试,则为0
  6. };
  7. #define TEST_TIEM_ARRAY_SIZE(obj) (sizeof((obj))/sizeof(struct test_item_for_keypad_ui))
  8. struct test_item_for_keypad_ui fct_test_item_array[] =
  9. {
  10. {"KEY", "Key_Start", 1 }, // 需要处理对应的按键测试内容
  11. {"LCD", "Lcd_Start", 1 },
  12. {"LCM_BACKLIGHT", "Lcd_Backlight_Start", 1 },
  13. {"LED", "Led_Start", 1 },
  14. {"BUTTON_BACKLIGHT", "Button_Backlight_Start", 1 },
  15. {"BLUETOOTH", "BT_Start", 0 },
  16. {"WIFI", "WIFI_Start", 0 },
  17. {"GPS", "GPS_Start", 0 },
  18. {"NETSIGNAL", "Netsignal_Start", 0 },
  19. {"SIMCARD1", "Sim_Start", 0 },
  20. {"SDCARD", "Sd_Start", 0 },
  21. {"BATTERY", "Battery_Start", 0 },
  22. {"PRIMARY MIC", "Primary_Mic_Start", 0 },
  23. {"SPEAKER", "Speaker_Start" , 0 },
  24. {"HANDSET PLAY", "Headset_Start", 0 },
  25. {"HEADSET MIC", "Headset_Mic_Start" , 0 },
  26. {"GSENSOR", "Gsensor_Start", 1 }, // 由于这一项会在内部提前结束,因此必须放在最后(调试这块的功能不再本文关心的范围内)
  27. // 以下部分 算是 保留项目
  28. //{"VERSION", "Version_Start" },
  29. };
  30. static int item_index = 0;
  31. int get_next_test_item(char*buff)
  32. {
  33. if(!buff) return -1;
  34. if((item_index ) >= TEST_TIEM_ARRAY_SIZE(fct_test_item_array))
  35. {
  36. ALOGE("full\n");
  37. sleep(5);
  38. return -1;
  39. }
  40. ALOGE("get cmd buff from array : [%s]\n", fct_test_item_array[item_index].item_name);
  41. ALOGE("item_index / Max : %d/%ld\n", item_index+1, TEST_TIEM_ARRAY_SIZE(fct_test_item_array));
  42. sprintf(buff, "%s", fct_test_item_array[item_index].item_name);
  43. item_index ++;
  44. return 1;
  45. }
  46. void reset_test_item_index(void)
  47. {
  48. item_index = 0;
  49. }
  50. void get_back_this_test_item(void)
  51. {
  52. if(item_index)
  53. item_index--;
  54. }
  55. int is_buff_in_test_array(char *buff)
  56. {
  57. int i;
  58. for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
  59. {
  60. if(!strcmp(buff, fct_test_item_array[i].item_name))
  61. return 1;
  62. }
  63. return 0;
  64. }
  65. int is_mod_in_test_array(char *mod_name, char*buff)
  66. {
  67. int i;
  68. if(!mod_name || !buff) return -1;
  69. for(i = 0; i < TEST_TIEM_ARRAY_SIZE(fct_test_item_array); i++)
  70. {
  71. // 找到对应的模块
  72. if(!strcmp(mod_name, fct_test_item_array[i].module_name))
  73. {
  74. // 判断此时的命令是否匹配
  75. if(!strcmp(buff, fct_test_item_array[i].item_name))
  76. return 1;
  77. }
  78. }
  79. return 0;
  80. }
  81. /*! \enum test_thread_loop_type
  82. *
  83. * 判断auto_loop_for_each_cmd_thread进入了哪个if
  84. */
  85. enum test_thread_loop_type {
  86. loop_no_set, // 默认状态
  87. loop_for_end, // 命令结束
  88. loop_for_build_in_auto_test, // 内置的自动测试命令
  89. loop_for_page_up_down, // 翻页命令
  90. loop_for_get_result, // 获取总结果的命令
  91. loop_for_test_single_item, // 匹配测试的每一项
  92. };
  93. static void *auto_loop_for_each_cmd_thread(void *)
  94. {
  95. int loop_type = loop_no_set;
  96. char buff[255];
  97. void *ate_module = NULL;
  98. bool ate_test = false;
  99. button *btn = NULL;
  100. int ret;
  101. ALOGE("Schips create auto_loop_for_each_cmd_thread \n");
  102. signal(SIGUSR1, signal_handler);
  103. while(1)
  104. {
  105. sleep(1);
  106. memset(buff,0,255);
  107. // wait_for_auto_test(); 实现这个接口的阻塞等待与唤醒即可完成在某个时候自动执行
  108. loop_while:
  109. while((get_next_test_item(buff)==1))
  110. {
  111. ALOGE("this cmd is [%s] \n",buff);
  112. button *btn = NULL;
  113. ate_test = false;
  114. /*step one:Get the main layout */
  115. module_info *mod = get_main_module();
  116. if(mod == NULL) {
  117. ALOGE("%s Not main screen",__FUNCTION__);
  118. get_back_this_test_item();
  119. break;
  120. }
  121. layout *lay = g_layout_map[mod->config_list[KEY_LAYOUT]];;
  122. if(lay == NULL || lay->m_listview == NULL) {
  123. ALOGE("%s No Main layout",__FUNCTION__);
  124. get_back_this_test_item();
  125. break;
  126. } else
  127. {
  128. // 检查 当前是否 有 正在运行的模块
  129. list < item_t * >*items = lay->m_listview->get_items();
  130. list < item_t * >::iterator iter;
  131. for(iter = items->begin(); iter != items->end(); iter++)
  132. {
  133. item_t *item = (item_t *) (*iter);
  134. module_info *tmod = item->mod;
  135. if(tmod->running_state == MODULE_RUNNING)
  136. {
  137. ALOGI("[%s] FCT module [%s] is in running,please waiting", __FUNCTION__, tmod->module);
  138. ate_test = true;
  139. get_back_this_test_item();
  140. sleep(1);
  141. goto loop_while;
  142. }
  143. }
  144. }
  145. runnable_t *r = new runnable_t;
  146. r->cb = NULL;
  147. r->module = NULL;
  148. if(is_buff_in_test_array(buff)) // !strcmp(buff,"Key_Start") || !strcmp(buff,"Lcd_Start") || ...
  149. {
  150. ALOGI("[%s][%d] Receive ATE Test Command:%s", __FUNCTION__, __LINE__, buff);
  151. /*step two:check modules running state */ // 由于 这里还需要进行界面切换,因此需要先判断这一步。
  152. if(lay != NULL && lay->m_listview != NULL)
  153. {
  154. list < item_t * >*items = lay->m_listview->get_items();
  155. list < item_t * >::iterator iter;
  156. for(iter = items->begin(); iter != items->end(); iter++)
  157. {
  158. item_t *item = (item_t *) (*iter);
  159. module_info *tmod = item->mod;
  160. if(tmod->running_state == MODULE_RUNNING){
  161. ALOGI("[%s] FCT module %s is in running,please waiting", __FUNCTION__, tmod->module);
  162. ate_test = true;
  163. }
  164. if(!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start"))
  165. {
  166. clear_all_pushed_keys();
  167. }
  168. #if 0
  169. if((!strcmp(tmod->module,"KEY") && !strcmp(buff,"Key_Start")) ||
  170. (!strcmp(tmod->module,"LCD") && !strcmp(buff,"Lcd_Start")) ||
  171. // ...
  172. #else
  173. if(is_mod_in_test_array(tmod->module, buff))
  174. #endif
  175. {
  176. ALOGI("[%s] : [%s]-module, cmd is [%s]", __FUNCTION__, tmod->module, buff);
  177. ate_module = tmod;
  178. r->module = ate_module;
  179. r->cb = lay->m_listview->get_cb();
  180. //set_mmi_response(resp_buf_ok);
  181. loop_type = loop_for_test_single_item;
  182. }
  183. }
  184. }
  185. }
  186. if(ate_test)
  187. {
  188. ate_module = NULL;
  189. r->module = NULL;
  190. r->cb = NULL;
  191. get_back_this_test_item();
  192. break;
  193. }
  194. // 执行动作
  195. if(loop_type == loop_no_set)
  196. {
  197. ALOGE("[%s][%d] Nothing to do,Start Next Loop", __FUNCTION__, __LINE__);
  198. }else if((r != NULL) && (r->cb != NULL) && (r->module != NULL))
  199. {
  200. module_info *rmod = (module_info *)(r->module);
  201. ALOGI("[%s] Callback Activated Module:%s Command:%s", __FUNCTION__, rmod->module, buff);
  202. ALOGE("[%s][%d] cmd is [%s]", __FUNCTION__, __LINE__, buff);
  203. // 只关心单项测试
  204. if(loop_type == loop_for_test_single_item)
  205. {
  206. r->cb(r->module);
  207. }
  208. }
  209. if(btn != NULL && btn->get_disabled())
  210. {
  211. ALOGI("[%s] Btn Set Enable", __FUNCTION__);
  212. btn->set_disabled(false);
  213. btn = NULL;
  214. }
  215. sleep(2);
  216. }
  217. }
  218. return NULL;
  219. }

附录:分析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")) {

  1. in vendor/qcom/proprietary/fastmmi
  2. ===============================
  3. # res/layout/layout_xxx.xml (任意一个)
  4. <layout>
  5. <!--
  6. ....
  7. -->
  8. <include layout="footer.xml"/>
  9. </layout>
  10. ===============================
  11. # res/layout/footer.xml
  12. <button
  13. name="pass"
  14. onclick="do_pass"
  15. text="btn_pass"
  16. h_rel="16"
  17. w_rel="49"
  18. x_rel="0"
  19. y_rel="84"
  20. color="0x007D7Dff" />
  21. ===============================
  22. # mmi/func_map.cpp:
  23. void process_exit(void *m) {
  24. if(m == NULL) return;
  25. module_info *mod = (module_info *) m;
  26. mod->running_state = MODULE_IDLE;
  27. flush_result();
  28. module_cleanup(mod);
  29. ALOGI("[%s] Test finished with result =%d ", mod->module, mod->result);
  30. launch_main();
  31. usleep(100);
  32. sem_post(&g_sem_mod_complete);
  33. }
  34. void process_exit(void *m, int result) {
  35. if(m == NULL) {
  36. MMI_ALOGE("Invalid parameter");
  37. return;
  38. }
  39. module_info *mod = (module_info *) m;
  40. time(&mod->last_time);
  41. mod->duration = difftime(mod->last_time, mod->start_time);
  42. mod->result = result;
  43. MMI_ALOGI("[%s] Test finished with result=%s, test duration=%f seconds",
  44. mod->module, MMI_TEST_RESULT(result), mod->duration);
  45. process_exit(m);
  46. }
  47. static void do_pass(void *m) {
  48. process_exit(m, SUCCESS);
  49. }
  50. static void do_fail(void *m) {
  51. sem_post(&g_sem_confirm);
  52. process_exit(m, FAILED);
  53. }
  54. static func_map_t func_list[] = {
  55. {"do_cancel", do_cancel},
  56. {"do_extra_cmd", do_extra_cmd},
  57. {"do_fail", do_fail},
  58. {"do_ok", do_ok},
  59. {"do_report", do_report},
  60. {"do_page_down", do_page_down},
  61. {"do_page_up", do_page_up},
  62. {"do_pass", do_pass},
  63. {"switch_module", switch_module},
  64. {"do_reboot", do_reboot},
  65. {"do_run_all", do_run_all},
  66. {"do_reset", do_reset},
  67. {"do_show_fail", do_show_fail},
  68. {"do_show_all", do_show_all},
  69. #ifdef ANDROID
  70. {"do_next", do_next},
  71. #endif
  72. {"do_exit", do_exit},
  73. {"onchange_poweroff", onchange_poweroff},
  74. {"onchange_reboot_ffbm", onchange_reboot_ffbm},
  75. {"onchange_reboot_android", onchange_reboot_android},
  76. };
  77. static unordered_map < string, cb_t > func_map;
  78. void create_func_map() {
  79. uint32_t i = 0;
  80. for(i = 0; i < sizeof(func_list) / sizeof(func_map_t); i++) {
  81. func_map[(string) func_list[i].name] = func_list[i].cb;
  82. }
  83. }
  84. cb_t get_cb(string func_name) {
  85. return func_map[func_name];
  86. }
  87. ===============================
  88. # libmmi/common.h
  89. typedef void (*cb_t) (void *);
  90. class module_info {
  91. public:
  92. char module[64];
  93. int socket_fd;
  94. int result;
  95. pid_t pid;
  96. int mode;
  97. int running_state;
  98. extra_cmd_t extracmd;
  99. time_t start_time; //start test time
  100. double duration; //test duration
  101. time_t last_time; //last time to modify test result data
  102. char data[SIZE_512]; //module test data
  103. unordered_map < string, string > config_list;
  104. module_info(char *mod) {
  105. if(mod != NULL)
  106. strlcpy(module, mod, sizeof(module));
  107. memset(data, 0, sizeof(data));
  108. result = INT_MAX;
  109. pid = -1;
  110. socket_fd = -1;
  111. extracmd.is_valid = false;
  112. running_state = MODULE_IDLE;
  113. }
  114. };
  115. typedef struct {
  116. char name[32];
  117. cb_t cb;
  118. } func_map_t;
  119. ===============================
  120. # mmi/config.cpp
  121. static void parse_button(xmlNodePtr node, button * btn) {
  122. xmlAttrPtr attr;
  123. rect_t rect;
  124. attr = node->properties;
  125. while(attr != NULL) {
  126. char *value = (char *) xmlGetProp(node, (const xmlChar *) attr->name);
  127. if(value != NULL) {
  128. if(!xmlStrcmp(attr->name, (const xmlChar *) "name")) {
  129. btn->set_name(value);
  130. } else if(!xmlStrcmp(attr->name, (const xmlChar *) "text"))
  131. btn->set_text(get_string(value));
  132. else if(!xmlStrcmp(attr->name, (const xmlChar *) "image")) {
  133. btn->set_image(value);
  134. } else if(!xmlStrcmp(attr->name, (const xmlChar *) "onclick")) { // 这里对应的是值是 do_pass、do_failed 等
  135. btn->set_cb(get_cb(value));
  136. } else if // ...
  137. } else if(!xmlStrcmp(attr->name, (const xmlChar *) "visibility")) {
  138. if(!strcmp("invisible", value))
  139. btn->set_visibility(false);
  140. else
  141. btn->set_visibility(true);
  142. }
  143. xmlFree(value);
  144. }
  145. attr = attr->next;
  146. }
  147. btn->set_rect(&rect);
  148. }

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

附录:机器上的按键值

  1. getevent -l
  2. ===============================================
  3. add device 5: /dev/input/event3
  4. name: "gpio-keys"
  5. # .
  6. /dev/input/event3: EV_KEY KEY_F2 DOWN
  7. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  8. /dev/input/event3: EV_KEY KEY_F2 UP
  9. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  10. # PTT
  11. /dev/input/event3: EV_KEY KEY_F1 DOWN
  12. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  13. /dev/input/event3: EV_KEY KEY_F1 UP
  14. # ..
  15. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  16. /dev/input/event3: EV_KEY KEY_F3 DOWN
  17. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  18. /dev/input/event3: EV_KEY KEY_F3 UP
  19. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  20. # emergency
  21. /dev/input/event3: EV_KEY KEY_F4 DOWN
  22. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  23. /dev/input/event3: EV_KEY KEY_F4 UP
  24. /dev/input/event3: EV_SYN SYN_REPORT 00000000
  25. ===============================================
  26. add device 3: /dev/input/event2
  27. name: "qpnp_pon"
  28. # trick-power
  29. 旋钮 按键电源键(POWER, OK
  30. /dev/input/event2: EV_KEY KEY_POWER DOWN
  31. /dev/input/event2: EV_SYN SYN_REPORT 00000000
  32. /dev/input/event2: EV_KEY KEY_POWER UP
  33. /dev/input/event2: EV_SYN SYN_REPORT 00000000
  34. ===============================================
  35. add device 4: /dev/input/event1
  36. name: "gpiokey-pulley"
  37. 旋钮 相关 音量 上下(OK
  38. # anticlockwise
  39. /dev/input/event1: EV_KEY KEY_VOLUMEUP DOWN
  40. /dev/input/event1: EV_SYN SYN_REPORT 00000000
  41. /dev/input/event1: EV_KEY KEY_VOLUMEUP UP
  42. /dev/input/event1: EV_SYN SYN_REPORT 00000000
  43. # clockwise
  44. /dev/input/event1: EV_KEY KEY_VOLUMEDOWN DOWN
  45. /dev/input/event1: EV_SYN SYN_REPORT 00000000
  46. /dev/input/event1: EV_KEY KEY_VOLUMEDOWN UP
  47. /dev/input/event1: EV_SYN SYN_REPORT 00000000
  48. ===============================================
  49. add device 6: /dev/input/event0
  50. name: "aw9523-keys"
  51. .. ..

  52. 拨号键 电源键
  53. 1 2 3
  54. 4 5 6
  55. 7 8 9
  56. * 0 #
  57. # ..
  58. /dev/input/event0: EV_KEY KEY_ENTER DOWN
  59. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  60. /dev/input/event0: EV_KEY KEY_ENTER UP
  61. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  62. # ↑
  63. /dev/input/event0: EV_KEY KEY_UP DOWN
  64. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  65. /dev/input/event0: EV_KEY KEY_UP UP
  66. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  67. # ..
  68. /dev/input/event0: EV_KEY KEY_BACK DOWN
  69. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  70. /dev/input/event0: EV_KEY KEY_BACK UP
  71. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  72. # ←
  73. /dev/input/event0: EV_KEY KEY_LEFT DOWN
  74. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  75. /dev/input/event0: EV_KEY KEY_LEFT UP
  76. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  77. # →
  78. /dev/input/event0: EV_KEY KEY_RIGHT DOWN
  79. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  80. /dev/input/event0: EV_KEY KEY_RIGHT UP
  81. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  82. # 拨号键
  83. /dev/input/event0: EV_KEY KEY_SEND DOWN
  84. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  85. /dev/input/event0: EV_KEY KEY_SEND UP
  86. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  87. # ↓
  88. /dev/input/event0: EV_KEY KEY_DOWN DOWN
  89. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  90. /dev/input/event0: EV_KEY KEY_DOWN UP
  91. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  92. # 电源键(挂断键)
  93. /dev/input/event0: EV_KEY KEY_ESC DOWN
  94. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  95. /dev/input/event0: EV_KEY KEY_ESC UP
  96. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  97. # 1
  98. /dev/input/event0: EV_KEY KEY_1 DOWN
  99. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  100. /dev/input/event0: EV_KEY KEY_1 UP
  101. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  102. # 2
  103. /dev/input/event0: EV_KEY KEY_2 DOWN
  104. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  105. /dev/input/event0: EV_KEY KEY_2 UP
  106. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  107. # 3
  108. /dev/input/event0: EV_KEY KEY_3 DOWN
  109. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  110. /dev/input/event0: EV_KEY KEY_3 UP
  111. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  112. # 4
  113. /dev/input/event0: EV_KEY KEY_4 DOWN
  114. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  115. /dev/input/event0: EV_KEY KEY_4 UP
  116. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  117. # 5
  118. /dev/input/event0: EV_KEY KEY_5 DOWN
  119. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  120. /dev/input/event0: EV_KEY KEY_5 UP
  121. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  122. # 6
  123. /dev/input/event0: EV_KEY KEY_6 DOWN
  124. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  125. /dev/input/event0: EV_KEY KEY_6 UP
  126. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  127. # 7
  128. /dev/input/event0: EV_KEY KEY_7 DOWN
  129. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  130. /dev/input/event0: EV_KEY KEY_7 UP
  131. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  132. # 8
  133. /dev/input/event0: EV_KEY KEY_8 DOWN
  134. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  135. /dev/input/event0: EV_KEY KEY_8 UP
  136. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  137. # 9
  138. /dev/input/event0: EV_KEY KEY_9 DOWN
  139. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  140. /dev/input/event0: EV_KEY KEY_9 UP
  141. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  142. # *
  143. /dev/input/event0: EV_KEY KEY_NUMERIC_STAR DOWN
  144. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  145. /dev/input/event0: EV_KEY KEY_NUMERIC_STAR UP
  146. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  147. # 0
  148. /dev/input/event0: EV_KEY KEY_0 DOWN
  149. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  150. /dev/input/event0: EV_KEY KEY_0 UP
  151. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  152. # #
  153. /dev/input/event0: EV_KEY KEY_NUMERIC_POUND DOWN
  154. /dev/input/event0: EV_SYN SYN_REPORT 00000000
  155. /dev/input/event0: EV_KEY KEY_NUMERIC_POUND UP
  156. /dev/input/event0: EV_SYN SYN_REPORT 00000000

在高通Fastmmi模式中增强交互方式的更多相关文章

  1. Android上HDMI介绍(基于高通平台)

    本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制.overlay在这里只是简单的介绍,后续会有文章再专门详述. 我没记错的话,高通从7X30开始,平台就可以支持HDMI ...

  2. 高通平台msm8909 LK 实现LCD 兼容

    前段时间小米出现红米note2 换屏门,现在我们公司也要上演了:有两个供应商提供不同IC 的LCD panel. 软件区分的办法是读取LCD IC 的ID 寄存器,下面解析高通平台LK中LCD兼容的过 ...

  3. 在高通平台Android环境下编译内核模块【转】

    本文转载自:http://blog.xeonxu.info/blog/2012/12/04/zai-gao-tong-ping-tai-androidhuan-jing-xia-bian-yi-nei ...

  4. 高通cDSP简单编程例子(实现查询高通cDSP使用率、签名),RK3588 npu使用率查询

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  5. 高通平台FastMMI(FFBM模式)简介与进入方法

    参考: http://blog.csdn.net/tfslovexizi/article/details/51499979 http://www.voidcn.com/blog/jimbo_lee/a ...

  6. 高通方案的Android设备几种开机模式的进入与退出

    高通方案的Android设备主要有以下几种开机模式,Android.EDL.Fastboot.Recovery和FFBM,其进入及退出的方式如下表. 开机模式 屏幕显示 冷启动 热启动 按键退出 命令 ...

  7. 高通电池管理基于qpnp-vm-bms电压模式

    CV:Constant Voltage恒压 SMMB charger:Switch-ModeBattery Charger and Boost peripheral开关模式电池充电器和升压外围设备 O ...

  8. 高通ASOC中的machine驱动

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

  9. 高通ASOC中的codec驱动

    ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...

  10. 高通Audio中ASOC的machine驱动(一)

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

随机推荐

  1. C++编程英语词汇

    abstract抽象的 abstraction抽象性.抽象件 access访问 access level访问级别 access function访问函数 adapter适配器 address地址 ad ...

  2. 【爬虫+情感判定+饼图+Top10高频词+词云图】"王心凌"热门弹幕python舆情分析

    目录 一.背景介绍 二.代码讲解-爬虫部分 2.1 分析弹幕接口 2.2 讲解爬虫代码 三.代码讲解-情感分析部分 3.1 整体思路 3.2 情感分析打标 3.3 统计top10高频词 3.4 绘制词 ...

  3. 火山引擎A/B测试平台的实验管理重构与DDD实践

    本次分享的主题是火山引擎数智平台VeDI旗下的A/B测试平台 DataTester 实验管理架构升级与DDD实践.这里说明的一点是,代码的第一目标肯定是满足产品需求,能够满足产品需求的代码都是好代码. ...

  4. 获取list集合中最大值、最小值及索引值

    一.获取最大最小值的同时,获取到最大/小值在list中的索引值 public static void main(String[] args) { List<Integer> numList ...

  5. 热更学习笔记--toLau中lua脚本对C#中枚举和数组的访问

    [8]Lua脚本调用C#中的枚举学习 --调用枚举类型 print("----------------------toLua中调用C#中枚举类型----------------------- ...

  6. java学习之旅(day.05)

    switch多选择结构 多选择结构还有一个实现方式就是switch case switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支 switch(expression ...

  7. Kubernetes:kubelet 源码分析之探针

    0. 前言 kubernetes 提供三种探针,配置探针(Liveness),就绪探针(Readiness)和启动(Startup)探针判断容器健康状态.其中,存活探针确定什么时候重启容器,就绪探针确 ...

  8. Android 13 - Media框架(2)- Demo App与MediaPlayer Api了解

    关注公众号免费阅读全文,进入音视频开发技术分享群! 尝试用MediaPlayer写了一个播放demo,实现了网络流和本地流的播放.由于本人对app开发一窍不通,所以demo中很多内容是边查资料边写的, ...

  9. 使用 Microsoft Edge WebDriver 自动执行和测试 WebView2 应用 Selenium

    https://learn.microsoft.com/zh-cn/microsoft-edge/webview2/how-to/webdriver

  10. .NET周刊【5月第4期 2024-05-26】

    国内文章 开源低代码框架 ReZero API 正式版本发布 ,界面操作直接生成API https://www.cnblogs.com/sunkaixuan/p/18201175 ReZero是一款. ...