http://blog.csdn.net/g_salamander/article/details/8487328


新项目的手机需要实现关机状态下的闹钟,早在刚开始接触 android 的时候都在想为什么 android 不支持关机状态下的一些功能呢?像充电或者闹钟什么的,虽然每个平台的驱动不一样但上层应用是可以提供统一接口的呀,果然在 4.0 的时候支持关机充电了,关机闹钟仍然不在默认支持中。市场上的很多品牌手机也都不支持这个功能,让很多用惯了 Feature Phone 以及担心辐射的用户都不习惯。这次做关机闹钟在一些思路上借鉴了关机充电的实现方法。

整体思路如下:

在 uboot 中通过 PMU 判断开机的原因,如果是RTC 模块使能开机则在 uboot 中传递启动参数androidboot.mode=alarm,然后在 init 进程中判断启动模式(当前系统有 3 种启动模式:normal、charger、alarm),如果是 alarm 模式则启动 alarm 服务,alarm 服务与应用程序 alarm关联,因此需要编写应用程序来实现关机闹钟的功能。应用程序主要实现以下几个个方面的功能:1、显示关机闹钟的 UI 以及当前时间;2、播放闹铃;3、读取 input 事件判断用户操作;4、用户可以在 UI 中选择懒人模式、开机或者关机。下面逐个解析这几个功能的实现:

1、UI

在 zygote 没有启动之前完成 UI 显示可以参考 charger 的做法,用 android 的 minui 接口,这些接口实现了图形的描绘以及固定大小的文字显示,函数介绍如下:

[cpp] view plain copy
  1. intgr_init(void);/*初始化图形显示,主要是打开设备、分配内存、初始化一些参数*/
  2. voidgr_exit(void);/*注销图形显示,关闭设备并释放内存*/
  3. intgr_fb_width(void);/*获取屏幕的宽度*/
  4. intgr_fb_height(void);/*获取屏幕的高度*/
  5. gr_pixel*gr_fb_data(void);/*获取显示数据缓存的地址*/
  6. voidgr_flip(void);/*刷新显示内容*/
  7. voidgr_fb_blank(boolblank);/*清屏*/
  8. voidgr_color(unsignedcharr,unsignedcharg,unsignedcharb,unsignedchara);/*设置字体颜色*/
  9. voidgr_fill(intx,inty,intw,inth);/*填充矩形区域,参数分别代表起始坐标、矩形区域大小*/
  10. intgr_text(intx,inty,constchar*s);/*显示字符串*/
  11. intgr_measure(constchar*s);/*获取字符串在默认字库中占用的像素长度*/
  12. voidgr_font_size(int*x,int*y);/*获取当前字库一个字符所占的长宽*/
  13. voidgr_blit(gr_surfacesource,intsx,intsy,intw,inth,intdx,intdy);/*填充由source指定的图片*/
  14. unsignedintgr_get_width(gr_surfacesurface);/*获取图片宽度*/
  15. unsignedintgr_get_height(gr_surfacesurface);/*获取图片高度*/
  16. /*根据图片创建显示资源数据,name为图片在mk文件指定的相对路径*/
  17. intres_create_surface(constchar*name,gr_surface*pSurface);
  18. voidres_free_surface(gr_surfacesurface);/*释放资源数据*/
图片只支持 png 格式,做这个 UI 的图片资源花了不少时间(没做过美工),一般图片的显示先由res_create_surface 创建资源数据,然后调用gr_blit 填充,最后调用 gr_flip 刷新显示。在关机闹钟的界面还需要显示当前时间,最开始调用 minui 默认的字库来显示,但是默认字库的字体太小了,只支持 10 x 18 ASIC-II 编码的字符,效果很不好,后来就把时间需要的 10 个数字以及符号以图片的形式显示。

2、闹铃

在这个阶段播放闹铃只能选择tinyplay,tinyplay 是 android 自带的一款简易播放器,只能播放固定格式的 wav 文件。UI 显示以及播放闹铃分别独占一个线程,以保证各自不被干扰。

3、input evnt

当闹钟开始响后,用户可以通过触摸屏点击选择是否开关机或者进入懒人模式,这里就需要对用户操作做出判断,即在程序中去读取/dev/input下面设备的数据。当进入懒人模式后会停止闹铃 5 分钟再响,这个阶段需要关闭 lcd 和 触摸屏,用户可以通过按键提前唤醒。input event 是在进程中循环读取并处理的,示例代码如下:

[cpp] view plain copy
  1. staticintevent_loop(void)
  2. {
  3. inti;
  4. intret=0;
  5. intnfds=ALARM_MAX_DEVICE;
  6. structinput_eventevent;
  7. constchar*device=NULL;
  8. constchar*device_path="/dev/input";
  9. ret=scan_dir(device_path);/*扫描该目录下的设备节点,我们只打开触摸屏和按键*/
  10. if(ret<0){
  11. printf("scandirfailedfor%s.\n",device_path);
  12. returnret;
  13. }
  14. for(;;){
  15. poll(ufds,nfds,-1);/*轮询检测是否有触摸屏或者按键事件*/
  16. for(i=0;i<nfds;i++){
  17. if(ufds[i].revents){/*havevalidvalue.*/
  18. if(ufds[i].revents&POLLIN){
  19. ret=read(ufds[i].fd,&event,sizeof(event));/*读取事件*/
  20. if(ret<(int)sizeof(event)){
  21. printf("couldnotgetevent.\n");
  22. continue;
  23. }
  24. handle_event(event.type,event.code,event.value);/*处理事件*/
  25. }
  26. }
  27. }
  28. }
  29. return0;
  30. }

