时间概览

本文介绍了 Android 上时间和时区检测功能的工作原理。其中包括 Android 如何自动检测时间和时区、适用于设备制造商的配置选项以及测试信息。

时间和时区概览

为确定在状态栏等位置中显示的用户当地时间,Android 会跟踪两种相关又独立的状态:

  • 当前 Unix 纪元时间
  • 当前时区

当前 Unix 纪元时间和当前时区是设备级状态,这意味着它们将由设备的所有用户共享。

当前 Unix 纪元时间不是固定值。它会自动更新,以体现时间的流逝。除了正常的时间流逝之外,如果发现设备的当前 Unix 纪元时间不正确(例如,在设备断电后),系统会对这个时间加以调整。

当前时区决定了将当前 Unix 纪元时间转换为当地时间时所要进行的调整。例如,在洛杉矶的夏季,设备会将当前 Unix 纪元时间减去 7 小时,而在冬季,则会减去 8 小时。

为了支持这些当地时间计算,所有 Android 设备均拥有一个包含所有全球时区规则的数据库。 如需详细了解时区规则,请参阅时区规则

当用户前往使用一个不同时区的新位置时,当前 Unix 纪元时间无需调整,但用户通常希望看到当地时间,而不是他们先前所在地的时间。通过更改当前时区,系统可以确保向当前 Unix 纪元时间应用正确的偏移值,从而正确显示新位置的当地时间。

AOSP 提供以下机制,允许用户独立控制是否自动设置时间和时区。

  • 自动时间检测:确保设备的当前 Unix 纪元时间正确无误。
  • 自动时区检测:确保设备的当前时区正确无误。

自动时间检测

本部分简要介绍 time_detector 服务,该服务用于管理自动时间检测、用户控件、配置选项和测试详情。

time_detector 服务

搭载 Android 10 或更高版本的设备上提供的 time_detector 服务用于管理自动时间检测。在已启用自动时间检测的情况下,该服务会根据需要调整设备的当前 Unix 纪元时间。

time_detector 服务始终处于以下两种状态之一:不确定或确定。该服务的确定或不确定状态取决于它从各种源收到的时间建议。

time_detector 服务处于确定状态(即已收到包含 Unix 纪元时间信息的建议)时,如果时间建议与当前 Unix 纪元时间不同,它会替换当前 Unix 纪元时间。

time_detector 处于不确定状态时,则不会替换当前时间。不确定状态通常表示 time_detector 服务未收到时间建议。此外,如果 time_detector 服务收到的建议被认为太旧而不能使用,它也会变为不确定状态。系统会考虑建议的存在时长,因为使用旧 Unix 纪元时间建议的调整项依赖于设备上已流逝的实时时钟时间,系统会假定后者在很长一段时间后会变得不准确。

为了自动确定当前 Unix 纪元时间,设备可使用各种源。这些在本文档中称为“源站”(origin)。time_detector 服务会根据源站将各个建议序列视为不同的内容。

time_detector 服务是有状态的,这意味着它会记录每个源站提供的最新建议。如果源站有更新的 Unix 纪元时间信息,系统会向 time_detector 提出新建议。time_detector 服务会重新评估新建议和现有建议,并在收到建议后更新设备状态。

尽管 UTC 时间在国际上得到了认可,但对 Android 设备来说,建立当前 Unix 纪元时间并非总是那么简单的,原因多种多样:

  • Unix 纪元时间和 UTC 时间是两个略有不同的计时系统。 若要在这两者之间转换,则需了解闰秒的出现时间以及源站对它们的处理方式。
  • 源站可能仅在特定时间或在特定情况下可用。例如,如果源站需要网络连接,那么可能只有在设备连接到互联网后才能使用源站。
  • 源站可能不准确或不精确,或者有误。例如,如果电话手机基站没正确跟踪“世界时间”,telephony 源站可能会提供不准确的时间建议。
  • 获取 Unix 纪元时间时可能会引入不准确因素。 例如,网络延迟、缓冲或进程调度可能会导致 Unix 纪元时间不准确。
  • 用于在收到建议后流逝的时间内调整该建议的参考时钟可能不准确。

