前言

RTC

RTC 即实时时钟(Real-Time Clock),主要是功能有:

  1. 时间保持:RTC可以在断电的时候,仍然保持计时功能,保证时间的连续性
  2. 时间显示与设置:RTC可以向系统提供年、月、日、时、分、秒等信息,系统也可以通过接口校准RTC的时间保证准确性

关机闹钟PowerOffAlarm

PowerOffAlarm 是一个与安卓系统关机闹钟功能相关的应用或组件。

当用户设置好关机闹钟后,会向 PowerOffAlarm 发送设定关机闹钟广播并传入闹钟时间参数,PowerOffAlarm 接收到广播后,根据预设提前开机时间和闹钟时间往实时时钟(RTC)中写入时间,并将该时间写入文件中暂存

需要注意的是,PowerOffAlarm 中使用的时间默认是当前时区的时间,若传入的时间戳是其他时区的,则需要调整为当前时区的时间戳

时区

时区是为了适应地球自转造成的不同地区时间差异而划分的区域。

正文

源码分析

关机闹钟 PowerOffAlarm 源码路径:vendor/qcom/proprietary/PowerOffAlarm/src/com/qualcomm/qti/poweroffalarm

首先查看 AndroidManifest.xml 中与关机闹钟相关的广播接收器的源代码:

<receiver android:name=".PowerOffAlarmBroadcastReceiver"
android:permission="org.codeaurora.permission.POWER_OFF_ALARM"
android:exported="true"
android:directBootAware="true"
android:label="PowerOffAlarmBroadcastReceiver">
<intent-filter android:priority="1000">
<!-- 取消闹钟的广播 -->
<action android:name="org.codeaurora.poweroffalarm.action.CANCEL_ALARM" />
<!-- 设置闹钟的广播 -->
<action android:name="org.codeaurora.poweroffalarm.action.SET_ALARM" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name="com.qualcomm.qti.poweroffalarm.PowerOffAlarmDialog$ShutDownReceiver"
android:permission="org.codeaurora.permission.POWER_OFF_ALARM">
<intent-filter>
<!-- 关机广播 -->
<action android:name="org.codeaurora.poweroffalarm.action.ALARM_POWER_OFF"/>
</intent-filter>
</receiver>

对于设置关机闹钟和取消关机闹钟的逻辑,跟踪到 PowerOffAlarmBroadcastReceiver 的源代码:\vendor\qcom\proprietary\PowerOffAlarm\src\com\qualcomm\qti\poweroffalarm\PowerOffAlarmBroadcastReceiver.java

