Hệ thống khôi phục bao gồm một số trình bổ trợ để chèn mã dành riêng cho thiết bị, nhờ đó, các bản cập nhật OTA cũng có thể cập nhật các phần của thiết bị ngoài hệ thống Android (ví dụ: bộ xử lý băng tần cơ sở hoặc bộ xử lý vô tuyến).
Các phần và ví dụ sau đây sẽ 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ị đó. Thư viện này cũng hỗ trợ các thiết bị flash Thiết bị công nghệ bộ nhớ (MTD) và hệ thống tệp yaffs2 từ các bản phát hành cũ.
Tệp bản đồ phân vùng do TARGET_RECOVERY_FSTAB chỉ định; tệp này được cả tệp nhị phân khôi phục và các công cụ tạo gói sử dụng. Bạn có thể chỉ định tên của tệp ánh xạ trong TARGET_RECOVERY_FSTAB trong BoardConfig.mk.
Tệp bản đồ 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), tất cả các điểm gắn trong ví dụ này phải được xác định (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 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í của phân vùng. "device" phải là tên của phân vùng MTD trong
/proc/mtd
. - ext4
- Một 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
- Thiết bị khối eMMc thô, 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 gắn thực sự, nhưng chuỗi điểm gắn được dùng để xác định vị trí thiết bị trong bảng.
- vfat
-
Hệ thống tệp FAT trên thiết bị khối, thường là bộ nhớ ngoài 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 bằng bảng phân vùng hoặc khô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). Quy định hạn chế này chỉ áp dụng cho việc gắn hệ thống tệp trong chế độ khôi phục; hệ thống chính có thể gắn các hệ thống tệp đó ở bất kỳ đâu. Thư mục
/boot
,/recovery
và/misc
phải là loại thô (mtd hoặc emmc), trong khi thư mục/system
,/data
,/cache
và/sdcard
(nếu có) phải là loại hệ thống tệp (yaffs2, ext4 hoặc vfat).
Kể từ Android 3.0, tệp recovery.fstab sẽ có thêm một trường không bắt buộc là options. Hiện tại, tuỳ chọn duy nhất được xác định là length (chiều dài), cho phép bạn chỉ định rõ chiều dài của phân vùng. Chiều dài này được dùng khi định dạng lại phân vùng (ví dụ: cho 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 cho phân vùng hệ thống trong quá trình cài đặt gói OTA đầy đủ). Nếu giá trị chiều dài là âm, thì kích thước để định dạng sẽ được lấy bằng cách thêm giá trị chiều dài vào 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 (trong đó siêu dữ liệu mã hoá được lưu trữ ở cuối phân vùng không được ghi đè).
Lưu ý: Bạn không bắt buộc phải sử dụng các trường device2 và options, điều này tạo ra sự mơ hồ trong quá trình phân tích cú pháp. Nếu mục nhập trong trường thứ tư trên dòng bắt đầu bằng ký tự "/", thì mục nhập đó được coi là mục nhập device2; nếu mục nhập không bắt đầu bằng ký tự "/", thì mục nhập đó được coi là trường options.
Ảnh động khởi động
Nhà sản xuất thiết bị có thể tuỳ chỉnh ảnh động hiển thị khi thiết bị Android 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 thông số kỹ thuật trong định dạng ảnh động khởi động.
Đố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 về 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 trong Partner Marketing Hub.
Giao diện người dùng khôi phục
Để hỗ trợ các thiết bị có nhiều phần cứng (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 theo cách 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 sử dụng theo mặc định và là điểm xuất phát phù hợp để sao chép khi viết phiên bản tệp này cho thiết bị của bạn.
Lưu ý: Bạn có thể thấy thông báo No Command (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, hãy nhấn và giữ nút bất kỳ để bật/tắt 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à 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 bị ẩn. Tiêu đề mô tả cách vận hành trình đơn (tức là các chế độ đ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 ngắn (không được cuộn), vì vậy, hãy lưu ý đến chiều rộng của màn hình thiết bị.
Tuỳ chỉnh CheckKey
Tiếp theo, hãy xác định cách triển khai RecoveryUI của thiết bị. Ví dụ này giả định thiết bị tardis có màn hình, vì vậy, bạn có thể kế thừa từ cách triển khai ScreenRecoveryUI tích hợp (xem hướng dẫn cho 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 thực hiện việc 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 diễn ra trong phần còn lại của quá trình khôi phục: khi trình đơn được bật, khi trình đơn được tắ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ố:
- BẬT/TẮT. 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 ĐẦU LẠI. Khởi động lại thiết bị ngay lập tức
- IGNORE (BỎ QUA). Bỏ qua thao tác nhấn phím này
- ENQUEUE (Thêm vào hàng đợi). Thêm thao tác nhấn phím này vào hàng đợi để 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 một sự kiện nhấn phím theo sau là một sự kiện nhả phím cho cùng một phím. (Trình tự 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 phím khác có đang được giữ hay không. (Trong trình tự sự kiện chính ở trên, nếu CheckKey(B)
gọi IsKeyPressed(A)
, thì sự kiện này 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 trong việc phát hiện trình tự các khoá. Ví dụ này cho thấy một cá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 và giữ nút nguồn rồi nhấn nút tăng âm lượng, đồng thời bạn có thể khởi động lại thiết bị ngay lập tức bằng cách nhấn nút nguồn năm lần liên tiếp (không có phím nào khác can thiệp):
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 mình (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 độ khung hình/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. Đặ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 các 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 triển khai RecoveryUI, hãy xác định lớp thiết bị (lớp con từ lớp Thiết bị tích hợp). Hàm này sẽ tạo một thực thể của lớp giao diện người dùng và trả về thực thể đó 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 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ỳ hành động nào. Phương thức triển khai mặc định không làm gì cả, vì vậy, bạn không cần cung cấp phương thức này trong lớp con nếu không 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 gọi hai phương thức để lấy danh sách các dòng tiêu đề và danh sách các mục. Trong cách triển khai này, hàm này 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 thao tác nhấn phím và chế độ hiển thị trình đơn hiện tại, đồng thời quyết định hành động cần thực hiện:
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ột 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 và được gọi ngay lập tức (xem phương thức InvokeMenuItem()
bên dưới). Nếu không, đó có thể là một trong các hằng số được xác định trước sau đây:
- kHighlightUp. Di chuyển phần đánh dấu trên trình đơn đến mục trước
- kHighlightDown. Di chuyển phần đánh dấu trình đơn đến mục tiếp theo
- kInvokeItem. Gọi mục hiện được đánh dấu
- kNoAction. Không làm gì với thao tác nhấn phím này
Như ngụ ý của đối số hiển thị, HandleMenuKey()
được gọi ngay cả khi trình đơn không hiển thị. 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 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à chờ dữ liệu đầu vào.
Cơ chế bi xoay
Nếu thiết bị của bạn có cơ chế nhập giống như bi xoay (tạo sự kiện đầu vào có loại EV_REL và mã REL_Y), thì tính năng khôi phục sẽ tổng hợp các thao tác nhấn phím KEY_UP và KEY_DOWN bất cứ khi nào thiết bị đầu vào giống 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. Việc á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 trình 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 các phím đang được giữ xuống dưới dạng đối tượng sửa đổi, 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 ở chế độ khôi phục sẽ bắt đầu quá trình xoá dữ liệu cho dù trình đơn có hiển thị 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 (hiển thị) là sai, thì việc trả về các giá trị đặc biệt để thao tác với trình đơn (di chuyển phần đánh dấu, gọi mục được đánh dấu) là không hợp lý vì người dùng không thể thấy phần đá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 hành động. Đố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 viên nào của enum BuiltinAction để yêu cầu hệ thống thực hiện hành động đó (hoặc thành viê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 chức năng khôi phục bổ sung ngoài những chức năng có trong hệ thống: Thêm một mục cho chức năng đó trong 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 ĐẦU 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 không qua cửa hàng.
- WIPE_CACHE. Chỉ định dạng lại phân vùng bộ nhớ đệm. Bạn không cần xác nhận vì việc này 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 gọi là đặt lại dữ liệu về trạng thái ban đầu. Người dùng sẽ đượ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 mỗi khi 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 xoá dữ liệu người dùng và các phân vùng bộ nhớ đệm. Nếu thiết bị của bạn lưu trữ dữ liệu người dùng ở bất kỳ đâu 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 cho biết không thành công, mặc dù hiện tại giá trị trả về bị bỏ qua. Dữ liệu người dùng và các phân vùng bộ nhớ đệm sẽ bị xoá sạch cho dù bạn trả về kết quả thành công hay không thành công.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Tạo thiết bị
Cuối cùng, hãy thêm một số mã nguyên mẫu vào cuối tệp recovery_ui.cpp cho hàm make_device()
. Hàm này sẽ tạo và trả về một thực thể của lớp Device:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Tạo và liên kết với tính năng 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 tính năng 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 bo mạch cho thiết bị này, hãy chỉ định thư viện tĩnh 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 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. Trong trường hợp xảy ra sự cố cập nhật hệ thống, người dùng chỉ có thể gọi điện đến bộ phận chăm sóc khách hàng.
Giao diện chỉ có hình ảnh giúp bạn không cần phải 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 của Android 5.0 trở lên sử dụng hai 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. 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 ảnh động xen kẽ theo hàng (đây là lý do Hình 2 trông bị vỡ). Ví dụ: đối với ảnh động 200x200 gồm 7 khung hình, hãy tạo một hình ảnh 200x1400, trong đó khung hình đầu tiên là hàng 0, 7, 14, 21, ...; khung hình thứ hai là 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ố khung hình/giây (FPS). Công cụ bootable/recovery/interlace-frames.py
lấy một tập hợp các khung đầ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; nó 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 lỗi (như minh hoạ ở trên) và ảnh động đang cài đặt cùng một số hình ảnh lớp phủ:
![]() Hình 3. icon_installing.png |
![]() Hình 4. icon-installing_overlay01.png |
![]() Hình 5. 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 phủ lên để làm nổi bật vị trí đặt lớp phủ 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. Đang cài đặt khung ảnh động 7 (icon_installing.png + icon_installing_overlay07.png) |
Các khung hình tiếp theo sẽ hiển thị bằng cách chỉ vẽ hình ảnh lớp phủ tiếp theo lên hình ảnh hiện có; hình ảnh cơ sở không được vẽ lại.
Số khung hình trong ảnh động, tốc độ mong muốn và độ dời x và y của lớp phủ so với cơ sở được đặt 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 tập hợp các 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 độ dời cần thiết.
Hình ảnh mặc định nằm trong bootable/recovery/res/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; nó 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ụ: "Installing system update..." (Đang cài đặt bản cập nhật hệ thống...) cùng với hình ảnh. Khi khởi động vào chế độ khôi phục, hệ thống chính sẽ chuyển ngôn ngữ hiện tại của người dùng dưới dạng tuỳ chọn dòng lệnh để khôi phục. Để hiển thị mỗi thông báo, quá trình khôi phục sẽ bao gồm một hình ảnh tổng hợp thứ hai với cá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/
hiển thị bản địa hoá của một thông báo và tạo hình ảnh tổng 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 các 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 hiển thị. Đừng đặt tin nhắn văn bản vào vị trí quan trọng trong quá trình khôi phục.
Lưu ý: Giao diện ẩn hiển thị thông điệp nhật ký và cho phép người dùng chọn thao tác trong trình đơn chỉ có 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 chính (hoặc ảnh động). Thanh tiến trình được tạo bằng cách kết hợp hai hình ảnh đầu vào, các hình ảnh này phải có cùng kích thước:

Hình 9. progress_empty.png

Hình 10. progress_fill.png
Phần cuối bên trái của hình ảnh đã lấp đầy sẽ hiển thị bên cạnh phần cuối bên phải của hình ảnh trống để tạo 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 hình ảnh dành riêng cho thiết bị bằng cách đặt các hình ảnh đó 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ẽ ưu tiên sử dụng tệp đó thay vì hình ảnh mặc định tương ứng. Chỉ hỗ trợ tệp PNG ở định dạng RGB hoặc RGBA với độ sâu màu 8 bit.
Lưu ý: Trong Android 5.x, nếu ngôn ngữ được biết là có thể 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ẽ lấp đầy 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à thiết bị không có giao diện người dùng hoặc có giao diện chỉ âm thanh, thì bạn có thể cần phải tuỳ chỉnh giao diện người dùng khôi phục rộng rãi hơn. Thay vì tạo một lớp con của ScreenRecoveryUI, hãy trực tiếp tạo lớp con của lớp mẹ RecoveryUI.
RecoveryUI có các phương thức để xử lý các thao tác trên giao diện người dùng cấp thấp hơn, chẳng hạn như "bật/tắt màn hình", "cập nhật thanh tiến trình", "hiển thị 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ể sử 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ợ trình đơn hoặc chế độ "hiển thị văn bản"; bạn có thể ngăn chặn việc truy cập vào các trình đơn đó bằng cách triển khai CheckKey()
và HandleMenuKey()
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 có thể chỉ là các mã giả trống.)
Hãy xem bootable/recovery/ui.h
để khai báo RecoveryUI và xem những phương thức bạn phải hỗ trợ. RecoveryUI là trừu tượng – một số phương thức là thuần tuý ảo và phải do các lớp con cung cấp – nhưng lớp này chứa mã để xử lý các dữ liệu đầu vào chính. Bạn cũng có thể ghi đè giá trị đó nếu thiết bị của bạn không có khoá hoặc bạn muốn xử lý các khoá 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 mở rộng riêng có thể được gọi từ bên 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 mở rộng đều có cùng chữ ký. Đối số là tên gọi của hàm, một cookie State*
, số lượng đối số sắp tới 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 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ể sử dụng các hàm mở rộng để triển khai cấu trúc điều khiển của riêng mình. Call Evaluate()
để đánh giá đối số Expr*
, trả về 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 (việ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ả sử hàm cần hai đối số: một khoá có giá trị chuỗi và một hình ảnh có giá trị 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ố đã đá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ã sau:
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 thực hiện việc kiểm tra kiểu, vì vậy, bạn phải thực hiện việc đó tại đây; bạn nên thực hiện việc này bằng một câu lệnh if với chi phí tạo ra một thông báo lỗi ít cụ thể hơn khi không thành công. Tuy nhiên, ReadValueArgs()
xử lý việc đánh giá từng đối số và giải phóng tất cả các đối số đã đá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ỳ lượt đánh giá nào không thành công. Bạn có thể sử dụng hàm tiện lợi ReadValueVarArgs()
để đánh giá số lượng đối số biến (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à đối tượng Value*
; quyền sở hữu đối tượng này sẽ chuyển sang đối tượng gọi. Phương thức gọi sẽ sở hữu mọi dữ liệu mà Value*
này trỏ đến, cụ thể là thành phần dữ liệu.
Trong trường hợp này, bạn muốn trả về giá trị true hoặc false để cho biết đã 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 đều là true. Bạn phải malloc một đối tượng Value với một bản sao malloc của chuỗi hằng số để trả về, vì phương thức gọi sẽ free()
cả hai. Đừng quên gọi FreeValue()
trên các đối tượng bạn nhận được bằng cách đánh giá các đối số!
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 lợi StringValue()
gói một chuỗi vào một đối tượng Value mới.
Sử dụng để viết mã trên một cách ngắn gọn hơn:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Để nối 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 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 tệp makefile 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 tại đâ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 khôi phục để lấy 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à tiện ích edify (tức là
không có hàm Register_libname
), bạn có thể liệt kê các hàm đó trong
TARGET_RECOVERY_UPDATER_EXTRA_LIBS để liên kết các hàm đó 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 các hàm đó. 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, 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ư mọi hàm 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 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 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 cho hệ thống xây 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 tệp này vào Android.mk, nhưng sau đó, tệp này phải được bảo vệ bằng một quy trình kiểm tra thiết bị, vì tất cả tệp Android.mk trong cây đều được tải bất kể thiết bị nào đang được tạo. (Nếu cây của bạn bao gồm nhiều thiết bị, bạn chỉ muốn thêm tệp tardis.dat 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
Các tệp này được gọi là tệp radio vì lý do lịch sử; chúng có thể không liên quan gì đến radio của thiết bị (nếu có). Chúng chỉ là các blob dữ liệu mờ mà hệ thống xây dựng sao chép vào tệp mục tiêu .zip mà các công cụ tạo OTA sử dụng. Khi bạn tạo 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 sẽ 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 các hàm bạn cần).
FullOTA_Assertions()
- Được gọi gần thời điểm bắt đầu tạo OTA đầy đủ. Đây là vị trí thích hợp để phát ra các câu nhận đị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 thay đổi thiết bị.
FullOTA_InstallBegin()
- Được gọi sau khi tất cả các câu nhận định về trạng thái thiết bị đã được xác nhận nhưng trước khi có bất kỳ thay đổi nào. Bạn có thể phát lệnh cho các bản cập nhật dành riêng cho thiết bị phải chạy trước khi mọi nội dung khác trên thiết bị được thay đổi.
FullOTA_InstallEnd()
- Được gọi khi kết thúc quá trình tạo tập lệnh, sau khi các lệnh tập lệnh để cập nhật các phân vùng khởi động và hệ thống đã được phát. Bạn cũng có thể phát thêm các lệnh 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 nhận định về trạng thái thiết bị đã được xác nhận nhưng trước khi có bất kỳ thay đổi nào. Bạn có thể phát lệnh cho các bản cập nhật dành riêng cho thiết bị phải chạy trước khi mọi thứ khác trên thiết bị được thay đổi.
IncrementalOTA_VerifyEnd()
- Được gọi ở cuối giai đoạn xác minh, khi tập lệnh đã xác nhận xong các tệp mà tập lệnh sẽ chạm vào có nội dung bắt đầu dự kiến. Tại thời điểm này, chưa có gì trên thiết bị thay đổi. Bạn cũng có thể phát mã cho các quy trình xác minh bổ sung dành riêng cho thiết bị.
IncrementalOTA_InstallBegin()
- Được gọi sau khi các tệp cần vá được xác minh là có trạng thái trước dự kiến nhưng trước khi thực hiện bất kỳ thay đổi nào. Bạn có thể phát lệnh cho các bản cập nhật dành riêng cho thiết bị phải chạy trước khi thay đổi bất kỳ nội dung nào khác trên thiết bị.
IncrementalOTA_InstallEnd()
- Tương tự như gói OTA đầy đủ, hàm 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 các phân vùng khởi động và hệ thống đã được phát. Bạn cũng có thể phát thêm các lệnh 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ể bắt đầu lại từ đầu. Hãy chuẩn bị để xử lý các thiết bị đã chạy các lệnh này, hoàn toàn hoặc một phần.
Truyền hàm đến đối tượng thông tin
Truyền các hàm đến một đối tượng thông tin chứa nhiều mục hữu ích:
-
info.input_zip. (Chỉ dành cho OTA đầy đủ) Đối tượng
zipfile.ZipFile
cho tệp đích đầu vào .zip. -
info.source_zip. (Chỉ dành cho OTA gia tăng) Đối tượng
zipfile.ZipFile
cho tệp đích nguồn .zip (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ỉ dành cho OTA gia tăng) Đối tượng
zipfile.ZipFile
cho tệp .zip mục tiêu 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 vào. 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 của thiết bị trong tệp BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Nếu bạn không đặt TARGET_RELEASETOOLS_EXTENSIONS, 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 đưa vào tệp .zip của tệp mục tiêu (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 của tệp mục tiêu (nếu có) sẽ được ưu tiên hơn tập lệnh từ 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 tuỳ chọn -s
(hoặc --device_specific
). Tuỳ chọn này có mức độ ưu tiên cao nhất. Điều này cho phép bạn
khắc phục lỗi và thực hiện thay đổi trong các tiện ích releasetools, đồng thời áp dụng các thay đổi đó cho
các tệp mục tiêu 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 .zip target_files và sử dụng mô-đun đó khi tạo 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 ý: Để biết danh sách đầy đủ các tuỳ 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
Recovery có cơ chế tải không qua cửa hàng để 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 xuống qua mạng không dây. Tính năng tải không qua cửa hàng sẽ hữu ích khi gỡ lỗi hoặc thực hiện các thay đổi trên các thiết bị không thể khởi động hệ thống chính.
Trước đây, việc tải không qua cửa hàng ứng dụng được thực hiện thông qua việc 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, bạn có thể đặt gói vào thẻ SD bằng một số máy tính khác, sau đó lắp thẻ SD vào thiết bị. Để hỗ trợ các thiết bị Android không có bộ nhớ ngoài có thể tháo rời, tính năng khôi phục hỗ trợ thêm hai cơ chế cài đặt không qua cửa hà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 không qua cửa hàng, 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 một gói cập nhật không qua cửa hàng từ bộ nhớ ngoài (thư mục
/sdcard
). recovery.fstab của bạn phải xác định điểm gắn/sdcard
. Bạn không thể sử dụng tính năng này trên các thiết bị mô phỏng thẻ SD bằng đường liên kết tượng trưng đến/data
(hoặc một số cơ chế tương tự)./data
thường không được khôi phục vì có thể đã được mã hoá. Giao diện người dùng khôi phục hiển thị 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 gói từ
/sdcard
, ngoại trừ việc thư mục/cache
(luôn có sẵn để khôi phục) được sử dụng. Trong hệ thống thông thường, chỉ người dùng đặc quyền mới có thể ghi vào/cache
, và nếu thiết bị không thể khởi động thì bạn 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ó ích hạn chế). -
APPLY_ADB_SIDELOAD. Cho phép người dùng gửi gói đến thiết bị thông qua cáp USB và công cụ phát triển adb. Khi cơ chế này được gọi, tính năng khôi phục sẽ khởi động phiên bản trình nền adbd mini riêng để cho phép adb trên máy tính lưu trữ đã kết nối giao tiếp với trình nền đó. Phiên bản nhỏ này chỉ hỗ trợ một lệnh duy nhất:
adb sideload filename
. Tệp được đặt tên sẽ được gửi từ máy chủ lưu trữ đến thiết bị, sau đó xác minh và cài đặt tệp đó giống như khi tệp đó nằm trên bộ nhớ cục bộ.
Một số lưu ý:
- Chỉ hỗ trợ phương thức truyền qua USB.
-
Nếu quá trình khôi phục chạy adbd bình thường (thường đúng với các bản dựng userdebug và eng), thì quá trình này sẽ bị tắt khi thiết bị ở chế độ tải không qua cửa hàng của adb và sẽ được khởi động lại khi tải không qua cửa hàng của adb hoàn tất việc nhận gói. Khi ở chế độ tải không qua cửa hàng của 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 hoạt động). -
Bạn không thể thoát khỏi chế độ cài đặt không qua cửa hàng trên thiết bị. Để huỷ, bạn có thể gửi
/dev/null
(hoặc bất kỳ nội dung nào khác không phải là gói hợp lệ) dưới dạng 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 thao tác nhấn phím, vì vậy, bạn có thể cung cấp một trình tự phím để khởi động lại thiết bị và hoạt động ở chế độ tải không qua cửa hàng của adb.