在 AOSP 中,有两个主要的时间检测源站已配置为默认使用:

  • telephony:使用网络身份和时区 (NITZ) 电话信号。
  • network:使用网络时间协议 (NTP) 时间服务器。

telephony 和 network 源站都需要连接到外部网络,但网络并非始终可用。

从 Android 12 开始,Android 还支持以下源站,它们未配置为默认使用:

  • gnss:使用 GPS 位置信息提供程序从 GNSS 源获取时间。
  • external:支持设备制造商集成自有 Unix 纪元时间源的通用源站。

时间设置

用户可以在 AOSP“设置”应用的系统 > 日期和时间中启用自动时间检测。

“设置”中的自动时间检测

图 1. “设置”中的自动时间检测。

下表介绍了 AOSP“设置”应用中用于时间检测的用户控件。

*在 Android 11 及更低版本中,此设置带有使用网络提供的时间标记

在 AOSP“设置”中的位置 在 AOSP“设置”中的名称 范围 行为
系统 > 日期和时间 自动设置时间* 所有用户

一个切换开关。

处于开启状态时,设备会负责检测当前 Unix 纪元时间。处于关闭状态时,系统会为用户提供用于手动设置设备时间的控件。

当用户手动输入时间时,输入的是当地时间,而不是 Unix 纪元时间。当前 Unix 纪元时间通过使用当前时区得出 Unix 纪元时间来计算。

配置

设备制造商可以采用多种方式配置 time_detector 服务,例如要使用哪些源站,以及如何为来自这些源站的信号排定优先级。

源站优先级

从 Android 12 开始,设备制造商可以更改 core/res/res/values/config.xml 配置文件,以指定要在自动时间检测中包含的时间源站,以及 time_detector 考虑这些源站的优先级。

对于搭载 Android 11 或更低版本的设备,源站优先级会被硬编码为 ["telephony", "network"],这意味着 telephony 建议的优先级高于 network 建议。

默认 AOSP 配置如下所示:

<!-- Specifies priority of automatic time sources. Suggestions from higher entries in the list
     take precedence over lower ones.
     See com.android.server.timedetector.TimeDetectorStrategy for available sources. -->
<string-array name="config_autoTimeSourcesPriority">
    <item>network</item>
    <item>telephony</item>
</string-array>

在 Android 12 中,network 和 telephony 建议被配置为要默认使用的源站。network 时间建议的优先级高于 telephony 时间建议。设备制造商可以更改源站的顺序,以还原 Android 11 或更低版本(其中 network 建议的优先级较高)中的行为。

默认情况下,如果优先级最高的有效建议与设备当前的系统时钟时间仅相差几秒,系统会视为两者互相匹配,不会更改设备时间。这是为了避免为监听 ACTION_TIME_CHANGED intent 的已安装应用增加负担。

允许的源站值包括:

时间下限

Android 12 引入了一个时间下限,用于验证 time_detector 服务收到的时间建议。下限时间值根据 build 时间戳设置。这基于以下原则:在设备的系统映像构建之前无法发生有效时间。如果时间建议是在时间下限之前,time_detector 服务会舍弃这条建议,因为如果 build 时间戳是正确的,这条建议就不可能有效。

对于搭载 Android 11 或更低版本的设备,time_detector 服务不会验证传入的 Unix 纪元时间建议。

Android 未强制规定时间上限。

时间调试和测试

本部分介绍如何调试和测试 time_detector 服务及由所有源站共享的其他组件的行为。

与 time_detector 服务交互

如需查看 time_detector 服务的配置和 time_detector 服务的状态,请使用以下命令:

adb shell cmd time_detector dump

如需查看用于调试和测试时区检测的其他命令,请使用以下命令:

adb shell cmd time_detector help

