Hệ thống khôi phục có một số điểm móc để chèn mã dành riêng cho thiết bị, nhờ đó các bản cập nhật qua mạng (OTA) cũng có thể cập nhật các phần khác của thiết bị ngoài hệ thống Android (ví dụ: bộ xử lý cơ sở hoặc bộ xử lý vô tuyến).
Các phần và ví dụ sau đây tuỳ chỉnh thiết bị tardis do nhà cung cấp yoyodyne sản xuất.
Bản đồ phân vùng
Kể từ Android 2.3, nền tảng này hỗ trợ các thiết bị flash eMMC và hệ thống tệp ext4 chạy trên các thiết bị đó. Nền tảng này cũng hỗ trợ các thiết bị flash Memory Technology Device (MTD) và hệ thống tệp yaffs2 từ các bản phát hành cũ.
Tệp sơ đồ phân vùng được chỉ định bằng TARGET_RECOVERY_FSTAB; cả tệp nhị phân khôi phục và các công cụ tạo gói đều dùng tệp này. Bạn có thể chỉ định tên của tệp bản đồ trong TARGET_RECOVERY_FSTAB trong BoardConfig.mk.
Tệp sơ đồ phân vùng mẫu có thể có dạng như sau:
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
Ngoại trừ /sdcard
(không bắt buộc), bạn phải xác định tất cả các điểm gắn kết trong ví dụ này (thiết bị cũng có thể thêm các phân vùng bổ sung). Có 5 loại hệ thống tệp được hỗ trợ:
- yaffs2
-
Hệ thống tệp yaffs2 trên thiết bị flash MTD. "device" phải là tên của phân vùng MTD và phải xuất hiện trong
/proc/mtd
. - mtd
-
Một phân vùng MTD thô, dùng cho các phân vùng có thể khởi động, chẳng hạn như khởi động và khôi phục. MTD không thực sự được gắn, nhưng điểm gắn được dùng làm khoá để xác định vị trí phân vùng. "device" phải là tên của phân vùng MTD trong
/proc/mtd
. - ext4
- Hệ thống tệp ext4 trên thiết bị flash eMMC. "device" phải là đường dẫn của thiết bị khối.
- emmc
- Một thiết bị khối eMMC thô, được dùng cho các phân vùng có thể khởi động như khởi động và khôi phục. Tương tự như loại mtd, eMMc không bao giờ được thực sự gắn kết, nhưng chuỗi điểm gắn kết được dùng để xác định vị trí của thiết bị trong bảng.
- vfat
-
Hệ thống tệp FAT trên thiết bị khối, thường dùng cho bộ nhớ ngoài, chẳng hạn như thẻ SD. Thiết bị là thiết bị khối; device2 là thiết bị khối thứ hai mà hệ thống cố gắng gắn nếu không gắn được thiết bị chính (để tương thích với thẻ SD có thể được định dạng hoặc không được định dạng bằng bảng phân vùng).
Tất cả các phân vùng phải được gắn trong thư mục gốc (tức là giá trị điểm gắn phải bắt đầu bằng dấu gạch chéo và không có dấu gạch chéo nào khác). Hạn chế này chỉ áp dụng cho việc gắn hệ thống tệp trong quá trình khôi phục; hệ thống chính có thể gắn hệ thống tệp ở bất kỳ đâu. Các thư mục
/boot
,/recovery
và/misc
phải là các loại thô (mtd hoặc emmc), trong khi các thư mục/system
,/data
,/cache
và/sdcard
(nếu có) phải là các loại hệ thống tệp (yaffs2, ext4 hoặc vfat).
Kể từ Android 3.0, tệp recovery.fstab có thêm một trường không bắt buộc khác là options. Hiện tại, lựa chọn duy nhất được xác định là length , cho phép bạn chỉ định rõ ràng độ dài của phân vùng. Độ dài này được dùng khi định dạng lại phân vùng (ví dụ: đối với phân vùng dữ liệu người dùng trong quá trình xoá dữ liệu/đặt lại về trạng thái ban đầu hoặc đối với phân vùng hệ thống trong quá trình cài đặt gói OTA đầy đủ). Nếu giá trị độ dài là số âm, thì kích thước cần định dạng sẽ được lấy bằng cách cộng giá trị độ dài với kích thước phân vùng thực. Ví dụ: việc đặt "length=-16384" có nghĩa là 16k cuối cùng của phân vùng đó sẽ không bị ghi đè khi phân vùng đó được định dạng lại. Điều này hỗ trợ các tính năng như mã hoá phân vùng dữ liệu người dùng (nơi siêu dữ liệu mã hoá được lưu trữ ở cuối phân vùng mà không được ghi đè).
Lưu ý: Các trường device2 và options là không bắt buộc, gây ra sự mơ hồ trong quá trình phân tích cú pháp. Nếu mục trong trường thứ tư trên dòng bắt đầu bằng ký tự "/", thì đó được coi là mục device2; nếu mục không bắt đầu bằng ký tự "/", thì đó được coi là trường options.
Ảnh động khi khởi động
Nhà sản xuất thiết bị có thể tuỳ chỉnh ảnh động xuất hiện khi thiết bị Android đang khởi động. Để thực hiện việc này, hãy tạo một tệp .zip được sắp xếp và đặt theo quy cách trong định dạng bootanimation.
Đối với các thiết bị Android Things, bạn có thể tải tệp nén lên bảng điều khiển Android Things để đưa hình ảnh vào sản phẩm đã chọn.
Lưu ý: Những hình ảnh này phải đáp ứng nguyên tắc sử dụng thương hiệu Android. Để biết nguyên tắc sử dụng thương hiệu, hãy tham khảo phần Android của Partner Marketing Hub.
Giao diện người dùng khôi phục
Để hỗ trợ các thiết bị có phần cứng khác nhau (nút vật lý, đèn LED, màn hình, v.v.), bạn có thể tuỳ chỉnh giao diện khôi phục để hiển thị trạng thái và truy cập vào các tính năng ẩn được vận hành thủ công cho từng thiết bị.
Mục tiêu của bạn là tạo một thư viện tĩnh nhỏ với một vài đối tượng C++ để cung cấp chức năng dành riêng cho thiết bị. Tệp bootable/recovery/default_device.cpp
được dùng theo mặc định và là một điểm khởi đầu tốt để sao chép khi viết một phiên bản của tệp này cho thiết bị.
Lưu ý: Bạn có thể thấy thông báo Không có lệnh tại đây. Để bật/tắt văn bản, hãy giữ nút nguồn trong khi nhấn nút tăng âm lượng. Nếu thiết bị của bạn không có cả hai nút này, hãy nhấn và giữ một nút bất kỳ để chuyển đổi văn bản.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
Hàm tiêu đề và hàm mục
Lớp Device yêu cầu các hàm trả về tiêu đề và mục xuất hiện trong trình đơn khôi phục ẩn. Tiêu đề mô tả cách vận hành trình đơn (tức là các nút điều khiển để thay đổi/chọn mục được làm nổi bật).
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 };
Lưu ý: Các dòng dài sẽ bị cắt bớt (không được xuống dòng), vì vậy, hãy lưu ý đến chiều rộng màn hình thiết bị của bạn.
Tuỳ chỉnh CheckKey
Tiếp theo, hãy xác định việc triển khai RecoveryUI của thiết bị. Ví dụ này giả định rằng thiết bị tardis có màn hình, vì vậy, bạn có thể kế thừa từ ScreenRecoveryUIimplementation tích hợp (xem hướng dẫn cho các thiết bị không có màn hình). Hàm duy nhất để tuỳ chỉnh từ ScreenRecoveryUI là CheckKey()
, hàm này xử lý khoá không đồng bộ ban đầu:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
Hằng số KEY
Các hằng số KEY_* được xác định trong linux/input.h
. CheckKey()
được gọi bất kể điều gì đang xảy ra trong quá trình khôi phục: khi trình đơn bị tắt, khi trình đơn đang bật, trong quá trình cài đặt gói, trong quá trình xoá dữ liệu người dùng, v.v. Phương thức này có thể trả về một trong bốn hằng số:
- CHUYỂN ĐỔI. Bật hoặc tắt chế độ hiển thị trình đơn và/hoặc nhật ký văn bản
- KHỞI ĐỘNG LẠI. Khởi động lại thiết bị ngay lập tức
- BỎ QUA. Bỏ qua thao tác nhấn phím này
- ENQUEUE. Xếp hàng phím này để được sử dụng đồng bộ (tức là bằng hệ thống trình đơn khôi phục nếu màn hình được bật)
CheckKey()
được gọi mỗi khi sự kiện nhấn phím được theo sau bởi sự kiện nhả phím cho cùng một phím. (Trình tự các sự kiện A-down B-down B-up A-up chỉ dẫn đến việc gọi CheckKey(B)
.) CheckKey()
có thể gọi IsKeyPressed()
để tìm hiểu xem các khoá khác có đang được giữ hay không. (Trong chuỗi sự kiện bàn phím ở trên, nếu CheckKey(B)
gọi IsKeyPressed(A)
thì nó sẽ trả về giá trị true.)
CheckKey()
có thể duy trì trạng thái trong lớp của nó; điều này có thể hữu ích để phát hiện các chuỗi khoá. Ví dụ này cho thấy một chế độ thiết lập phức tạp hơn một chút: màn hình được bật/tắt bằng cách nhấn giữ nút nguồn và nhấn nút tăng âm lượng, đồng thời thiết bị có thể khởi động lại ngay lập tức bằng cách nhấn nút nguồn 5 lần liên tiếp (không có nút nào khác ở giữa):
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
Khi sử dụng hình ảnh của riêng bạn (biểu tượng lỗi, ảnh động cài đặt, thanh tiến trình) với ScreenRecoveryUI, bạn có thể đặt biến animation_fps
để kiểm soát tốc độ theo số khung hình trên giây (FPS) của ảnh động.
Lưu ý: Tập lệnh interlace-frames.py
hiện tại cho phép bạn lưu trữ thông tin animation_fps
trong chính hình ảnh. Trong các phiên bản Android trước, bạn cần tự đặt animation_fps
.
Để đặt biến animation_fps
, hãy ghi đè hàm ScreenRecoveryUI::Init()
trong lớp con của bạn. Đặt giá trị, sau đó gọi hàm parent Init()
để hoàn tất quá trình khởi chạy. Giá trị mặc định (20 FPS) tương ứng với hình ảnh khôi phục mặc định; khi sử dụng những hình ảnh này, bạn không cần cung cấp hàm Init()
. Để biết thông tin chi tiết về hình ảnh, hãy xem phần Hình ảnh giao diện người dùng khôi phục.
Lớp thiết bị
Sau khi bạn triển khai RecoveryUI, hãy xác định lớp thiết bị (được phân lớp từ lớp Thiết bị tích hợp). Thao tác này sẽ tạo một phiên bản duy nhất của lớp giao diện người dùng và trả về phiên bản đó từ hàm GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
Phương thức StartRecovery()
được gọi khi bắt đầu quá trình khôi phục, sau khi giao diện người dùng được khởi chạy và sau khi các đối số được phân tích cú pháp, nhưng trước khi thực hiện bất kỳ thao tác nào. Quá trình triển khai mặc định không làm gì cả, vì vậy, bạn không cần cung cấp quá trình này trong lớp con nếu không có việc gì cần làm:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
Cung cấp và quản lý trình đơn khôi phục
Hệ thống sẽ gọi 2 phương thức để lấy danh sách các dòng tiêu đề và danh sách các mục. Trong quá trình triển khai này, phương thức sẽ trả về các mảng tĩnh được xác định ở đầu tệp:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
Tiếp theo, hãy cung cấp một hàm HandleMenuKey()
. Hàm này sẽ nhận một lần nhấn phím và trạng thái hiển thị trình đơn hiện tại, đồng thời quyết định biện pháp xử lý:
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; }
Phương thức này lấy mã khoá (trước đó đã được phương thức CheckKey()
của đối tượng giao diện người dùng xử lý và đưa vào hàng đợi) và trạng thái hiện tại của chế độ hiển thị nhật ký trình đơn/văn bản. Giá trị trả về là một số nguyên. Nếu giá trị là 0 trở lên, thì giá trị đó được coi là vị trí của một mục trong trình đơn, được gọi ngay lập tức (xem phương thức InvokeMenuItem()
bên dưới). Nếu không, bạn có thể đặt chính sách này thành một trong các hằng số được xác định trước sau đây:
- kHighlightUp. Di chuyển điểm đánh dấu trên trình đơn đến mục trước
- kHighlightDown. Di chuyển điểm đánh dấu trên trình đơn đến mục tiếp theo
- kInvokeItem. Gọi mục hiện đang được đánh dấu
- kNoAction. Không làm gì khi nhấn phím này
Như ngụ ý của đối số có thể nhìn thấy, HandleMenuKey()
được gọi ngay cả khi trình đơn không xuất hiện. Không giống như CheckKey()
, phương thức này không được gọi trong khi quá trình khôi phục đang thực hiện một thao tác nào đó, chẳng hạn như xoá dữ liệu hoặc cài đặt một gói. Phương thức này chỉ được gọi khi quá trình khôi phục ở trạng thái rảnh và đang chờ dữ liệu đầu vào.
Cơ chế bi xoay
Nếu thiết bị của bạn có cơ chế đầu vào tương tự như bi xoay (tạo các sự kiện đầu vào có loại EV_REL và mã REL_Y), thì quá trình khôi phục sẽ tổng hợp các lần nhấn phím KEY_UP và KEY_DOWN bất cứ khi nào thiết bị đầu vào tương tự như bi xoay báo cáo chuyển động theo trục Y. Bạn chỉ cần liên kết các sự kiện KEY_UP và KEY_DOWN với các thao tác trên trình đơn. Quá trình ánh xạ này không xảy ra đối với CheckKey()
, vì vậy, bạn không thể sử dụng chuyển động của bi xoay làm điều kiện kích hoạt để khởi động lại hoặc bật/tắt màn hình.
Phím bổ trợ
Để kiểm tra xem các khoá có đang được giữ làm đối tượng sửa đổi hay không, hãy gọi phương thức IsKeyPressed()
của đối tượng giao diện người dùng của riêng bạn. Ví dụ: trên một số thiết bị, việc nhấn tổ hợp phím Alt-W trong chế độ khôi phục sẽ bắt đầu xoá dữ liệu dù trình đơn có xuất hiện hay không. Bạn có thể triển khai như sau:
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 } ... }
Lưu ý: Nếu visible là false, thì việc trả về các giá trị đặc biệt để thao tác với trình đơn (di chuyển điểm đánh dấu, gọi mục được đánh dấu) sẽ không có ý nghĩa vì người dùng không thể thấy điểm đánh dấu. Tuy nhiên, bạn có thể trả về các giá trị nếu muốn.
InvokeMenuItem
Tiếp theo, hãy cung cấp một phương thức InvokeMenuItem()
để liên kết các vị trí số nguyên trong mảng các mục do GetMenuItems()
trả về với các thao tác. Đối với mảng các mục trong ví dụ về tardis, hãy sử dụng:
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; } }
Phương thức này có thể trả về bất kỳ thành phần nào của enum BuiltinAction để cho hệ thống biết cần thực hiện hành động đó (hoặc thành phần NO_ACTION nếu bạn muốn hệ thống không làm gì cả). Đây là nơi cung cấp thêm chức năng khôi phục ngoài những chức năng có trong hệ thống: Thêm một mục cho chức năng đó vào trình đơn, thực thi chức năng đó tại đây khi mục trình đơn đó được gọi và trả về NO_ACTION để hệ thống không làm gì khác.
BuiltinAction chứa các giá trị sau:
- NO_ACTION. Không làm gì cả.
- KHỞI ĐỘNG LẠI. Thoát chế độ khôi phục và khởi động lại thiết bị theo cách thông thường.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Cài đặt gói cập nhật từ nhiều nơi. Để biết thông tin chi tiết, hãy xem phần Tải ứng dụng bên ngoài.
- WIPE_CACHE. Chỉ định dạng lại phân vùng bộ nhớ đệm. Không cần xác nhận vì đây là một hành động tương đối vô hại.
- WIPE_DATA. Định dạng lại phân vùng dữ liệu người dùng và bộ nhớ đệm, còn được gọi là thao tác đặt lại dữ liệu về trạng thái ban đầu. Người dùng được yêu cầu xác nhận hành động này trước khi tiếp tục.
Phương thức cuối cùng, WipeData()
, là không bắt buộc và được gọi bất cứ khi nào một thao tác xoá dữ liệu được bắt đầu (từ quá trình khôi phục thông qua trình đơn hoặc khi người dùng chọn đặt lại dữ liệu về trạng thái ban đầu từ hệ thống chính). Phương thức này được gọi trước khi các phân vùng dữ liệu người dùng và bộ nhớ đệm bị xoá. Nếu thiết bị của bạn lưu trữ dữ liệu người dùng ở bất kỳ nơi nào khác ngoài hai phân vùng đó, bạn nên xoá dữ liệu tại đây. Bạn nên trả về 0 để cho biết thành công và một giá trị khác nếu không thành công, mặc dù hiện tại giá trị trả về sẽ bị bỏ qua. Các phân vùng dữ liệu người dùng và bộ nhớ đệm sẽ bị xoá sạch cho dù bạn trả về kết quả thành công hay thất bại.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Nhãn hiệu thiết bị
Cuối cùng, hãy thêm một số mã chuẩn ở cuối tệp recovery_ui.cpp cho hàm make_device()
tạo và trả về một phiên bản của lớp Thiết bị:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Tạo và liên kết với chế độ khôi phục thiết bị
Sau khi hoàn tất tệp recovery_ui.cpp, hãy tạo và liên kết tệp đó với chế độ khôi phục trên thiết bị. Trong Android.mk, hãy tạo một thư viện tĩnh chỉ chứa tệp C++ này:
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)
Sau đó, trong cấu hình bảng cho thiết bị này, hãy chỉ định thư viện tĩnh của bạn làm giá trị của TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
Hình ảnh giao diện người dùng khôi phục
Giao diện người dùng chế độ khôi phục bao gồm các hình ảnh. Lý tưởng nhất là người dùng không bao giờ tương tác với giao diện người dùng: Trong quá trình cập nhật thông thường, điện thoại sẽ khởi động vào chế độ khôi phục, điền vào thanh tiến trình cài đặt và khởi động lại vào hệ thống mới mà không cần người dùng nhập dữ liệu. Trong trường hợp gặp vấn đề về bản cập nhật hệ thống, người dùng chỉ có thể gọi cho bộ phận chăm sóc khách hàng.
Giao diện chỉ có hình ảnh giúp loại bỏ nhu cầu bản địa hoá. Tuy nhiên, kể từ Android 5.0, bản cập nhật có thể hiển thị một chuỗi văn bản (ví dụ: "Đang cài đặt bản cập nhật hệ thống...") cùng với hình ảnh. Để biết thông tin chi tiết, hãy xem phần Văn bản khôi phục được bản địa hoá.
Android 5.0 trở lên
Giao diện người dùng khôi phục Android 5.0 trở lên sử dụng 2 hình ảnh chính: hình ảnh lỗi và ảnh động đang cài đặt.
![]() Hình 1. icon_error.png |
![]() Hình 2. biểu tượng icon_installing.png |
Ảnh động cài đặt được biểu thị dưới dạng một hình ảnh PNG duy nhất với các khung hình khác nhau của ảnh động được xen kẽ theo hàng (đó là lý do khiến Hình 2 xuất hiện dưới dạng bị bóp méo). Ví dụ: đối với ảnh động 200x200 gồm 7 khung hình, hãy tạo một hình ảnh duy nhất có kích thước 200x1400, trong đó khung hình đầu tiên là các hàng 0, 7, 14, 21, ...; khung hình thứ hai là các hàng 1, 8, 15, 22, ...; v.v. Hình ảnh kết hợp bao gồm một đoạn văn bản cho biết số lượng khung hình ảnh động và số lượng khung hình trên giây (FPS). Công cụ bootable/recovery/interlace-frames.py
lấy một tập hợp các khung hình đầu vào và kết hợp chúng thành hình ảnh tổng hợp cần thiết mà tính năng khôi phục sử dụng.
Hình ảnh mặc định có nhiều mật độ và nằm trong bootable/recovery/res-$DENSITY/images
(ví dụ:
bootable/recovery/res-hdpi/images
). Để sử dụng hình ảnh tĩnh trong quá trình cài đặt, bạn chỉ cần cung cấp hình ảnh icon_installing.png và đặt số khung hình trong ảnh động thành 0 (biểu tượng lỗi không phải là ảnh động mà luôn là hình ảnh tĩnh).
Android 4.x trở xuống
Giao diện người dùng khôi phục của Android 4.x trở xuống sử dụng hình ảnh error (lỗi) (như minh hoạ ở trên) và ảnh động installing (đang cài đặt) cùng một số hình ảnh lớp phủ:
![]() Hình 3. biểu tượng icon_installing.png |
![]() Hình 4. biểu tượng icon-installing_overlay01.png |
![]() Hình 5. biểu tượng icon_installing_overlay07.png |
Trong quá trình cài đặt, màn hình hiển thị được tạo bằng cách vẽ hình ảnh icon_installing.png, sau đó vẽ một trong các khung lớp phủ lên trên hình ảnh đó ở độ lệch thích hợp. Ở đây, một hộp màu đỏ được đặt chồng lên để làm nổi bật vị trí lớp phủ được đặt lên trên hình ảnh cơ sở:
![]() Hình 6. Cài đặt khung ảnh động 1 (icon_installing.png + icon_installing_overlay01.png) |
![]() Hình 7. Cài đặt khung ảnh động 7 (icon_installing.png + icon_installing_overlay07.png) |
Các khung hình tiếp theo được hiển thị bằng cách chỉ vẽ hình ảnh lớp phủ tiếp theo lên trên những gì đã có; hình ảnh cơ sở không được vẽ lại.
Số lượng khung hình trong ảnh động, tốc độ mong muốn và độ lệch x và y của lớp phủ so với cơ sở được thiết lập bằng các biến thành viên của lớp ScreenRecoveryUI. Khi sử dụng hình ảnh tuỳ chỉnh thay vì hình ảnh mặc định, hãy ghi đè phương thức Init()
trong lớp con để thay đổi các giá trị này cho hình ảnh tuỳ chỉnh (để biết thông tin chi tiết, hãy xem ScreenRecoveryUI). Tập lệnh bootable/recovery/make-overlay.py
có thể hỗ trợ chuyển đổi một nhóm khung hình ảnh sang dạng "hình ảnh cơ sở + hình ảnh lớp phủ" cần thiết cho quá trình khôi phục, bao gồm cả việc tính toán các độ lệch cần thiết.
Hình ảnh mặc định nằm trong bootable/recovery/res/images
. Để dùng hình ảnh tĩnh trong quá trình cài đặt, bạn chỉ cần cung cấp hình ảnh icon_installing.png và đặt số khung hình trong ảnh động thành 0 (biểu tượng lỗi không phải là ảnh động; biểu tượng này luôn là hình ảnh tĩnh).
Văn bản khôi phục đã bản địa hoá
Android 5.x hiển thị một chuỗi văn bản (ví dụ: "Đang cài đặt bản cập nhật hệ thống...") cùng với hình ảnh. Khi hệ thống chính khởi động vào chế độ khôi phục, hệ thống sẽ truyền ngôn ngữ hiện tại của người dùng dưới dạng một lựa chọn dòng lệnh để khôi phục. Đối với mỗi thông báo cần hiển thị, quá trình khôi phục sẽ bao gồm một hình ảnh kết hợp thứ hai có chuỗi văn bản được kết xuất trước cho thông báo đó ở mỗi ngôn ngữ.
Hình ảnh mẫu về chuỗi văn bản khôi phục:

