時區規則

Android 10 棄用了基於 APK 的時區數據更新機制(在 Android 8.1 和 Android 9 中可用),並將其替換為基於 APEX 的模塊更新機制。 AOSP 繼續包含 OEM 啟用基於 APK 的更新所需的平台代碼,因此升級到 Android 10 的設備仍然可以通過 APK 接收合作夥伴提供的時區數據更新。但是,不應在同時接收模塊更新的生產設備上使用 APK 更新機制,因為基於 APK 的更新取代了基於 APEX 的更新(即,接收 APK 更新的設備將忽略基於 APEX 的更新)。

Android 10 及更高版本支持的時區數據模塊更新了 Android 設備上的夏令時 (DST) 和時區,標準化了可能因宗教、政治和地緣政治原因而頻繁更改的數據。

更新使用以下過程:

  1. IANA發佈時區數據庫更新 發布更新以響應一個或多個政府更改其國家/地區的時區規則。
  2. Google 或 Android 合作夥伴準備了包含更新後的時區的時區數據模塊更新(APEX 文件)。
  3. 最終用戶設備下載更新,重新啟動,然後應用更改,之後設備的時區數據包含來自更新的新時區數據。

有關模塊的詳細信息,請參閱模塊化系統組件

時區更新 (Android 8.1–9)

在 Android 8.1 和 Android 9 中,OEM 可以使用基於 APK 的機制將更新的時區規則數據推送到設備,而無需系統更新。此機制使用戶能夠及時接收更新(從而延長 Android 設備的使用壽命),並使 Android 合作夥伴能夠獨立於系統映像更新測試時區更新。

Android 核心庫團隊提供了更新現有 Android 設備上的時區規則所需的數據文件。 OEM 在為其設備創建時區更新時可以選擇使用這些數據文件,也可以根據需要創建自己的數據文件。在所有情況下,OEM 都保留對其支持的設備的質量保證/測試、時間安排和時區規則更新的啟動控制。

Android時區源碼及數據

所有庫存的 Android 設備,即使是那些不使用此功能的設備,都需要時區規則數據,並且必須在/system分區中附帶一組默認的時區規則數據。然後,Android 源代碼樹中以下庫中的代碼將使用此數據:

  • 來自libcore/的託管代碼(例如java.util.TimeZone )使用tzdatatzlookup.xml文件。
  • bionic/中的本機庫代碼(例如,對於mktime ,本地時間系統調用)使用tzdata文件。
  • external/icu/中的 ICU4J/ICU4C 庫代碼使用 icu .dat文件。

這些庫跟踪可能存在於/data/misc/zoneinfo/current目錄中的覆蓋文件。覆蓋文件預計將包含改進的時區規則數據,從而能夠在不更改/system的情況下更新設備。

需要時區規則數據的Android系統組件首先檢查以下位置:

  • libcore/bionic/代碼使用tzdatatzlookup.xml文件的/data副本。
  • ICU4J/ICU4C 代碼使用/data中的文件並回退到/system文件以獲取不存在的數據(用於格式、本地化字符串等)。

發行版文件

Distro .zip文件包含填充/data/misc/zoneinfo/current目錄所需的數據文件。發行版文件還包含允許設備檢測版本控制問題的元數據。

發行版文件格式取決於 Android 版本,因為內容會隨著 ICU 版本、Android 平台要求和其他版本的變化而變化。 Android 為每個 IANA 更新(除了更新平台系統文件)提供支持的 Android 版本的發行版文件。為了使他們的設備保持最新狀態,OEM 可以使用這些發行版文件或使用 Android 源代碼樹(其中包含生成發行版文件所需的腳本和其他文件)創建自己的發行版文件。

時區更新組件