帮助输出还描述了 device_config 服务属性,它们可用于在测试环境或生产环境中影响 time_detector 的行为。如需了解详情,请参阅使用 device_config 服务配置设备

如需验证自动时间检测,测试人员必须知道 time_detector 服务使用的是哪个源站。以下是 adb shell cmd time_zone_detector dump 命令的输出内容示例,其中有关当前源站和服务状态的信息以粗体显示:

$ adb shell cmd time_detector dump

TimeDetectorStrategy:
  mLastAutoSystemClockTimeSet=null
  mEnvironment.isAutoTimeDetectionEnabled()=true
  mEnvironment.elapsedRealtimeMillis()=23717241
  mEnvironment.systemClockMillis()=1626707861336
  mEnvironment.systemClockUpdateThresholdMillis()=2000
  mEnvironment.autoTimeLowerBound()=2021-07-19T07:48:05Z(1626680885000)
  mEnvironment.autoOriginPriorities()=[network,telephony]
  Time change log:
    ...
  Telephony suggestion history:
    ...
  Network suggestion history:
    ...
  Gnss suggestion history:
    ...
  External suggestion history:
    ...

这些信息可解读为:

mEnvironment.isAutoTimeDetectionEnabled() 是否启用了自动时间检测。
mEnvironment.autoTimeLowerBound() 用于验证时间建议的当前下限。
mEnvironment.autoOriginPriorities() 正在使用的源站和优先级顺序。

时间更改日志用于指明 time_detector 服务更改了设备的当前 Unix 纪元时间的情况。

建议历史记录信息用于指明每个源站提供的建议。

自动时区检测

本部分简要介绍 time_zone_detector 服务,该服务用于管理自动时区检测、“设置”中的用户控件、电话和位置信息时区检测以及测试详情。

time_zone_detector 服务

搭载 Android 11 或更高版本的设备上提供的 time_zone_detector 服务用于管理自动时区检测。在自动时区检测启用的情况下,该服务可根据需要调整设备的当前时区。

启用自动时区检测后,time_zone_detector 可以处于以下两种状态之一:不确定和确定。

time_zone_detector 服务处于确定状态时,这意味着 time_zone_detector 服务已收到强时区信息,这可能会导致它替换当前时区。当该服务处于不确定状态时,这表示它未收到任何信息或者仅有低置信度的信息,这意味着它不会替换当前时区。

time_zone_detector 服务的确定状态可能包括 time_zone_detector 无可用时区信息,或有多个时区可供选择的情况。这些状态如下:

  • 当设备处于没有时区的地方(例如国际海域或争议区域)时,就会进入不带时区的确定状态。这种状态与不确定状态类似,但表示 time_zone_detector 无需执行进一步操作即可尝试确定时区。
  • 当存在不明确的情况或边界条件时,设备就会进入带有多个时区的确定状态。在这种状态下,如果当前时区是 time_zone_detector 确定的时区之一,则当前时区将保持不变。否则,系统将使用某个可用时区。如果用户之前手动选择了时区,或者设备接近边界,这就给了 time_zone_detector 一个粘性元素。

time_zone_detector 服务的确定或不确定状态取决于源站发送的时区建议。

通常,建议分为与 time_zone_detector 可能的状态高度匹配的两种类型:确定和不确定。以下是建议类型的示例:

  • type = "uncertain", zoneIds = []

    • 源站并不知道时区是哪个。
  • type = "certain", zoneIds = ["Europe/London"]

    • 源站确定时区为“Europe/London”。
  • type = "certain", zoneIds = []

    • 源站确定是哪个时区,但没有与当前位置关联的时区 ID。
  • type = "certain", zoneIds = ["America/Denver", "America/Phoenix"]

    • 源站确定时区就是“America/Denver”和“America/Phoenix”这两个时区中的一个,但无法从中进行选择。

time_zone_detector 服务会根据源站将建议序列视为不同的内容。根据源站,建议还可能包含表明源站确定程度的元数据。

