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

Hệ thống khôi phục bao gồm một số móc để chèn mã dành riêng cho thiết bị để các bản cập nhật OTA cũng có thể cập nhật các bộ phận của thiết bị ngoài hệ thống Android (ví dụ: băng cơ sở hoặc bộ xử lý vô tuyến).

Các phần và ví dụ sau đây tùy chỉnh thiết bị tardis do nhà cung cấp yoyodyne sản xuất.

Sơ đồ 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 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ũ hơn.

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ả công cụ khôi phục nhị phân và xây dựng 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ể trông như thế này:

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 là tùy chọn, tất cả các điểm gắn kết 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ó năm loại hệ thống tập tin được hỗ trợ:

yaffs2
Hệ thống tập tin yaffs2 trên 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 trong /proc/mtd .
mtd
Phân vùng MTD thô, được sử dụng cho các phân vùng có khả năng khởi động như boot và recovery. MTD thực sự không được gắn kết nhưng điểm gắn kết được sử dụng làm chìa khóa để xác định vị trí phân vùng. "thiết bị" phải là tên của phân vùng MTD trong /proc/mtd .
ext4
Hệ thống tệp ext4 trên thiết bị flash eMMc. "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ô, được sử dụng cho các phân vùng có khả năng khởi động như boot và recovery. Tương tự như loại mtd, eMMc không bao giờ được gắn kết thực sự mà chuỗi điểm gắn kết được sử dụng để định vị thiết bị trong bảng.
vfat
Hệ thống tệp FAT trên thiết bị khối, thường dành cho 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 kết nếu việc gắn thiết bị chính không thành công (để tương thích với thẻ SD có thể được định dạng hoặc không bằng bảng phân vùng).

Tất cả cá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 kết phải bắt đầu bằng dấu gạch chéo và không có dấu gạch chéo nào khác). Hạn chế này chỉ áp dụng cho việc gắn hệ thống tập tin vào quá trình khôi phục; hệ thống chính có thể tự do gắn chúng ở bất cứ đâu. Các 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).

Bắt đầu từ Android 3.0, tệp recovery.fstab có thêm một trường tùy chọn, options . Hiện tại, tùy chọn được xác định duy nhất là length , tùy chọn này cho phép bạn chỉ định rõ ràng độ dài của phân vùng. Độ dài này được sử dụng khi định dạng lại phân vùng (ví dụ: đối với phân vùng dữ liệu người dùng trong quá trình xóa dữ liệu/khôi phục cài đặt gốc hoặc cho phân vùng hệ thống trong khi cài đặt gói OTA đầy đủ). Nếu giá trị độ dài là âm thì kích thước cần đị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. Ví dụ: cài đặ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ã hóa phân vùng dữ liệu người dùng (nơi siêu dữ liệu mã hóa được lưu trữ ở cuối phân vùng không được ghi đè).

Lưu ý: Các trường device2options là tùy chọn, 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 tùy chọn .

Khởi động hoạt hình

Các nhà sản xuất thiết bị có khả năng tùy chỉnh hì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à định vị theo các thông số kỹ thuật ở định dạng bootanimation .

Đối với 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 thương hiệu của Android .

Giao diện người dùng khôi phục

Để hỗ trợ các thiết bị có phần cứng sẵn có khác nhau (nút vật lý, đèn LED, màn hình, v.v.), bạn có thể tùy chỉnh giao diện khôi phục để hiển thị trạng thái và truy cập các tính năng ẩn được vận hành thủ công cho từng thiết bị.

Mục tiêu của bạn là 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ị. Tệp bootable/recovery/default_device.cpp được sử dụng theo mặc định và là điểm khởi đầu tốt để sao chép khi ghi 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 ở đây. Để chuyển đổi 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ỳ để 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"

Chức năng tiêu đề và mục