時區規則更新涉及將發行版文件傳輸到設備以及安全安裝其中包含的文件。傳輸和安裝需要以下內容:

  • 平台服務功能 ( timezone.RulesManagerService ),默認禁用。 OEM 必須通過配置啟用該功能。 RulesManagerService在系統服務器進程中運行,並通過寫入/data/misc/zoneinfo/staged來暫存時區更新操作。 RulesManagerService還可以替換或刪除已暫存的操作。
  • TimeZoneUpdater ,一個不可更新的系統應用程序(又名Updater 應用程序)。 OEM 必須將此應用程序包含在使用該功能的設備的系統映像中。
  • OEM TimeZoneData ,一個可更新的系統應用程序(又名數據應用程序),將發行版文件傳送到設備並使其可供更新程序應用程序使用。 OEM 必須將此應用程序包含在使用該功能的設備的系統映像中。
  • tzdatacheck ,正確和安全的時區更新操作所需的啟動時二進製文件。

Android 源代碼樹包含上述組件的通用源代碼,OEM 可以選擇使用而無需修改。提供測試代碼以使 OEM 能夠自動檢查他們是否已正確啟用該功能。

發行版安裝

發行版安裝過程包括以下步驟:

  1. 數據應用通過應用商店下載或旁加載進行更新。系統服務器進程(通過timezone.RulesManagerServer/timezone.PackageTracker類)監視對配置的、OEM 特定的數據應用程序包名稱的更改。

    數據應用更新
    圖 1.數據應用更新
  2. 系統服務器進程通過向更新程序應用程序廣播帶有唯一、一次性令牌的目標意圖來觸發更新檢查。系統服務器跟踪它生成的最新令牌,以便確定它觸發的最新檢查何時完成;任何其他標記都將被忽略。

    觸發更新
    圖 2.觸發器更新檢查
  3. 在更新檢查期間,Updater 應用程序執行以下任務:
    • 通過調用 RulesManagerService 查詢當前設備狀態。

      調用 RulesManagerService
      圖 3.數據應用更新,調用 RulesManagerService
    • 通過查詢明確定義的 ContentProvider URL 和列規範來查詢 Data 應用程序以獲取有關發行版的信息。

      獲取發行版信息
      圖 4.數據應用更新,獲取有關發行版的信息
  4. 更新程序應用程序會根據其擁有的信息採取適當的行動。可用的操作包括:
    • 請求安裝。 Distro 數據從 Data 應用程序中讀取並傳遞給系統服務器中的 RulesManagerService。 RulesManagerService 再次確認發行版格式版本和內容適用於設備並分階段安裝。
    • 請求卸載(這種情況很少見)。例如,如果/data中更新的 APK 被禁用或卸載,並且設備返回到/system中的版本。
    • 沒做什麼。當發現數據應用發行版無效時發生。
    在所有情況下,更新程序應用程序都會使用檢查令牌調用 RulesManagerService,以便系統服務器知道檢查已完成且成功。

    檢查完成
    圖 5.檢查完成
  5. 重新啟動並進行 tzdatacheck。當設備下次啟動時,tzdatacheck 二進製文件執行任何分階段操作。 tzdatacheck 二進製文件可以執行以下任務:
    • 在其他系統組件打開並開始使用文件之前,通過處理/data/misc/zoneinfo/current文件的創建、替換和/或刪除來執行分階段操作。
    • 檢查/data中的文件對於當前平台版本是否正確,如果設備剛剛收到系統更新並且發行版格式版本已更改,則可能不是這種情況。
    • 確保 IANA 規則版本與/system中的版本相同或更新。這可以防止系統更新使設備的時區規則數據比/system映像中的數據更舊。

可靠性

端到端安裝過程是異步的,並且跨三個操作系統進程進行拆分。在安裝過程中的任何時候,設備都可能斷電、磁盤空間不足或遇到其他問題,從而導致安裝檢查不完整。在最好的不成功情況下,Updater 應用程序會通知系統服務器它不成功;在最糟糕的不成功情況下,RulesManagerService 根本不會收到任何調用。

為了處理這個問題,系統服務器代碼會跟踪觸發的更新檢查是否已完成以及數據應用程序的最後檢查版本代碼是什麼。當設備空閒和充電時,系統服務器代碼可以檢查當前狀態。如果它發現不完整的更新檢查或意外的 Data App 版本,它會自發觸發更新檢查。