/*******************************************************************************
@file PowerOffAlarmBroadcastReceiver.java
@brief Receive "org.codeaurora.poweroffalarm.action.SET_ALARM" action to set
power off alarm and receive "org.codeaurora.poweroffalarm.action.CANCEL_ALARM"
action to cancel alarm.
******************************************************************************/ public class PowerOffAlarmBroadcastReceiver extends BroadcastReceiver {
//设置关机闹钟的动作
private static final String ACTION_SET_POWEROFF_ALARM = "org.codeaurora.poweroffalarm.action.SET_ALARM";
//设置取消关机闹钟的动作
private static final String ACTION_CANCEL_POWEROFF_ALARM = "org.codeaurora.poweroffalarm.action.CANCEL_ALARM";
//设置关机闹钟的意图携带的 extra 的 key
private static final String TIME = "time";
//设置或取消闹钟
if (ACTION_SET_POWEROFF_ALARM.equals(action)) {//设置闹钟动作
long alarmTime = intent.getLongExtra(TIME, PowerOffAlarmUtils.DEFAULT_ALARM_TIME);//取出要设置的时间戳
long alarmInPref = PowerOffAlarmUtils.getAlarmFromPreference(context);//获取之前设置的时间戳
Log.d(TAG, "Set power off alarm : alarm time " + alarmTime + " time in pref " + alarmInPref);
PowerOffAlarmUtils.saveAlarmToPreference(context, alarmTime);//保存新的时间戳
long alarmTimeToRtc = PowerOffAlarmUtils.setAlarmToRtc(alarmTime);//转化为 RTC 的时间
if (alarmTimeToRtc != FAILURE) {//是个合法时间
persistData.setAlarmTime(alarmTime);
persistData.setAlarmStatus(PowerOffAlarmUtils.ALARM_STATUS_TO_FIRE);
persistData.setSnoozeTime(PowerOffAlarmUtils.DEFAULT_ALARM_TIME);
persistData.writeDataToFile();//会把设置的 RTC 时间存在文件里面
PowerOffAlarmUtils.saveRtcAlarmToPreference(context, alarmTimeToRtc);
}
} else if (ACTION_CANCEL_POWEROFF_ALARM.equals(action)){//取消闹钟动作
long alarmTime = intent.getLongExtra(TIME, PowerOffAlarmUtils.DEFAULT_ALARM_TIME);//获取要取消的闹钟时间戳
long alarmInPref = PowerOffAlarmUtils.getAlarmFromPreference(context);//获取之前设置的时间戳
if (alarmTime == alarmInPref) {//与之前设置的时间戳相同,那就可以取消设置的闹钟
PowerOffAlarmUtils.saveAlarmToPreference(context, PowerOffAlarmUtils.DEFAULT_ALARM_TIME);
PowerOffAlarmUtils.saveRtcAlarmToPreference(context, PowerOffAlarmUtils.DEFAULT_ALARM_TIME);
int rtc = PowerOffAlarmUtils.cancelAlarmInRtc();
if (rtc < 0) {
Log.d(TAG, "Cancel alarm time in rtc failed ");
}
persistData.setAlarmStatus(PowerOffAlarmUtils.ALARM_STATUS_NONE);
persistData.setSnoozeTime(PowerOffAlarmUtils.DEFAULT_ALARM_TIME);
persistData.setAlarmTime(PowerOffAlarmUtils.DEFAULT_ALARM_TIME);
persistData.writeDataToFile();
}
}
}

最后追踪到 PowerOffAlarmUtils 的源代码:\vendor\qcom\proprietary\PowerOffAlarm\src\com\qualcomm\qti\poweroffalarm\PowerOffAlarmUtils.java

/**
* Set alarm time to rtc register
*
* @param time alarm time based on current time (ms)
* @return set result -- Fail, return FAILURE; Success,
* return the alarm time to rtc
*/
public static final long MS_IN_ONE_MIN = 60000L;//一分钟
private static final long SEC_TO_MS = 1000L;//将秒转化为毫秒 public static long setAlarmToRtc(long alarmTime/*未来时间的时间戳*/) {
long currentTime = System.currentTimeMillis();//当前时间的时间戳
long alarmInRtc = getAlarmFromRtc();
long rtcTime = getRtcTime();
// MS_IN_ONE_MIN 是系统预留的开机时间
long timeDelta = alarmTime - currentTime - MS_IN_ONE_MIN;//获取当前到未来的时间戳的差值(单位:ms)
// alarm in two minute is not supported
if (timeDelta <= 0) {
Log.d(TAG, "setAlarmToRtc failed: alarm time is in one minute");
return FAILURE;//设置的时间若过短就返回失败
}
long alarmTimeToRtc = timeDelta / SEC_TO_MS + rtcTime;//计算出要唤醒机器的 RTC 时间(单位:ms)
try {
IAlarm mProxy = IAlarm.getService();
int ret = mProxy.setAlarm(alarmTimeToRtc);//设置 RTC 时间
if (ret == SUCCESS) {//设置成功
return alarmTimeToRtc;
} else {
return FAILURE;//设置失败
}
} catch (Exception e) {
Log.d(TAG, e.toString());
return FAILURE;
}
}