Lớp Thiết bị yêu cầu các chức năng trả về các tiêu đề và mục xuất hiện trong menu khôi phục ẩn. Tiêu đề mô tả cách vận hành menu (tức là 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 lưu ý đến chiều rộng của màn hình thiết bị của bạn.

Tùy chỉnh CheckKey

Tiếp theo, xác định cách triển khai RecoveryUI trên thiết bị của bạn. Ví dụ này giả định thiết bị tardis có màn hình, do đó bạn có thể kế thừa từ ScreenRecoveryUIimplementation tích hợp sẵn (xem hướng dẫn dành cho các thiết bị không có màn hình .) Chức năng duy nhất để tùy chỉnh từ ScreenRecoveryUI là CheckKey() , chức năng này thực hiện việc xử lý khóa không đồng bộ ban đầu:

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

hằng số KEY

Các hằng số KEY_* được xác định trong linux/input.h . CheckKey() được gọi bất kể điều gì đang xảy ra trong phần còn lại của quá trình khôi phục: khi menu tắt, khi menu bật, trong khi cài đặt gói, trong khi xóa dữ liệu người dùng, v.v. Nó có thể trả về một trong bốn hằng số:

  • CHUYỂN ĐỔI . Bật hoặc tắt hiển thị menu và/hoặc đăng nhập văn bản
  • KHỞI ĐỘNG LẠI . Khởi động lại thiết bị ngay lập tức
  • PHỚT LỜ . Bỏ qua phím nhấn này
  • ENQUEUE . Sắp xếp hàng đợi phím nhấn này được sử dụng đồng bộ (tức là bởi hệ thống menu 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 được theo sau bởi một sự kiện nhấn phím cho cùng một khóa. (Chuỗi sự kiện A-down B-down B-up A-up chỉ dẫn đến việc CheckKey(B) được gọi.) 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 chuỗi các sự kiện quan trọng ở trên, nếu CheckKey(B) được gọi là IsKeyPressed(A) thì nó sẽ trả về đúng.)

CheckKey() có thể duy trì trạng thái trong lớp của nó; điều này có thể hữu ích để phát hiện chuỗi khóa. Ví dụ này cho thấy cách thiết lập phức tạp hơn một chút: màn hình được bật bằng cách giữ nguồn và nhấn tăng âm lượng, đồng thời 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 can thiệp 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 phục hồi màn hình

Khi sử dụng hình ảnh của riêng bạn (biểu tượng lỗi, hoạt ảnh 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 độ tính bằng khung hình trên giây (FPS) của hoạt ảnh.

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 đó, cần phải tự đặt animation_fps .

Để đặt biến animation_fps , hãy ghi đè hàm ScreenRecoveryUI::Init() trong lớp con của bạn. Đặt giá trị, sau đó gọi parent Init() gốc để hoàn tất quá trình khởi tạo. Giá trị mặc định (20 FPS) tương ứng với ả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 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ủa bạn (được phân lớp từ lớp Thiết bị tích hợp sẵn). Nó sẽ tạo một phiên bản duy nhất của lớp UI của bạn và trả về phiên bản đó từ hàm GetUI() :

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

Bắt đầuPhục hồi

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 tạo 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. Việ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 điều này trong lớp con của mình nếu bạn không có việc gì phải làm:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

Cung cấp và quản lý menu recovery

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, nó 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; }

Xử lýMenuKey

Tiếp theo, cung cấp hàm HandleMenuKey() , hàm này nhận một lần nhấn phím và hiển thị menu 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ã khóa (trước đó đã được xử lý và xếp vào hàng đợi bằng phương thức CheckKey() của đối tượng UI) và trạng thái hiện tại của khả năng hiển thị nhật ký văn bản/menu. Giá trị trả về là một số nguyên. Nếu giá trị là 0 hoặc cao hơn, giá trị đó được lấy làm vị trí của một mục menu và được gọi ngay lập tức (xem phương thức InvokeMenuItem() bên dưới). Nếu không, nó có thể là một trong các hằng số được xác định trước sau:

  • kHighlightUp . Di chuyển phần tô sáng của menu đến mục trước đó
  • kHighlightDown . Di chuyển phần đánh dấu menu sang mục tiếp theo
  • kInvokeItem . Gọi mục hiện được đánh dấu
  • kNoAction . Không làm gì với phím nhấn này

Như được ngụ ý bởi đối số hiển thị, HandleMenuKey() được gọi ngay cả khi menu không hiển thị. Không giống như CheckKey() , nó không được gọi khi quá trình khôi phục đang thực hiện một số việc như xóa dữ liệu hoặc cài đặt gói—nó chỉ được gọi khi quá trình khôi phục không hoạt động và chờ dữ liệu đầu vào.

Cơ chế trackball

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 với loại EV_REL và mã REL_Y), quá trình khôi phục sẽ tổng hợp các phím nhấn KEY_UP và KEY_DOWN bất cứ khi nào thiết bị nhập giống bi xoay báo cáo chuyển động theo trục Y. Tất cả những gì bạn cần làm là ánh xạ các sự kiện KEY_UP và KEY_DOWN vào các hành động trên menu. Việc ánh xạ này không xảy ra với CheckKey() , vì vậy bạn không thể sử dụng chuyển động bi xoay làm trình kích hoạt khởi động lại hoặc chuyển đổi màn hình.

