首页 > 编程语言 >【Android】谷歌应用关机闹钟 PowerOffAlarm 源码分析,并实现定时开、关机

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

时间:2024-12-14 14:10:38浏览次数:6  
标签:ALARM 关机闹钟 long 源码 intent PowerOffAlarmUtils static Android

前言

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);
}

标签:ALARM,关机闹钟,long,源码,intent,PowerOffAlarmUtils,static,Android
From: https://www.cnblogs.com/RomanLin/p/18605078

相关文章

  • Vue3 + vite + Ts + pinia + 实战 + 源码
    https://www.bilibili.com/video/BV1dS4y1y7vd?from=search&seid=230935554877610115&spm_id_from=333.337.0.0Vue3+vite+Ts+pinia+实战+源码通过本课程你可以学到vue3最新语法setup语法糖,和最新的vite构建工具,和vue3的源码解读,实战项目我们会做PC项目,H5项目,小程序项......
  • Vue.js 源码全方位深入解析
    Vue.js源码全方位深入解析https://ustbhuangyi.github.io/vue-analysis/https://ustbhuangyi.github.io/vue-analysis/v2/prepare/F:\Vue教程\Vue.js源码全方位深入解析\第1章准备工作第1章准备工作1-2准备工作.mp41-3认识Flow.docx1-4认识Flow.mp4服漏npmi-......
  • express食品采购平台-毕业设计源码28367
    目录1绪论1.1课题目的与意义1.2国内外研究现状1.3论文结构与章节安排1.4express框架介绍2 基于Vue+Node的食品采购平台系统分析2.1可行性分析2.2系统功能分析2.3系统用例分析2.4本章小结3基于Vue+Node的食品采购平台总体设计3.1系统功能模块......
  • springboot美容院会员管理系统-毕业设计源码28455
    目录摘要1绪论1.1选题背景与意义1.2国内外研究现状1.3论文结构与章节安排2系统分析2.1可行性分析2.2系统流程分析2.2.1系统开发流程2.2.2用户登录流程2.2.3系统操作流程2.2.4添加信息流程2.2.5修改信息流程2.2.6删除信息流程2.3 系统......
  • 浅入浅出docker run命令源码2-containerd篇
    1、前情回顾上次《浅入浅出dockerrun命令源码》代码看到调用了grpc去让containerd启动容器就没有继续看了.连一刻都没有为dockerd的无疾而终而哀悼,立刻来到战场的是containerd…这次,我们先解决下面的问题1、dockerd是怎么启动的containerd2、怎么调试containerd的源......
  • springboot二手动漫周边交易网站-毕业设计源码16260
    目 录摘要1绪论1.1选题背景1.2研究意义1.3论文结构与章节安排2 二手动漫周边交易网站系统分析2.1可行性分析2.2系统流程分析2.2.1数据流程3.3.2业务流程2.3系统功能分析2.3.1功能性分析2.3.2非功能性分析2.4系统用例分析2.5本章小......
  • django停车场管理系统-毕业设计源码16856
    目 录1绪论1.1研究背景和意义1.2国内外研究现状1.3论文结构与章节安排2 系统分析2.1可行性分析2.1.1技术可行性分析2.1.2 经济可行性分析2.1.3操作可行性分析2.2系统功能分析2.2.1功能性分析2.2.2非功能性分析2.3 系统用例分析2.4系......
  • 免费送源码:Java+ssm+MySQL SSM智慧旅游系统 计算机毕业设计原创定制
     摘要随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。智慧旅游系统设计,主要的模块包括查看首页、站点内容(轮播图、公告栏)系统用户(管理员、注册用户、导游)公共内容(旅游资讯、资讯分类)模块管理(地区管理、景点信息......
  • 免费送源码:Java+B/S+MySQL 多元化智能选课系统的设计与实现 计算机毕业设计原创定制
    摘 要多元化智能选课系统使用Java语言的Springboot框架,采用MVVM模式进行开发,数据方面主要采用的是微软的Mysql关系型数据库来作为数据存储媒介,配合前台技术完成系统的开发。论文主要论述了如何使用JAVA语言开发一个多元化智能选课系统,本系统将严格按照软件开发流程进行各个......
  • Android 12.0 Launcher3从首页开始安装app功能实现
    1.前言 在12.0的系统rom定制化开发中,在进行Launcher3的某些功能开发实现过程中,在某些项目中,安装的app比较多,要求在launcher3的首页开始安装app应用,接下来就需要分析下app安装图标排序的流程,然后在实现相关的功能2.Launcher3从首页开始安装app功能实现的核心类packages/a......