对于关机广播的源代码,首先追踪到源代码:\vendor\qcom\proprietary\PowerOffAlarm\src\com\qualcomm\qti\poweroffalarm.java

public class PowerOffAlarmDialog  extends Activity{
public static class ShutDownReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
PowerOffAlarmUtils.powerOff(context);//关机
}
}
}

随后追踪到 PowerOffAlarmUtils.java 中:

private static final String ACTION_REQUEST_SHUTDOWN = "com.android.internal.intent.action.REQUEST_SHUTDOWN";

public static void powerOff(Context context) {
//想要直接使用这个函数的话,app必须是系统app,且具有如下权限:
//<uses-permission android:name="android.permission.SHUTDOWN"/>
Intent requestShutdown = new Intent(ACTION_REQUEST_SHUTDOWN);
requestShutdown.putExtra(EXTRA_KEY_CONFIRM, false);//true 是弹窗询问,false 是不弹窗直接关机
requestShutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(requestShutdown);
}

项目实战

设置关机闹钟

public static final String POWER_OFF_ALARM_PACKAGE = "com.qualcomm.qti.poweroffalarm";
private static final long MS_IN_ONE_MIN = 6000L; /**
* 设置关机闹钟,在到达预定的时间戳时开机
*
* @param mContext 上下文
* @param millis 未来的时间戳
*/
public static void startPowerOnAlarm(Context mContext, long millis) {//开机
Intent intent = new Intent(Constants.ACTION_SET_POWEROFF_ALARM);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setPackage(POWER_OFF_ALARM_PACKAGE);
long time = millis + MS_IN_ONE_MIN;
intent.putExtra("time", time);
mContext.sendBroadcast(intent);
}

取消关机闹钟

public static final String ACTION_CANCEL_POWEROFF_ALARM = "org.codeaurora.poweroffalarm.action.CANCEL_ALARM";

/**
* 取消关机闹钟,取消设定在时间戳 millis 的闹钟
*
* @param mContext 上下文
* @param millis 取消的闹钟时间戳
*/
public static void cancelAlarm(Context mContext, long millis) {
Intent intent = new Intent(ACTION_CANCEL_POWEROFF_ALARM);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setPackage(POWER_OFF_ALARM_PACKAGE);
millis += MS_IN_ONE_MIN;
intent.putExtra("time", millis);
sendBroadcast(intent);
}

关机功能