Hình 8. Văn bản đã bản địa hoá cho thông báo khôi phục
Văn bản khôi phục có thể hiển thị các thông báo sau:
- Đang cài đặt bản cập nhật hệ thống...
- Lỗi!
- Đang xoá... (khi xoá dữ liệu/đặt lại về trạng thái ban đầu)
- Không có lệnh nào (khi người dùng khởi động vào chế độ khôi phục theo cách thủ công)
Ứng dụng Android trong bootable/recovery/tools/recovery_l10n/
kết xuất bản địa hoá của một thông báo và tạo hình ảnh kết hợp. Để biết thông tin chi tiết về cách sử dụng ứng dụng này, hãy tham khảo phần bình luận trong bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
Khi người dùng khởi động vào chế độ khôi phục theo cách thủ công, ngôn ngữ có thể không có sẵn và không có văn bản nào xuất hiện. Đừng coi tin nhắn văn bản là yếu tố quan trọng trong quy trình khôi phục.
Lưu ý: Giao diện ẩn hiển thị thông báo nhật ký và cho phép người dùng chọn các thao tác trong trình đơn chỉ có bằng tiếng Anh.
Thanh tiến trình
Thanh tiến trình có thể xuất hiện bên dưới hình ảnh (hoặc ảnh động) chính. Thanh tiến trình được tạo bằng cách kết hợp 2 hình ảnh đầu vào (phải có cùng kích thước):

