AOSP14 时区更新
本文面向刚接触 AOSP Framework 的同学,按"先知道数据在哪里,再知道怎么更新,最后知道系统怎么用起来"的顺序整理。
参考当前源码:
system/timezoneexternal/icuexternal/cldrpackages/modules/GeoTZframeworks/base/services/core/java/com/android/server/timezonedetectorframeworks/base/services/core/java/com/android/server/timezonedetector/location
参考文档:
aaa/自动时区设置.mdaaa/geotz_gnss_sequence.mdaaa/geotz_architecture.md
1. AOSP14 中时区数据分两类
很多人看到"时区更新"会以为只改一个文件,其实 AOSP14 里至少要分清两条线。
第一类是普通时区规则数据,也就是 IANA tzdata。它决定 Asia/Shanghai、Europe/London 这类时区 ID 的历史偏移、夏令时规则、国家映射、ICU 时区显示等。这条线主要在:
text
system/timezone
external/icu
external/cldr
第二类是"位置到时区"的离线边界数据。GNSS / LocationManager 给系统一个经纬度后,GeoTZ 用本地 tzs2.dat 判断这个经纬度属于哪些时区。这条线主要在:
text
packages/modules/GeoTZ
简单说:
text
IANA tzdata:时区规则,例如 Asia/Shanghai 当前 UTC+8
GeoTZ tzs2.dat:经纬度查时区,例如 31.2,121.4 -> Asia/Shanghai
这两者都和时区有关,但用途不一样,更新方法也不一样。
2. AOSP14 世界时间的数据来源
2.1 IANA tzdata
当前源码中的 IANA 输入文件在:
text
system/timezone/input_data/iana/tzdata2023a.tar.gz
当前生成后的 Android 运行时文件在:
text
system/timezone/output_data/iana/tzdata
system/timezone/output_data/android/tzlookup.xml
system/timezone/output_data/android/telephonylookup.xml
system/timezone/output_data/android/tzids.prototxt
system/timezone/output_data/icu_overlay/icu_tzdata.dat
system/timezone/output_data/version/tz_version
当前工程里的版本文件内容是:
text
007.001|2023a|001
可以理解为:
2023a:IANA tzdata 版本。001:Android 自己的修订号。007.001:Android 时区数据集格式版本。
2.2 ICU / CLDR
ICU 负责很多 Java / native 层国际化相关能力,时区显示、格式化、部分时区名称也会受它影响。
相关目录:
text
external/icu
external/cldr
system/timezone/update-tzdata.py 会重新生成 system/timezone/output_data,也会更新 ICU runtime 数据。所以改 IANA tzdata 时,不要只盯着 system/timezone/output_data/iana/tzdata。
2.3 GeoTZ 的 timezone-boundary-builder 数据
GeoTZ 的边界数据来自外部 timezone-boundary-builder 项目。
当前源码输入文件:
text
packages/modules/GeoTZ/tzbb_data/input-data.zip
packages/modules/GeoTZ/tzbb_data/timezones.geojson.zip
当前源码输出文件:
text
packages/modules/GeoTZ/output_data/odbl/tzs2.dat
tzs2.dat 是离线经纬度查时区的核心数据文件。
3. AOSP14 时区更新机制
3.1 com.android.tzdata APEX 更新
AOSP14 的系统时区规则通过 com.android.tzdata APEX 承载。
定义位置:
text
system/timezone/apex/Android.bp
APEX 中包含这些预置数据:
text
apex_tz_version
apex_tzlookup.xml
apex_telephonylookup.xml
apex_tzdata
apex_icu_tzdata.dat
对应来源在:
text
system/timezone/output_data
这条更新影响系统里的时区规则、国家到时区映射、Telephony 时区查找、ICU 时区 overlay。
3.2 固件 / OTA 更新
如果产品没有单独走 Mainline APEX 更新,也可以通过整包 OTA 或刷机方式更新系统镜像里的数据。
兜底文件还会安装到:
text
/system/usr/share/zoneinfo/tzdata
源码定义在:
text
system/timezone/output_data/iana/Android.bp
3.3 GeoTZ APEX 或 APK 更新
GeoTZ 也支持不同部署形态:
text
packages/modules/GeoTZ/apex/com.android.geotz
packages/modules/GeoTZ/app
apex 是 com.android.geotz APEX 形态,里面带 tzs2.dat。
app 是 OfflineLocationTimeZoneProviderService privileged APK 形态,里面也可以把 tzs2.dat 作为资源带进去。
也就是说,IANA 时区规则和 GeoTZ 位置边界可以分开更新。
4. AOSP14 中如何更新普通时区规则
这一节对应 system/timezone。
4.1 准备编译环境
先进入 AOSP 根目录:
bash
source build/envsetup.sh
lunch <your_target>
脚本依赖 ANDROID_BUILD_TOP,所以不要直接在普通 shell 里运行。
4.2 下载新的 IANA 数据
官方脚本:
bash
system/timezone/download-iana-files.py --data
如果要更新 zic / tzcode 工具,再跑:
bash
system/timezone/download-iana-files.py --tools
普通时区规则更新通常先只做 --data。
脚本会从 IANA 下载新的 tzdata20xx?.tar.gz,放入:
text
system/timezone/input_data/iana
4.3 检查 Android 自己维护的映射
这些文件不是 IANA 原始数据,而是 Android 自己维护的输入:
text
system/timezone/input_data/android/countryzones.txt
system/timezone/input_data/android/telephonylookup.txt
如果某个国家、MCC、默认时区映射发生变化,要检查这些文件。
4.4 重新生成输出数据
执行:
bash
system/timezone/update-tzdata.py
它会生成或更新:
text
system/timezone/output_data/iana/tzdata
system/timezone/output_data/android/tzlookup.xml
system/timezone/output_data/android/telephonylookup.xml
system/timezone/output_data/android/tzids.prototxt
system/timezone/output_data/icu_overlay/icu_tzdata.dat
system/timezone/output_data/version/tz_version
external/icu runtime 数据
脚本内部会调用 Soong 编译 host 工具,例如:
text
zone_compactor
tzlookup_generator
telephonylookup_generator
4.5 编译相关模块
可以先单独编译:
bash
m com.android.tzdata
如果要确认整体系统镜像:
bash
m
如果只验证 framework 侧逻辑,可以根据修改范围选择:
bash
m services
m framework
5. AOSP14 中如何更新 GeoTZ 离线位置时区数据
这一节对应 packages/modules/GeoTZ。
GeoTZ 的目标不是更新 Asia/Shanghai 的 UTC 偏移规则,而是更新"经纬度落在哪个时区边界里"。
5.1 下载 timezone-boundary-builder 数据
脚本:
bash
packages/modules/GeoTZ/download-tzbb-files.sh <release-id>
例子:
bash
packages/modules/GeoTZ/download-tzbb-files.sh 2023d
脚本会刷新:
text
packages/modules/GeoTZ/tzbb_data/DATA_LICENSE
packages/modules/GeoTZ/tzbb_data/input-data.zip
packages/modules/GeoTZ/tzbb_data/timezones.geojson.zip
packages/modules/GeoTZ/tzbb_data/METADATA
5.2 运行 GeoTZ 数据流水线
脚本:
bash
packages/modules/GeoTZ/run-data-pipeline.sh
它大致做这些步骤:
text
Step 0:解压 timezones.geojson.zip
Step 1:geojson -> TzS2Polygon proto
Step 2:用 system/timezone/output_data/android/tzids.prototxt 做时区 ID 规范化
Step 3:TzS2Polygon -> S2 CellUnion
Step 4:S2 CellUnion -> TzS2Range
Step 5:合并多个 TzS2Range
Step 6:生成 geotz_createtzs2file 输入
Step 7:生成 output_data/odbl/tzs2.dat
最终文件:
text
packages/modules/GeoTZ/output_data/odbl/tzs2.dat
注意:run-data-pipeline.sh 默认 S2_LEVEL=12,并且 Java 参数使用 -Xmx32G,说明这个流程比较吃内存,不是普通轻量脚本。
5.3 GeoTZ 的运行时查找链路
运行时核心类:
text
packages/modules/GeoTZ/geotz_lookup/src/main/java/com/android/timezone/location/lookup/GeoTimeZonesFinder.java
packages/modules/GeoTZ/geotz_lookup/src/main/java/com/android/timezone/location/lookup/S2RangeFileBasedGeoTimeZonesFinder.java
调用逻辑是:
text
lat/lng
-> S2CellId
-> TzS2RangeFileReader.findEntryByCellId()
-> tzId list
也就是:
text
经纬度 -> S2 网格 -> tzs2.dat 范围查询 -> 时区 ID 列表
6. 自动时区设置和更新数据的关系
自动时区开关不是直接写时区数据,它只是决定系统使用哪种检测算法。
参考 aaa/自动时区设置.md,应用侧常见入口是:
java
timeManager.updateTimeZoneConfiguration(
new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(true)
.setGeoDetectionEnabled(true)
.build());
对应 Settings:
text
Settings.Global.AUTO_TIME_ZONE
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED
Framework 侧核心类:
text
frameworks/base/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
frameworks/base/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
frameworks/base/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
frameworks/base/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
ConfigurationInternal.getDetectionMode() 会决定当前模式:
text
DETECTION_MODE_MANUAL
DETECTION_MODE_GEO
DETECTION_MODE_TELEPHONY
DETECTION_MODE_UNKNOWN
如果进入 DETECTION_MODE_GEO,系统会使用 location time zone provider。
7. GNSS / 位置自动时区调用链
结合 aaa/geotz_gnss_sequence.md 和 aaa/geotz_architecture.md,可以把调用链理解成:
text
Settings / 系统应用
-> TimeManager
-> TimeZoneDetectorService
-> TimeZoneDetectorStrategyImpl
-> LocationTimeZoneManagerService
-> LocationTimeZoneProviderController
-> RealLocationTimeZoneProviderProxy
-> OfflineLocationTimeZoneProviderService
-> OfflineLocationTimeZoneDelegate
-> EnvironmentImpl
-> LocationManager
-> GeoTimeZonesFinder
-> tzs2.dat
-> TimeZoneProviderSuggestion
-> TimeZoneDetectorStrategyImpl
-> AlarmManagerInternal.setTimeZone()
-> persist.sys.timezone
源码中的关键点:
text
LocationTimeZoneManagerService
使用 TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE
RealLocationTimeZoneProviderProxy
绑定实现 TimeZoneProviderService 的远端 provider
OfflineLocationTimeZoneProviderService
继承 android.service.timezone.TimeZoneProviderService
OfflineLocationTimeZoneDelegate
根据 Location 查询 tzs2.dat 并构造 TimeZoneProviderSuggestion
TimeZoneDetectorStrategyImpl
如果 geo suggestion 有结果,则选择 zoneId 并设置系统时区
当前源码中 EnvironmentImpl.setDeviceTimeZoneAndConfidence() 最终会调用:
java
AlarmManagerInternal.setTimeZone(zoneId, confidence, logInfo);
而系统属性读取点是:
text
persist.sys.timezone
8. 设置中添加或调整时区显示
如果只是想在 Settings 页面里显示新的城市 / 时区项,通常不是改 GeoTZ。
需要先区分目标:
- 改时区规则:看
system/timezone、external/icu。 - 改时区名称 / 本地化显示:看
external/icu、external/cldr和 Settings 资源。 - 改经纬度到时区边界:看
packages/modules/GeoTZ。 - 改自动时区开关逻辑:看
frameworks/base/services/core/java/com/android/server/timezonedetector。
Settings 具体资源路径不同产品可能有定制,需要在当前源码中搜索:
bash
rg -n "timezones|timezone|Asia/Shanghai" packages/apps frameworks/base/packages
不要把 Settings 展示问题误判成 tzdata 数据问题。
9. 常用验证方法
9.1 查看当前设备时区
bash
adb shell getprop persist.sys.timezone
adb shell date
9.2 打开自动时区
调试方式:
bash
adb shell settings put global auto_time_zone 1
如果要打开位置时区检测,还要关注:
bash
adb shell settings get secure location_time_zone_detection_enabled
正式系统应用更推荐走:
java
TimeManager.updateTimeZoneConfiguration()
因为它会经过权限、capability 和用户配置检查。
9.3 查看 tzdata 版本
源码侧:
bash
cat system/timezone/output_data/version/tz_version
设备侧路径会和 APEX 挂载相关,可以重点看:
bash
adb shell ls -l /apex/com.android.tzdata/etc/tz
adb shell ls -l /system/usr/share/zoneinfo
9.4 验证 GeoTZ 数据是否进包
源码侧:
bash
ls -lh packages/modules/GeoTZ/output_data/odbl/tzs2.dat
APEX 形态配置中使用:
text
geodata.path=/apex/com.android.geotz/etc/tzs2.dat
APK 形态配置中使用:
text
geodata.resource=tzs2.dat
对应文件:
text
packages/modules/GeoTZ/apex/com.android.geotz/resources/offlineltzprovider.properties
packages/modules/GeoTZ/app/resources/offlineltzprovider.properties
9.5 跑测试
普通时区规则建议至少跑:
bash
atest CtsLibcoreTestCases
atest CtsIcuTestCases
GeoTZ 相关测试可以看:
text
packages/modules/GeoTZ/common/src/TEST_MAPPING
packages/modules/GeoTZ/geotz_lookup/TEST_MAPPING
packages/modules/GeoTZ/locationtzprovider/TEST_MAPPING
常见模块:
bash
atest GeotzCommonTests
atest GeotzLookupTests
atest OfflineLocationTimeZoneProviderTests
10. 常见误区
10.1 改了 tzdata,不代表 GNSS 自动时区边界会变
system/timezone/output_data/iana/tzdata 影响时区规则。
packages/modules/GeoTZ/output_data/odbl/tzs2.dat 影响经纬度查时区。
如果问题是"某个边境坐标识别错时区",大概率要看 GeoTZ / timezone-boundary-builder。
如果问题是"某个国家夏令时规则变了",大概率要看 IANA tzdata。
10.2 直接写 persist.sys.timezone 不是自动时区
自动时区的系统入口在 TimeZoneDetectorService 和 TimeZoneDetectorStrategyImpl。
直接设置:
bash
adb shell setprop persist.sys.timezone Asia/Shanghai
只适合临时调试,不等于验证自动时区链路。
10.3 打开 AUTO_TIME_ZONE 不一定使用 GeoTZ
系统会根据 ConfigurationInternal.getDetectionMode() 选择:
text
geo 可用且位置开关允许 -> DETECTION_MODE_GEO
geo 不可用但 telephony 可用 -> DETECTION_MODE_TELEPHONY
用户关闭自动时区 -> DETECTION_MODE_MANUAL
所以只打开 auto_time_zone=1,不代表一定会走 GNSS / GeoTZ。
10.4 APEX 和 APK 是部署形态,不是两套算法
packages/modules/GeoTZ/app 是 privileged APK 形态。
packages/modules/GeoTZ/apex/com.android.geotz 是 APEX 形态。
核心算法仍然来自:
text
locationtzprovider
geotz_lookup
s2storage
common
11. 新手建议的排查顺序
如果你接到一个"时区不对"的问题,建议按下面顺序判断。
第一步,确认当前系统时区:
bash
adb shell getprop persist.sys.timezone
adb shell date
第二步,确认是手动时区、Telephony 自动时区,还是 Geo 自动时区:
bash
adb shell settings get global auto_time_zone
adb shell settings get secure location_time_zone_detection_enabled
第三步,确认问题类型:
text
时区规则错:查 system/timezone、external/icu
经纬度识别错:查 packages/modules/GeoTZ/output_data/odbl/tzs2.dat
Settings 页面显示错:查 Settings 资源、ICU / CLDR
自动时区没触发:查 timezonedetector 和 location time zone manager
第四步,看源码调用链:
text
TimeZoneDetectorService
TimeZoneDetectorStrategyImpl
LocationTimeZoneManagerService
RealLocationTimeZoneProviderProxy
OfflineLocationTimeZoneProviderService
GeoTimeZonesFinder
第五步,再决定改哪条数据线。
12. 总结
AOSP14 的时区更新可以分成两件事:
text
1. 更新时区规则:system/timezone + external/icu + external/cldr
2. 更新离线位置边界:packages/modules/GeoTZ
普通世界时间、夏令时、国家映射、ICU 显示,优先看 system/timezone。
GNSS / 经纬度自动识别时区,优先看 packages/modules/GeoTZ。
自动时区是否真的生效,要看 Framework 的 timezonedetector 策略,不要只看某一个 setting 或某一个数据文件。
参考