附:android 权限管理机制

这次在编译使用 alarm 的时候遇到了一个权限问题:编译出来的 alarm 可执行程序在 out 目录下面是拥有可执行权限的,但是在烧录到机器后发现没有可执行权限了,最后才发现是 android 的权限管理机制引起的。

1、文件系统中的权限设定

在 android 系统编译完成后会生成后缀为 img 的文件如:system.img、boot.img,包含了许多目录和文件。在编译的时候会用到mkbootfs等命令,这些命令将会调用system/core/include/private/android_filesystem_config.h文件中预定义的权限,来预置这些目录和文件的初始访问权限。关机闹钟的可执行程序就需要在这里预置权限。示例定义如下:

[cpp] view plain copy
  1. /*目录的预定义权限*/
  2. staticstructfs_path_configandroid_dirs[]={
  3. {00770,AID_SYSTEM,AID_CACHE,"cache"},
  4. {00771,AID_SYSTEM,AID_SYSTEM,"data/app"},/*app目录的权限*/
  5. {00771,AID_SYSTEM,AID_SYSTEM,"data/app-private"},
  6. ...
  7. {00755,AID_ROOT,AID_ROOT,"system/etc/ppp"},
  8. {00777,AID_ROOT,AID_ROOT,"sdcard"},/*sdcard目录的权限*/
  9. {00755,AID_ROOT,AID_ROOT,0},
  10. };
  11. /*文件的预定义权限*/
  12. staticstructfs_path_configandroid_files[]={
  13. {00440,AID_ROOT,AID_SHELL,"system/etc/init.goldfish.rc"},
  14. {00550,AID_ROOT,AID_SHELL,"system/etc/init.goldfish.sh"},
  15. {00644,AID_SYSTEM,AID_SYSTEM,"data/app/*"},/*app目录下所有文件的权限*/
  16. {00644,AID_MEDIA_RW,AID_MEDIA_RW,"data/media/*"},
  17. {00644,AID_SYSTEM,AID_SYSTEM,"data/app-private/*"},
  18. {00644,AID_APP,AID_APP,"data/data/*"},/*data目录下所有文件的权限*/
  19. ...
  20. {06750,AID_ROOT,AID_SHELL,"system/bin/run-as"},
  21. {00755,AID_ROOT,AID_SHELL,"system/bin/*"},/*所有系统可执行程序的权限*/
  22. {00755,AID_ROOT,AID_ROOT,"system/lib/valgrind/*"},
  23. {00755,AID_ROOT,AID_SHELL,"system/xbin/*"},
  24. {00755,AID_ROOT,AID_SHELL,"system/xlib/*"},
  25. {00755,AID_ROOT,AID_SHELL,"system/vendor/bin/*"},
  26. {00750,AID_ROOT,AID_SHELL,"sbin/*"},
  27. {00755,AID_ROOT,AID_ROOT,"bin/*"},
  28. {00750,AID_ROOT,AID_SHELL,"init*"},
  29. {00750,AID_ROOT,AID_SHELL,"charger*"},/*关机充电可执行程序的权限*/
  30. {00750,AID_ROOT,AID_SHELL,"alarm*"},/*关机闹钟可执行程序的权限*/
  31. {00644,AID_ROOT,AID_ROOT,0},
  32. };
如果有新的需要只要把相应的预定义设置添加到相应数组即可。

2、设备文件的权限

android 系统中,kernel 启动后运行的第一个进程为init,init 进程会打开定义为NETLINK_KOBJECT_UEVENT的 socket,接收内核发送的添加或删除设备的uevnet消息。并通过调用 system/core/init/devices.c文件中的 add_dev_perms 函数完成设备文件的权限初始化,参数来自于xxx.rc文件。

其他由 kernel 或 init 及其脚本创建的目录或文件,如果需要修改访问权限,可以通过 init 使用的脚本文件init.rc控制,但不建议直接修改 init.rc 文件,而应该集中在和硬件相关的或新增的 init.vendore.rc 脚本文件中修改,这样做的好处是集中管理,便于移植和版本升级。


更多相关文章

  1. 【Android测试工具】02. Android抓包解析全过程
  2. android 访问/assets 和/res目录下文件的方法
  3. Android写SD卡的坑
  4. 《Android(安卓)安全(二)》Smali语法基础
  5. 浅谈Android的移动存储SharedPreferences技术
  6. Android(安卓)APK签名
  7. [置顶] Android(安卓)studio build.gradle 各种错误解决总结
  8. 【DiskLruCache完全解析】Android(安卓)AdapterView图片硬盘缓存
  9. android与Unity3D之间的相爱相杀

随机推荐

  1. 举例说明Android中AnalogClock的使用
  2. image button on android
  3. android 判断是否第一次进入欢迎页
  4. MySQL数据库开发的36条原则(小结)
  5. MySql 知识点之事务、索引、锁原理与用法
  6. MySQL 查询速度慢与性能差的原因与解决方
  7. mysql查询慢的原因和解决方案
  8. 老鸟带你开发专业规范的MySQL启动脚本
  9. Win7 安装 Mysql 5.6的教程图解
  10. mysql备份脚本并保留7天