Hệ thống khôi phục bao gồm một số nút để chèn mã dành riêng cho thiết bị để OTA các bản cập nhật 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ăng tần cơ sở) hoặc bộ xử lý radio).
Các phần và ví dụ sau đây sẽ tuỳ chỉnh thiết bị tardis do yoyodyne.
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ó cũng hỗ trợ các thiết bị flash của Thiết bị công nghệ bộ nhớ (MTD) và yaffs2 hệ thống tệp của các bản phát hành trước đó.
Tệp bản đồ phân vùng được chỉ định bởi TARGET_RECOVERY_FSTAB; tệp này được sử dụng bởi cả tệp nhị phân khôi phục và công cụ tạo gói. Bạn có thể chỉ định tên của tệp bản đồ 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ụ phải được xác định (thiết bị cũng có thể thêm phân vùng bổ sung). Có năm chiến lược được hỗ trợ
loại hệ thống tệp:
- yaffs2
-
Một hệ thống tệp yaffs2 trên một thiết bị flash MTD. "thiết bị" phải là tên của phân vùng MTD
và phải xuất hiện ở
/proc/mtd
. - mtd
-
Một phân vùng MTD thô, dùng cho các phân vùng có khả năng khởi động, chẳng hạn như khởi động và khôi phục. MTD không phải là
thực sự được gắn kết, nhưng điểm gắn được dùng làm chìa khoá để định vị phân vùng. "thiết bị"
phải là tên của phân vùng MTD trong
/proc/mtd
. - nội dung mở rộng 4
- Hệ thống tệp ext4 trên thiết bị flash eMMc. "thiết bị" phải là đường dẫn của thiết bị khối.
- emmc
- Một thiết bị khối eMMc thô, dùng cho các phân vùng có khả năng khởi động, chẳng hạn như khởi động và khôi phục. Tương tự với loại mtd, eMMc không bao giờ thực sự được gắn kết, nhưng chuỗi điểm gắn được dùng để định vị thiết bị trong bảng.
- vfat
-
Hệ thống tệp FAT nằm trên một thiết bị khối, thường dành cho bộ nhớ ngoài như thẻ SD. Chiến lược phát hành đĩa đơn
thiết bị là thiết bị khối; thiết bị 2 là thiết bị khối thứ hai mà hệ thống cố gắng gắn kết nếu
không kết nối đượ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).
Tất cả phân vùng phải được gắn kết trong thư mục gốc (tức là giá trị điểm gắn phải bắt đầu bằng một dấu gạch chéo và không có dấu gạch chéo khác). Quy định hạn chế này chỉ áp dụng cho việc gắn kết hệ thống tệp khôi phục; hệ thống chính có thể thoải mái gắn chúng ở bất cứ đâu. Thư mục
/boot
,/recovery
và/misc
phải là 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à loại hệ thống tệp (yaffs2, ext4 hoặc vfat).
Kể từ Android 3.0, tệp recovery.fstab sẽ nhận thêm trường tùy chọn, options. Hiện tại, tuỳ chọn duy nhất được xác định là length , tuỳ chọn này cho phép bạn trình bày rõ ràng chỉ định chiều 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 thao tác xoá sạch dữ liệu/đặt lại về trạng thái ban đầu hoặc phân vùng hệ thống trong khi cài đặt gói OTA đầy đủ). Nếu giá trị độ dài là số âm, thì kích thước thành định dạng sẽ được lấy bằng cách thêm giá trị độ dài vào kích thước phân vùng thực. Cho chẳng hạn như đặt "length=-16384" có nghĩa là 16k cuối cùng của phân vùng đó sẽ không được bị ghi đè khi phân vùng đó được định dạng lại. Công cụ 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 không được ghi đè).
Lưu ý: Các trường device2 và options là không bắt buộc, không rõ ràng trong 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ự này được coi là mục nhập device2 ; nếu mục nhập không bắt đầu bằng '/' thì trường này đượ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 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 thông số kỹ thuật ở định dạng hình động.
Cho Android Things, bạn có thể tải tệp nén lên trong bảng điều khiển Android Things để thêm hình ảnh vào sản phẩm đã chọn.
Lưu ý: Những hình ảnh này phải đáp ứng các nguyên tắc sử dụng thương hiệu của Android. Để biết nguyên tắc sử dụng thương hiệu, hãy xem mục Android trong Tiếp thị dành cho đối tác Nội dung theo lịch.
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 tính năng ẩn cho từng thiết bị.
Mục tiêu của bạn là xây dựng 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ị cụ thể. Tệp
bootable/recovery/default_device.cpp
được sử dụng theo mặc định và tạo ra một trải nghiệm tốt
điểm bắt đầu để sao chép khi viết một phiên bản của 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 nội dung 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, nhấn và giữ 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 của tiêu đề và mục
Lớp Thiết bị yêu cầu các hàm để trả về tiêu đề và các mục xuất hiện trong phần ẩn trình đơn khôi phục. Tiêu đề mô tả cách vận hành trình đơn (ví dụ: các điều khiển để thay đổi/chọn mục được đánh dấu).
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 ngắt dòng), vì vậy, hãy giữ chiều rộng của dòng màn hình thiết bị.
Tuỳ chỉnh CheckKey
Tiếp theo, hãy xác định phương thứ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 nên bạn có thể kế thừa từ tính năng tích hợp sẵn
ScreenRecoveryUIimplementation (xem các hướng dẫn về
thiết bị không có màn hình.) Chức năng duy nhất để
tuỳ chỉnh từ ScreenRecoveryUI là CheckKey()
. Đây là thao tác ban đầu
xử lý khoá không đồng bộ:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
Hằng số KEY
Hằng số KEY_* được định nghĩa trong linux/input.h
. CheckKey()
là
bất kể điều gì đang xảy ra trong phần còn lại của quá trình khôi phục: khi trình đơn bị tắt, khi
được bật, trong quá trình cài đặt gói, trong khi xoá dữ liệu người dùng, v.v. Dịch vụ này có thể trả về 1 trong 4 vị trí
hằng số:
- CỬA SỔ. Bật/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
- ĐỢI ĐÂY. Thêm lần nhấn phím này vào hàng đợi để được sử dụng đồng bộ (tức là bởi quá trình khôi phục hệ thống trình đơn nếu màn hình đang bật)
CheckKey()
được gọi mỗi khi theo sau một sự kiện nhấn phím là một sự kiện nhấn phím cho
cùng một khoá. (Chuỗi sự kiện A-Xuống-B-Lên-A-Lên chỉ dẫn đến
CheckKey(B)
đang được gọi.) CheckKey()
có thể gọi
IsKeyPressed()
để tìm hiểu xem các khoá khác có đang bị nhấn hay không. (Trong phần trên
trình tự các sự kiện chính, nếu CheckKey(B)
gọi IsKeyPressed(A)
thì
sẽ trả về true.)
CheckKey()
có thể duy trì trạng thái trong lớp; điều này có thể hữu ích để phát hiện
các chuỗi khoá. Ví dụ này minh hoạ 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
giữ nguồn và nhấn 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ó phím xen kẽ nào khác):
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; } };
Giao diện người dùng khôi phục màn hình
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) bằng
ScreenRecoveryUI, bạn có thể đặt biến animation_fps
để kiểm soát tốc độ trong
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 trước đây của
Android cần phải tự đặt animation_fps
.
Để đặt biến animation_fps
, hãy ghi đè phương thức
Hàm ScreenRecoveryUI::Init()
trong lớp con của bạn. Đặt giá trị rồi gọi phương thức
Hàm parent Init()
để hoàn tất quá trình khởi chạy. Giá trị mặc định (20 khung hình/giây)
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
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 loại con từ
lớp Thiết bị tích hợp). Phương thức này sẽ tạo một thực thể duy nhất của lớp giao diện người dùng và trả về phiên bản đó
qua hàm GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
Bắt đầu khôi phục
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 tạo và sau khi các đối số đã được phân tích cú pháp, nhưng trước khi bất kỳ hành động nào được thực hiện
đã thực hiện. Việc triển khai mặc định không có tác dụng gì, vì vậy, bạn không cần phải cung cấp thông tin này trong
lớp con nếu bạn không làm gì:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
Trình đơn cung cấp và quản lý khôi phục
Hệ thống 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 phần này phương thức 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; }
Tay cầm MenuKey
Tiếp theo, hãy cung cấp một hàm HandleMenuKey()
, thực hiện thao tác nhấn phím và
chế độ hiển thị trình đơn và 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 sẽ lấy một mã khoá (trước đó đã được xử lý và đưa vào hàng đợi
CheckKey()
của đối tượng giao diện người dùng) và trạng thái hiện tại của trình đơn/nhật ký văn bản
khả năng hiển thị. Giá trị trả về là một số nguyên. Nếu từ 0 trở lên, giá trị đó được xem là
vị trí của một mục trong trình đơn, được gọi ngay lập tức (xem
InvokeMenuItem()
bên dưới). Nếu không, vấn đề này có thể là một trong những nguyên nhân sau
hằng số định sẵn:
- khighlightUp. Di chuyển phần đánh dấu trình đơn về mục trước
- khighlightDown. Di chuyể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ì khi 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 là
không hiển thị. Không giống như CheckKey()
, phương thức này không được gọi trong quá trình khôi phục
thao tác nào đó như xoá dữ liệu hoặc cài đặt gói — hành động này chỉ được gọi khi quá trình khôi phục ở trạng thái rảnh
và chờ thông tin đầu vào.
Cơ chế bi xoay
Nếu thiết bị của bạn có cơ chế nhập giống bi xoay (tạo các sự kiện đầu vào có loại EV_REL
và mã REL_Y), tính năng khôi phục 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 bi xoay báo cáo chuyển động trên trục Y. Tất cả những gì bạn cần làm là lập bản đồ KEY_UP và
KEY_DOWN sự kiện vào 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()
nên bạn không thể dùng chuyển động bi xoay làm yếu tố 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 khoá đang được giữ làm đố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ị, nhấn tổ hợp phím Alt-W trong quá trình khôi phục sẽ khởi động
xoá dữ liệu 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 là sai, thì việc trả lại đặc biệt sẽ không hợp lý các giá trị thao tác với trình đơn (di chuyển phần đánh dấu, gọi mục được đánh dấu) vì người dùng không thể xem điểm nổi bật. Tuy nhiên, bạn có thể trả về các giá trị này nếu muốn.
InvokeTrình đơnItem
Tiếp theo, hãy cung cấp một phương thức InvokeMenuItem()
liên kết vị trí số nguyên trong mảng
trong số mục được GetMenuItems()
trả về 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 phần nào của enum BuildinAction để yêu cầu hệ thống lấy phần tử đó 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 chức năng khôi phục bổ sung ngoài chức năng có trong hệ thống: Thêm một mục cho nó trong trình đơn của bạn, thực thi tại đây khi mục trong trình đơn đó được gọi và trả về NO_ACTION để hệ thống không làm gì khác.
BuildinAction có chứa các giá trị sau:
- KHÔNG_HÀNH ĐỘNG. Không làm gì cả.
- KHỞI ĐỘNG LẠI. Thoát khỏi quy trình khôi phục và khởi động lại thiết bị theo cách thông thường.
- ÁP DỤNG_EXT, APPLY_CACHE, APPLY_ADB_SIDETẢI. Cài đặt gói cập nhật từ nhiều địa điểm. Để biết thông tin chi tiết, hãy xem phần Cài đặt không qua cửa hàng ứng dụng.
- 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à tương đối vô hại.
- WIPE_DATA. Định dạng lại các phân vùng bộ nhớ đệm và dữ liệu người dùng, còn gọi là dữ liệu gốc đã đặt lại. Người dùng được yêu cầu xác nhận thao tác 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 xoá sạch dữ liệu
bắt đầu hoạt động (từ khôi phục qua trình đơn hoặc khi người dùng đã chọn thực hiệ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 dữ liệu người dùng và bộ nhớ đệm
phân vùng đã 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 2 nơi đó
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 lỗi khác
giá trị cho lỗi, mặc dù hiện tại giá trị trả về bị bỏ qua. Dữ liệu người dùng và bộ nhớ đệm
các phân vùng bị xoá cho dù bạn trả về kết quả thành công hay khô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 phần tử
Hàm make_device()
tạo và trả về một thực thể 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 việc 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 này với tính năng khôi phục trên thiết bị của bạn. 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 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 hình ảnh. Tốt nhất là người dùng tuyệt đối không 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 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 thông tin đầu vào từ người dùng. Trong trường hợp bị hệ thống Cập nhật, hành động duy nhất mà người dùng có thể thực hiện là gọi cho bộ phận chăm sóc khách hàng.
Giao diện chỉ có hình ảnh 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 chi tiết, xem Văn bản khôi phụ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 cài đặt.
Ảnh động cài đặt được thể hiện dưới dạng một hình ảnh PNG đơn lẻ với các khung hình khác nhau của
ảnh động xen kẽ theo hàng (đó là lý do tại sao Hình 2 xuất hiện bị bóp méo). Ví dụ: để có một
Hoạt ảnh 7 khung hình 200x200, tạo một hình ảnh 200x1400 duy nhất trong đó khung đầu tiên là các hàng 0, 7,
14, 21, ...; khung thứ hai là các hàng 1, 8, 15, 22, ...; v.v. Hình ảnh kết hợp bao gồm
đoạn văn bản cho biết số khung ảnh động và số khung hình/giây
(khung hình/giây). Công cụ bootable/recovery/interlace-frames.py
lấy một tập hợp các khung nhập
và kết hợp chúng vào hình ảnh kết hợp cần thiết được sử dụng trong quá trình khôi phục.
Hình ảnh mặc định có sẵn ở nhiều mật độ khác nhau 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_Installation.png và đặt số lượng khung 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 lỗi (minh hoạ ở trên) và ảnh động đang cài đặt cùng với một số hình ảnh lớp phủ:
Trong khi cài đặt, màn hình trên màn hình được tạo bằng cách vẽ icon_install.png hình ảnh, sau đó vẽ một trong các khung lớp phủ lên trên hình ảnh đó theo độ lệch thích hợp. Ở đây, một màu đỏ ô này được xếp chồng để làm nổi bật vị trí lớp phủ được đặt ở đầu hình ảnh gốc:
Các khung tiếp theo được hiển thị bằng cách chỉ vẽ hình ảnh lớp phủ tiếp theo trên nội dung đã có ở đó; hình ảnh gốc sẽ không được vẽ lại.
Số khung hình trong ảnh động, tốc độ mong muốn cũng như các mức chênh lệch x và y của lớp phủ
so với cơ sở là do các biến thành phần của lớp ScreenRecoveryUI đặt. 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 của bạn (để biết thông tin chi tiết, hãy xem
ScreenRecoveryUI). Kịch bản
bootable/recovery/make-overlay.py
có thể hỗ trợ chuyển đổi một nhóm các khung hình ảnh
thành "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
bù trừ cần thiết.
Hình ảnh mặc định được đặt trong bootable/recovery/res/images
. Cách 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_installation.png và đặt số
khung trong ảnh động thành 0 (biểu tượng lỗi không ở dạng động mà 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 quá trình khôi phục, hệ thống 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. Để mỗi thông báo hiển thị, quá trình khôi phục bao gồm một giây hình ảnh tổng hợp với chuỗi văn bản được kết xuất trước cho thông báo đó ở từng ngôn ngữ.
Hình ảnh mẫu của chuỗi văn bản 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á sạch dữ liệu/đặt lại về trạng thái ban đầu)
- Không có lệnh (khi người dùng khởi động vào tính năng khôi phục theo cách thủ công)
Ứng dụng Android trong bootable/recovery/tools/recovery_l10n/
kết xuất nội dung bản địa hoá
của một thông điệp và tạo hình ảnh kết hợp. Để biết chi tiết về cách sử dụng ứng dụng này, hãy tham khảo
nhận xét trong
bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
Khi người dùng khởi động vào quá trình 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 được hiển thị. Đừng xem tin nhắn văn bản quan trọng đối với 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 hãy chọn hành động từ 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 chính (hoặc ảnh động). Thanh tiến trình được tạo bởi kết hợp hai hình ảnh đầu vào, phải có cùng kích thước:
Điểm cuối bên trái của hình ảnh điền hiển thị bên cạnh phần cuối bên phải của trống để tạo thanh tiến trình. Vị trí ranh giới giữa hai hình ảnh được 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, màn hình:
Bạn có thể cung cấp các phiên bản dành riêng cho từng thiết bị của những hình ảnh này bằng cách đặt chúng vào (trong
ví dụ) device/yoyodyne/tardis/recovery/res/images
của Google. Tên tệp phải khớp với tên được liệt kê ở trên; khi một tệp được tìm thấy trong thư mục đó,
hệ thống xây dựng sẽ ưu tiên sử dụng hàm này để so sánh với hình ảnh mặc định tương ứng. Chỉ PNG ở định dạng RGB hoặc
Định dạng RGBA với độ sâu màu 8 bit được hỗ trợ.
Lưu ý: Trong Android 5.x, nếu ngôn ngữ được xác định là khôi phục và là một ngôn ngữ từ phải sang trái (RTL) (tiếng Ả Rập, tiếng Do Thái, v.v.), thanh tiến trình sẽ điền từ phải sang 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ỉ có âm thanh, bạn có thể cần tuỳ chỉnh sâu hơn đối với giao diện người dùng khôi phục. Thay vào đó về việc tạo một lớp con của ScreenRecoveryUI, trực tiếp đặt lớp con gốc RecoveryUI của lớp đó.
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ư "chuyển đổi màn hình"
"cập nhật thanh tiến trình" "cho xem thực đơn," "thay đổi lựa chọn trình đơn," v.v. Bạn có thể ghi đè
để cung cấp giao diện thích hợp cho thiết bị của bạn. Có thể thiết bị của bạn có đèn LED
bạn có thể sử dụng các màu sắc hoặc mẫu nhấp nháy khác nhau để biểu thị trạng thái, hoặc có thể bạn có thể phát
âm thanh. (Có lẽ 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 việc truy cập vào chúng bằng CheckKey()
và
Những cách triển khai HandleMenuKey()
không bao giờ bật màn hình hoặc chọn một trình đơn
mục. Trong trường hợp này, bạn có thể để trống nhiều phương thức RecoveryUI cần cung cấp
mã giả lập.)
Hãy xem bootable/recovery/ui.h
để biết phần khai báo về RecoveryUI để biết phương thức nào
mà bạn phải hỗ trợ. RecoveryUI mang tính trừu tượng – một số phương thức là phương thức ảo thuần tuý và phải được cung cấp bởi
nhưng lớp này chứa mã để xử lý dữ liệu đầu vào chính. Bạn có thể ghi đè thông tin đó
nếu thiết bị của bạn không có khoá hoặc bạn muốn xử lý 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 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. Sau đây là ví dụ chức năng dành 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 một chữ ký. Đối số là tên mà
hàm đã được gọi, 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 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); }
Đối số của bạn chưa được đánh giá tại thời điểm gọi hàm—hàm của bạn
logic xác định thông số nào được đánh giá và số lần đánh giá. Do đó, bạn có thể sử dụng tiện ích
để triển khai cấu trúc điều khiển của riêng bạn. Call Evaluate()
để đánh giá
một đối số Expr*
trả về một Value*
. Nếu Evaluate()
sẽ trả về giá trị NULL, bạn nên giải phóng mọi tài nguyên đang giữ và trả về giá trị NULL ngay lập tức (thành phần này
truyền huỷ 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 về việc rốt cuộc gọi
FreeValue()
trên trang đó.
Giả sử hàm cần 2 đối số: khoá có giá trị chuỗi và giá trị blob hình ảnh. 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 khi bạn có nhiều đối số
đố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 như 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 kiểm tra loại, vì vậy bạn phải thực hiện việc đó tại đây; hơn
thuận tiện để thực hiện việc đó bằng một câu lệnh if với chi phí tạo ra
khi không thành công. Tuy nhiên, ReadValueArgs()
có 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 giá trị hữu ích
thông báo lỗi) nếu có bất kỳ đá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á một số biến
đối số (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 của đối tượng này sẽ chuyển đến
phương thức gọi. Phương thức gọi nắm quyền sở hữu mọi dữ liệu được trỏ đến
Value*
– cụ thể là datamember.
Trong trường hợp này, bạn muốn trả về một giá trị true hoặc false để cho biết đã thực hiện 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
Malloc phải có một đối tượng Giá trị có bản sao của chuỗi hằng số Malloc để trả về, vì
phương thức gọi sẽ free()
cả hai. Đừng quên gọi FreeValue()
trên
mà mình có được bằng cách đánh giá đố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 Giá trị 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 phiên 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 mở rộng. Theo
quy ước, đặt tên cho hàm dành riêng cho thiết bị device.whatever
để tránh
xung đột với các hàm tích hợp sẵn sau này.
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 thư viện tĩnh bằng mã của mình. (Điều này tương tự 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 ở đâ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 đó.
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ả các thư viện này đề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 tự tạo tiện ích (tức là
chúng không có hàm Register_libname
), bạn có thể liệt kê chúng 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
chức năng đăng ký (không tồn tại). 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ư bất kỳ hàm nào khác. Để lập trình lại
thiết bị tardis của bạn, tập lệnh cập nhật có thể chứa:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Chiến dịch này sử dụng
phiên bản một đối số của hàm tích hợp package_extract_file()
,
Phương thức 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
Phần cuối cùng là thu thập thông tin về các công cụ tạo gói OTA để tìm hiểu về dữ liệu cụ thể theo thiết bị 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 yêu cầu hệ thống xây dựng tìm hiểu về một blob dữ liệu dành riêng cho thiết bị. Giả sử 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 ở định dạng Android.mk, nhưng sau đó tệp phải được bảo vệ bằng một thiết bị hãy kiểm tra, vì tất cả các tệp Android.mk trong cây đều được tải dù bạn đang dùng thiết bị nào 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.date 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
Vì lý do trước đây, các tệp này được gọi là các tệp radio; chúng có thể không liên quan gì đến
đài phát trên thiết bị (nếu có). Chúng chỉ đơn giản là những đốm dữ liệu mờ mà hệ thống xây dựng sao chép vào
tệp target-files .zip mà công cụ tạo OTA sử dụng. Khi bạn tạo một bản dựng, tardis.date sẽ là
đượ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ông cụ phát hành, hãy viết một mô-đun Python (phải đặt tên là releasetools.py) cho 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 tăng dần. Để làm việc này Ví dụ: giả sử bạn cần lập trình lại tardis chỉ khi tệp tardis.date đã 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"));""")
Các hàm của 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 thời điểm bắt đầu tạo một OTA đầy đủ. Đây là nơi phù hợp để đưa ra câu nhận định về trạng thái hiện tại của thiết bị. Không phát ra các lệnh của 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ả xác nhận về trạng thái thiết bị đã trôi qua nhưng trước khi có bất kỳ thay đổi nào đã được tạo. Bạn có thể phát các lệnh cho các bản cập nhật dành riêng cho thiết bị. Các lệnh này phải chạy trước mọi thứ khác trên thiết bị đã bị thay đổi.
FullOTA_InstallEnd()
- Được gọi vào cuối quá trình tạo tập lệnh, sau khi có các lệnh của tập lệnh để cập nhật quy trình khởi động và phân vùng hệ thống đã được phát. Bạn cũng có thể phát các lệnh bổ sung cho bản cập nhật dành riêng cho từng thiết bị.
IncrementalOTA_Assertions()
-
Tương tự như
FullOTA_Assertions()
nhưng được gọi khi tạo cập nhật gói. IncrementalOTA_VerifyBegin()
- Được gọi sau khi mọi xác nhận về trạng thái thiết bị đã 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 các bản cập nhật dành riêng cho thiết bị. Các lệnh này phải chạy trước khi bất kỳ thiết bị nào phát hành khác trên thiết bị đã được thay đổi.
IncrementalOTA_VerifyEnd()
- Được gọi vào cuối giai đoạn xác minh, khi tập lệnh đã xác nhận xong tệp mà nó sắp chạm vào có nội dung bắt đầu dự kiến. Tại thời điểm này, không có gì trên thiết bị đã được thay đổi. Bạn cũng có thể tạo mã cho các thiết bị khác xác minh.
IncrementalOTA_InstallBegin()
- Được gọi sau khi các tệp cần vá đã được xác minh là có trạng thái như dự kiến trước trạng thái nhưng trước khi có bất kỳ thay đổi nào được thực hiện. 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 bất kỳ nội dung nào khác trên thiết bị được thay đổi.
IncrementalOTA_InstallEnd()
- Tương tự như phiên bản gói OTA đầy đủ, thuộc tính này được gọi ở cuối tập lệnh sau khi các lệnh của tập lệnh để cập nhật phân vùng hệ thống và khởi động đã được phát ra. Bạn cũng có thể phát hành 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ị mất nguồn, quá trình cài đặt OTA có thể bắt đầu lại từ đầu tiên. Hãy chuẩn bị để đối phó với các thiết bị đã chạy các lệnh này, toàn bộ hoặc một phần.
Truyền hàm đến đối tượng thông tin
Truyền các hàm vào một đối tượng thông tin duy nhất chứa nhiều mục hữu ích:
-
info.input_zip. (Chỉ dành cho OTA đầy đủ) Đối tượng
zipfile.ZipFile
cho nhập tệp đích .zip. -
info.source_zip. (Chỉ dành cho OTA gia tăng) Đối tượng
zipfile.ZipFile
cho source target-files .zip (bản dựng đã có trên thiết bị khi gói tăng dần đang được cài đặt). -
info.target_zip. (Chỉ dành cho OTA gia tăng) Đối tượng
zipfile.ZipFile
cho tệp target-files .zip (bản dựng mà gói tăng dần đặt trên thiết bị). -
info.output_zip. Gói đang được tạo; đã mở một đối tượng
zipfile.ZipFile
để viết. Sử dụng Common.ZipWriteStr(info.output_zip, filename, data) để thêm vào gói. -
info.script. Đối tượng tập lệnh mà bạn có thể nối thêm các lệnh. Gọi điện
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 các lệnh được phát ra 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 cho các 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 không đặt TARGET_releaseTOOLS_EXTENSIONS, cài đặt mặc định 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õ ràng vị trí của tập lệnh releasetools.py.
Khi tạo thiết bị tardis, tập lệnh releasetools.py được đưa vào trong các tệp đích
Tệp .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 target-file .zip, nếu
hiện tại, được ưu tiên hơn mã trong cây nguồn Android. Bạn cũng có thể nói rõ
chỉ định đường dẫn đến các tiện ích dành riêng cho thiết bị bằng -s
(hoặc
--device_specific
), trong đó có ưu tiên hàng đầu. Điều này cho phép bạn
sửa lỗi và thực hiện các thay đổi trong tiện ích công cụ phát hành và áp dụng những thay đổi đó cho các phiên bản cũ
tệp đích.
Giờ đây, khi bạn chạy ota_from_target_files
, trình duyệt sẽ tự động nhận
mô-đun dành riêng cho thiết bị từ tệp target_files .zip và sử dụng mô-đun này khi tạo OTA
gói:
./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 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
ota_from_target_files
nhận xét trong
build/make/tools/releasetools/ota_from_target_files
.
Cơ chế cài đặt không qua cửa hàng ứng dụng
Khôi phục có cơ chế cài đặt không qua cửa hàng ứng dụng để cài đặt gói cập nhật theo cách thủ công mà không cần tải xuống qua mạng không dây bằng hệ thống chính. Tính năng cài đặt không qua cửa hàng rất hữu ích khi gỡ lỗi hoặc tạo 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 cài đặt 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 ra khỏi thẻ SD của thiết bị; inch trong trường hợp thiết bị không khởi động, gói có thể được đưa vào thẻ SD bằng một số máy tính sau đó cắm thẻ SD vào thiết bị. Để phù hợp với thiết bị Android không có bộ nhớ ngoài có thể tháo rời, khôi phục hỗ trợ hai 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 rồi tải các gói đó qua USB bằng adb.
Để gọi từng cơ chế cài đặt không qua cửa hàng ứng dụng, phương thức Device::InvokeMenuItem()
của thiết bị
có thể trả về các giá trị BuildinAction sau đây:
-
ÁP DỤNG_EXT. Cài đặt gói cập nhật từ bộ nhớ ngoài không qua cửa hàng (
/sdcard
) thư mục). recovery.fstab phải xác định điểm gắn/sdcard
. Đây là không sử dụng được trên các thiết bị mô phỏng thẻ SD có đườ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ó sẵn để khôi phục vì có thể được mã hoá. Giao diện người dùng khôi phục 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 báo cáo. -
ÁP DỤNG_CACHE. Tương tự như 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 sử dụng thay thế. Trong hệ thống thông thường, chỉ người dùng có đặc quyền mới có thể ghi/cache
và nếu thiết bị không khởi động được thì bạn cũng không thể ghi thư mục/cache
vào (điều này khiến cơ chế này trở nên hữu ích hạn chế). -
ÁP DỤNG_ADB_SIDETẢI. Cho phép người dùng gửi một gói tới thiết bị qua cáp USB và
công cụ phát triển adb. Khi cơ chế này được gọi, quá trình khôi phục sẽ khởi động quy trình khôi phục nhỏ
phiên bản của adbd daemon để 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 này. Hình đại diện này
phiên bản chỉ hỗ trợ một lệnh duy nhất:
adb sideload filename
. Tệp đã đặt tên được gửi từ máy chủ đến thiết bị, sau đó sẽ xác minh và cài đặt ứng dụng đó giống như khi cài đặt trên bộ nhớ cục bộ.
Một số điều cần lưu ý:
- Chỉ hỗ trợ cổng USB.
-
Nếu quá trình khôi phục của bạn chạy adbd bình thường (thường đúng với bản dựng userdebug và eng), thì điều đó sẽ
tắt trong khi thiết bị đang ở chế độ tải không qua cửa hàng adb và sẽ được khởi động lại khi adb
cài đặt không qua cửa hàng ứng dụng đã nhận xong gói. Khi ở chế độ tải không qua cửa hàng adb, không có lệnh adb nào khác
hơn
sideload
cơ quan (logcat
,reboot
,push
,pull
,shell
, v.v. đều không thành công). -
Bạn không thể thoát khỏi chế độ tải không qua cửa hàng adb trên thiết bị. Để huỷ bỏ, bạn có thể gửi
/dev/null
(hoặc bất kỳ gói nào khác không phải là gói hợp lệ) làm gói, và thì thiết bị sẽ không xác minh được và dừng quy trình cài đặt. Giao diện người dùng phục hồi phương thứcCheckKey()
của quá trình triển khai sẽ tiếp tục được gọi cho các thao tác nhấn phím, để bạn có thể cung cấp một tổ hợp phím giúp khởi động lại thiết bị và hoạt động ở chế độ tải không qua cửa hàng adb.