time_zone_detector 服务是有状态的,这意味着它会记录每个源站提供的最新建议。如果之前的建议不再正确,也就是说,如果源站现在有不同的建议,或者它失去了检测时区的能力,新建议就会发送到 time_zone_detector 服务。time_zone_detector 服务会重新评估新建议和现有建议,并在收到建议时更新设备状态。

Android 支持两种时区检测源站:

  • telephony
  • location

time_zone_detector 服务仅使用单个源站来确定时区。如果设备支持 location 源站,则会根据由用户配置的时区设置确定当前要使用的源站。如果当前源站不确定时区是哪个,time_zone_detector 不会使用来自其他源站的建议。与除当前源站外的源站关联的建议可以由 time_zone_detector 保存在内存中,但除非当前源站发生变化,否则系统不会使用它们。当用户更改自动时区检测设置且当前源站发生变化时,系统会使用适用于新源站的最新建议。

在搭载 Android 13 及更高版本的设备上,time_zone_detector 服务支持电话回退模式。在该模式下,如果位置信息检测无法检测时区,或者位置信息检测要用比电话检测更长的时间检测时区,则 Android 可以暂时使用电话检测建议。

电话回退模式适用于同时支持电话检测和位置信息检测且用户已在时区设置中启用“使用位置信息设置时区”的设备。当设备重新启动以及当停用飞行模式时,该模式会自动启用。在电话回退模式下,time_zone_detector 服务会使用电话建议,就像位置信息服务检测已停用一样,直到位置信息源站提供特定建议为止。系统在收到特定建议后会停用电话回退模式,并仅使用位置信息建议。

如需了解电话回退模式的配置详细信息,请参阅时区检测配置

时区设置

用户可以在 AOSP“设置”应用中启用和配置自动时区检测设置。

“设置”中的自动时区检测设置

图 2. “设置”中的自动时区检测设置。

下表介绍了 AOSP“设置”应用中用于时区检测的用户控件。

*在 Android 11 及更低版本中,此设置带有使用网络提供的时区标记

在 AOSP“设置”中的位置 在 AOSP“设置”中的名称 范围 行为
系统 > 日期和时间 自动设置时区* 所有用户

一个切换开关。

开启时,设备负责检测当前时区。关闭时,系统会为用户提供用于手动设置设备时区的控件。

系统 > 日期和时间 使用位置信息设置时区 当前用户

一个切换开关。

从 Android 12 开始提供。仅当设备支持位置信息时区检测时,才会显示此切换开关。

位置信息 使用位置信息 当前用户

一个切换开关。

一般来说,用于允许或阻止使用设备的位置信息。如果设备支持位置时区检测,则此值相关。

下面简要介绍了在用户所选设置下的设备时区检测行为:

  • [日期和时间] 自动设置时区:关闭

    • 用户必须手动选择时区。
  • [日期和时间] 自动设置时区:开启

    • [位置信息] 使用位置信息:关闭

      • 电话信号将用于检测时区。(请参见下面的备注。)
    • [位置信息] 使用位置信息:开启

      • [日期和时间] 使用位置信息设置时区:开启

        • 位置信息将用于检测时区。
      • [日期和时间] 使用位置信息设置时区:关闭

        • 电话信号将用于检测时区。(请参见下面的备注。)

多用户设备

由于所涉及的多项设置是针对当前用户的,因此在多用户 Android 设备上,如果当前用户发生切换,设备的时区检测行为可能会发生变化。

使用位置信息设置时区切换开关针对的是当前用户,且不受设备政策的限制,这意味着用户始终可以更改其值,即使自动设置时区切换开关处于关闭状态,或者设备政策控制器限制了其他时间或时区控件,也是如此。

更改为自动检测模式以及从自动检测模式改为其他模式时的行为

当用户将时区检测模式从手动切换为自动时,time_zone_detector 可能已确定当前时区。如果是这样的话,当用户启用自动检测时,设备的时区可能会同时发生变化,以便与 time_zone_detector 服务的观点保持一致。