安全

啟用後,系統服務器中的 RulesManagerService 代碼會執行多項檢查以確保系統可以安全使用。

  • 表明系統映像配置錯誤的問題會阻止設備啟動;示例包括錯誤的更新程序或數據應用程序配置或更新程序或數據應用程序不在/system/priv-app中。
  • 表明安裝了錯誤數據應用程序的問題不會阻止設備啟動,但會阻止觸發更新檢查;示例包括缺少所需的系統權限或數據應用程序未在預期的 URI 上公開 ContentProvider。

/data/misc/zoneinfo目錄的文件權限是使用 SELinux 規則強制執行的。與任何 APK 一樣,Data 應用必須使用與/system/priv-app版本簽名相同的密鑰進行簽名。 Data 應用程序應具有專用的 OEM 特定包名稱和密鑰。

集成時區更新

要啟用時區更新功能,OEM 通常:

  • 創建自己的數據應用程序。
  • 在系統映像構建中包含更新程序和數據應用程序。
  • 配置系統服務器以啟用 RulesManagerService。

準備中

在開始之前,OEM 應審查以下政策、質量保證和安全注意事項:

  • 為其數據應用創建專用的應用特定簽名密鑰。
  • 為時區更新創建發布和版本控制策略,以了解哪些設備將被更新,以及它們如何確保更新僅安裝在需要它們的設備上。例如,OEM 可能希望為其所有設備配備一個數據應用程序,或者可能選擇為不同設備配備不同的數據應用程序。該決定會影響包名稱的選擇,可能會影響使用的版本代碼和 QA 策略。
  • 了解他們是想使用來自 AOSP 的庫存 Android 時區數據還是自己創建。

創建數據應用

AOSP 包括在packages/apps/TimeZoneData中創建數據應用所需的所有源代碼和構建規則,以及位於packages/apps/TimeZoneData/oem_template中的AndroidManifest.xml和其他文件的說明和示例模板。示例模板包括真實數據應用 APK 的構建目標和用於創建數據應用測試版本的額外目標。

OEM 可以使用自己的圖標、名稱、翻譯和其他詳細信息自定義 Data 應用程序。但是,由於無法啟動數據應用程序,該圖標僅出現在設置 > 應用程序屏幕中。

Data 應用程序旨在使用生成適合添加到系統映像(用於初始版本)並通過應用程序商店(用於後續更新)簽名和分發的 APK 的小吃構建。有關使用小吃的詳細信息,請參閱使用小吃構建數據應用程序

OEM 必須在/system/priv-app中的設備系統映像中安裝預構建的數據應用程序。要在系統映像中包含預構建的 APK(由 Tapas 構建過程生成),OEM 可以復制packages/apps/TimeZoneData/oem_template/data_app_prebuilt中的示例文件。示例模板還包括構建目標,用於在測試套件中包含 Data 應用程序的測試版本。

在系統映像中包含更新程序和數據應用程序

OEM 必須將更新程序和數據應用 APK 放置在系統映像的/system/priv-app目錄中。為此,系統映像構建必須明確包含更新程序應用程序和數據應用程序預構建目標。

更新程序應用程序應使用平台密鑰進行簽名,並作為任何其他系統應用程序包含在內。目標在packages/apps/TimeZoneUpdater中定義為TimeZoneUpdater 。數據應用程序包含特定於 OEM,並取決於為預構建選擇的目標名稱。

配置系統服務器

要啟用時區更新,OEM 可以通過覆蓋frameworks/base/core/res/res/values/config.xml中定義的配置屬性來配置系統服務器。

財產描述需要覆蓋?
config_enableUpdateableTimeZoneRules
必須設置為true才能啟用 RulesManagerService。是的
config_timeZoneRulesUpdateTrackingEnabled
必須設置為true才能讓系統偵聽 Data 應用程序的更改。是的
config_timeZoneRulesDataPackage
OEM 特定數據應用程序的包名稱。是的
config_timeZoneRulesUpdaterPackage
為默認更新程序應用程序配置。僅在提供不同的 Updater 應用程序實現時進行更改。
config_timeZoneRulesCheckTimeMillisAllowed
由 RulesManagerService 觸發的更新檢查與安裝、卸載或什麼都不做響應之間允許的時間。在這點之後,可能會產生自發的可靠性觸發。
config_timeZoneRulesCheckRetryCount
在 RulesManagerService 停止生成更多之前允許的連續不成功更新檢查的數量。

