Android关机闹钟实现

时间转换网站:http://tool.chinaz.com/Tools/unixtime.aspx


1、apk层

这个还是比较简单的,百度一下就可以看到apk的代码,我之前也有贴出来过还是看一下核心代码吧。

写好的apk(里面有Android.mk文件 加入system/app/下面进行编译):http://download.csdn.net/detail/weiqifa0/9237021

[java] view plain copy

print?

  1. package com.example.helloworld;  
  2. import java.util.Calendar;  
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5. import android.app.AlarmManager;  
  6. import android.app.PendingIntent;  
  7. import android.app.Service;  
  8. import android.app.TimePickerDialog;  
  9. import android.content.Context;  
  10. import android.content.Intent;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.view.View.OnClickListener;  
  14. import android.widget.Button;  
  15. import android.widget.TimePicker;  
  16. import android.widget.Toast;  
  17. public class AlarmTest extends Activity  
  18. {  
  19. Button setTime;  
  20. AlarmManager aManager;  
  21. Calendar currentTime = Calendar.getInstance();  
  22. public static final int POWER_OFF_WAKE_UP = 8;//用来设置关机启动的参数 平台这边已经设置好了
  23. @Override
  24. public void onCreate(Bundle savedInstanceState)  
  25. {  
  26. super.onCreate(savedInstanceState);  
  27. setContentView(R.layout.activity_main);  
  28. Log.e("weiqifa", test());  
  29. setTime = (Button) findViewById(R.id.setTime);  
  30. aManager=(AlarmManager)AlarmTest.this.getSystemService(Service.ALARM_SERVICE);  
  31. setTime.setOnClickListener(new OnClickListener()  
  32. {  
  33. @Override
  34. public void onClick(View source)  
  35. {  
  36. Calendar currentTime = Calendar.getInstance();  
  37. new TimePickerDialog(AlarmTest.this, 0,   
  38. new TimePickerDialog.OnTimeSetListener()  
  39. {  
  40. @Override
  41. public void onTimeSet(TimePicker tp,  
  42. int hourOfDay, int minute)  
  43. {  
  44. Intent intent = new Intent(AlarmTest.this,AlarmActivity.class);  
  45. PendingIntent pi = PendingIntent.getActivity(AlarmTest.this, 0, intent, 0);  
  46. Calendar c = Calendar.getInstance();  
  47. c.set(Calendar.HOUR, hourOfDay);  
  48. c.set(Calendar.MINUTE, minute);  
  49. Log.v("weiqifa", "c.getTimeMillis()"+c.getTimeInMillis());  
  50. //aManager.setExact(POWER_OFF_WAKE_UP,c.getTimeInMillis(), pi);
  51. aManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,c.getTimeInMillis(), pi);  
  52. Toast.makeText(AlarmTest.this, "设置闹钟成功", Toast.LENGTH_SHORT).show();  
  53. Log.e("weiqifa", "set the clock success!");  
  54. }  
  55. }, currentTime.get(Calendar.HOUR_OF_DAY), currentTime  
  56. .get(Calendar.MINUTE), false).show();  
  57. }  
  58. });  
  59. }  
  60. public native String  test();  
  61. static {  
  62. try{  
  63. Log.i("JNI", "Trying to load libhelloworld.so");  
  64. System.loadLibrary("helloWorld");  
  65. }catch(UnsatisfiedLinkError ule){  
  66. Log.e("JNI", "Warning : could not load the libhelloworld.so");  
  67. }  
  68. }  
  69. }</span>

好了关键就是 aManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,c.getTimeInMillis(), pi);

这句代码

2、AlarmManager和AlarmManagerService

他们一个是客户端一个是服务端 他们通过ipc binder通信,我对这个还不是非常懂,但是有个网友总结的非常好

http://blog.csdn.net/jdsjlzx/article/details/20936709

3、JNI 要想达到关机闹钟的功能就一定要调用JNI

[java] view plain copy

print?

  1. public void setExact(int type, long triggerAtMillis, PendingIntent operation) {  
  2. Log.i(TAG,"setExact"+type);  
  3. setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null);  
  4. }</span>

[java] view plain copy

