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. 用Visual Studio 2012+Xamarin搭建C#开发Andriod的环境

    第一步:安装Visual Studio: Visual Studio 2012(或者Visual Studio 2010),原因是目前为止Xamarin for Visual Studio的插件只支持 ...

  2. Git 使用的配置 常用命令

    老文一篇 搬过来 1. git的部分配置 # 全局提交用户名与邮箱 git config --global user.name "simon" git config --globa ...

  3. Rs2008内存管理策略

    Rs2008 在内存管理方面已经有了很大的改变.主要增加了文件缓存,允许把内存数据卸载到文件缓存中.而Rs2005 都是把数据放到内存中.对于大数据量的报表而言,很容易出现OutOfMemory 错误 ...

  4. 改变对update的做法

    以前都是 先根据id或者其他条件查出来  再根据查出来的结果 进行修改  再update提交 这里可以改所有的字段 现在是做法 是直接new 一个 Do或者Vo  把要改变的值 先填充进去  然后再去 ...

  5. Java基础之创建窗口——使用网格布局管理器(TryGridLayout)

    控制台程序. 网格布局管理器可以在容器的矩形网格中布局组件. import javax.swing.*; import java.awt.*; import javax.swing.border.Et ...

  6. ajax提交含有html数据时的处理方法

    这两天在做一个文章内修改的功能,由于前端选用的Extjs控件库,于是就使用Ext.form.HtmlEditor. 在使用ajax提交数据的时候,需要提交包含有html代码的数据.这时候问题就来了,不 ...

  7. M面经Prepare: Find integer Average of 2 integers.

    The definition of integer average is the highest smaller integer if average is floating point number ...

  8. java基础(59):Eclipse把自动括号匹配改成C++那样的(强迫症,没办法)

    在Eclipse开发环境下依次选择: 1.Windows->Preferences->Java->Code Style->Formatter: 2.在Active Profil ...

  9. struts配置通配符*来匹配方法,实现动态调用

    01:web.xml中配置,启动struts2 <?xml version="1.0" encoding="UTF-8"?> <web-app ...

  10. .NET 4.0 MemoryCache with SqlChangeMonitor

    Summary There isn't a lot of documentation on the internet about how to use the SqlChangeMonitor wit ...