配置覆蓋應該在系統映像中(不是供應商或其他),因為配置錯誤的設備可能會拒絕啟動。如果配置覆蓋位於供應商映像中,則更新到沒有數據應用程序(或具有不同數據應用程序/更新程序應用程序包名稱)的系統映像將被視為配置錯誤。

xTS 測試

xTS 是指與使用 Tradefed 的標準 Android 測試套件(例如 CTS 和 VTS)類似的任何 OEM 特定測試套件。擁有此類測試套件的 OEM 可以添加以下位置提供的 Android 時區更新測試:

  • packages/apps/TimeZoneData/testing/xts包含基本自動化功能測試所需的代碼。
  • packages/apps/TimeZoneData/oem_template/xts包含一個示例目錄結構,用於在類似 Tradefed 的 xTS 套件中包含測試。與其他模板目錄一樣,OEM 應根據自己的需要進行複制和定制。
  • packages/apps/TimeZoneData/oem_template/data_app_prebuilt包含構建時配置,用於包含測試所需的預構建測試 APK。

創建時區更新

當 IANA 發布一組新的時區規則時,Android 核心庫團隊會生成補丁以更新 AOSP 中的版本。使用庫存 Android 系統和發行版文件的 OEM 可以獲取這些提交,使用它們創建其 Data 應用程序的新版本,然後發布新版本以更新其生產中的設備。

由於數據應用程序包含與 Android 版本密切相關的發行版文件,因此 OEM 必須為 OEM 想要更新的每個受支持的 Android 版本創建數據應用程序的新版本。例如,如果 OEM 想要為 Android 8.1、9 和 10 設備提供更新,他們必須完成該過程 3 次。

第 1 步:更新系統/時區和外部/icu 數據文件

在此步驟中,OEM 從 AOSP 中的release -dev 分支獲取system/timezoneexternal/icu的 Android 提交,並將這些提交應用到他們的 Android 源代碼副本。

system/timezone AOSP 補丁包含system/timezone/input_datasystem/timezone/output_data中的更新文件。需要進行額外本地修復的 OEM 可以修改輸入文件,然後使用system/timezone/input_dataexternal/icu中的文件在output_data中生成文件。

最重要的文件是system/timezone/output_data/distro/distro.zip ,在構建數據應用 APK 時會自動包含該文件。

第 2 步:更新數據應用程序的版本代碼

在此步驟中,OEM 更新數據應用的版本代碼。構建會自動選擇distro.zip ,但新版本的 Data 應用程序必須具有新版本代碼,以便將其識別為新版本,並用於替換預先加載的 Data 應用程序或通過先前更新安裝在設備上的 Data 應用程序。

使用從package/apps/TimeZoneData/oem_template/data_app複製的文件構建數據應用程序時,您可以在Android.mk中找到應用於 APK 的版本代碼/版本名稱:

TIME_ZONE_DATA_APP_VERSION_CODE :=
TIME_ZONE_DATA_APP_VERSION_NAME
:=

testing/Android.mk中可以找到類似的條目(但測試版本代碼必須高於系統映像版本)。詳見示例版本代碼策略方案;如果使用示例方案或類似方案,則無需更新測試版本代碼,因為它們保證高於真實版本代碼。

第 3 步:重建、簽名、測試和發布

在此步驟中,OEM 使用 Tapas 重建 APK,對生成的 APK 進行簽名,然後測試並發布 APK:

  • 對於未發布的設備(或在為已發布的設備準備系統更新時),在 Data app prebuilt 目錄中提交新的 APK,以確保系統映像和 xTS 測試具有最新的 APK。 OEM 應測試新文件是否正常工作(即,它通過 CTS 和任何 OEM 特定的自動和手動測試)。
  • 對於不再接收系統更新的已發佈設備,簽名的 APK 可能只能通過應用商店發布。