print?

  1. private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,  
  2. PendingIntent operation, WorkSource workSource) {  
  3. Log.i(TAG,"setImpl"+type);  
  4. if (triggerAtMillis < 0) {  
  5. /* NOTYET
  6. if (mAlwaysExact) {
  7. // Fatal error for KLP+ apps to use negative trigger times
  8. throw new IllegalArgumentException("Invalid alarm trigger time "
  9. + triggerAtMillis);
  10. }
  11. */
  12. triggerAtMillis = 0;  
  13. }  
  14. try {  
  15. mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,  
  16. workSource);  
  17. } catch (RemoteException ex) {  
  18. }  
  19. }

mService.set这个函数在服务端实现,所以要到AlarmManagerService里面去找到这个函数的实现,因为太长就不贴出来了

通过JNI去设置闹钟,在AlarmManagerService里面有调用jni函数

[java] view plain copy

print?

  1. public void scheduleTimeTickEvent() {  
  2. final long currentTime = System.currentTimeMillis();  
  3. final long nextTime = 60000 * ((currentTime / 60000) + 1);  
  4. // Schedule this event for the amount of time that it would take to get to
  5. // the top of the next minute.
  6. final long tickEventDelay = nextTime - currentTime;  
  7. final WorkSource workSource = null; // Let system take blame for time tick events.
  8. set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,  
  9. 0, mTimeTickSender, true, workSource);  
  10. }

[java] view plain copy

print?

  1. private native int init();  
  2. private native void close(int fd);  
  3. private native void set(int fd, int type, long seconds, long nanoseconds);  
  4. private native int waitForAlarm(int fd);  
  5. private native int setKernelTimezone(int fd, int minuteswest);</span>

里面的set(mDescriptor, 6, latestTime / 1000, (latestTime % 1000) * 1000 * 1000); 调用到jni里面的函数。

看到这个native就知道这是一个jni函数了吧

4、JNI通过ioctl去调用驱动里面的代码

这里是在./frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp里面

[java] view plain copy

print?

  1. static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds)  
  2. {  
  3. struct timespec ts;  
  4. ts.tv_sec = seconds;  
  5. ts.tv_nsec = nanoseconds;  
  6. ALOGE("weiqifa type=%d set fd[%d]alarm to %lld.%09lld: %s\n",type,fd, seconds, nanoseconds, strerror(errno));  
  7. int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);  
  8. if (result < 0)  
  9. {  
  10. ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));  
  11. }  
  12. }

5、驱动代码

[java] view plain copy

print?

  1. static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  2. {  
  3. int rv = 0;  
  4. unsigned long flags;  
  5. struct timespec new_alarm_time;  
  6. struct timespec new_rtc_time;  
  7. struct timespec tmp_time;  
  8. struct rtc_time new_rtc_tm;  
  9. struct rtc_device *rtc_dev;  
  10. struct rtc_wkalrm pwron_alm;  
  11. enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);  
  12. uint32_t alarm_type_mask = 1U << alarm_type;  
  13. printk("%s cmd=[0x%x] ANDROID_ALARM_IOCTL_TO_TYPE(cmd)=[%d]\n",__func__,cmd,ANDROID_ALARM_IOCTL_TO_TYPE(cmd));  
  14. if (alarm_type >= ANDROID_ALARM_TYPE_COUNT &&  
  15. alarm_type != ANDROID_ALARM_POWER_ON &&  
  16. alarm_type != ANDROID_ALARM_POWER_ON_LOGO) {  
  17. return -EINVAL;  
  18. }  
  19. if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {  
  20. if ((file->f_flags & O_ACCMODE) == O_RDONLY)  
  21. return -EPERM;  
  22. if (file->private_data == NULL &&  
  23. cmd != ANDROID_ALARM_SET_RTC) {  
  24. spin_lock_irqsave(&alarm_slock, flags);  
  25. if (alarm_opened) {  
  26. spin_unlock_irqrestore(&alarm_slock, flags);  
  27. return -EBUSY;  
  28. }  
  29. alarm_opened = 1;  
  30. file->private_data = (void *)1;  
  31. spin_unlock_irqrestore(&alarm_slock, flags);  
  32. }  
  33. }</span>

后面再调用到,mtk_rtc_hal.c里面