Phím bổ trợ

Để kiểm tra xem các phím có được giữ làm công cụ sửa đổi hay không, hãy gọi phương thức IsKeyPressed() của đối tượng UI của riêng bạn. Ví dụ: trên một số thiết bị, nhấn Alt-W trong quá trình khôi phục sẽ bắt đầu xóa dữ liệu cho dù menu có hiển thị hay không. BẠN có thể thực hiện như thế này:

   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 hiển thị là sai, sẽ không có ý nghĩa gì khi trả về các giá trị đặc biệt thao tác menu (di chuyển phần đánh dấu, gọi mục được đánh dấu) vì người dùng không thể nhìn thấy phần đánh dấu. Tuy nhiên, bạn có thể trả về các giá trị nếu muốn.

GọiMenuItem

Tiếp theo, cung cấp một phương thức InvokeMenuItem() để ánh xạ các vị trí số nguyên trong mảng các mục được GetMenuItems() trả về tớ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 gì có trong hệ thống: Thêm một mục cho nó trong menu của bạn, thực thi nó ở đây khi mục menu đó đượ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:

  • KHÔNG CÓ HÀNH ĐỘNG . Không làm gì cả.
  • KHỞI ĐỘNG LẠI . Thoát khỏi recovery và khởi động lại thiết bị bình thường.
  • ỨNG DỤNG_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD . Cài đặt gói cập nhật từ nhiều nơi khác nhau. Để biết chi tiết, xem Sideloading .
  • LAU BỘ NHỚ CACHE . Chỉ định dạng lại phân vùng bộ đệm. Không cần xác nhận vì điều này tương đối vô hại.
  • XÓA DỮ LIỆU . Định dạng lại phân vùng dữ liệu người dùng và bộ đệm, còn được gọi là thiết lập lại dữ liệu ban đầu. Người dùng được yêu cầu xác nhận hành động này trước khi tiếp tục.

Phương thức cuối cùng, WipeData() , là tùy chọn và được gọi bất cứ khi nào thao tác xóa dữ liệu được bắt đầu (từ quá trình khôi phục qua menu hoặc khi người dùng đã chọn thực hiện thiết lập lại dữ liệu ban đầu từ hệ thống chính). Phương thức này được gọi trước khi dữ liệu người dùng và phân vùng bộ đệm bị xóa. 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 xóa dữ liệu đó tại đây. Bạn nên trả về 0 để biểu thị thành công và một giá trị khác cho thất bại, mặc dù hiện tại giá trị trả về bị bỏ qua. Dữ liệu người dùng và phân vùng bộ đệm sẽ bị xóa cho dù bạn trả về thành công hay thất bại.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

Tạo thiết bị

Cuối cùng, bao gồm một số bản soạn sẵn ở cuối tệp recovery_ui.cpp cho hàm make_device() tạo và trả về một phiên bản của lớp Thiết bị của bạn:

class TardisDevice : public Device {
   // ... all the above methods ...
};

Device* make_device() {
    return new TardisDevice();
}

Sau khi hoàn thành file recovery_ui.cpp, hãy build nó và liên kết nó với recovery trên thiết bị của bạn. Trong Android.mk, tạo một thư viện tĩnh chỉ chứa tệp C++ này:

device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_SRC_FILES := recovery_ui.cpp

# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
LOCAL_MODULE := librecovery_ui_tardis

include $(BUILD_STATIC_LIBRARY)