OEM 負責質量保證並在發布前在其設備上測試更新的 Data 應用程序。

數據應用版本代碼策略

數據應用程序必須具有合適的版本控制策略,以確保設備接收正確的 APK。例如,如果收到的系統更新包含比從應用商店下載的 APK 更舊的 APK,則應保留應用商店版本。

APK 版本代碼應包含以下信息:

  • 發行格式版本(主要 + 次要)
  • 遞增(不透明)的版本號

目前,平台 API 級別與發行版格式版本密切相關,因為每個 API 級別通常與新版本的 ICU 相關聯(這使得發行版文件不兼容)。將來,Android 可能會對此進行更改,以便發行版文件可以跨多個 Android 平台版本運行(並且 API 級別未在數據應用版本代碼方案中使用)。

示例版本代碼策略

此示例版本編號方案確保較高的發行版格式版本取代較低的發行版格式版本。 AndroidManifest.xml使用android:minSdkVersion來確保舊設備不會收到高於它們可以處理的發行版格式版本的版本。

版本檢查
圖 6.示例版本代碼策略
例子價值目的
預訂的允許未來的替代方案/測試 APK。它最初(隱式)為 0。因為基礎類型是有符號的 32 位 int 類型,所以該方案支持最多兩個未來編號方案修訂版。
01主要格式版本跟踪 3 個十進制數字的主要格式版本。發行版格式支持 3 位十進制數字,但此處僅使用 2 位數字。考慮到每個 API 級別的預期主要增量,它不太可能達到 100。主要版本 1 相當於 API 級別 27。
1次要格式版本跟踪 3 個十進制數字的次要格式版本。發行版格式支持 3 位十進制數字,但此處僅使用 1 位數字。不太可能達到10。
X預訂的對於生產版本為 0(對於測試 APK 可能不同)。
ZZZZZ不透明的版本號按需分配的十進制數。包括間隙以允許在需要時進行插頁式更新。

如果使用二進製而不是十進制,則該方案可以更好地打包,但該方案具有人類可讀的優點。如果用盡了完整的數字範圍,則數據應用程序包名稱可能會更改。

版本名稱是詳細信息的人類可讀表示,例如: major=001,minor=001,iana=2017a, revision=1,respin=2 。示例如下表所示。

#版本代碼minSdkVersion {主要格式版本},{次要格式版本},{IANA 規則版本},{修訂版}
1 11000010 O-MR1主要=001,次要=001,iana=2017a,修訂版=1
2 21000010主要=002,次要=001,iana=2017a,修訂=1
3 11000020 O-MR1主要=001,次要=001,iana=2017a,修訂版=2
4 11000030 O-MR1主要=001,次要=001,iana=2017b,修訂=1
5 21000020主要=002,次要=001,iana=2017b,修訂=1
6 11000040 O-MR1主要=001,次要=001,iana=2018a,修訂版=1
7 21000030主要=002,次要=001,iana=2018a,修訂=1
8 1123456789 - -
9 11000021 O-MR1主要=001,次要=001,iana=2017a,revision=2,respin=2
  • 示例 1 和 2 顯示了同一 2017a IANA 版本的兩個 APK 版本,具有不同的主要格式版本。 2 在數值上高於 1,這是確保較新設備接收更高格式版本所必需的。 minSdkVersion 確保不會將 P 版本提供給 O 設備。
  • 示例 3 是對 1 的修訂/修復,在數值上高於 1。
  • 示例 4 和 5 顯示了 O-MR1 和 P 的 2017b 版本。由於數值更高,它們取代了之前的 IANA 版本/Android 版本的各自前身。
  • 示例 6 和 7 顯示了 O-MR1 和 P 的 2018a 版本。
  • 示例 8 演示了使用 Y 來完全替代 Y=0 方案。
  • 示例 9 演示了使用 3 和 4 之間的間隙來重新旋轉 apk。