[java] view plain copy

print?

  1. void hal_rtc_set_alarm_time(struct rtc_time *tm) {  
  2. u16 irqen;  
  3. dump_stack();  
  4. printk("weiqifa read tc time = %04d/%02d/%02d (%d) %02d:%02d:%02d\n",  
  5. tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,  
  6. tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);  
  7. hal_rtc_xinfo("a = %d\n",(rtc_read(RTC_AL_MTH)& (RTC_NEW_SPARE3))|tm->tm_mon);  
  8. hal_rtc_xinfo("b = %d\n",(rtc_read(RTC_AL_DOM)& (RTC_NEW_SPARE1))|tm->tm_mday);  
  9. hal_rtc_xinfo("c = %d\n",(rtc_read(RTC_AL_HOU)& (RTC_NEW_SPARE_FG_MASK))|tm->tm_hour);  
  10. rtc_write(RTC_AL_YEA, tm->tm_year);  
  11. rtc_write(RTC_AL_MTH, (rtc_read(RTC_AL_MTH) & (RTC_NEW_SPARE3))|tm->tm_mon);  
  12. rtc_write(RTC_AL_DOM, (rtc_read(RTC_AL_DOM) & (RTC_NEW_SPARE1))|tm->tm_mday);  
  13. rtc_write(RTC_AL_HOU, (rtc_read(RTC_AL_HOU) & (RTC_NEW_SPARE_FG_MASK))|tm->tm_hour);  
  14. rtc_write(RTC_AL_MIN, tm->tm_min);  
  15. rtc_write(RTC_AL_SEC, tm->tm_sec);  
  16. rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW);        /* mask DOW */
  17. rtc_write_trigger();  
  18. irqen = rtc_read(RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;  
  19. rtc_write(RTC_IRQ_EN, irqen);  
  20. rtc_write_trigger();  
  21. }

驱动的调用关系可以用dump_stack()来调试 

然后加载vmlinux可以定位到哪一行,

最后闹钟可以在手机关机的时候也能够响。

6、问题

通过上面的流程,基本上可以理通了整个思路,但是还是要修改一下代码,贴出git diff

[cpp] view plain copy