Sau đó, trong cấu hình bảng cho thiết bị này, hãy chỉ định thư viện tĩnh của bạn làm giá trị 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 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, hành động duy nhất của 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 giúp loại bỏ nhu cầu bản địa hóa. 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, hãy xem Văn bản khôi phục được bản địa hóa .

Android 5.0 trở lên

Giao diện người dùng khôi phục Android 5.0 trở lên sử dụng hai hình ảnh chính: hình ảnh lỗi và hoạt ảnh cài đặt .

hình ảnh hiển thị trong lỗi ota

Hình 1. icon_error.png

hình ảnh hiển thị trong quá trình cài đặt ota

Hình 2. icon_installing.png

Hoạt ảnh cài đặt được thể hiện dưới dạng một hình ảnh PNG duy nhất với các khung hoạt ảnh khác nhau được xen kẽ theo hàng (đó là lý do tại sao Hình 2 có vẻ bị bóp méo). Ví dụ: đối với hoạt ảnh bảy khung hình 200x200, hãy tạo một hình ảnh 200x1400 trong đó khung hình đầu tiên là các hàng 0, 7, 14, 21, ...; khung 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 hoạt hình và số lượng khung hình mỗi 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 được sử dụng bởi quá trình khôi phục.

Hình ảnh mặc định có sẵn ở các mật độ khác nhau và nằm trong bootable/recovery/res-$DENSITY/images (ví dụ: bootable/recovery/res-hdpi/images ). Để sử dụng ảnh tĩnh trong quá trình cài đặt, bạn chỉ cần cung cấp ảnh icon_installing.png và đặt số khung hình trong ảnh động về 0 (biểu tượng lỗi không phải là ảnh động; nó luôn là ảnh tĩnh).

Android 4.x trở về trước

Giao diện người dùng khôi phục Android 4.x trở về trước sử dụng hình ảnh lỗi (hiển thị ở trên) và hoạt ảnh cài đặt cùng với một số hình ảnh lớp phủ:

hình ảnh hiển thị trong quá trình cài đặt ota

Hình 3. icon_installing.png

hình ảnh được hiển thị dưới dạng lớp phủ đầu tiên

Hình 4. icon-installing_overlay01.png

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

Hình 5. icon_installing_overlay07.png

Trong quá trình cài đặt, màn hình hiển thị trên màn hình được xây dựng 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 nó ở độ lệch thích hợp. Ở đây, một hộp màu đỏ được xếp chồng lên nhau để làm nổi bật vị trí lớp phủ được đặt ở trên cùng của hình ảnh cơ sở:

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

Hình 6. Cài đặt animation frame 1 (icon_installing.png + icon_installing_overlay01.png)

hình ảnh tổng hợp của cài đặt cộng với lớp phủ thứ bảy

Hình 7. Cài đặt animation frame 7 (icon_installing.png + icon_installing_overlay07.png)

Các khung hình tiếp theo được hiển thị bằng cách chỉ vẽ hình ảnh lớp phủ tiếp theo lên trên những gì đã có; hình ảnh cơ bản không được vẽ lại.

