Android关机闹钟实现

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.     }  

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日志打印出来的时间如下图

posted on 2016-03-14 16:04  木花猫  阅读(1510)  评论(1编辑  收藏  举报

导航