• 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 ",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 ", 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] ",__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 ",  
    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 ",(rtc_read(RTC_AL_MTH)& (RTC_NEW_SPARE3))|tm->tm_mon);  
    8.         hal_rtc_xinfo("b = %d ",(rtc_read(RTC_AL_DOM)& (RTC_NEW_SPARE1))|tm->tm_mday);  
    9.         hal_rtc_xinfo("c = %d ",(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日志打印出来的时间如下图

  • 相关阅读:
    uniapp 教程(未完)
    vue3 迁移指南
    promise 简单封装 ajax 解决回调地狱
    知识图谱内容综述(一)
    运营活动服务端研发总结
    Flurl 组件使用
    个性化电商系统开发之订单物流分拆思路
    快付生态系统积分交易核心源码段
    java使用cxf写的webservices,PB进行调用
    mac系统命令
  • 原文地址:https://www.cnblogs.com/muhuacat/p/5276047.html
Copyright © 2020-2023  润新知