android 关机闹钟
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
- intgr_init(void);/*初始化图形显示,主要是打开设备、分配内存、初始化一些参数*/
- voidgr_exit(void);/*注销图形显示,关闭设备并释放内存*/
- intgr_fb_width(void);/*获取屏幕的宽度*/
- intgr_fb_height(void);/*获取屏幕的高度*/
- gr_pixel*gr_fb_data(void);/*获取显示数据缓存的地址*/
- voidgr_flip(void);/*刷新显示内容*/
- voidgr_fb_blank(boolblank);/*清屏*/
- voidgr_color(unsignedcharr,unsignedcharg,unsignedcharb,unsignedchara);/*设置字体颜色*/
- voidgr_fill(intx,inty,intw,inth);/*填充矩形区域,参数分别代表起始坐标、矩形区域大小*/
- intgr_text(intx,inty,constchar*s);/*显示字符串*/
- intgr_measure(constchar*s);/*获取字符串在默认字库中占用的像素长度*/
- voidgr_font_size(int*x,int*y);/*获取当前字库一个字符所占的长宽*/
- voidgr_blit(gr_surfacesource,intsx,intsy,intw,inth,intdx,intdy);/*填充由source指定的图片*/
- unsignedintgr_get_width(gr_surfacesurface);/*获取图片宽度*/
- unsignedintgr_get_height(gr_surfacesurface);/*获取图片高度*/
- /*根据图片创建显示资源数据,name为图片在mk文件指定的相对路径*/
- intres_create_surface(constchar*name,gr_surface*pSurface);
- voidres_free_surface(gr_surfacesurface);/*释放资源数据*/
2、闹铃
在这个阶段播放闹铃只能选择tinyplay,tinyplay 是 android 自带的一款简易播放器,只能播放固定格式的 wav 文件。UI 显示以及播放闹铃分别独占一个线程,以保证各自不被干扰。
3、input evnt
当闹钟开始响后,用户可以通过触摸屏点击选择是否开关机或者进入懒人模式,这里就需要对用户操作做出判断,即在程序中去读取/dev/input下面设备的数据。当进入懒人模式后会停止闹铃 5 分钟再响,这个阶段需要关闭 lcd 和 触摸屏,用户可以通过按键提前唤醒。input event 是在进程中循环读取并处理的,示例代码如下:
[cpp] view plain copy
- staticintevent_loop(void)
- {
- inti;
- intret=0;
- intnfds=ALARM_MAX_DEVICE;
- structinput_eventevent;
- constchar*device=NULL;
- constchar*device_path="/dev/input";
- ret=scan_dir(device_path);/*扫描该目录下的设备节点,我们只打开触摸屏和按键*/
- if(ret<0){
- printf("scandirfailedfor%s.\n",device_path);
- returnret;
- }
- for(;;){
- poll(ufds,nfds,-1);/*轮询检测是否有触摸屏或者按键事件*/
- for(i=0;i<nfds;i++){
- if(ufds[i].revents){/*havevalidvalue.*/
- if(ufds[i].revents&POLLIN){
- ret=read(ufds[i].fd,&event,sizeof(event));/*读取事件*/
- if(ret<(int)sizeof(event)){
- printf("couldnotgetevent.\n");
- continue;
- }
- handle_event(event.type,event.code,event.value);/*处理事件*/
- }
- }
- }
- }
- return0;
- }
附: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
- /*目录的预定义权限*/
- staticstructfs_path_configandroid_dirs[]={
- {00770,AID_SYSTEM,AID_CACHE,"cache"},
- {00771,AID_SYSTEM,AID_SYSTEM,"data/app"},/*app目录的权限*/
- {00771,AID_SYSTEM,AID_SYSTEM,"data/app-private"},
- ...
- {00755,AID_ROOT,AID_ROOT,"system/etc/ppp"},
- {00777,AID_ROOT,AID_ROOT,"sdcard"},/*sdcard目录的权限*/
- {00755,AID_ROOT,AID_ROOT,0},
- };
- /*文件的预定义权限*/
- staticstructfs_path_configandroid_files[]={
- {00440,AID_ROOT,AID_SHELL,"system/etc/init.goldfish.rc"},
- {00550,AID_ROOT,AID_SHELL,"system/etc/init.goldfish.sh"},
- {00644,AID_SYSTEM,AID_SYSTEM,"data/app/*"},/*app目录下所有文件的权限*/
- {00644,AID_MEDIA_RW,AID_MEDIA_RW,"data/media/*"},
- {00644,AID_SYSTEM,AID_SYSTEM,"data/app-private/*"},
- {00644,AID_APP,AID_APP,"data/data/*"},/*data目录下所有文件的权限*/
- ...
- {06750,AID_ROOT,AID_SHELL,"system/bin/run-as"},
- {00755,AID_ROOT,AID_SHELL,"system/bin/*"},/*所有系统可执行程序的权限*/
- {00755,AID_ROOT,AID_ROOT,"system/lib/valgrind/*"},
- {00755,AID_ROOT,AID_SHELL,"system/xbin/*"},
- {00755,AID_ROOT,AID_SHELL,"system/xlib/*"},
- {00755,AID_ROOT,AID_SHELL,"system/vendor/bin/*"},
- {00750,AID_ROOT,AID_SHELL,"sbin/*"},
- {00755,AID_ROOT,AID_ROOT,"bin/*"},
- {00750,AID_ROOT,AID_SHELL,"init*"},
- {00750,AID_ROOT,AID_SHELL,"charger*"},/*关机充电可执行程序的权限*/
- {00750,AID_ROOT,AID_SHELL,"alarm*"},/*关机闹钟可执行程序的权限*/
- {00644,AID_ROOT,AID_ROOT,0},
- };
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 脚本文件中修改,这样做的好处是集中管理,便于移植和版本升级。
更多相关文章
- 【Android测试工具】02. Android抓包解析全过程
- android 访问/assets 和/res目录下文件的方法
- Android写SD卡的坑
- 《Android(安卓)安全(二)》Smali语法基础
- 浅谈Android的移动存储SharedPreferences技术
- Android(安卓)APK签名
- [置顶] Android(安卓)studio build.gradle 各种错误解决总结
- 【DiskLruCache完全解析】Android(安卓)AdapterView图片硬盘缓存
- android与Unity3D之间的相爱相杀