1. 概述
NITZ (Network Identity and Time Zone) 是 3GPP TS 22.042 定义的标准机制,允许移动网络通过 NAS 信令(MM Information / GMM Information)向 UE 下发当前时间、时区偏移和夏令时信息。Android Telephony 框架通过NitzStateMachine状态机处理 NITZ 信号,并将同步后的时间/时区建议发送给TimeDetectorService/TimeZoneDetectorService(系统级时间服务)。
1.1 NITZ 信号格式
yy/mm/dd,hh:mm:ss(±)tz[,dt[,tzid]]
| 示例 | 含义 |
|---|
22/05/15,10:30:00+32,01 | 2022-05-15 10:30:00, UTC+8:00, DST+15min |
22/05/15,10:30:00-20,00 | 2022-05-15 10:30:00, UTC-5:00, 无 DST |
22/05/15,10:30:00+00,00,Asia/Shanghai | 含 Olson TZ ID(3GPP R9+) |
2. 核心类与源码锚点
| 类名 | 路径 | 说明 |
|---|
| NitzStateMachine | [NitzStateMachine.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzStateMachine.java) | NITZ 状态机接口 |
| NitzStateMachineImpl | [NitzStateMachineImpl.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java) | NITZ 状态机实现 |
| NitzData | [NitzData.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzData.java) | NITZ 原始数据解析与封装 |
| NitzSignal | [NitzSignal.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/NitzSignal.java) | NITZ 信号封装(含接收时间戳) |
| TimeZoneSuggesterImpl | [TimeZoneSuggesterImpl.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/nitz/TimeZoneSuggesterImpl.java) | 时区建议生成器 |
| TimeZoneLookupHelper | [TimeZoneLookupHelper.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java) | MCC+Country→TimeZone 查找 |
| TimeServiceHelperImpl | [TimeServiceHelperImpl.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java) | 系统时间/时区服务门面 |
| NitzSignalInputFilterPredicateFactory | [NitzSignalInputFilterPredicateFactory.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactory.java) | NITZ 信号输入过滤策略 |
| ServiceStateTracker | [ServiceStateTracker.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java) | 监听EVENT_NITZ_TIME,解析 NITZ 字符串 |
3. NITZ 从 Modem 到系统的全链路
3.1 信号接收:Modem → RIL → ServiceStateTracker
// ServiceStateTracker.java — 注册 NITZ 监听mCi.setOnNITZTime(this,EVENT_NITZ_TIME,null);// ServiceStateTracker.java — 处理 EVENT_NITZ_TIMEcaseEVENT_NITZ_TIME:{ar=(AsyncResult)msg.obj;Object[]nitzArgs=(Object[])ar.result;StringnitzString=(String)nitzArgs[0];// NITZ 原始字符串longnitzReceiveTimeMs=((Long)nitzArgs[1]);// 框架收到的时间戳longageMs=0;if(nitzArgs.length>=3){ageMs=((Long)nitzArgs[2]);// NITZ 在 RIL/Modem 中缓存的时长}setTimeFromNITZString(nitzString,nitzReceiveTimeMs,ageMs);break;}
3.2 解析:NitzData.parse()
// NitzData.javapublicstaticNitzDataparse(Stringnitz){// "yy/mm/dd,hh:mm:ss(+/-)tz[,dt[,tzid]]"String[]nitzSubs=NITZ_SPLIT_PATTERN.split(nitz);intyear=Integer.parseInt(nitzSubs[0]);intmonth=Integer.parseInt(nitzSubs[1]);intday=Integer.parseInt(nitzSubs[2]);inthour=Integer.parseInt(nitzSubs[3]);intminute=Integer.parseInt(nitzSubs[4]);intsecond=Integer.parseInt(nitzSubs[5]);// 时区偏移:以 15 分钟为单位的整数intzoneOffset=Integer.parseInt(nitzSubs[6])*MS_PER_QUARTER_HOUR;// 夏令时偏移(可选)IntegerdstOffset=null;if(nitzSubs.length>=8){dstOffset=Integer.parseInt(nitzSubs[7])*MS_PER_QUARTER_HOUR;}// Olson 时区 ID(可选,3GPP R9+)TimeZoneemulatorHostTimeZone=null;if(nitzSubs.length>=9){emulatorHostTimeZone=TimeZone.getTimeZone(nitzSubs[8]);}longunixEpochTimeMillis=LocalDateTime.of(2000+year,month,day,hour,minute,second).toInstant(ZoneOffset.UTC).toEpochMilli();returnnewNitzData(nitz,zoneOffset,dstOffset,unixEpochTimeMillis,emulatorHostTimeZone);}
3.3 转发:NitzSignal → NitzStateMachineImpl
// ServiceStateTracker.java — setTimeFromNITZStringprivatevoidsetTimeFromNITZString(StringnitzString,longnitzReceiveTimeMs,longageMs){NitzDatanewNitzData=NitzData.parse(nitzString);mLastNitzData=newNitzData;if(newNitzData!=null){NitzSignalnitzSignal=newNitzSignal(nitzReceiveTimeMs,newNitzData,ageMs)