Hình 9. progress_empty.png

Hình 10. progress_fill.png
Đầu bên trái của hình ảnh fill (đầy) sẽ xuất hiện bên cạnh đầu bên phải của hình ảnh empty (trống) để tạo thành thanh tiến trình. Vị trí của đường ranh giới giữa hai hình ảnh sẽ thay đổi để cho biết tiến trình. Ví dụ: với các cặp hình ảnh đầu vào ở trên, hãy hiển thị:

Hình 11. Thanh tiến trình ở mức 1%>

Hình 12. Thanh tiến trình ở mức 10%

Hình 13. Thanh tiến trình ở mức 50%
Bạn có thể cung cấp các phiên bản dành riêng cho thiết bị của những hình ảnh này bằng cách đặt chúng vào (trong ví dụ này) device/yoyodyne/tardis/recovery/res/images
. Tên tệp phải khớp với tên được liệt kê ở trên; khi tìm thấy một tệp trong thư mục đó, hệ thống xây dựng sẽ sử dụng tệp đó thay vì hình ảnh mặc định tương ứng. Chỉ hỗ trợ các tệp PNG ở định dạng RGB hoặc RGBA có độ sâu màu 8 bit.
Lưu ý: Trong Android 5.x, nếu ngôn ngữ được biết đến là ngôn ngữ khôi phục và là ngôn ngữ từ phải sang trái (RTL) (tiếng Ả Rập, tiếng Do Thái, v.v.), thì thanh tiến trình sẽ điền từ phải sang trái.
Thiết bị không có màn hình
Không phải thiết bị Android nào cũng có màn hình. Nếu thiết bị của bạn là một thiết bị không có màn hình hoặc chỉ có giao diện âm thanh, thì bạn có thể cần tuỳ chỉnh giao diện người dùng khôi phục một cách rộng rãi hơn. Thay vì tạo một lớp con của ScreenRecoveryUI, hãy tạo lớp con trực tiếp cho lớp mẹ RecoveryUI.
RecoveryUI có các phương thức xử lý các thao tác giao diện người dùng cấp thấp, chẳng hạn như "bật/tắt màn hình", "cập nhật thanh tiến trình", "hiện trình đơn", "thay đổi lựa chọn trong trình đơn", v.v. Bạn có thể ghi đè các phương thức này để cung cấp giao diện phù hợp cho thiết bị của mình. Có thể thiết bị của bạn có đèn LED mà bạn có thể dùng nhiều màu sắc hoặc kiểu nhấp nháy để cho biết trạng thái, hoặc có thể bạn có thể phát âm thanh. (Có thể bạn không muốn hỗ trợ một trình đơn hoặc chế độ "hiển thị văn bản"; bạn có thể ngăn truy cập vào các chế độ này bằng cách triển khai CheckKey()
và HandleMenuKey()
mà không bao giờ bật màn hình hoặc chọn một mục trình đơn. Trong trường hợp này, nhiều phương thức RecoveryUI mà bạn cần cung cấp chỉ có thể là các phần giữ chỗ trống.)
Hãy xem bootable/recovery/ui.h
để biết nội dung khai báo RecoveryUI nhằm xem những phương thức mà bạn phải hỗ trợ. RecoveryUI là trừu tượng – một số phương thức là ảo thuần tuý và phải do các lớp con cung cấp – nhưng nó có chứa mã để xử lý các đầu vào khoá. Bạn cũng có thể ghi đè điều đó nếu thiết bị của bạn không có khoá hoặc bạn muốn xử lý chúng theo cách khác.
Updater
Bạn có thể sử dụng mã dành riêng cho thiết bị trong quá trình cài đặt gói cập nhật bằng cách cung cấp các hàm tiện ích riêng mà bạn có thể gọi trong tập lệnh trình cập nhật. Dưới đây là một hàm mẫu cho thiết bị tardis:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
Mọi hàm tiện ích đều có cùng chữ ký. Các đối số là tên mà hàm được gọi, một cookie State*
, số lượng đối số đến và một mảng con trỏ Expr*
đại diện cho các đối số. Giá trị trả về là một Value*
mới được phân bổ.
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); }
Các đối số của bạn chưa được đánh giá tại thời điểm hàm của bạn được gọi – logic của hàm sẽ xác định đối số nào được đánh giá và số lần đánh giá. Do đó, bạn có thể dùng các hàm tiện ích để triển khai cấu trúc điều khiển của riêng mình. Call Evaluate()
để đánh giá một đối số Expr*
, trả về một Value*
. Nếu Evaluate()
trả về giá trị NULL, bạn nên giải phóng mọi tài nguyên mà bạn đang giữ và trả về giá trị NULL ngay lập tức (thao tác này sẽ truyền các thao tác huỷ lên ngăn xếp edify). Nếu không, bạn sẽ sở hữu Giá trị được trả về và chịu trách nhiệm gọi FreeValue()
trên giá trị đó.
Giả sử hàm cần 2 đối số: một khoá có giá trị là chuỗi và một hình ảnh có giá trị là blob. Bạn có thể đọc các đối số như sau:
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; }
Việc kiểm tra giá trị NULL và giải phóng các đối số đã được đánh giá trước đó có thể trở nên tẻ nhạt đối với nhiều đối số. Hàm ReadValueArgs()
có thể giúp bạn thực hiện việc này dễ dàng hơn. Thay vì mã ở trên, bạn có thể viết mã này:
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()
không kiểm tra kiểu, vì vậy bạn phải thực hiện việc này tại đây; sẽ thuận tiện hơn nếu thực hiện bằng một câu lệnh if với chi phí là tạo ra một thông báo lỗi ít cụ thể hơn khi không thành công. Nhưng ReadValueArgs()
xử lý việc đánh giá từng đối số và giải phóng tất cả các đối số đã được đánh giá trước đó (cũng như đặt một thông báo lỗi hữu ích) nếu có bất kỳ đánh giá nào không thành công. Bạn có thể dùng hàm tiện ích ReadValueVarArgs()
để đánh giá một số lượng biến đối số (hàm này trả về một mảng Value*
).
Sau khi đánh giá các đối số, hãy thực hiện công việc của hàm:
// 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 ...
Giá trị trả về phải là một đối tượng Value*
; quyền sở hữu đối tượng này sẽ được chuyển cho phương thức gọi. Phương thức gọi sẽ sở hữu mọi dữ liệu được chỉ đến bằng Value*
này, cụ thể là datamember.
Trong trường hợp này, bạn muốn trả về giá trị true hoặc false để cho biết trạng thái thành công. Hãy nhớ quy ước rằng chuỗi trống là false và tất cả các chuỗi khác là true. Bạn phải malloc một đối tượng Giá trị có bản sao malloc của chuỗi hằng số để trả về, vì người gọi sẽ free()
cả hai. Đừng quên gọi FreeValue()
trên các đối tượng mà bạn nhận được bằng cách đánh giá các đối số của mình!
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; }
Hàm tiện ích StringValue()
bao bọc một chuỗi trong một đối tượng Value mới.
Sử dụng để viết mã trên một cách súc tích hơn:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Để liên kết các hàm vào trình thông dịch edify, hãy cung cấp hàm Register_foo
, trong đó foo là tên của thư viện tĩnh chứa mã này. Gọi RegisterFunction()
để đăng ký từng hàm tiện ích. Theo quy ước, hãy đặt tên cho các hàm dành riêng cho thiết bị là device.whatever
để tránh xung đột với các hàm tích hợp sẵn sẽ được thêm trong tương lai.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
Giờ đây, bạn có thể định cấu hình tệp makefile để tạo một thư viện tĩnh bằng mã của mình. (Đây là cùng một makefile được dùng để tuỳ chỉnh giao diện người dùng khôi phục trong phần trước; thiết bị của bạn có thể có cả hai thư viện tĩnh được xác định ở đây.)
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
Tên của thư viện tĩnh phải khớp với tên của hàm Register_libname
có trong thư viện đó.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Cuối cùng, hãy định cấu hình bản dựng của quy trình khôi phục để kéo vào thư viện của bạn. Thêm thư viện của bạn vào TARGET_RECOVERY_UPDATER_LIBS (có thể chứa nhiều thư viện; tất cả đều được đăng ký).
Nếu mã của bạn phụ thuộc vào các thư viện tĩnh khác không phải là các tiện ích edify (tức là
chúng không có hàm Register_libname
), bạn có thể liệt kê những hàm đó trong TARGET_RECOVERY_UPDATER_EXTRA_LIBS để liên kết chúng với trình cập nhật mà không cần gọi hàm đăng ký (không tồn tại) của chúng. Ví dụ: nếu mã dành riêng cho thiết bị của bạn muốn sử dụng zlib để giải nén dữ liệu, thì bạn sẽ đưa libz vào đây.
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 +=
Giờ đây, các tập lệnh trình cập nhật trong gói OTA có thể gọi hàm của bạn như bất kỳ hàm nào khác. Để lập trình lại thiết bị tardis, tập lệnh cập nhật có thể chứa: tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Thao tác này sử dụng phiên bản một đối số của hàm tích hợp package_extract_file()
. Hàm này trả về nội dung của một tệp được trích xuất từ gói cập nhật dưới dạng một blob để tạo đối số thứ hai cho hàm mở rộng mới.
Tạo gói OTA
Thành phần cuối cùng là giúp các công cụ tạo gói OTA biết về dữ liệu dành riêng cho thiết bị của bạn và phát ra các tập lệnh trình cập nhật bao gồm các lệnh gọi đến các hàm tiện ích của bạn.
Trước tiên, hãy yêu cầu hệ thống bản dựng biết về một blob dữ liệu dành riêng cho thiết bị. Giả sử tệp dữ liệu của bạn nằm trong device/yoyodyne/tardis/tardis.dat
, hãy khai báo nội dung sau trong AndroidBoard.mk của thiết bị:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Bạn cũng có thể đặt nó trong Android.mk, nhưng sau đó, bạn phải được bảo vệ bằng một quy trình kiểm tra thiết bị, vì tất cả các tệp Android.mk trong cây đều được tải bất kể thiết bị nào đang được xây dựng. (Nếu cây của bạn có nhiều thiết bị, bạn chỉ muốn tệp tardis.dat được thêm vào khi tạo thiết bị tardis.)
device/yoyodyne/tardis/Android.mk
[...] # an alternative to specifying it in AndroidBoard.mk ifeq (($TARGET_DEVICE),tardis) $(call add-radio-file,tardis.dat) endif
Đây được gọi là tệp vô tuyến vì lý do lịch sử; chúng có thể không liên quan gì đến đài vô tuyến của thiết bị (nếu có). Đây chỉ là các khối dữ liệu mờ mà hệ thống tạo bản dựng sao chép vào tệp .zip target-files mà các công cụ tạo OTA sử dụng. Khi bạn thực hiện một bản dựng, tardis.dat sẽ được lưu trữ trong target-files.zip dưới dạng RADIO/tardis.dat
. Bạn có thể gọi add-radio-file
nhiều lần để thêm bao nhiêu tệp tuỳ thích.
Mô-đun Python
Để mở rộng các công cụ phát hành, hãy viết một mô-đun Python (phải có tên là releasetools.py) mà các công cụ có thể gọi vào nếu có. Ví dụ:
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"));""")
Một hàm riêng biệt xử lý trường hợp tạo gói OTA gia tăng. Trong ví dụ này, giả sử bạn chỉ cần lập trình lại tardis khi tệp tardis.dat đã thay đổi giữa hai bản dựng.
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"));""")
Hàm mô-đun
Bạn có thể cung cấp các hàm sau trong mô-đun (chỉ triển khai những hàm bạn cần).
FullOTA_Assertions()
- Được gọi gần khi bắt đầu tạo một bản cập nhật qua mạng (OTA) đầy đủ. Đây là nơi thích hợp để đưa ra các câu khẳng định về trạng thái hiện tại của thiết bị. Không phát các lệnh tập lệnh thực hiện thay đổi đối với thiết bị.
FullOTA_InstallBegin()
- Được gọi sau khi tất cả các câu khẳng định về trạng thái thiết bị đã được thông qua nhưng trước khi có bất kỳ thay đổi nào được thực hiện. Bạn có thể phát các lệnh cho những bản cập nhật dành riêng cho thiết bị. Các bản cập nhật này phải chạy trước khi mọi thứ khác trên thiết bị được thay đổi.
FullOTA_InstallEnd()
- Được gọi vào cuối quá trình tạo tập lệnh, sau khi các lệnh tập lệnh để cập nhật phân vùng khởi động và hệ thống được phát ra. Bạn cũng có thể phát ra các lệnh bổ sung cho các bản cập nhật dành riêng cho thiết bị.
IncrementalOTA_Assertions()
-
Tương tự như
FullOTA_Assertions()
nhưng được gọi khi tạo một gói cập nhật gia tăng. IncrementalOTA_VerifyBegin()
- Được gọi sau khi tất cả các câu khẳng định về trạng thái thiết bị đã được thông qua nhưng trước khi có bất kỳ thay đổi nào được thực hiện. Bạn có thể phát các lệnh cho những bản cập nhật dành riêng cho thiết bị. Các bản cập nhật này phải chạy trước khi có bất kỳ thay đổi nào khác trên thiết bị.
IncrementalOTA_VerifyEnd()
- Được gọi ở cuối giai đoạn xác minh, khi tập lệnh đã hoàn tất việc xác nhận các tệp mà tập lệnh sẽ chạm vào có nội dung bắt đầu như dự kiến. Tại thời điểm này, chưa có gì thay đổi trên thiết bị. Bạn cũng có thể phát mã để xác minh bổ sung theo từng thiết bị.
IncrementalOTA_InstallBegin()
- Được gọi sau khi các tệp cần được vá đã được xác minh là có trạng thái trước như mong đợi nhưng trước khi có bất kỳ thay đổi nào. Bạn có thể phát các lệnh cho những bản cập nhật dành riêng cho thiết bị. Các bản cập nhật này phải chạy trước khi bất kỳ nội dung nào khác trên thiết bị bị thay đổi.
IncrementalOTA_InstallEnd()
- Tương tự như đối tác gói OTA đầy đủ, lệnh này được gọi ở cuối quá trình tạo tập lệnh, sau khi các lệnh tập lệnh để cập nhật phân vùng khởi động và hệ thống đã được phát. Bạn cũng có thể phát các lệnh bổ sung cho các bản cập nhật dành riêng cho thiết bị.
Lưu ý: Nếu thiết bị hết pin, quá trình cài đặt OTA có thể khởi động lại từ đầu. Hãy chuẩn bị đối phó với những thiết bị đã chạy các lệnh này, một cách toàn bộ hoặc một phần.
Truyền các hàm đến các đối tượng thông tin
Truyền các hàm đến một đối tượng thông tin duy nhất chứa nhiều mục hữu ích:
-
info.input_zip. (Chỉ OTA đầy đủ) Đối tượng
zipfile.ZipFile
cho tệp .zip đầu vào target-files. -
info.source_zip. (Chỉ OTA gia tăng) Đối tượng
zipfile.ZipFile
cho tệp .zip đích nguồn (bản dựng đã có trên thiết bị khi gói gia tăng đang được cài đặt). -
info.target_zip. (Chỉ OTA gia tăng) Đối tượng
zipfile.ZipFile
cho target-files .zip mục tiêu (bản dựng mà gói gia tăng đặt trên thiết bị). -
info.output_zip. Gói đang được tạo; một đối tượng
zipfile.ZipFile
được mở để ghi. Sử dụng common.ZipWriteStr(info.output_zip, filename, data) để thêm một tệp vào gói. -
info.script. Đối tượng tập lệnh mà bạn có thể thêm các lệnh. Gọi
info.script.AppendExtra(script_text)
để xuất văn bản vào tập lệnh. Đảm bảo văn bản đầu ra kết thúc bằng dấu chấm phẩy để không gặp phải các lệnh được phát sau đó.
Để biết thông tin chi tiết về đối tượng thông tin, hãy tham khảo tài liệu của Python Software Foundation về tệp lưu trữ ZIP.
Chỉ định vị trí mô-đun
Chỉ định vị trí của tập lệnh releasetools.py trên thiết bị trong tệp BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Nếu TARGET_RELEASETOOLS_EXTENSIONS chưa được đặt, thì giá trị mặc định sẽ là thư mục $(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
trong ví dụ này). Tốt nhất là bạn nên xác định rõ vị trí của tập lệnh releasetools.py.
Khi tạo thiết bị tardis, tập lệnh releasetools.py sẽ có trong tệp target-files.zip (META/releasetools.py
).
Khi bạn chạy các công cụ phát hành (img_from_target_files
hoặc ota_from_target_files
), tập lệnh releasetools.py trong tệp .zip target-files (nếu có) sẽ được ưu tiên hơn tập lệnh trong cây nguồn Android. Bạn cũng có thể chỉ định rõ ràng đường dẫn đến các tiện ích dành riêng cho thiết bị bằng lựa chọn -s
(hoặc --device_specific
), lựa chọn này sẽ được ưu tiên hàng đầu. Việc này cho phép bạn sửa lỗi và thực hiện các thay đổi trong các tiện ích releasetools, đồng thời áp dụng những thay đổi đó cho các tệp đích cũ.
Giờ đây, khi bạn chạy ota_from_target_files
, công cụ này sẽ tự động chọn mô-đun dành riêng cho thiết bị từ tệp target_files .zip và sử dụng mô-đun đó khi tạo các gói 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
Ngoài ra, bạn có thể chỉ định các tiện ích dành riêng cho thiết bị khi chạy 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
Lưu ý: Để xem danh sách đầy đủ các lựa chọn, hãy tham khảo các nhận xét ota_from_target_files
trong build/make/tools/releasetools/ota_from_target_files
.
Cơ chế cài đặt không qua cửa hàng ứng dụng
Chế độ khôi phục có cơ chế tải qua một bên để cài đặt gói cập nhật theo cách thủ công mà không cần hệ thống chính tải gói đó qua mạng. Cài đặt không qua cửa hàng ứng dụng rất hữu ích cho việc gỡ lỗi hoặc thực hiện các thay đổi trên những thiết bị không khởi động được hệ thống chính.
Trước đây, việc cài đặt ứng dụng bên ngoài được thực hiện bằng cách tải các gói từ thẻ SD của thiết bị; trong trường hợp thiết bị không khởi động được, gói có thể được đưa vào thẻ SD bằng một máy tính khác, sau đó thẻ SD được lắp vào thiết bị. Để phù hợp với các thiết bị Android không có bộ nhớ ngoài có thể tháo rời, chế độ khôi phục hỗ trợ 2 cơ chế bổ sung để cài đặt không qua cửa hàng ứng dụng: tải các gói từ phân vùng bộ nhớ đệm và tải các gói qua USB bằng adb.
Để gọi từng cơ chế tải lên từ bên ngoài, phương thức Device::InvokeMenuItem()
của thiết bị có thể trả về các giá trị sau của BuiltinAction:
-
APPLY_EXT. Cài đặt gói cập nhật từ bộ nhớ ngoài (thư mục
/sdcard
). recovery.fstab phải xác định điểm gắn/sdcard
. Thư mục này không dùng được trên các thiết bị mô phỏng thẻ SD bằng một đường liên kết tượng trưng đến/data
(hoặc một cơ chế tương tự)./data
thường không thể khôi phục vì có thể đã được mã hoá. Giao diện người dùng khôi phục sẽ hiển thị một trình đơn gồm các tệp .zip trong/sdcard
và cho phép người dùng chọn một tệp. -
APPLY_CACHE. Tương tự như việc tải một gói từ
/sdcard
, ngoại trừ việc thư mục/cache
(luôn có sẵn để khôi phục) được dùng thay thế. Từ hệ thống thông thường, chỉ người dùng có đặc quyền mới có thể ghi vào/cache
và nếu thiết bị không khởi động được thì hoàn toàn không thể ghi vào thư mục/cache
(điều này khiến cơ chế này có ít tiện ích). -
APPLY_ADB_SIDELOAD. Cho phép người dùng gửi một gói đến thiết bị qua cáp USB và công cụ phát triển adb. Khi cơ chế này được gọi, quy trình khôi phục sẽ khởi động phiên bản adbd thu nhỏ của riêng nó để cho phép adb trên máy tính chủ được kết nối giao tiếp với phiên bản này. Phiên bản mini này chỉ hỗ trợ một lệnh duy nhất:
adb sideload filename
. Tệp có tên được gửi từ máy chủ lưu trữ đến thiết bị, sau đó thiết bị sẽ xác minh và cài đặt tệp đó như thể tệp đó nằm trên bộ nhớ cục bộ.
Một số điều cần lưu ý:
- Chỉ hỗ trợ phương thức truyền USB.
-
Nếu quy trình khôi phục của bạn chạy adbd bình thường (thường đúng với các bản dựng userdebug và eng), thì quy trình đó sẽ tắt khi thiết bị ở chế độ tải qua adb và sẽ khởi động lại khi quy trình tải qua adb hoàn tất việc nhận gói. Trong chế độ tải qua adb, không có lệnh adb nào khác ngoài
sideload
hoạt động (logcat
,reboot
,push
,pull
,shell
, v.v. đều không thành công). -
Bạn không thể thoát chế độ cài đặt qua adb trên thiết bị. Để huỷ, bạn có thể gửi
/dev/null
(hoặc bất kỳ gói nào khác không hợp lệ) làm gói, sau đó thiết bị sẽ không xác minh được gói đó và dừng quy trình cài đặt. Phương thứcCheckKey()
của quá trình triển khai RecoveryUI sẽ tiếp tục được gọi cho các lần nhấn phím, vì vậy, bạn có thể cung cấp một tổ hợp phím khởi động lại thiết bị và hoạt động ở chế độ tải qua adb.