public static void startShutDown(Context mContext) {//关机
Intent intent = new Intent("org.codeaurora.poweroffalarm.action.ALARM_POWER_OFF");
intent.setPackage(POWER_OFF_ALARM_PACKAGE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.sendBroadcast(intent);
//也可以使用如下代码实现,但必须是系统应用且带有如下权限:
// <uses-permission android:name="android.permission.SHUTDOWN"/>
// String action = "com.android.internal.intent.action.REQUEST_SHUTDOWN";
// if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N){
// action = "android.intent.action.ACTION_REQUEST_SHUTDOWN";
// }
// Intent intent = new Intent(action);
// intent.putExtra("android.intent.extra.KEY_CONFIRM", false);
// intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// mContext.startActivity(intent);
}

【Android】谷歌应用关机闹钟 PowerOffAlarm 源码分析,并实现定时开、关机的更多相关文章

  1. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

  2. 【Android】Handler、Looper源码分析

    一.前言 源码分析使用的版本是 4.4.2_r1. Handler和Looper的入门知识以及讲解可以参考我的另外一篇博客:Android Handler机制 简单而言:Handler和Looper是 ...

  3. Android View事件分发-从源码分析

    View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...

  4. Android线程间异步通信机制源码分析

    本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础 ...

  5. Android JobService的使用及源码分析

    Google在Android 5.0中引入JobScheduler来执行一些需要满足特定条件但不紧急的后台任务,APP利用JobScheduler来执行这些特殊的后台任务时来减少电量的消耗.本文首先介 ...

  6. Android服务之PackageManagerService启动源码分析

    了解了Android系统的启动过程的读者应该知道,Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中.SystemServer进程在启 ...

  7. Android 7.0 Gallery图库源码分析3 - 数据加载及显示流程

    前面分析Gallery启动流程时,说了传给DataManager的data的key是AlbumSetPage.KEY_MEDIA_PATH,value值,是”/combo/{/local/all,/p ...

  8. ScheduledThreadPoolExecutor源码分析-你知道定时线程池是如何实现延迟执行和周期执行的吗?

    Java版本:8u261. 1 简介 ScheduledThreadPoolExecutor即定时线程池,是用来执行延迟任务或周期性任务的.相比于Timer的单线程,定时线程池在遇到任务抛出异常的时候 ...

  9. Android ViewManger解析 从ViewRoot 源码分析invalidate

    转载请标明出处:http://blog.csdn.net/sk719887916/article/details/48443429,作者:skay     通过学习了AndroidUI之绘图机基础知道 ...

  10. Android图片处理神器BitmapFun源码分析

    作为一名Android开发人员,相信大家对图片OOM的问题已经耳熟能详了,关于图片缓存和解决OOM的开源项目也是相当的多,被大家熟知的就是Universal_image_loader和Volley了, ...

随机推荐

  1. Spring —— 注解开发(总结)

    XML配置与注解配置对比   

  2. 暑假集训PVZ提高模拟9

    没关 exe 让这货挂了一天 UPD:又挂了一晚上,现在被我正义制裁了 A.大众点评 交互红题啊,交互会写,但是忘记判 \(n=1\) 了 这个题交互库函数实现起来还是挺简单的,我 Windows 不 ...

  3. ceph-rbd和cephfs使用

    目录 1 用户权限管理和授权流程 1.1 列出用户 1.2 用户管理 1.2.1 ceph auth add 1.2.3 ceph auth get-or-create 1.2.4 ceph auth ...

  4. neo4j 统计多个节点之间关系总和的写法-包含多个collect合并成一个并去重操作

    在使用neo4j数据库时,会遇到计算与指定节点产生关联的数量统计需求,例如指定6个节点1,2,3,4,5,6需要找出与这6个节点中4个节点有关联的节点(要求排除这6个节点的数据) 先看实现查询语句: ...

  5. 逆向WeChat(七)

    上篇介绍了如何通过嗅探MojoIPC抓包小程序的HTTPS数据. 本篇逆向微信客户端本地数据库相关事宜. 本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/184235 ...

  6. Nuxt.js 应用中的 page:finish 钩子详解

    title: Nuxt.js 应用中的 page:finish 钩子详解 date: 2024/10/9 updated: 2024/10/9 author: cmdragon excerpt: pa ...

  7. python数据结构学习第一章——栈

    在这片文章中,我们使用python3.8自制一个具有基本功能的栈结构,它的功能只有push,pop,peek这三个功能 ` #!/usr/bin/env python # * coding: utf- ...

  8. docker部署Prometheus与Grafana

    prometheus部署 建立文件 mkdir -p /ops/prometheus-data && cd /ops/prometheus-data vi /ops/prometheu ...

  9. KubeSphere 使用 HTTPS 协议集成 Harbor 镜像仓库指南

    作者:申红磊,青云科技容器解决方案架构师,开源项目爱好者,KubeSphere Member. 上面两篇文章讲了如何部署 HTTPS Harbor 和对接 HTTP 的 Harbor 镜像仓库:接下来 ...

  10. 写代码被大语言模型坑之使用LocalDateTime比较两个时间差了几天

    自从去年ChatGPT3.5发布后使用了几次,现在写代码基本上离不开它和它的衍生产品们了.一方面查资料很方便,快速提炼要点总结:另一方面想写什么样的代码一问就能生成出来,功能大差不差,稍微改改就能用, ...