Số lượng khung hình trong hoạt ảnh, tốc độ mong muốn và độ lệch x và y của lớp phủ so với cơ sở được đặt bởi các biến thành viên của lớp ScreenRecoveryUI. Khi sử dụng hình ảnh tùy chỉnh thay vì hình ảnh mặc định, hãy ghi đè phương thức Init() trong lớp con của bạn để thay đổi các giá trị này cho hình ảnh tùy chỉnh của bạn (để biết 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 độ lệch cần thiết.

Hình ảnh mặc định được đặt ở bootable/recovery/res/images . Để sử dụng ảnh tĩnh trong quá trình cài đặt, bạn chỉ cần cung cấp ảnh icon_installing.png và đặt số khung hình trong ảnh động về 0 (biểu tượng lỗi không phải là ảnh động; nó luôn là ảnh tĩnh).

Văn bản khôi phục được bản địa hóa

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, nó sẽ chuyển ngôn ngữ hiện tại của người dùng dưới dạng tùy 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 sẽ bao gồm 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 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 được bản địa hóa cho các 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 xóa... (khi thực hiện xóa dữ liệu/khôi phục cài đặt gốc)
  • Không có lệnh (khi người dùng khởi động vào recovery theo cách thủ công)

Ứng dụng Android trong bootable/recovery/tools/recovery_l10n/ hiển thị bản địa hóa của tin nhắn và tạo hình ảnh tổng 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 nào được hiển thị. Đừng làm cho tin nhắn văn bản trở nê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 báo tường trình và cho phép người dùng chọn hành động từ menu 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 hì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 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 đầy đủ

Hình 10. Progress_fill.png

Đầu bên trái của hình ảnh lấp đầy được hiển thị bên cạnh đầu bên phải của hình ảnh trống để làm thanh tiến trình. Vị trí ranh giới giữa hai hình ảnh được thay đổi để biểu thị tiến trình. Ví dụ: với các cặp ảnh đầu vào ở trên, hiển thị:

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 phiên bản dành riêng cho thiết bị của những hình ảnh này bằng cách đặt chúng vào (trong ví dụ này) device/yoyodyne/tardis/recovery/res/images . Tên tệp phải khớp với tên được liệt kê ở trên; khi tìm thấy một tệp trong thư mục đó, hệ thống xây dựng sẽ ưu tiên sử dụng tệp đó thay vì hình ảnh mặc định tương ứng. Chỉ hỗ trợ 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ữ viết 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 tất cả các thiết bị Android đều có màn hình. Nếu thiết bị của bạn là một thiết bị không có đầu hoặc có giao diện chỉ có âm thanh, bạn có thể cần thực hiện tùy chỉnh rộng rãi hơn cho giao diện người dùng khôi phục. Thay vì tạo một lớp con của ScreenRecoveryUI, hãy phân lớp trực tiếp lớp cha RecoveryUI của nó.

RecoveryUI có các phương pháp xử lý các thao tác UI cấp thấp hơn như "chuyển đổi màn hình", "cập nhật thanh tiến trình", "hiển thị menu", "thay đổi lựa chọn menu", v.v. Bạn có thể ghi đè các phương pháp này để cung cấp giao diện phù hợp cho thiết bị của bạn. Có thể thiết bị của bạn có đèn LED nơi bạn có thể sử dụng các màu sắc hoặc kiể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ợ một menu hoặc chế độ "hiển thị văn bản" nào cả; bạn có thể ngăn truy cập chúng bằng các triển khai CheckKey()HandleMenuKey() không bao giờ bật màn hình hoặc chọn một mục menu. Trong trường hợp này , nhiều phương thức RecoveryUI bạn cần cung cấp có thể chỉ là sơ khai trống.)

Xem bootable/recovery/ui.h để biết phần khai báo RecoveryUI để biết bạn phải hỗ trợ những phương pháp nào. RecoveryUI là trừu tượng—một số phương thức hoàn toàn ảo và phải được cung cấp bởi các lớp con—nhưng nó có chứa mã để xử lý các đầu vào chính. Bạn cũng có thể ghi đè lên nếu thiết bị của bạn không có khóa hoặc bạn muốn xử lý chúng theo cách khác.

Trình cập nhật

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 của riêng bạn mà có thể được gọi từ bên trong tập lệnh cập nhật của bạn. Đây là một chức năng 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 chức năng mở rộng đều có cùng một chữ ký. Các đố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* biểu thị các đối số. Giá trị trả về là 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);
    }

Đối số của bạn chưa được đánh giá tại thời điểm hàm của bạn được gọi—logic của hàm sẽ xác định đối số nào trong số chúng được đánh giá và số lần đánh giá. Vì vậy, 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ề NULL, bạn nên giải phóng mọi tài nguyên bạn đang nắm giữ và trả về NULL ngay lập tức (điều này sẽ hủy bỏ ngăn xếp chỉnh sửa). Nếu không, bạn có quyền sở hữu Giá trị được trả về và cuối cùng chịu trách nhiệm gọi FreeValue() trên đó.