print?

  1. a/frameworks/base/services/java/com/android/server/AlarmManagerService.java  
  2. +++ b/frameworks/base/services/java/com/android/server/AlarmManagerService.java  
  3. @@ -673,6 +673,7 @@ class AlarmManagerService extends IAlarmManager.Stub {  
  4. String setPackageName = null;  
  5. long nowTime = System.currentTimeMillis();  
  6. +            Slog.d(TAG,"weiqifa nowTime="+nowTime+"  triggerAtTime="+triggerAtTime);  
  7. if (triggerAtTime < nowTime) {  
  8. Slog.w(TAG, "power off alarm set time is wrong!");  
  9. return;  
  10. @@ -684,11 +685,12 @@ class AlarmManagerService extends IAlarmManager.Stub {  
  11. Alarm alarm = new Alarm(type, triggerAtTime, 0, 0, 0, interval, operation, workSource);  
  12. int index = addPoweroffAlarmLocked(alarm);  
  13. if (index == 0) {  
  14. +                                       Slog.w(TAG, "weiqifa==================1");  
  15. resetPoweroffAlarm(alarm);  
  16. }  
  17. }  
  18. type = RTC_WAKEUP;  
  19. -  
  20. +                  Slog.w(TAG, "weiqifa==================2");  
  21. }  
  22. // /@}
  23. @@ -2095,7 +2097,12 @@ class AlarmManagerService extends IAlarmManager.Stub {  
  24. SystemProperties.set("persist.sys.bootpackage", "2"); // for
  25. // poweronofftest
  26. set(mDescriptor, 7, latestTime / 1000, (latestTime % 1000) * 1000 * 1000);  
  27. -        } else {  
  28. +        } else if(setPackageName.equals("com.example.helloworld")){  
  29. +               Slog.i(TAG, "mBootPackage = " + setPackageName + " set Prop 1");  
  30. +            SystemProperties.set("persist.sys.bootpackage", "1"); // for
  31. +                                                                  // helloworld test clock
  32. +            set(mDescriptor, 6, latestTime / 1000, (latestTime % 1000) * 1000 * 1000);   
  33. +        }else {  
  34. Slog.w(TAG, "unknown package (" + setPackageName + ") to set power off alarm");  
  35. }  
  36. // [Note] Power off Alarm -
  37. (END)

这里要加上这个包的名字,要不然就设置不上去了。

apk里面的时间 是格林威治时间 这个可以网上百度一下

然后apk的日志打印出来的时间,如下图

kernel日志打印出来的时间如下图

Android关机闹钟实现的更多相关文章

  1. [置顶] android关机闹钟设计思路

    1: 首先需要硬件支持,支持alarm中断触发开机,目前高通平台几乎都支持: 2:关机前需要在rtc-xxx.c中做到enable_irq_wake,和不disable alarm功能(默认开机后al ...

  2. Android的Recovery中font_10x10.h字库文件制作

    任务是要汉化Android中的Recovery,就了解了bootable/recovery/minui/font_10x18.h这个英文字库的来历,最终汉化的时候并没有自己汉字字库,用的github上 ...

  3. rtc关机闹钟3 IAlarmManager

    vim framework/base/core/java/android/app/IAlarmManager.aidl import android.app.AlarmManager;import a ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  5. 配置android sdk 环境

    1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/

  6. Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记

    以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...

  7. Android Studio配置 AndroidAnnotations——Hi_博客 Android App 开发笔记

    以前用Eclicps 用习惯了现在 想学学 用Android Studio 两天的钻研终于 在我电脑上装了一个Android Studio 并完成了AndroidAnnotations 的配置. An ...

  8. Android请求网络共通类——Hi_博客 Android App 开发笔记

    今天 ,来分享一下 ,一个博客App的开发过程,以前也没开发过这种类型App 的经验,求大神们轻点喷. 首先我们要创建一个Andriod 项目 因为要从网络请求数据所以我们先来一个请求网络的共通类. ...

  9. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

随机推荐

  1. 鼠标放上去,不同的cursor光标类型

    <!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf ...

  2. 学习OpenCV——用OpenCv画漫画

    闲的时候用OpenCV画漫画也挺有意思,虽然效果不好(达不到上面所实现的效果), 参数需要调整,还是大头贴而且噪声小的图像比较合适 而且可以熟悉一下关于各种滤波的操作比如:双边滤波: #include ...

  3. poj 2509 Peter's smokes

    http://poj.org/problem?id=2509 Peter's smokes Time Limit: 1000MS   Memory Limit: 65536K Total Submis ...

  4. android Activity的启动模式与flag的见解

    最近做一个安卓项目,想要实现的效果就是:当打开一个按钮的时候,启动了一个A功能,当用户返回到桌面再继续进去的时候,不过之前在哪个Activity,都会先跳转到A功能的那个界面,当用户点击返回的时候,再 ...

  5. C++之路进阶——codevs1036(商务旅行)

    1036 商务旅行 题目描述 Description 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇 ...

  6. 侧菜单栏的实现SlidingPaneLayout

    SlidingPaneLayout分为两部分,上面的 左划出部分和没划出的时候 <?xml version="1.0" encoding="utf-8"? ...

  7. Android中实现两次点击返回键退出本程序

    1,当用户使用我们的app的时候,有时候无意的或者不是有心的按下了我们的返回键,这时候为了更好的用体验,我们需要让用户再一次确定一下,以便判断用户的真实意图 代码如下: //该功能实现退出时提示的功能 ...

  8. 夺命雷公狗ThinkPHP项目之----企业网站25之网站前台面包屑导航URL的完善

    如果想取出面包屑导航的url那么就必须在model层里面进行多取一个了: <?php namespace Home\Model; use Think\Model; class CategoryM ...

  9. [ubuntu] Can not run OpenProj on Ubuntu

    Download openproj from http://sourceforge.net/projects/openproj/ Yes, you can only download the rpm ...

  10. Visual Studio 2012 怪异的自动重启

    学生在做项目的过程中遇到这种问题: -------------- 用 Visual Studio 2012  开发W中eb 项目时,最近总是莫名其妙的自动重启. 后来试了一下,发现是只要在页面中输入 ...