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
- package com.example.helloworld;
- import java.util.Calendar;
- import android.os.Bundle;
- import android.app.Activity;
- import android.app.AlarmManager;
- import android.app.PendingIntent;
- import android.app.Service;
- import android.app.TimePickerDialog;
- import android.content.Context;
- import android.content.Intent;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.TimePicker;
- import android.widget.Toast;
- public class AlarmTest extends Activity
- {
- Button setTime;
- AlarmManager aManager;
- Calendar currentTime = Calendar.getInstance();
- public static final int POWER_OFF_WAKE_UP = 8;//用来设置关机启动的参数 平台这边已经设置好了
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.e("weiqifa", test());
- setTime = (Button) findViewById(R.id.setTime);
- aManager=(AlarmManager)AlarmTest.this.getSystemService(Service.ALARM_SERVICE);
- setTime.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View source)
- {
- Calendar currentTime = Calendar.getInstance();
- new TimePickerDialog(AlarmTest.this, 0,
- new TimePickerDialog.OnTimeSetListener()
- {
- @Override
- public void onTimeSet(TimePicker tp,
- int hourOfDay, int minute)
- {
- Intent intent = new Intent(AlarmTest.this,AlarmActivity.class);
- PendingIntent pi = PendingIntent.getActivity(AlarmTest.this, 0, intent, 0);
- Calendar c = Calendar.getInstance();
- c.set(Calendar.HOUR, hourOfDay);
- c.set(Calendar.MINUTE, minute);
- Log.v("weiqifa", "c.getTimeMillis()"+c.getTimeInMillis());
- //aManager.setExact(POWER_OFF_WAKE_UP,c.getTimeInMillis(), pi);
- aManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,c.getTimeInMillis(), pi);
- Toast.makeText(AlarmTest.this, "设置闹钟成功", Toast.LENGTH_SHORT).show();
- Log.e("weiqifa", "set the clock success!");
- }
- }, currentTime.get(Calendar.HOUR_OF_DAY), currentTime
- .get(Calendar.MINUTE), false).show();
- }
- });
- }
- public native String test();
- static {
- try{
- Log.i("JNI", "Trying to load libhelloworld.so");
- System.loadLibrary("helloWorld");
- }catch(UnsatisfiedLinkError ule){
- Log.e("JNI", "Warning : could not load the libhelloworld.so");
- }
- }
- }</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
- public void setExact(int type, long triggerAtMillis, PendingIntent operation) {
- Log.i(TAG,"setExact"+type);
- setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null);
- }</span>
[java] view plain copy
- private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
- PendingIntent operation, WorkSource workSource) {
- Log.i(TAG,"setImpl"+type);
- if (triggerAtMillis < 0) {
- /* NOTYET
- if (mAlwaysExact) {
- // Fatal error for KLP+ apps to use negative trigger times
- throw new IllegalArgumentException("Invalid alarm trigger time "
- + triggerAtMillis);
- }
- */
- triggerAtMillis = 0;
- }
- try {
- mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
- workSource);
- } catch (RemoteException ex) {
- }
- }
mService.set这个函数在服务端实现,所以要到AlarmManagerService里面去找到这个函数的实现,因为太长就不贴出来了
通过JNI去设置闹钟,在AlarmManagerService里面有调用jni函数
[java] view plain copy
- public void scheduleTimeTickEvent() {
- final long currentTime = System.currentTimeMillis();
- final long nextTime = 60000 * ((currentTime / 60000) + 1);
- // Schedule this event for the amount of time that it would take to get to
- // the top of the next minute.
- final long tickEventDelay = nextTime - currentTime;
- final WorkSource workSource = null; // Let system take blame for time tick events.
- set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
- 0, mTimeTickSender, true, workSource);
- }
[java] view plain copy
- private native int init();
- private native void close(int fd);
- private native void set(int fd, int type, long seconds, long nanoseconds);
- private native int waitForAlarm(int fd);
- 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
- static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds)
- {
- struct timespec ts;
- ts.tv_sec = seconds;
- ts.tv_nsec = nanoseconds;
- ALOGE("weiqifa type=%d set fd[%d]alarm to %lld.%09lld: %s\n",type,fd, seconds, nanoseconds, strerror(errno));
- int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
- if (result < 0)
- {
- ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));
- }
- }
5、驱动代码
[java] view plain copy
- static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- int rv = 0;
- unsigned long flags;
- struct timespec new_alarm_time;
- struct timespec new_rtc_time;
- struct timespec tmp_time;
- struct rtc_time new_rtc_tm;
- struct rtc_device *rtc_dev;
- struct rtc_wkalrm pwron_alm;
- enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
- uint32_t alarm_type_mask = 1U << alarm_type;
- printk("%s cmd=[0x%x] ANDROID_ALARM_IOCTL_TO_TYPE(cmd)=[%d]\n",__func__,cmd,ANDROID_ALARM_IOCTL_TO_TYPE(cmd));
- if (alarm_type >= ANDROID_ALARM_TYPE_COUNT &&
- alarm_type != ANDROID_ALARM_POWER_ON &&
- alarm_type != ANDROID_ALARM_POWER_ON_LOGO) {
- return -EINVAL;
- }
- if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
- if ((file->f_flags & O_ACCMODE) == O_RDONLY)
- return -EPERM;
- if (file->private_data == NULL &&
- cmd != ANDROID_ALARM_SET_RTC) {
- spin_lock_irqsave(&alarm_slock, flags);
- if (alarm_opened) {
- spin_unlock_irqrestore(&alarm_slock, flags);
- return -EBUSY;
- }
- alarm_opened = 1;
- file->private_data = (void *)1;
- spin_unlock_irqrestore(&alarm_slock, flags);
- }
- }</span>
后面再调用到,mtk_rtc_hal.c里面
[java] view plain copy
- void hal_rtc_set_alarm_time(struct rtc_time *tm) {
- u16 irqen;
- dump_stack();
- printk("weiqifa read tc time = %04d/%02d/%02d (%d) %02d:%02d:%02d\n",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
- hal_rtc_xinfo("a = %d\n",(rtc_read(RTC_AL_MTH)& (RTC_NEW_SPARE3))|tm->tm_mon);
- hal_rtc_xinfo("b = %d\n",(rtc_read(RTC_AL_DOM)& (RTC_NEW_SPARE1))|tm->tm_mday);
- hal_rtc_xinfo("c = %d\n",(rtc_read(RTC_AL_HOU)& (RTC_NEW_SPARE_FG_MASK))|tm->tm_hour);
- rtc_write(RTC_AL_YEA, tm->tm_year);
- rtc_write(RTC_AL_MTH, (rtc_read(RTC_AL_MTH) & (RTC_NEW_SPARE3))|tm->tm_mon);
- rtc_write(RTC_AL_DOM, (rtc_read(RTC_AL_DOM) & (RTC_NEW_SPARE1))|tm->tm_mday);
- rtc_write(RTC_AL_HOU, (rtc_read(RTC_AL_HOU) & (RTC_NEW_SPARE_FG_MASK))|tm->tm_hour);
- rtc_write(RTC_AL_MIN, tm->tm_min);
- rtc_write(RTC_AL_SEC, tm->tm_sec);
- rtc_write(RTC_AL_MASK, RTC_AL_MASK_DOW); /* mask DOW */
- rtc_write_trigger();
- irqen = rtc_read(RTC_IRQ_EN) | RTC_IRQ_EN_ONESHOT_AL;
- rtc_write(RTC_IRQ_EN, irqen);
- rtc_write_trigger();
- }
驱动的调用关系可以用dump_stack()来调试
然后加载vmlinux可以定位到哪一行,
最后闹钟可以在手机关机的时候也能够响。
6、问题
通过上面的流程,基本上可以理通了整个思路,但是还是要修改一下代码,贴出git diff
[cpp] view plain copy
- a/frameworks/base/services/java/com/android/server/AlarmManagerService.java
- +++ b/frameworks/base/services/java/com/android/server/AlarmManagerService.java
- @@ -673,6 +673,7 @@ class AlarmManagerService extends IAlarmManager.Stub {
- String setPackageName = null;
- long nowTime = System.currentTimeMillis();
- + Slog.d(TAG,"weiqifa nowTime="+nowTime+" triggerAtTime="+triggerAtTime);
- if (triggerAtTime < nowTime) {
- Slog.w(TAG, "power off alarm set time is wrong!");
- return;
- @@ -684,11 +685,12 @@ class AlarmManagerService extends IAlarmManager.Stub {
- Alarm alarm = new Alarm(type, triggerAtTime, 0, 0, 0, interval, operation, workSource);
- int index = addPoweroffAlarmLocked(alarm);
- if (index == 0) {
- + Slog.w(TAG, "weiqifa==================1");
- resetPoweroffAlarm(alarm);
- }
- }
- type = RTC_WAKEUP;
- -
- + Slog.w(TAG, "weiqifa==================2");
- }
- // /@}
- @@ -2095,7 +2097,12 @@ class AlarmManagerService extends IAlarmManager.Stub {
- SystemProperties.set("persist.sys.bootpackage", "2"); // for
- // poweronofftest
- set(mDescriptor, 7, latestTime / 1000, (latestTime % 1000) * 1000 * 1000);
- - } else {
- + } else if(setPackageName.equals("com.example.helloworld")){
- + Slog.i(TAG, "mBootPackage = " + setPackageName + " set Prop 1");
- + SystemProperties.set("persist.sys.bootpackage", "1"); // for
- + // helloworld test clock
- + set(mDescriptor, 6, latestTime / 1000, (latestTime % 1000) * 1000 * 1000);
- + }else {
- Slog.w(TAG, "unknown package (" + setPackageName + ") to set power off alarm");
- }
- // [Note] Power off Alarm -
- (END)
这里要加上这个包的名字,要不然就设置不上去了。
apk里面的时间 是格林威治时间 这个可以网上百度一下
然后apk的日志打印出来的时间,如下图
kernel日志打印出来的时间如下图
Android关机闹钟实现的更多相关文章
- [置顶] android关机闹钟设计思路
1: 首先需要硬件支持,支持alarm中断触发开机,目前高通平台几乎都支持: 2:关机前需要在rtc-xxx.c中做到enable_irq_wake,和不disable alarm功能(默认开机后al ...
- Android的Recovery中font_10x10.h字库文件制作
任务是要汉化Android中的Recovery,就了解了bootable/recovery/minui/font_10x18.h这个英文字库的来历,最终汉化的时候并没有自己汉字字库,用的github上 ...
- rtc关机闹钟3 IAlarmManager
vim framework/base/core/java/android/app/IAlarmManager.aidl import android.app.AlarmManager;import a ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 配置android sdk 环境
1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/
- Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记
以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...
- Android Studio配置 AndroidAnnotations——Hi_博客 Android App 开发笔记
以前用Eclicps 用习惯了现在 想学学 用Android Studio 两天的钻研终于 在我电脑上装了一个Android Studio 并完成了AndroidAnnotations 的配置. An ...
- Android请求网络共通类——Hi_博客 Android App 开发笔记
今天 ,来分享一下 ,一个博客App的开发过程,以前也没开发过这种类型App 的经验,求大神们轻点喷. 首先我们要创建一个Andriod 项目 因为要从网络请求数据所以我们先来一个请求网络的共通类. ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
随机推荐
- 使用CollectionView做横向滑动分页效果:
一开始几页滑动是没有问题的,等滑到三四个页面之后,就出现奇怪的缝隙,一开始死活找不到原因,最后在layout的代理方法minimumLineSpacingForSectionAtIndex返回值设置为 ...
- Web Service测试利器 Postman
利器推荐,下载地址 https://github.com/a85/POSTMan-Chrome-Extension 在chrome的扩展商店中安装失败 所以下载zip文件下来再导入到chrome扩展
- linux:指令与档案的搜索
linux下的五种搜索方法(参考自鸟哥linux私房菜基础篇): 一.find :功能很强大,直接搜寻整个硬碟的(速度不是很快,如果系统硬碟较旧的话)----特色:find后面可以接多个目录搜索,它本 ...
- [转] HashMap和HashSet的区别
HashMap和HashSet的区别是Java面试中最常被问到的问题.如果没有涉及到Collection框架以及多线程的面试,可以说是不完整.而Collection框架的问题不涉及到HashSet和H ...
- php session memcache
ini_set("session.save_handler", "memcache"); ini_set("session.save_path&quo ...
- 夺命雷公狗---node.js---19之项目的构建在node+express+mongo的博客项目4mongodb在项目中的基本引入
首先我们在命令行下先建立这个库: 然后我们在项目中引入mongodb的模块: var MongoClient = require('mongodb').MongoClient; var DB_STR ...
- 使用java访问 动态链接库(dll)
在这个时候,我们可以使用的java技术有jni.jna.jnative,这个大部分都可以完成任务.但是有时候我们在实际情况中拿到的dll有变化,当我们需要用的函数是在dll中的类里面的话,我们再使用前 ...
- SQL——存储过程
1. 为什么使用存储过程 应用程序通过T-SQL语句到服务器的过程是不安全的. 1) 数据不安全 2)每次提交SQL代码都要经过语法编译后在执行,影响应用程序的运行性能 3) 网络流量大 2. 什么是 ...
- jquery表单重置另一种方法
页面中按钮为<a>标签时,点击取消按钮,表单内容重置,需要给form表单id="form": <a class="demo_one1" onc ...
- php防止刷新(流量攻击)的一段代码
<?php//查询禁止IP$ip =$_SERVER['REMOTE_ADDR'];$fileht=".htaccess2";if(!file_exists($fileht) ...