由於每個設備都在系統映像中附帶了一個默認的、適當版本的 APK,因此不存在在 P 設備上安裝 O-MR1 版本的風險,因為它的版本號低於 P 系統映像版本。在/data中安裝了 O-MR1 版本的設備然後接收到 P 的系統更新使用/system版本,而不是/data中的 O-MR1 版本,因為 P 版本始終高於任何適用於 O- 的應用程序MR1。

使用小吃構建數據應用程序

OEM 負責管理時區數據應用程序的大部分方面並正確配置系統映像。 Data 應用程序旨在使用生成適合添加到系統映像(用於初始版本)並通過應用程序商店(用於後續更新)簽名和分發的 APK 的小吃構建。

Tapas是 Android 構建系統的精簡版本,它使用精簡的源代碼樹來生成可分發的應用程序版本。熟悉普通 Android 構建系統的 OEM 應該能夠識別來自普通 Android 平台構建的構建文件。

創建清單

簡化的源代碼樹通常通過自定義清單文件來實現,該清單文件僅引用構建系統和構建應用程序所需的 Git 項目。按照創建數據應用中的說明進行操作後,OEM 應該至少有兩個使用packages/apps/TimeZoneData/oem_template下的模板文件創建的 OEM 特定 Git 項目:

  • 一個 Git 項目包含應用程序文件,例如清單和創建應用程序 APK 文件所需的構建文件(例如, vendor/ oem /apps/TimeZoneData )。該項目還包含 xTS 測試可以使用的測試 APK 的構建規則。
  • 一個 Git 項目包含由應用構建生成的簽名 APK,用於包含在系統映像構建和 xTS 測試中。

應用程序構建利用了與平台構建共享或包含與 OEM 無關的代碼庫的其他幾個 Git 項目。

以下清單片段包含支持時區數據應用程序的 O-MR1 構建所需的最小 Git 項目集。 OEM 必須將其 OEM 特定的 Git 項目(通常包括包含簽名證書的項目)添加到此清單,並且可以相應地配置不同的分支。

   <!-- Tapas Build -->
   
<project
       
path="build"
       
name="platform/build">
       
<copyfile src="core/root.mk" dest="Makefile" />
   
</project>
   
<project
       
path="prebuilts/build-tools"
       
name="platform/prebuilts/build-tools"
       
clone-depth="1" />
   
<project
       
path="prebuilts/go/linux-x86"
       
name="platform/prebuilts/go/linux-x86"
       
clone-depth="1" />
   
<project
       
path="build/blueprint"
       
name="platform/build/blueprint" />
   
<project
       
path="build/kati"
       
name="platform/build/kati" />
   
<project
       
path="build/soong"
       
name="platform/build/soong">
       
<linkfile src="root.bp" dest="Android.bp" />
       
<linkfile src="bootstrap.bash" dest="bootstrap.bash" />
   
</project>

   
<!-- SDK for system / public API stubs -->
   
<project
       
path="prebuilts/sdk"
       
name="platform/prebuilts/sdk"
       
clone-depth="1" />
   
<!-- App source -->
   
<project
       
path="system/timezone"
       
name="platform/system/timezone" />
   
<project
       
path="packages/apps/TimeZoneData"
       
name="platform/packages/apps/TimeZoneData" />
   
<!-- Enable repohooks -->
   
<project
       
path="tools/repohooks"
       
name="platform/tools/repohooks"
       
revision="master"
       
clone_depth="1" />
   
<repo-hooks
       
in-project="platform/tools/repohooks"
       
enabled-list="pre-upload" />

運行小吃構建

建立源代碼樹後,使用以下命令調用tapas構建:

source build/envsetup.sh
tapas
make -j30 showcommands dist TARGET_BUILD_APPS='TimeZoneData TimeZoneData_test1 TimeZoneData_test2'  TARGET_BUILD_VARIANT=userdebug

成功的構建會在out/dist目錄中生成用於測試的文件。這些文件可以放置到 prebuilts 目錄中以包含在系統映像中和/或通過兼容設備的應用商店分發。