alarm 小结

关键词: JNI , HAL,Sqlite   

在手机中,闹钟功能是一个最基本的功能,也是我们一个很常用的功能,现在来分析下 android是怎么实现的,下面将采用自上而下的方式讲解。 

  

1、APP层 

进入桌面闹钟应用程序,选择设置新闹钟,会调到 Alarms.java 里面的 
public static long setAlarm(Context context, Alarm alarm) { 
.... 
setNextAlert(context); 
.... 

接着也会调用到  
public static void setNextAlert(final Context context) { 
if (!enableSnoozeAlert(context)) { 

//通过查询数据库获取到一个离当前时间最近的alarm 
Alarm alarm = calculateNextAlert(context);  
if (alarm != null) { 
enableAlert(context, alarm, alarm.time); 
} else { 
disableAlert(context); 



然后继续调用到  
private static void enableAlert(Context context, final Alarm alarm, final longatTimeInMillis) { 
....... 

//下面参数RTC_WAKEUP, 就是保证即使系统睡眠了,都能唤醒,闹钟工作 
am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);..... 


/packages/apps/DeskClock/src/com/android/deskclock/Alarms.java 

  

2、Framework层 
然后就调用到了AlarmManager.java里面方法 
public void set(int type, long triggerAtTime, PendingIntent operation) { 
try { 
mService.set(type, triggerAtTime, operation); 
} catch (RemoteException ex) { 

}  //www.sctarena.com
然后就调用到了AlarmManagerService.java里面方法 
public void set(int type, long triggerAtTime, PendingIntent operation) { 
setRepeating(type, triggerAtTime, 0, operation); 

然后继续调用 
public void setRepeating(int type, long triggerAtTime, long interval,  
PendingIntent operation) { 
..... 
synchronized (mLock) { 
Alarm alarm = new Alarm(); 
alarm.type = type; 
alarm.when = triggerAtTime; 
alarm.repeatInterval = interval; 
alarm.operation = operation; 

// Remove this alarm if already scheduled. 
removeLocked(operation); 

int index = addAlarmLocked(alarm); 
if (index == 0) { 
setLocked(alarm); 



然后就调用到 
private void setLocked(Alarm alarm) 

...... 

//mDescriptor 这里的文件是 /dev/alarm 
set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);  
..... 


./frameworks/base/core/java/android/app/AlarmManager.java 
./frameworks/base/services/java/com/android/server/AlarmManagerService.java 

  

3、JNI 的层 

JNI : (Java Natative Interface ) 它允许 JAVA代码和其他语言的代码进行交互,比如C/C++代码. 

private nativevoid set(int fd, int type, long seconds, long nanoseconds); 
这就调用到了com_android_server_AlarmManagerService.cpp里面 
static JNINativeMethod sMethods[] = { 
/* name, signature, funcPtr */ 
{"init", "()I",(void*)android_server_AlarmManagerService_init}, 
{"close", "(I)V",(void*)android_server_AlarmManagerService_close}, 
{"set", "(IIJJ)V",(void*)android_server_AlarmManagerService_set}, 
{"waitForAlarm", "(I)I",(void*)android_server_AlarmManagerService_waitForAlarm}, 
{"setKernelTimezone", "(II)I",(void*)android_server_AlarmManagerService_setKernelTimezone}, 
}; 

set 对应的是android_server_AlarmManagerService_set,具体是 
static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj,jint fd, jint type, jlong seconds, jlong nanoseconds) 

#if HAVE_ANDROID_OS 
struct timespec ts; 
ts.tv_sec = seconds; 
ts.tv_nsec = nanoseconds; 

int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); 
if (result < 0) 

LOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds,nanoseconds, strerror(errno)); 

#endif 


/frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp 


4.HAL 层 

    AndroidHAL层,即硬件抽象层(Hardware abstract layer),是Google响应厂家“希望不公开源码”的要求推出的新概念。 

上面的ioctl 就调用到了alarm-dev.c 
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 

.... 
case ANDROID_ALARM_SET(0): 
if (copy_from_user(&new_alarm_time, (void __user *)arg, 
sizeof(new_alarm_time))) { 
rv = -EFAULT; 
goto err1; 

from_old_alarm_set: 
spin_lock_irqsave(&alarm_slock, flags); 
pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, 
new_alarm_time.tv_sec, new_alarm_time.tv_nsec); 
alarm_enabled |= alarm_type_mask; 
alarm_start_range(&alarms[alarm_type], 
timespec_to_ktime(new_alarm_time), 
timespec_to_ktime(new_alarm_time)); 
spin_unlock_irqrestore(&alarm_slock, flags); 
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) 
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) 
break; 
/* fall though */ 
.... 

case ANDROID_ALARM_SET_RTC: 
if (copy_from_user(&new_rtc_time, (void __user *)arg, 
sizeof(new_rtc_time))) { 
rv = -EFAULT; 
goto err1; 

rv = alarm_set_rtc(new_rtc_time); 
spin_lock_irqsave(&alarm_slock, flags); 
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; 
wake_up(&alarm_wait_queue); 
spin_unlock_irqrestore(&alarm_slock, flags); 
if (rv < 0) 
goto err1; 
break; 
.... 


然后这边根据用户上层传过来的CMD就调用到了alarm_start_range设置闹钟,alarm_set_rtc 设置RTC时间,这些函数的具体实现在alarm.c文件中; 

/* set rtc while preserving elapsed realtime */ 
int alarm_set_rtc(const struct timespec ts); 

下面看alarm.c里面实现: 
int alarm_set_rtc(struct timespec new_time) 

.... 
ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); 
.... 


上面会调用到了interface.c  
intrtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) 

.... 
err =rtc->ops->set_time(rtc->dev.parent, tm); 
.... 


文件位置 

kernel/drivers/rtc/alarm-dev.c 
kernel/drivers/rtc/alarm.c 

  

4. Drive 层 
然后set_time 就看到具体的是那个RTC芯片,这边我们参考rtc-pcf8563.c 
static conststruct rtc_class_ops  pcf8563_rtc_ops = { 
.read_time =pcf8563_rtc_read_time, 
.set_time =pcf8563_rtc_set_time, 
.read_alarm= pcf8563_rtc_read_alarm, 
.set_alarm =pcf8563_rtc_set_alarm, 
}; 
然后就到了 
static intpcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) 

unsignedchar buf[TIME_NUM]; 
int ret; 

ret =data_calc(buf, tm, TIME_NUM); 
if (ret < 0) 
goto out; 
//这边就调用i2c统一接口,往pcf8563rtc芯片寄存器里面写出数据 

ret= i2c_smbus_write_i2c_block_data(pcf8563_info->client,PCF8563_RTC_SEC, TIME_NUM, buf);  
out: 
return ret; 

文件位置 

kernel/drivers/rtc/rtc-pcf8563.c 

以上就是闹钟从应用层到驱动层的整个流程。 



                    问题分析 

问题一:设置闹钟后,系统如何在后台轮询的 

AlarmManagerService 里面有个AlarmThread 会一直轮询/dev/alarm文件,如果打开失败就直接返回,成功就会做一些动作,比如查找时间最近的 
alarm,比如睡眠被闹钟唤醒的时候,这边就发一个intent出去,然后在AlarmReceiver.java里面弹出里面会收到就会调用下面的 
context.startActivity(alarmAlert); 

然后弹出alarm 这个界面 
Class c = AlarmAlert.class; 
其中publicclass AlarmAlert extends AlarmAlertFullScreen 所以系统睡眠之后被alarm唤醒弹出的alarm就是这边start的 
public class AlarmReceiver extends BroadcastReceiver { 

/** If the alarm is older than STALE_WINDOW, ignore. It 
is probably the result of a time or timezone change */ 
private final static int STALE_WINDOW = 30 * 60 * 1000; 

@Override 
public void onReceive(Context context, Intent intent) { 
......... 
handleIntent(Context context, Intent intent) 
........ 



问题二:数据库操作:添加和删除闹钟 

当我们设置闹钟的时候,会进入到设置闹钟的窗口,当用户确认点击保存后,会来到 (SetAlarm.java) 

private long saveAlarm() { 

.... 

time = Alarms.setAlarm(this, alarm); 

...... 



继续跟进会来到(Alarms) 

public static long setAlarm(Context context, Alarm alarm){ 

..... 

       ContentValues values = createContentValues(alarm); 

       ContentResolver resolver = context.getContentResolver(); 

       resolver.update( 

               ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id), 

               values, null, null); 

..... 

}  //www.sctarena.com

到此就完成了闹钟添加到数据库的功能,想了解其中怎么实现的,可以去了解下 

ContentResolver ,ContentProvider,SQLiteOpenHelper .从它们那里能找到你要的答案 

  

问题三: 定时短信 

对于这个功能实现的方式很多,我讲下我个人的思路,然后做了个小demo. 

思路1:使用AlarmManager的set方法来实现该功能,当时间到时发送短信。 

思路2:后台开启一个Server ,采用轮询的方式来实现该功能. 

### 智能温室系统实验小结 #### 实验目标 设计并实现一个基于 ThingsBoard 平台的智能温室系统,该系统能够通过温度、湿度和电流传感器采集数据,并在湿度过大或电流过载时触发报警。同时,用户可以通过图形化仪表板远程切断电源开关。 #### 系统功能概述 - **数据采集**:通过温度、湿度和电流传感器实时采集温室环境参数[^1]。 - **规则引擎配置**:利用 ThingsBoard 的规则引擎检测异常情况,例如湿度过大或电流过载导致温度升高[^2]。 - **报警机制**:当检测到异常时,ThingsBoard 平台生成报警信息,并通知用户[^3]。 - **远程控制**:用户可以通过 ThingsBoard 仪表板手动或自动控制电源开关状态[^4]。 #### 数据采集与传输 使用 Python 脚本模拟传感器数据的生成,并通过 MQTT 协议将数据上传至 ThingsBoard 平台。以下是一个简单的数据生成与传输示例: ```python import paho.mqtt.client as mqtt import time import random MQTT_BROKER = "demo.thingsboard.io" MQTT_PORT = 1883 DEVICE_TOKEN = "YOUR_DEVICE_ACCESS_TOKEN" client = mqtt.Client() client.username_pw_set(DEVICE_TOKEN) client.connect(MQTT_BROKER, MQTT_PORT, 60) while True: temperature = random.uniform(20.0, 35.0) humidity = random.uniform(50.0, 90.0) current = random.uniform(5.0, 20.0) payload = { "temperature": temperature, "humidity": humidity, "current": current } client.publish("v1/devices/me/telemetry", str(payload)) print(f"Published: {payload}") time.sleep(5) ``` #### 规则引擎配置 在 ThingsBoard 中配置规则引擎以检测异常情况并触发相应动作。以下是两个关键规则的实现逻辑: - 当湿度超过阈值(如 80%)且温度升高时,触发报警[^5]。 - 当电流过载(如超过 15A)且温度升高时,自动切断电源开关[^6]。 规则引擎脚本示例: ```javascript if (msg.temperature > 30 && msg.humidity > 80) { return { alarm: "High Humidity Detected" }; } if (msg.current > 15 && msg.temperature > 40) { return { command: "TURN_OFF_POWER" }; } ``` #### 报警与通知 ThingsBoard 支持多种通知方式,包括电子邮件、短信和推送通知。通过规则引擎配置报警条件,并指定通知接收人。报警信息包含详细描述,便于用户快速响应问题[^7]。 #### 远程控制功能 通过 ThingsBoard 的 RPC 功能实现对开关设备的远程控制。用户可以在仪表板中添加按钮控件,点击后向 ThingsBoard 发送 RPC 请求,从而调整开关状态[^8]。 RPC 控制脚本示例: ```python def on_rpc_request(request_id, method, params): if method == "setPowerSwitch": if params == "ON": set_power_switch(True) elif params == "OFF": set_power_switch(False) return {"success": True} ``` #### 实验总结 本实验成功设计并实现了一个基于 ThingsBoard 平台的智能温室系统。系统能够实时采集温度、湿度和电流数据,并通过规则引擎检测异常情况。在湿度过大或电流过载时,平台能够及时触发报警并自动切断电源开关。此外,用户还可以通过图形化仪表板手动控制设备状态,确保系统的稳定运行[^9]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值