Android对RTC时间的操作流程 您所在的位置:网站首页 安卓如何设置日期 Android对RTC时间的操作流程

Android对RTC时间的操作流程

2023-12-23 19:59| 来源: 网络整理| 查看: 265

文章目录

1.RTC概述 2.平台对应的rtc接口 3.android settime实现流程 3.1 android 层更新时间的起始 3.2 setCurrentTimeMillis调用远程接口aidl 3.3 AlarmManagerService 实现aidl 实现settime接口方法 3.4 settime jni native层实现 4. rtc readtime流程(qcom) 5. rtc settime流程(qcom) 4.总结 5.参考

1.RTC概述

RTC(Real Time Clock),用于关机时继续计算系统日期和时间。是基于硬件的功能。也可以RTC做Alarm来设置power on/off

2.平台对应的rtc接口 Linux 提供了三种用户空间调用接口。对于笔者所用的平台,在其中对应的路径为:

SYSFS接口:/sys/class/rtc/rtc0/ PROCFS接口: /proc/driver/rtc IOCTL接口: /dev/rtc0

3.android settime实现流程

3.1 android 层更新时间的起始 凡事皆有因果,本次分析从因开始,由上至下,由因至果。 android层对时间的设置在settings系统应用中实现。

packages/apps/Settings/src/com/android/settings/DateTimeSettings.java

Android提供了两种方式对Android系统时间进行设置更新 1.网络校时 2.手动校时 这里先不对这两种校时方式的实现作分析,本文目的旨在rtc,那就顺着系统默认的设置,通过网络校时来分析。 网络校时android层最终是通过NetworkTimeUpdateService.java这个类来实现,咱直奔中心。 幕后操作则是onPollNetworkTimeUnderWakeLock方法。(先不管这个方法是在哪里被谁调用,如何调用的,记得本文目的是rtc)

//frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java private void onPollNetworkTimeUnderWakeLock(int event) { ...... SystemClock.setCurrentTimeMillis(ntp); ...... }

这里为了更快的进入主题,笔者直接将该方法的其他有用没用的都省略了~~~。真是刺激呀。 可以看到android层设置更新时间的起始就是通过 SystemClock.setCurrentTimeMillis 开始的。 这样我们跟去看看它的实现。

//frameworks/base/core/java/android/os/SystemClock.java /** * Sets the current wall time, in milliseconds. Requires the calling * process to have appropriate permissions. * * @return if the clock was successfully set to the specified time. */ public static boolean setCurrentTimeMillis(long millis) { IBinder b = ServiceManager.getService(Context.ALARM_SERVICE); IAlarmManager mgr = IAlarmManager.Stub.asInterface(b); if (mgr == null) { return false; } try { return mgr.setTime(millis); } catch (RemoteException e) { Slog.e(TAG, "Unable to set RTC", e); } catch (SecurityException e) { Slog.e(TAG, "Unable to set RTC", e); } return false; }

这里注释里提到一个wall time,翻译为墙上时间。这里解释下墙上时间。 系统启动时,内核通过读取RTC来初始化内核时钟,又叫墙上时间,该时间放在struct timespec xtime变量中。 回到上面,setCurrentTimeMillis仅仅作了一件事。那就是mgr.setTime(millis); 这个mgr是什么,往上看。 1.首先取得ALARM_SERVICE的binder对象。 2.通过binder对象得到IAlarmManager的接口方法。

3.2 setCurrentTimeMillis调用远程接口aidl 那么,这就走到了IAlarmManager.setTime里去了。 不过这里IAlarmManager很明显是个接口类。

//frameworks/base/core/java/android/app/IAlarmManager.aidl /** * System private API for talking with the alarm manager service. * * {@hide} */ interface IAlarmManager { /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */ void set(String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, in PendingIntent operation, in IAlarmListener listener, String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock); boolean setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation, in IAlarmListener listener); long getNextWakeFromIdleTime(); AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); // update the uids being synchronized by network socket request manager void updateBlockedUids(int uid, boolean isBlocked); }