同样,如果用户在“设置”中做出的更改导致 time_zone_detector 服务的当前源站发生变化,而 time_zone_detector 可能已收到针对新源站的建议,那么,设备的时间可能会相应更改,以与 time_zone_detector 服务的观点保持一致。

电话时区检测

电话时区检测使用电话信号来确定当前时区。如需了解详情,请参阅电话时区检测

位置信息时区检测

Android 12 或更高版本支持位置信息时区检测功能。这是一项可选的自动时区检测功能,可让设备使用其位置信息来确定当前时区。

Android 12 中引入的 location_time_zone_manager 服务在系统服务器中运行,其中包含负责向 time_zone_detector 服务提交位置信息源站建议的代码。如需了解详情,请参阅位置信息时区检测

功能采用方面的注意事项

本部分介绍位置信息时区检测功能的各个方面,帮助设备制造商确定是否在设备上采用该功能。

比较电话检测和位置信息检测

下表对比了分别使用位置信息以及电话信号进行时区检测的优缺点。

电话检测 位置信息检测
正确性 因国家/地区而异。

取决于 MCC 和 NITZ 的正确性及可用性。
取决于功能配置或插件组件。

正确性通常因以下因素而异:
  • 位置信息提供程序的准确性和规律性。
  • 时区地图数据的质量。
可更新性 电话检测依赖于可更新的时区数据模块 (com.android.tzdata APEX) 中包含的文件。 取决于功能配置或插件组件。

可更新性通常取决于设备使用的是服务器还是客户端时区地图数据。

注意:用于更新 Android 的 TZDB 及其他时区信息副本的时区数据模块中不包含时区地图数据。

此外,设备制造商还必须考虑时区规则和时区地图数据之间的版本一致性。
耗电量 耗电量为零或极低 取决于用户位置信息设置、正在使用的插件以及通常会有其他哪些应用请求位置信息。
可用性 仅电话设备提供此功能。通常需要有效的 SIM 卡。 位置信息检测取决于可用的位置信息提供程序。
用户隐私

用户的首选时区通常取决于用户所在的地理位置。位置信息属于敏感数据。在时区检测过程中,用户可能会担心他们的位置信息被共享。在设备上运行的所有应用无需 Android 权限即可读取设备的当前时区,并且应用可以根据这一信息推断出并不精确的设备位置,这与时区检测无关。

更具体地说,时区检测可以通过被动或主动方式进行:

  • 被动:设备所处环境中的某些组件告知设备该环境中所用的时区。
  • 主动:设备必须自行确定时区,并根据用户的隐私设置,在征得其同意后获取设备的位置信息,以达到此目的。然后,它可以与外部服务共享其位置信息。请参阅下文,详细了解用户隐私以及如何征得用户同意。

被动检测(例如使用 telephony 源站)不会给用户带来隐私权方面的其他影响。

主动检测(例如使用 location 源站)涉及确定设备的位置信息(用户可能不想同意),此外,位置信息可以通过网络发送,以确定时区 ID。

借助 Android 在时区检测时采用的用户隐私保护方法,用户能够单独停用预计会执行主动检测的源站。此外,AOSP 平台代码不直接处理位置信息本身:位置信息检测和将位置信息映射到时区 ID 的操作由设备制造商配置的插件组件完成。

如需详细了解用户隐私功能,请参阅位置信息时区检测

配置

设备制造商可以配置 time_zone_detector 服务以更改其行为。本部分介绍 time_zone_detector 服务的一般行为的配置选项。如需了解电话和时区检测源站的配置详细信息,请参阅电话时区检测位置信息时区检测

基本 AOSP 配置位于 frameworks/base/core/res/res/values/config.xml 中。

配置键 AOSP 值 说明
config_supportTelephonyTimeZoneFallback true 如果设置为 true,则 time_zone_detector 将使用电话回退模式。此设置适用于 Android 13 及更高版本。