Giả sử hàm cần hai đối số: khóa có giá trị chuỗi và hình ảnh có giá trị blob. Bạn có thể đọc những lập luận như thế này:

   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 NULL và giải phóng các đối số đã được đánh giá trước đó có thể trở nên tẻ nhạt đối với nhiều đối số. Hàm ReadValueArgs() có thể thực hiện việc này dễ dàng hơn. Thay vì đoạn mã trên, bạn có thể viết dòng này:

   Value* key;
    Value* image;
    if (ReadValueArgs(state, argv, 2, &key, &image) != 0) {
        return NULL;     // ReadValueArgs() will have set the error message
    }
    if (key->type != VAL_STRING || image->type != VAL_BLOB) {
        ErrorAbort(state, "arguments to %s() have wrong type", name);
        FreeValue(key);
        FreeValue(image)
        return NULL;
    }

ReadValueArgs() không thực hiện kiểm tra kiểu, vì vậy bạn phải thực hiện việc đó tại đây; sẽ thuận tiện hơn khi thực hiện điều đó bằng một câu lệnh if với chi phí là tạo ra một thông báo lỗi ít cụ thể hơn khi nó bị lỗi. Nhưng ReadValueArgs() xử lý việc đánh giá từng đối số và giải phóng tất cả các đối số được đánh giá trước đó (cũng như đặt thông báo lỗi hữu ích) nếu 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á số lượng đối số thay đổi (nó 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ẽ được chuyển cho người gọi. Người gọi có quyền sở hữu bất kỳ dữ liệu nào được trỏ đến bởi Value* này — cụ thể là thành viên dữ liệu.

Trong trường hợp này, bạn muốn trả về giá trị đúng hoặc sai để biểu thị thành công. Hãy nhớ quy ước rằng chuỗi trống là sai và tất cả các chuỗi khác là đúng . Bạn phải malloc một đối tượng Value có bản sao malloc'd của chuỗi không đổi để trả về, vì người 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ố của mình!

   FreeValue(key);
    FreeValue(image);

    Value* result = malloc(sizeof(Value));
    result->type = VAL_STRING;
    result->data = strdup(successful ? "t" : "");
    result->size = strlen(result->data);
    return result;
}

Hàm tiện lợi StringValue() gói một chuỗi thành một đối tượng Giá trị mới. Sử dụng để viết đoạn mã trên 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 chức năng mở rộng. Theo quy ước, đặt tên cho các chức năng cụ thể device . whatever để tránh xung đột với các chức năng tích hợp trong tương lai được thêm vào.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

