復原系統包含數種掛鉤,可插入裝置專屬程式碼,以便 OTA 更新亦可更新 Android 系統以外的裝置部分 (例如基本頻帶) 或無線電處理器)。
以下各節和範例可自訂 tardis 裝置, yodyne 供應商。
分區地圖
自 Android 2.3 起,該平台支援 eMMc Flash 裝置以及執行 ext4 檔案系統 存取這些裝置也支援 Memory Technology 裝置 (MTD) 快閃裝置和 yaffs2 開發最新版本的檔案系統。
分區對應檔是由 TARGET_RECOVERY_FSTAB 指定;檔案 復原二進位檔和套件建構工具你可以在以下位置指定地圖檔案名稱: BoardConfig.mk 中的 TARGET_RECOVERY_FSTAB。
分區地圖檔案範例如下:
device/yoyodyne/tardis/recovery.fstab
# mount point fstype device [device2] [options (3.0+ only)] /sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 /cache yaffs2 cache /misc mtd misc /boot mtd boot /recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery /system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 /data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata
/sdcard
除外 (選用),此項目中的所有掛接點
都必須定義 (裝置也可以新增其他分區)。有五種
檔案系統類型:
- Yaffs2
-
MTD Flash 裝置上的 yaffs2 檔案系統。「裝置」必須是 MTD 分區的名稱
且必須以
/proc/mtd
顯示。 - mtd
-
原始 MTD 分區,用於啟動和復原等可開機分區。MTD 不是
但掛接點是用來尋找分區的索引鍵。「裝置」
必須是
/proc/mtd
中的 MTD 分區名稱。 - ext4
- eMMc Flash 裝置頂端的 ext4 檔案系統。「裝置」必須是區塊裝置的路徑。
- emmc
- 原始 eMMc 區塊裝置,用於啟動和復原等可開機分區。風格類似 mtd 類型,eMMc 實際上從未掛接,但掛接點字串是用於尋找
- Vfat
-
區塊裝置上的 FAT 檔案系統,通常用於外部儲存空間,例如 SD 卡。
是封鎖裝置;device2 是第二種區塊裝置,如果裝置未
無法安裝主要裝置 (與 SD 卡相容,不一定是
並採用分區表格)。
所有分區都必須掛接在根目錄中,也就是說,掛接點值必須 開頭為斜線,不得包含其他斜線)。這項限制僅適用於掛接 復原中的檔案系統可以安裝在任何位置目錄
/boot
、/recovery
和/misc
應為原始類型 (mtd 或 emmc),而/system
、/data
目錄/cache
和/sdcard
(如果有的話) 應為檔案系統類型 (yaffs2、ext4 或 vfat)。
從 Android 3.0 開始,Recovery.fstab 檔案會額外提供一個選用欄位, options。目前唯一定義的選項是 length ,後者可讓您 指定分區長度這個長度會用來重新格式化分區 (例如,用於資料抹除/恢復原廠設定作業期間的使用者資料分區,或是針對 系統磁碟分割區)。如果長度值為負數 系統會將長度值加到實際分區大小,轉換為格式大小。適用對象 例如,將「length=-16384」設為表示該分區的最後 16,000 個不會 則會覆寫覆寫值。支援加密 使用者資料分區 (加密中繼資料會儲存在 且不應覆寫)。
注意:「device2」和「options」為選用欄位,建立 剖析時可能模糊不清。如果第四個欄位中的項目以「/」開頭 屬於 device2 項目;如果項目開頭不是「/」 字元後會視為「選項」欄位。
開機動畫
裝置製造商可以自訂 Android 裝置中顯示的動畫 正在啟動。做法是建立依照 啟動格式。
適用對象 Android Things 裝置 您可以在 Android Things 主控台上傳經過壓縮的檔案,將圖片 和使用者選取的產品
注意:這些圖片必須符合 Android 品牌宣傳指南。品牌宣傳指南 請參閱 合作夥伴行銷 中心。
復原 UI
為了支援具有不同可用硬體 (實體按鈕、LED、螢幕等) 的裝置, 您可以自訂復原介面來顯示狀態,以及存取手動操作 隱藏功能
您的目標是建構具有幾個 C++ 物件的小型靜態程式庫,來提供
裝置專屬功能檔案
系統預設會使用 bootable/recovery/default_device.cpp
,這個做法更為實用
為裝置寫入此檔案版本時複製的起點。
注意:這裡可能會顯示「No Command」(沒有指令) 訊息。切換 文字,請按住電源按鈕,同時按下調高音量按鈕。如果您的裝置沒有 和兩個按鈕,長按任一按鈕即可切換文字。
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
標頭與項目函式
Device 類別需要函式來傳回標頭以及隱藏項目中顯示的項目 復原選單。標頭說明瞭如何操作選單 (也就是可變更/選取 醒目顯示的項目)。
static const char* HEADERS[] = { "Volume up/down to move highlight;", "power button to select.", "", NULL }; static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", NULL };
注意:長行會遭到截斷 (未換行),因此請保持 以裝置螢幕為主
自訂 CheckKey
接下來,請定義裝置的 RecoveryUI 實作方式。本例假設
「tardis」裝置有螢幕,可讓您沿用內建的
ScreenRecoveryUI 實作 (請參閱
沒有螢幕的裝置)。唯一一項函式
自訂 ScreenRecoveryUI 是 CheckKey()
,這會執行初始
非同步金鑰處理:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
KEY 常數
KEY_* 常數是在 linux/input.h
中定義。 CheckKey()
是
無論復原時間結束什麼都一樣
處於開啟狀態、安裝套件期間、清除使用者資料等時,應用程式可以傳回四個
常數:
- 切換。開啟或關閉功能表和/或文字記錄的顯示畫面
- 返回。立即重新啟動裝置
- 忽略。忽略這個按鍵操作
- ENQUEUE。將此按鍵加入佇列,以同步使用 (即復原作業) 選單系統)
每當有按鍵後置事件發生時,系統就會呼叫 CheckKey()
使用相同金鑰(事件序列 A-down B-down B-up A-up 只會觸發
CheckKey(B)
)。CheckKey()
可以撥打電話
IsKeyPressed()
,瞭解是否有其他按鍵遭到按住。(在上述
重要事件的順序 (如果 CheckKey(B)
呼叫 IsKeyPressed(A)
)
結果會是 true)。
CheckKey()
可保留類別中的狀態;這有助於偵測
鍵序列。這個範例中的設定方式略為複雜:顯示器切換依據為
按住電源並調高音量鍵,即可立即重新啟動裝置,做法如下:
在沒有其他介入鍵的情況下,連續按壓電源按鈕五次:
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
螢幕復原使用者介面
使用
ScreenRecoveryUI 環境下,您可以設定 animation_fps
變數來控制
動畫的每秒影格數 (FPS)
注意:目前的 interlace-frames.py
指令碼可讓您
也就是將 animation_fps
資訊儲存在圖片本身中。舊版
Android 必須自行設定 animation_fps
。
如要設定 animation_fps
變數,請覆寫
ScreenRecoveryUI::Init()
函式。設定值,然後呼叫
parent Init()
函式完成初始化。預設值 (20 FPS)
對應至預設的復原映像檔;使用這些圖片,您不需要提供
Init()
函式。如要進一步瞭解圖片,請參閱
復原 UI 映像檔。
裝置類別
實作 RecoveryUI 後,請定義裝置類別 (從
內建的裝置類別)。其應建立 UI 類別的單一例項並傳回
在 GetUI()
函式中執行:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
開始復原
UI 會在復原開始時呼叫 StartRecovery()
方法
初始化到引數剖析後,但在任何動作之前
。預設導入方式不會執行任何動作,因此您不用在
子類別:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
提供及管理復原選單
系統會呼叫兩種方法,取得標題行清單和項目清單。在本 執行時,會傳回檔案頂端定義的靜態陣列:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
處理選單鍵
接著,請提供 HandleMenuKey()
函式,可接收按鍵和目前指令
並決定後續動作:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
此方法採用索引鍵程式碼 (先前已處理過,並排入佇列
UI 物件的 CheckKey()
方法),以及選單/文字記錄的目前狀態
曝光率。傳回值為整數。如果值等於或大於 0,系統會採用
選單項目的位置,系統會立即叫用該選單項目 (請參閱
InvokeMenuItem()
方法)。否則可以是下列任一值
預先定義的常數:
- kHighlightUp。將選單醒目顯示項目移至上一個項目
- kHighlightDown。將選單醒目顯示範圍移至下一個項目
- kInvokeItem。叫用目前標示的項目
- kNoAction。使用這個按鍵時不執行任何操作
如同可見引數所隱含,即使選單HandleMenuKey()
不會顯示。與 CheckKey()
不同,系統在復原期間「不會」呼叫這個方法
例如抹除資料或安裝套件;只有在復原作業處於閒置狀態時才會呼叫
等待系統回應
軌跡球機制
如果裝置具有類似軌跡球的輸入機制 (產生類型為 EV_REL 的輸入事件)
和代碼 REL_Y),復原組合會每次觸發 KEY_UP 和 KEY_DOWN 鍵
軌跡球類輸入裝置回報 Y 軸的動作。您只需要對應 KEY_UP 和
產生選單動作的 KEY_DOWN 事件。「不會」產生這個對應關係
CheckKey()
,因此您無法使用軌跡球的動態效果做為重新啟動或
切換螢幕
輔助鍵
如要檢查按住按鍵做為輔助鍵,請呼叫 IsKeyPressed()
方法
您自己 UI 物件的舉例來說,在某些裝置上,在復原程序中按下 Alt-W 鍵可啟動
無論選單是否顯示,系統都會抹除裝置資料。YOu 的實作方式如下:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
注意:如果 visible 為 false,則沒有必要傳回特殊參數。 用於操控選單 (移動醒目顯示、叫用醒目顯示項目) 的值 即可看到醒目顯示的部分不過,您可以視需要傳回值。
叫用選單項目
接著,提供 InvokeMenuItem()
方法,用於對應陣列中的整數位置
傳送給動作的項目 (由 GetMenuItems()
傳回)。針對
tardis 範例,請使用:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
這個方法可傳回 BuiltinAction 列舉的任何成員,告知系統採取此行動 動作 (如果您希望系統不做任何動作,則需指定 NO_ACTION 成員)。這是 提供系統以外項目以外的其他復原功能: 並在系統叫用該選單項目時在此執行,並傳回 NO_ACTION 什麼都不做。
IntegrateinAction 包含下列值:
- NO_ACTION。不採取任何行動。
- 返回。結束還原程序,並正常重新啟動裝置。
- APPLY_EXT、APPLY_CACHE、APPLY_ADB_SIDELOAD安裝來自多種產品的更新套件 例如減少干擾詳情請參閱「側載」。
- WIPE_CACHE。請只重新格式化快取分區。因為這是 相對地
- WIPE_DATA。重新格式化使用者資料和快取分區 (也稱為工廠資料) 重設。系統會要求使用者確認這項操作再繼續操作。
最後一個方法 (WipeData()
) 是選用方法,每當資料抹除時呼叫
作業開始 (透過選單復原或使用者選擇執行其他動作時)
從主要系統恢復原廠設定)。系統會在使用者資料和快取之前呼叫此方法
則會抹除分區。如果裝置將使用者資料儲存在兩者以外的位置
應該在這裡清除。您應該傳回 0 表示成功,以及另一個
的值,但目前會傳回值。使用者資料和快取
無論您傳回成功或失敗,系統都會清除分區資料。
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
製作裝置
最後,在 recovery_ui.cpp 檔案結尾處加上一些樣板,
make_device()
函式,會建立並傳回裝置類別的執行個體:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
建構裝置並建立連結
完成 recovery_ui.cpp 檔案後建立並連結至裝置中的復原程序。於 Android.mk,建立僅包含這個 C++ 檔案的靜態程式庫:
device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery LOCAL_SRC_FILES := recovery_ui.cpp # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_tardis include $(BUILD_STATIC_LIBRARY)
然後在這部裝置的主面板設定中,將靜態資料庫的值指定為 TARGET_RECOVERY_UI_LIB。
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
復原 UI 映像檔
復原使用者介面由映像檔組成。在理想情況下,使用者從未與 UI 互動: 正常更新期間,手機會進入還原程序、完成安裝進度列, 並返回新系統,使用者無需輸入內容。發生系統事件時 更新問題時,你唯一可以採取的行動是撥打客戶服務專線。
只有圖片的介面能滿足本地化的需求。不過自 Android 5.0 起 update 會顯示一串文字 (例如「正在安裝系統更新...」) 和圖片。 詳情請參閱 本地化的復原文字。
Android 5.0 以上版本
Android 5.0 以上版本復原 UI 會使用兩個主要映像檔:error 映像檔 和「安裝」動畫。
安裝動畫會以單一 PNG 圖片顯示,
以列交錯呈現的動畫 (這就是圖 2 看起來擠壓的原因)。舉例來說
200x200 7 個頁框的動畫,建立一張 200x1400 圖片,第一個頁框是第 0、7、
14、21...;第二個影格是第 1、8、15、22...、以此類推。合併的圖片會包含
文字區塊,指出動畫影格數和每秒影格數
(FPS)。工具 bootable/recovery/interlace-frames.py
採用一組輸入影格
並合併成復原所需的合成圖像
預設圖片可設定不同密度,且位於
bootable/recovery/res-$DENSITY/images
(例如
bootable/recovery/res-hdpi/images
)。如要在安裝期間使用靜態映像檔,
您只需提供 icon_installing.png 圖片,並在
將動畫播放至 0 (錯誤圖示不會呈現動畫效果,而是顯示靜態圖片)。
Android 4.x 以下版本
Android 4.x 以下版本的復原 UI 會使用 error 映像檔 (如上所示),以及 安裝動畫與多個重疊圖片:
安裝期間,系統會透過繪製 icon_installing.png 來建構螢幕顯示內容。 隨後在圖片上,以適當的偏移值繪製其中一個重疊頁框。這裡是紅色 方塊,以醒目方式標示疊加層在基本圖片上方的位置:
「只」繪製上方的下一張圖片,即可顯示後續的影格 ;系統不會重新繪製基本映像檔
動畫的影格數量、所需速度,以及疊加層的 x 和 y 偏移
(相對於基準) 是由 ScreenRecoveryUI 類別的成員變數所設定。使用
來擷取自訂圖片而非預設圖片,請覆寫程式碼中的 Init()
方法
子類別,變更自訂映像檔的這些值 (詳情請參閱
「ScreenRecoveryUI」)。腳本
bootable/recovery/make-overlay.py
可協助轉換一組圖片影格
「基本圖片 + 重疊圖片」包括計算
需要偏移量
預設圖片位於 bootable/recovery/res/images
。如何使用靜態圖片
安裝時只需要提供 icon_installing.png 映像檔,並設定
將動畫的影格顯示至 0 (錯誤圖示不會以動畫呈現,而是一律顯示靜態圖片)。
本地化復原文字
Android 5.x 會顯示一串文字 (例如「正在安裝系統更新...」) 和 圖片。當主要系統開機並復原時,它會將使用者目前的語言代碼做為 復原為指令列選項復原為每封要顯示的郵件都要有一秒鐘的復原作業 包含各語言代碼訊息預先算繪文字字串的複合圖片。
復原文字字串的圖片範例:
復原文字可顯示下列訊息:
- 正在安裝系統更新...
- 錯誤!
- 清除中...(清除/恢復原廠設定時)
- 無指令 (使用者手動啟動復原作業時)
bootable/recovery/tools/recovery_l10n/
中的 Android 應用程式會轉譯本地化內容
並建立合成圖片。如要進一步瞭解如何使用這個應用程式,請參閱
中的留言
bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
。
當使用者手動啟動復原程序時,可能無法使用語言代碼,也沒有文字 高度。請勿讓簡訊一定要對復原程序至關重要。
注意:隱藏介面,顯示記錄訊息,並允許使用者 選單中的選取動作目前僅支援英文。
進度列
進度列可以顯示在主要圖片 (或動畫) 下方。進度列的組成元素 是結合兩個輸入圖片,大小必須相同:
填入圖片的最左邊會顯示在 空白圖片,讓狀態列成為進度列。邊界和邊界之間的邊界位置 更新圖片來表示進度以上述的輸入圖片組合為例 顯示:
將這些圖片放置於
範例) device/yoyodyne/tardis/recovery/res/images
,直接在 Google Cloud 控制台實際操作。檔案名稱必須與上述名稱相符;當檔案位於該目錄時,
建構系統優先使用它,而不是對應的預設映像檔。僅限 RGB 或
支援採用 8 位元色彩深度的 RGBA 格式。
注意:在 Android 5.x 中,如果已知復原語言代碼且為 由右到左 (RTL) 的語言 (阿拉伯文、希伯來文等),進度列由右至左排列 左側。
沒有螢幕的裝置
並非所有 Android 裝置都有螢幕。如果您的裝置是無頭設備或 純音訊介面,您可能需要對復原使用者介面進行更多自訂。改為 建立 ScreenRecoveryUI 的子類別,直接加入其父項類別 RecoveryUI 的子類別。
RecoveryUI 會使用多種方法處理較低層級的 UI 作業,例如「切換顯示畫面」
「更新進度列」「顯示菜單」「變更菜單選項」例如,您可以覆寫
以便為您的裝置提供適當的介面。假如你的裝置有 LED 燈,請
可以使用不同的顏色或閃爍模式來表示狀態,
音訊。(您或許不想支援選單或「文字顯示」模式,但
以 CheckKey()
和
絕不會切換顯示或選取選單的 HandleMenuKey()
實作方式
項目。在此情況下,您必須提供許多 RecoveryUI 方法,只需空白即可
虛設常式)。
如要瞭解 RecoveryUI 的宣告方式,請參閱 bootable/recovery/ui.h
以及您需要提供支援RecoveryUI 是抽象概念,有些方法則是純虛擬,且必須在
子類別—但包含處理鍵輸入的程式碼。您可以覆寫
在您的裝置沒有金鑰,或是您希望以不同方式處理金鑰時,也可以使用這個選項。
更新程式
您可以在安裝更新套件時使用裝置專屬程式碼,方法是提供 自更新工具指令碼中可呼叫的擴充功能函式。請看看 函式:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
每個擴充功能函式的簽名都相同。引數是
函式呼叫、State*
Cookie、傳入的引數數量,以及
代表引數的 Expr*
指標陣列。傳回值為
新分配的 Value*
。
Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); }
呼叫函式時並未評估引數,也就是函式的
邏輯會決定要評估哪個參數以及評估次數。因此,您可以使用擴充功能
函式來實作自己的控制結構。待評估 Call Evaluate()
Expr*
引數,傳回 Value*
。如果Evaluate()
會傳回空值,請釋放任何您持有的資源,並立即傳回 NULL (
會取消 edify 堆疊)。否則,您就能取得傳回的值,以及
負責最終呼叫
上有 FreeValue()
。
假設函式需要兩個引數:字串值的鍵和 blob 值 image 屬性。您可以透過以下方式讀取引數:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
如果檢查空值和釋放先前評估的引數,可能會耗費許多繁瑣的重複動作
引數。ReadValueArgs()
函式可以簡化這項作業。而不是程式碼
上面說:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
ReadValueArgs()
不會進行類型檢查,因此您必須在這裡完成輸入;這個
。一項 if 陳述式,表示投入的
系統就無法顯示特定錯誤訊息。但 ReadValueArgs()
會處理評估作業
並釋放所有先前評估過的引數 (以
錯誤訊息)。您可以使用
ReadValueVarArgs()
可用於評估變數數量的便捷函式
引數 (傳回 Value*
的陣列)。
評估引數之後,請執行函式:
// key->data is a NUL-terminated string // image->data and image->size define a block of binary data // // ... some device-specific magic here to // reprogram the tardis using those two values ...
傳回值必須是 Value*
物件。這個物件的擁有權會傳送到
呼叫函式。呼叫端會取得此節點所指向的任何資料的擁有權
Value*
:特別是資料成員。
在這個範例中,您想要傳回 true 或 false 值來表示成功。記住
空白字串是 false,所有其他字串都是 true。個人中心
必須透過 Malloc 這個 Value 物件,將要傳回的常數字串副本 (因為
呼叫端將同時free()
。別忘了呼叫 FreeValue()
上的
就會產生各種不同物件!
FreeValue(key); FreeValue(image); Value* result = malloc(sizeof(Value)); result->type = VAL_STRING; result->data = strdup(successful ? "t" : ""); result->size = strlen(result->data); return result; }
便利函式 StringValue()
可將字串納入新的 Value 物件。
使用 來更簡潔地編寫上述程式碼:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
如要將函式引入 edify 解譯器,請提供函式
Register_foo
,其中 foo 是內含
這個程式碼呼叫 RegisterFunction()
即可註冊各項擴充功能函式。變更者:
慣例,將裝置專屬函式命名為 device.whatever
,以避免
與日後新增的內建函式相衝突。
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
您現在可以設定 makefile,使用程式碼建構靜態程式庫。(與 上一節中用於自訂復原 UI 的 makefile;你的裝置可能同時具備 靜態程式庫)
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
靜態資料庫的名稱必須與
其中包含 Register_libname
函式。
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
最後,設定復原版本,以便提取您的程式庫。將媒體庫新增至
TARGET_RECOVERY_UPDATER_LIBS (可能包含多個程式庫,所有程式庫都會註冊)。
如果程式碼依附於其他靜態程式庫,且這些程式庫本身並非修飾擴充功能 (即
但沒有 Register_libname
函式),您可以在
TARGET_RECOVERY_UPDATER_EXTRA_LIBS 即可將其連結至更新程式,而不必呼叫
(不存在) 註冊函數。例如,假設裝置專屬代碼需要使用
zlib 解壓縮資料,請在這裡加入 libz。
device/yoyodyne/tardis/BoardConfig.mk
[...] # add device-specific extensions to the updater binary TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=
OTA 套件中的更新工具指令碼現在可以呼叫任何其他函式。重設程式
TAR 裝置,更新指令碼可能包含:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
。用途
內建函式 package_extract_file()
的單一引數版本,
這會傳回從更新套件擷取的檔案內容,做為 blob 以產生
新擴充功能函式的第二個引數
OTA 套件產生
最後一個部分是讓 OTA 套件產生工具瞭解 並發出更新程式指令碼,其中包含對擴充功能函式的呼叫。
首先,請取得建構系統來瞭解裝置特定 blob 的資料。假設您的資料
檔案位於 device/yoyodyne/tardis/tardis.dat
,請在您的
裝置的 AndroidBoard.mk:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
也可以改放在 Android.mk 中,但必須經過裝置防護 檢查,因為無論使用什麼裝置,樹狀結構中的所有 Android.mk 檔案都會載入。 。(如果您的樹狀結構中含有多部裝置,您只要將 tardis.dat 檔案加入 打造 TAR 裝置)。
device/yoyodyne/tardis/Android.mk
[...] # an alternative to specifying it in AndroidBoard.mk ifeq (($TARGET_DEVICE),tardis) $(call add-radio-file,tardis.dat) endif
出於歷史原因而稱為收音機檔案;這代表他們可能與
裝置無線電 (如果有的話)。金鑰只是建構系統複製資料的不透明區塊
OTA 生成工具所用的 target-file .zip。建立建構作業時,tardis.dat
在 target-files.zip 中儲存為 RADIO/tardis.dat
。你可以打電話
add-radio-file
多次可新增檔案,數量不限。
Python 模組
如要擴充發布工具,請編寫 Python 模組 (名稱必須是 releasetools.py) 工具 呼叫 (如果有的話)範例:
device/yoyodyne/tardis/releasetools.py
import common def FullOTA_InstallEnd(info): # copy the data into the package. tardis_dat = info.input_zip.read("RADIO/tardis.dat") common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
另一個函式會處理產生漸進式 OTA 套件的情況。為此 例如,假設您只在 tardis.dat 檔案已變更時,才將 tardis 重新編寫為 之間的差異
def IncrementalOTA_InstallEnd(info): # copy the data into the package. source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") if source_tardis_dat == target_tardis_dat: # tardis.dat is unchanged from previous build; no # need to reprogram it return # include the new tardis.dat in the OTA package common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
模組函式
您可以在模組中提供下列函式 (僅實作您需要的函式)。
FullOTA_Assertions()
- 在產生完整 OTA 的開始時呼叫。這是發出斷言的好地方 瞭解裝置目前的狀態不要發出用來變更網站原始碼的指令碼指令 裝置。
FullOTA_InstallBegin()
- 在通過所有關於裝置狀態的宣告後,但在任何變更之前呼叫 您可以對必須執行的裝置專屬更新發出指令 裝置上的任何其他設定都已變更
FullOTA_InstallEnd()
- 在指令碼生成結束時呼叫,即可在指令碼指令後更新開機程序, 發出系統分區。您也可以為 裝置專屬更新
IncrementalOTA_Assertions()
-
與
FullOTA_Assertions()
類似,但在產生漸進式摘要時呼叫 update 套件。 IncrementalOTA_VerifyBegin()
- 在通過所有關於裝置狀態的宣告後,但在任何變更之前呼叫 的執行緒數量您可以對必須執行的裝置專屬更新發出指令 裝置上的另一個項目已變更。
IncrementalOTA_VerifyEnd()
- 在驗證階段結束時,當指令碼完成確認 檔案內應該包含預期的起始內容目前, 裝置已變更。也可以針對特定裝置發出代碼 驗證。
IncrementalOTA_InstallBegin()
- 在要修補的檔案經過驗證為符合預期後呼叫 狀態,但在做出任何變更前。您可以針對事件執行 裝置專屬更新;如要變更裝置上的任何其他項目,就必須執行更新作業。
IncrementalOTA_InstallEnd()
- 這個檔案與完整的 OTA 套件類似,會在指令碼結束時呼叫 版本,在用於更新開機和系統分區的指令碼指令後 。您也可以發出其他指令,用於裝置專屬更新。
注意:如果裝置沒電,系統可能會透過 自訂機器學習模型 但不想花時間從頭調整機器學習參數請做好準備,以因應已執行這些指令的裝置。 完全或部分。
將函式傳送至資訊物件
將函式傳遞至包含各種實用項目的單一資訊物件:
-
info.input_zip。(僅限完整 OTA) 版本的
zipfile.ZipFile
物件 輸入目標檔案 .zip -
info.source_zip。(僅限漸進式網路旅行社) 的
zipfile.ZipFile
物件 來源目標檔案 .zip (當漸進式套件時,裝置上已有該版本 正在進行安裝)。 -
info.target_zip。(僅限漸進式網路旅行社) 的
zipfile.ZipFile
物件 目標檔案 .zip (裝置所安裝的漸進式套件)。 -
info.output_zip。正在建立套件;已開啟
zipfile.ZipFile
物件 以便進行寫作請使用 common.ZipWriteStr(info.output_zip, filename, data) 新增 檔案新增至套件中 -
info.script。可以附加命令的指令碼物件。致電
info.script.AppendExtra(script_text)
:將文字輸出至指令碼。 請確保輸出文字以分號結尾,以免在發出的指令中執行 說明。
如要進一步瞭解 info 物件,請參閱 ZIP 封存的 Python Software Foundation 說明文件。
指定模組位置
在 BoardConfig.mk 檔案中指定裝置 releasetools.py 指令碼的位置:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
如果未設定 TARGET_RELEASETOOLS_EXTENSIONS,系統會預設為
$(TARGET_DEVICE_DIR)/../common
目錄 (device/yoyodyne/common
)
相仿)。建議您明確定義 releasetools.py 指令碼的位置。
建構 tardis 裝置時,releasetools.py 指令碼包含在 target-file 中
.zip 檔案 (META/releasetools.py
)。
執行發布工具時 (img_from_target_files
或
ota_from_target_files
),這是目標檔案.zip 中的 releasetools .py 指令碼 (如果有的話)
則會優先使用,而非 Android 來源樹狀結構中的值。您也可以
使用 -s
指定裝置專用擴充功能的路徑 (或
--device_specific
) 選項,以接受最高優先順序。這讓您能
修正錯誤並在發布工具的擴充功能中進行變更,然後將這些變更套用至舊版應用程式
目標檔案。
現在,當您執行 ota_from_target_files
時,系統會自動選用
裝置專用模組 (來自 target_files .zip 檔案中),並在產生 OTA 時使用該模組
套件:
./build/make/tools/releasetools/ota_from_target_files \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
或者,您也可以在執行應用程式時指定裝置專用的擴充功能
ota_from_target_files
。
./build/make/tools/releasetools/ota_from_target_files \
-s device/yoyodyne/tardis \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
注意:如需完整選項清單,請參閱
ota_from_target_files
則留言:
build/make/tools/releasetools/ota_from_target_files
。
側載機制
復原具有「側載」機制,可在沒有 經由主要系統無線下載。側載功能適合用於偵錯或製作 對無法啟動主要系統的裝置所做的變更。
以往,系統已經將裝置 SD 卡載入的套件載入完成。英吋 在裝置無法啟動的情況下,您可以使用其他程式將套件放入 SD 卡 將 SD 卡插入裝置為了在沒有規格的情況下 卸除式外部儲存空間,復原支援兩種額外的側載機制: 從快取分區載入套件,並使用 ADB 透過 USB 載入套件。
如要叫用每個側載機制,請使用裝置的 Device::InvokeMenuItem()
方法
便可傳回下列 BuiltinAction 值:
-
APPLY_EXT。側載來自外部儲存空間 (
/sdcard
) 的更新套件 目錄)。您的 Recovery.fstab 必須定義/sdcard
掛接點。這是 不適用於模擬 SD 卡以及指向/data
(或部分連結) 的 SD 卡 類似的機制)。/data
通常無法恢復,因為這是這個原因 可能加密。復原 UI 會顯示/sdcard
中的 .zip 檔案選單,以及 可讓使用者選擇其中一項 -
APPLY_CACHE。類似從
/sdcard
載入套件,但 使用/cache
目錄 (「可以」復原) 。在一般系統中,/cache
只能由具有特殊權限的使用者寫入, 如果裝置無法開機,就無法將/cache
目錄寫入 (因此這個機制的功用有限)。 -
APPLY_ADB_SIDELOAD。允許使用者透過 USB 傳輸線將包裹傳送到裝置,且
ADB 開發工具叫用這項機制時,復原作業會啟動其最小的快取單位
ADB Daemon 版本的 ADB,讓連線主機電腦上的 ADB 能夠與其通訊。這部迷你人物
version 僅支援單一指令:
adb sideload filename
。將具名檔案從主機電腦傳送至裝置後,由裝置驗證並 就會像安裝在本機儲存空間一樣。
請注意以下事項:
- 僅支援 USB 傳輸。
-
如果復原作業正常執行 ADB (使用者偵錯和 eng 版本通常為 true),
會於裝置處於 ADB 側載模式時關機,而會在 ADB 時重新啟動
側載已完成接收套件。在 ADB 側載模式下,沒有其他 ADB 指令
比
sideload
職 (logcat
、reboot
、push
、pull
、shell
等都是失敗)。 -
您無法退出裝置上的 ADB 側載模式。如果不想取消,可以傳送
/dev/null
(或任何其他無效套件的項目) 做為套件,以及 則裝置將無法進行驗證,並停止安裝程序。RecoveryUI 系統會繼續針對按鍵操作呼叫實作的CheckKey()
方法, 以便提供一個按鍵序列,讓裝置重新啟動並在 ADB 側載模式下運作。