Mã dành riêng cho thiết bị

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/misc phải là loại thô (mtd hoặc emmc), trong khi các thư mục /system, /data, /cache/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 device2options 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();
}

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.

hình ảnh xuất hiện khi gặp lỗi qua hệ thống ô tô

Hình 1. icon_error.png

hình ảnh xuất hiện trong quá trình cài đặt qua Internet

Hình 2. icon_installation.png

Ả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ủ:

hình ảnh xuất hiện trong quá trình cài đặt qua Internet

Hình 3. icon_installation.png

hình ảnh hiển thị đầu tiên
lớp phủ

Hình 4. icon-Installation_overlay01.png

hình ảnh được hiển thị là thứ bảy
lớp phủ

Hình 5. biểu tượng_installation_overlay07.png

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:

hình ảnh kết hợp của
lượt cài đặt cộng lớp phủ đầu tiên

Hình 6. Cài đặt khung ảnh động 1 (icon_installed.png + biểu tượng cài đặt_overlay01.png)

hình ảnh kết hợp của
lượt cài đặt cộng với lớp phủ thứ 7

Hình 7. Đang cài đặt khung hoạt ảnh 7 (icon_installation.png + biểu tượng cài đặt_overlay07.png)

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:

hình ảnh 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á 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:

thanh tiến trình trống

Hình 9. Progress_Empty.png

thanh tiến trình toàn bộ

Hình 10.progress_fill.png

Đ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:

thanh tiến trình ở mức 1%

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

thanh tiến trình ở mức 10%

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

thanh tiến trình ở mức 50%

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 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 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ức CheckKey() 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.