Bây giờ bạn có thể định cấu hình tệp tạo tệp để xây dựng thư viện tĩnh bằng mã của mình. (Đây là cùng một tệp tạo tệp được sử dụng để tùy 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, đị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 mà bản thân chúng không phải là tiện ích mở rộng chỉnh sửa (nghĩa là chúng không có chức năng 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 hàm đăng ký (không tồn tại) của chúng. Ví dụ: nếu mã dành riêng cho thiết bị của bạn muốn sử dụng zlib để giải nén dữ liệu, 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 +=

Các tập lệnh cập nhật trong gói OTA của bạn giờ đây 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")) . Cái này sử dụng phiên bản một đối số của hàm dựng sẵn package_extract_file() , 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 blob để tạo đối số thứ hai cho hàm mở rộng mới.

Tạo gói OTA

Thành phần cuối cùng là giúp các công cụ tạo gói OTA biết về dữ liệu dành riêng cho thiết bị của bạn và phát ra các tập lệnh cập nhật bao gồm các lệnh gọi đến các chức năng tiện ích mở rộng của bạn.

Trước tiên, hãy làm cho hệ thống xây dựng biết về một khối 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 những điều sau trong AndroidBoard.mk của thiết bị của bạn:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

Thay vào đó, bạn cũng có thể đặt nó trong Android.mk, nhưng sau đó nó phải được bảo vệ bằng cách kiểm tra thiết bị, vì tất cả các tệp Android.mk trong cây đều được tải bất kể thiết bị nào đang được 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 xây dựng 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

Chúng được gọi là tập tin 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ỉ đơn giản là những khối dữ liệu mờ đục mà hệ thống xây dựng sao chép vào các tệp đích .zip được sử dụng bởi các công cụ tạo OTA. Khi bạn thực hiện quá trình xây dựng, tardis.dat đượ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 tùy 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 tên là Releasetools.py) mà các công cụ này có thể gọi 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 chức năng riêng biệt xử lý trường hợp tạo gói OTA gia tăng. Trong ví dụ này, giả sử bạn chỉ cần lập trình lại tardis khi tệp tardis.dat đã thay đổi giữa hai bản dựng.

def IncrementalOTA_InstallEnd(info):
  # copy the data into the package.
  source_tardis_dat = info.source_zip.read("RADIO/tardis.dat")
  target_tardis_dat = info.target_zip.read("RADIO/tardis.dat")

  if source_tardis_dat == target_tardis_dat:
      # tardis.dat is unchanged from previous build; no
      # need to reprogram it
      return

  # include the new tardis.dat in the OTA package
  common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat)

  # emit the script code to install this data on the device
  info.script.AppendExtra(
      """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")

Chức năng mô-đun

Bạn có thể cung cấp các chức năng sau trong mô-đun (chỉ triển khai những chức năng bạn cần).

FullOTA_Assertions()
Được gọi khi gần bắt đầu tạo OTA đầy đủ. Đây là nơi tốt để đưa ra các xác nhận về trạng thái hiện tại của thiết bị. Không phát ra các lệnh script làm thay đổi thiết bị.
FullOTA_InstallBegin()
Được gọi sau khi tất cả cá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 thực hiện. Bạn có thể phát ra 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.
FullOTA_InstallEnd()
Được gọi vào cuối quá trình tạo tập lệnh, sau khi các lệnh tập lệnh để cập nhật phân vùng khởi động và hệ thống đã được phát ra. Bạn cũng có thể phát ra các lệnh bổ sung để 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 gói cập nhật gia tăng.
IncrementalOTA_VerifyBegin()
Được gọi sau khi tất cả cá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 thực hiện. Bạn có thể phát ra 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 vào cuối giai đoạn xác minh, khi tập lệnh đã xác nhận xong các tập tin mà nó sẽ chạm vào có nội dung bắt đầu như mong đợi. 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ể phát mã để xác minh bổ sung cho từng thiết bị cụ thể.
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 ra 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_InstallEnd()
Tương tự như bản sao gói OTA đầy đủ của nó, điều này được gọi ở cuối quá trình tạo tập lệnh, sau khi các lệnh tập lệnh để cập nhật phân vùng hệ thống và khởi động đã được phát ra. Bạn cũng có thể phát ra các lệnh bổ sung để cập nhật dành riêng cho thiết bị.

Lưu ý: Nếu máy mất nguồn, quá trình cài đặt OTA có thể khởi động lại từ đầu. Hãy chuẩn bị sẵn sàng để đố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 cho đối tượng thông tin

Truyền các hàm cho một đối tượng thông tin chứa nhiều mục hữu ích khác nhau:

  • info.input_zip . (Chỉ các OTA đầy đủ) Đối tượng zipfile.ZipFile cho các tệp đích đầu vào .zip.
  • thông tin.source_zip . (Chỉ các OTA gia tăng) Đối tượng zipfile.ZipFile cho các 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).
  • thông tin.target_zip . (Chỉ các OTA gia tăng) Đối tượng zipfile.ZipFile cho các tệp đích đích .zip (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 tệp vào gói.
  • thông tin.script . Đối tượng tập lệnh mà bạn có thể thêm lệnh vào. Gọi info.script.AppendExtra( script_text ) để xuất văn bản thành 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 để nó không chạy theo các lệnh được phát ra sau đó.

Để biết 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ề kho lưu trữ ZIP .

Chỉ định vị trí mô-đun

Chỉ định vị trí tập lệnh Releasetools.py của thiết bị trong tệp BoardConfig.mk của bạn:

device/yoyodyne/tardis/BoardConfig.mk
 [...]

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Nếu TARGET_RELEASETOOLS_EXTENSIONS không được đặt, nó sẽ mặc định là thư mục $(TARGET_DEVICE_DIR)/../common ( device/yoyodyne/common trong ví dụ này). Tốt nhất là xác định rõ ràng vị trí của tập lệnh Releasetools.py. Khi xây dựng thiết bị tardis, tập lệnh Releasetools.py được bao gồm trong tệp .zip tệp đích ( 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 đích .zip, 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 tiện ích mở rộng dành riêng cho thiết bị bằng tùy chọn -s (hoặc --device_specific ), tùy chọn này có mức độ ưu tiên cao nhất. Điều này cho phép bạn sửa lỗi và thực hiện các thay đổi trong phần mở rộng của công cụ phát hành cũng như áp dụng những thay đổi đó cho các tệp mục tiêu cũ.

Bây giờ, khi bạn chạy ota_from_target_files , nó sẽ tự động chọn mô-đun dành riêng cho thiết bị từ tệp target_files .zip và sử dụng nó khi tạo các gói OTA:

./build/make/tools/releasetools/ota_from_target_files \
    -i PREVIOUS-tardis-target_files.zip \
    dist_output/tardis-target_files.zip \
    incremental_ota_update.zip

Ngoài ra, bạn có thể chỉ định các tiện ích mở rộng 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 tùy chọn, hãy tham khảo nhận xét ota_from_target_files trong build/make/tools/releasetools/ota_from_target_files .

Cơ chế sideloading

Recovery có cơ chế sideload để 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 hệ thống chính. Sideloading rất hữu ích cho việc gỡ lỗi hoặc thực hiện thay đổi trên các thiết bị mà hệ thống chính không thể khởi động được.

Trước đây, việc tải qua bên đượ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 được, gói có thể được đưa vào thẻ SD bằng một số máy tính khác và 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, recovery hỗ trợ hai cơ chế bổ sung để tải từ bên ngoài: tải các gói từ phân vùng bộ đệm và tải chúng qua USB bằng adb.

Để gọi từng cơ chế sideload, phương thức Device::InvokeMenuItem() trên thiết bị của bạn có thể trả về các giá trị sau của BuiltinAction:

  • ỨNG DỤNG_EXT . Sideload một gói cập nhật từ lưu trữ bên ngoài ( /sdcard ). Phục hồi của bạn.fstab phải xác định điểm gắn kết /sdcard . Điều này không thể sử dụng được trên các thiết bị mô phỏng thẻ SD có liên kết với /data (hoặc một số cơ chế tương tự). /data thường không có sẵn để phục hồi vì nó có thể được mã hóa. UI khôi phục hiển thị menu của các tệp .zip trong /sdcard và cho phép người dùng chọn một tệp.
  • Ứng dụng_cache . Tương tự như tải một gói từ /sdcard ngoại trừ thư mục /cache ( luôn có sẵn để phục hồi) được sử dụng thay thế. Từ hệ thống thông thường, /cache chỉ có thể ghi được bởi người dùng đặc quyền và nếu thiết bị không có khả năng khởi động thì thư mục /cache không thể được ghi vào tất cả (điều này tạo ra cơ chế tiện ích hạn chế này).
  • Áp dụng_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, Recovery sẽ bắt đầu phiên bản Mini của Daemon ADBD của riêng mình để cho ADB trên máy tính chủ được kết nối nói chuyện với nó. Phiên bản mini này chỉ hỗ trợ một lệnh duy nhất: adb sideload filename . Tệp được đặt tên được gửi từ máy chủ đến thiết bị, sau đó xác minh và cài đặt nó giống như nó đã có trên bộ nhớ cục bộ.

Một vài cảnh báo:

  • Chỉ có vận chuyển USB được hỗ trợ.
  • Nếu khôi phục của bạn chạy ADBD thông thường (thường đúng với các bản dựng UserDebug và Eng), sẽ ngừng hoạt động trong khi thiết bị ở chế độ ADB Sideload và sẽ được khởi động lại khi ADB Sideload hoàn thành việc nhận gói. Trong khi ở chế độ tải ADB, không có lệnh ADB nào khác ngoài công việc sideload ( logcat , reboot , push , pull , shell , v.v ... Tất cả đều thất bại).
  • Bạn không thể thoát khỏi chế độ adb sideload trên thiết bị. Để hủy bỏ, bạn có thể gửi /dev/null (hoặc bất cứ thứ gì khác không phải là gói hợp lệ) vì gói, và sau đó thiết bị sẽ không xác minh và dừng quy trình cài đặt. Phương thức CheckKey() của RecoveryUI sẽ tiếp tục được gọi cho Keypresses, do đó bạn có thể cung cấp một chuỗi chính để khởi động lại thiết bị và hoạt động ở chế độ tải Sideload ADB.