既然是接口,那么得有人去实现它。那就是–看注释,哈哈,告诉读者一个有意思的,在你没有相关知识,或者经验的时候,比如不知道谁调用实现了这个接口类,那么开源代码中的注释是非常有用的信息。这里就说到了它和 alarm manager service(后称AMS,注意区分ActivityManagerService) 进行talk 。 不过这里要注意了,这里的注释是talk。并不是实现。不过有点可以肯定。最终setTime在AMS中肯定也有,你也可以直接去看,不过这里笔者还是按照正常流程进行分析。 既然AMS和用来和它talk的,那么AMS很明显结合命名看它就是服务端,那么作为binder通讯,实现IAlarmManager肯定是客户端。还记得上面讲到setCurrentTimeMillis方法里的实现吗?没错第二步就是得到IAlarmManager的客户端。那现在就可以直接到AMS里去看setTime的实现了。在过去的时候,这里提一句。可能有些人在看到IAlarmManager会想到AlarmManager,这里可以抽点时间过去看看。AlarmManager.java中也会有setTime的实现。

//frameworks/base/core/java/android/app/AlarmManager.java /** * Set the system wall clock time. * Requires the permission android.permission.SET_TIME. * * @param millis time in milliseconds since the Epoch */ public void setTime(long millis) { try { mService.setTime(millis); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }

可以看到AlarmManager.setTime 最终是调用了mService.setTime。注意了,这里的mService就是IAlarmManager对象。这样看。它也算是个binder的客户端了。这里其实是为了给上层提供接口通过AlarmManager.java调用到AMS中setTime方法。这里暂且说到这,夹杂了些许binder的相关知识。不理解的可自行百度。

3.3 AlarmManagerService 实现aidl 实现settime接口方法 继续回到AMS中的setTime。

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

private final IBinder mService = new IAlarmManager.Stub() { @Override public void set(String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) { ....... } } @Override public boolean setTime(long millis) { ...... return setKernelTime(mNativeData, millis) == 0; } @Override public void setTimeZone(String tz) { ....... } @Override public void remove(PendingIntent operation, IAlarmListener listener) { ...... } @Override public long getNextWakeFromIdleTime() { ....... } @Override public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) { ....... } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { ....... } @Override /* updates the blocked uids, so if a wake lock is acquired to only fire * alarm for it, it can be released. */ public void updateBlockedUids(int uid, boolean isBlocked) { ....... } };

这里可以看到在AMS内部实现了IAlarmManager 的 IBinder对象 mService。 那么它就实现了IAlarmManager接口所定义的方法。找到setTime。在setTime中最终调用了setKernelTime()。

3.4 settime jni native层实现 在AMS中可以看到,这个setKernelTime方法是个native方式,看来最终到了jni层。

//frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis) { AlarmImpl *impl = reinterpret_cast(nativeData); struct timeval tv; int ret; if (millis = INT_MAX) { return -1; } tv.tv_sec = (time_t) (millis / 1000LL); tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); ret = impl->setTime(&tv); if(ret public: AlarmImpl(int *fds, size_t n_fds); virtual ~AlarmImpl(); virtual int set(int type, struct timespec *ts) = 0; virtual int clear(int type, struct timespec *ts) = 0; virtual int setTime(struct timeval *tv) = 0; virtual int waitForAlarm() = 0; protected: int *fds; size_t n_fds; }; class AlarmImplAlarmDriver : public AlarmImpl { public: AlarmImplAlarmDriver(int fd) : AlarmImpl(&fd, 1) { } int set(int type, struct timespec *ts); int clear(int type, struct timespec *ts); int setTime(struct timeval *tv); int waitForAlarm(); }; class AlarmImplTimerFd : public AlarmImpl { public: AlarmImplTimerFd(int fds[N_ANDROID_TIMERFDS], int epollfd, int rtc_id) : AlarmImpl(fds, N_ANDROID_TIMERFDS), epollfd(epollfd), rtc_id(rtc_id) { } ~AlarmImplTimerFd(); int set(int type, struct timespec *ts); int clear(int type, struct timespec *ts); int setTime(struct timeval *tv); int waitForAlarm(); private: int epollfd; int rtc_id; };

可以看到AlarmImpl的声明完全是个抽象类,得必须有子类去实现它,而在现在实现它的有两个子类。AlarmImplAlarmDriver和AlarmImplTimerFd ,都是公有继承。 而这两个子类都实现了setTime。那最终是走了哪里呢? 因为笔者C++不是太懂,这里直接跑系统加日志最终是走了AlarmImplTimerFd::setTime。 这里留个坑,为什么走了了这条路,还请知道的朋友记得在评论区告知。感谢~

//frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp int AlarmImplTimerFd::setTime(struct timeval *tv) { struct rtc_time rtc; struct tm tm, *gmtime_res; int fd; int res; res = settimeofday(tv, NULL); if (res ALOGV("Not setting RTC because wall clock RTC was not found"); errno = ENODEV; return -1; } android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id); fd = open(rtc_dev.string(), O_RDWR); if (fd ALOGV("gmtime_r() failed: %s\n", strerror(errno)); res = -1; goto done; } memset(&rtc, 0, sizeof(rtc)); rtc.tm_sec = tm.tm_sec; rtc.tm_min = tm.tm_min; rtc.tm_hour = tm.tm_hour; rtc.tm_mday = tm.tm_mday; rtc.tm_mon = tm.tm_mon; rtc.tm_year = tm.tm_year; rtc.tm_wday = tm.tm_wday; rtc.tm_yday = tm.tm_yday; rtc.tm_isdst = tm.tm_isdst; res = ioctl(fd, RTC_SET_TIME, &rtc); if (res ...... fd = open("/dev/rtc0", O_RDONLY); ...... rc = ioctl(fd,RTC_RD_TIME,&rtc_tm); ...... }

分析完qcom的rtc read,有没有想到一个问题,为啥源生代码中没有看到rtc read 相关,从app到jni 都没看到rtc read 相关的代码,这点有点疑问,知道的大佬还请在评论区留言告知。感谢!!!

5. rtc settime流程(qcom)

前面花了大篇幅跟踪了rtc的设置流程,跟到ams settime ioctl的时候,出现了问题。那么Android系统总的要有settime这个功能,3.2中分析了rtc read,同样的,在本平台中。set也是在该模块中实现。通过了广播触发的方式。下面看代码,因为是高通私有代码这里只做简要分析

//vendor/qcom/proprietary/time-services/src/com/qualcomm/timeservice/TimeServiceBroadcastReceiver.java public class TimeServiceBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "TimeService"; public native void setTimeServicesUserTime(long millis); //本地方法 static { System.loadLibrary("TimeService"); } //加载本地库TimeService @Override public void onReceive(Context context, Intent intent) { if ((Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) || (Intent.ACTION_DATE_CHANGED.equals(intent.getAction()))) { //可以看到这里就是触发设置的广播 Log.d(TAG, "Received" + intent.getAction() + " intent. " + "Current Time is " + System.currentTimeMillis()); setTimeServicesUserTime(System.currentTimeMillis()); } else { Log.w(TAG, "Received Unexpected Intent " + intent.toString()); } } }

TimeServiceBroadcastReceiver作了这么几件事。 1.声明本地native方法。 2.加载包含刚刚声明的方法的本地库。 3.实现BroadcastReceiver接口。监听Intent.ACTION_TIME_CHANGED和Intent.ACTION_DATE_CHANGED。可以推测当时间和日期改变的时候,将会触发将当前时间进行设置。 给setTimeServicesUserTime传值的时间是通过System.currentTimeMillis()得到的。这里应该还记得上面分析rtc 网络校时的时候,在NetworkTimeUpdateService中onPollNetworkTimeUnderWakeLock方法中调用了SystemClock.setCurrentTimeMillis(ntp);进行校时后的时间设置。那么在校时后,时间日期改变,这里广播也会监听到,从而这里获取到的当前时间就是当前的正确时间,那么通过setTimeServicesUserTime将正确时间进行设置。qcom rtc settime就完成了。 setTimeServicesUserTime的具体实现不进行分析了。

4.总结

因果分析完毕,因学艺不精,留了一些坑,望知道的大佬评论指教。关于rtc,还有两部分需要单独开博客分析,这里记录下: 1.ker层rtc框架和qcom rtc 驱动分析。 2.time-services模块分析。(这个不会太详细) 另外,前面分析了那么多,字多眼花。附上一张图看的舒心点,我个人就更喜欢看图~~~ 在这里插入图片描述 5.参考

Qcom平台RTC驱动分析 Linux内核中_IO,_IOR,_IOW,_IOWR宏的用法与解析 Android RTC 自下而上分析 Freescale i.MX6平台Android4.4.3之外部硬件RTC自动同步网络时间调试经验 GMT、UTC、DST、CST时区代表的意义

———————————————— 版权声明:本文为CSDN博主「wang 恒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_23327993/article/details/102562708



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有