时区调试和测试

本部分介绍如何调试和测试 time_zone_detector 服务及由所有源站共享的其他组件的行为。

使用 device_config 服务配置设备

device_config 服务是 Android 中所用的一种机制:使用由专有(非 AOSP)代码从远程服务器提取的值来配置可修改的行为。使用 device_config 值进行测试时,尤其是在长时间运行的手动测试期间,设备可能会同步这些标志,进而重置这些标志并清除为测试设置的值。

在 Android 12 或更高版本中,如需暂时阻止标志同步,请使用以下命令:

adb shell cmd device_config set_sync_disabled_for_tests persistent

如需在测试后恢复标志同步,请使用以下命令:

adb shell cmd device_config set_sync_disabled_for_tests none

恢复标志同步后,请重新启动设备。

如需了解详情,请使用 $ adb shell cmd device_config help

与 time_zone_detector 服务交互

如需查看 time_zone_detector 配置和 time_zone_detector 服务的状态,请使用以下命令:

adb shell cmd time_zone_detector dump

如需查看用于调试和测试时区检测的其他命令,请使用以下命令:

adb shell cmd time_zone_detector help

帮助输出还描述了 device_config 服务属性,它们可用于在测试环境或生产环境中影响 time_zone_detector 服务的行为。如需了解详情,请参阅使用 device_config 服务配置设备

如需验证时区检测,测试人员必须知道 time_zone_detector 使用的是哪个源站。如需了解和影响 time_zone_detector 的当前源站,请使用以下某个选项:

  • 通过“设置”界面直观地进行检查。如需了解详情,请参阅时区设置
  • 通过 adb 使用命令行:

    • 如需转储 time_zone_detector 状态,请使用 adb shell cmd time_zone_detector dump
    • 如需更改设备设置,请使用其他 time_zone_detector 命令。如需了解详情,请使用 adb shell cmd time_zone_detector help

以下是 adb shell cmd time_zone_detector dump 命令的输出内容示例,其中有关当前源站和服务状态的信息以粗体显示:

$ adb shell cmd time_zone_detector dump
TimeZoneDetectorStrategy:
  mEnvironment.getCurrentUserId()=0
  mEnvironment.getConfiguration(currentUserId)=ConfigurationInternal{mUserId=0, mUserConfigAllowed=true, mTelephonyDetectionSupported=true, mGeoDetectionSupported=true, mAutoDetectionEnabled=true, mLocationEnabled=true, mGeoDetectionEnabled=true}
  [Capabilities=TimeZoneCapabilitiesAndConfig{mCapabilities=TimeZoneDetectorCapabilities{mUserHandle=UserHandle{0}, mConfigureAutoDetectionEnabledCapability=40, mConfigureGeoDetectionEnabledCapability=40, mSuggestManualTimeZoneCapability=30}, mConfiguration=TimeZoneConfiguration{mBundle=Bundle[{geoDetectionEnabled=true, autoDetectionEnabled=true}]}}]
  mEnvironment.isDeviceTimeZoneInitialized()=true
  mEnvironment.getDeviceTimeZone()=Europe/London
  Time zone change log:
  Manual suggestion history:
...
  Geolocation suggestion history:
...
  Telephony suggestion history:
...

这些信息可解读为:

mUserConfigAllowed 设备政策控制器是否阻止用户控制日期和时间设置。
mTelephonyDetectionSupported 设备是否具有电话时区检测功能。
mGeoDetectionSupported 设备是否具有位置信息时区检测功能。这是基于配置以及至少存在一个 LTZP 时的有效状态。
mAutoDetectionEnabled 是否启用了自动时区检测。
mLocationEnabled 主位置信息切换开关。
mGeoDetectionEnabled 源站切换开关:`false` 表示 telephony 源站,而 `true` 表示 location 源站。

建议历史记录信息用于指明通过“设置”(手动)以及 telephony 和 location 源站提供的建议。