Kod specyficzny dla urządzenia

System odzyskiwania zawiera kilka zaczepów do wstawiania kodu specyficznego dla urządzenia, dzięki czemu aktualizacje OTA mogą aktualizować także części urządzenia inne niż system Android (np. pasmo podstawowe lub procesor radiowy).

Poniższe sekcje i przykłady dostosowują urządzenie tardis wyprodukowane przez dostawcę yoyodyne .

Mapa partycji

Od wersji Androida 2.3 platforma obsługuje urządzenia flash eMMc i system plików ext4 działający na tych urządzeniach. Obsługuje także urządzenia flash Memory Technology Device (MTD) i system plików yaffs2 ze starszych wersji.

Plik mapy partycji jest określony przez TARGET_RECOVERY_FSTAB; plik ten jest używany zarówno przez plik binarny odzyskiwania, jak i narzędzia do tworzenia pakietów. Możesz określić nazwę pliku mapy w TARGET_RECOVERY_FSTAB w BoardConfig.mk.

Przykładowy plik mapy partycji może wyglądać następująco:

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

Z wyjątkiem /sdcard , który jest opcjonalny, wszystkie punkty montowania w tym przykładzie muszą zostać zdefiniowane (urządzenia mogą również dodawać dodatkowe partycje). Istnieje pięć obsługiwanych typów systemów plików:

yaffs2
System plików yaffs2 na urządzeniu flash MTD. „urządzenie” musi być nazwą partycji MTD i musi występować w /proc/mtd .
mtd
Surowa partycja MTD, używana do partycji rozruchowych, takich jak partycje rozruchowe i odzyskiwanie. MTD nie jest w rzeczywistości zamontowany, ale punkt montowania służy jako klucz do zlokalizowania partycji. „urządzenie” musi być nazwą partycji MTD w /proc/mtd .
wew4
System plików ext4 na urządzeniu flash eMMc. „urządzenie” musi być ścieżką urządzenia blokowego.
emmc
Surowe urządzenie blokowe eMMc, używane do partycji startowych, takich jak rozruch i odzyskiwanie. Podobnie jak w przypadku typu mtd, eMMc nigdy nie jest faktycznie zamontowany, ale ciąg punktów podłączenia służy do zlokalizowania urządzenia w tabeli.
tłuszcz
System plików FAT na urządzeniu blokowym, zwykle przeznaczony do przechowywania zewnętrznego, takiego jak karta SD. Urządzenie jest urządzeniem blokowym; urządzenie2 to drugie urządzenie blokowe, które system próbuje zamontować, jeśli montaż urządzenia podstawowego się nie powiedzie (w celu zapewnienia zgodności z kartami SD, które mogą, ale nie muszą, być sformatowane przy użyciu tablicy partycji).

Wszystkie partycje muszą być zamontowane w katalogu głównym (tj. wartość punktu montowania musi zaczynać się od ukośnika i nie zawierać innych ukośników). To ograniczenie dotyczy wyłącznie montowania systemów plików podczas odzyskiwania; główny system może je zamontować w dowolnym miejscu. Katalogi /boot , /recovery i /misc powinny być typu surowego (mtd lub emmc), natomiast katalogi /system , /data , /cache i /sdcard (jeśli są dostępne) powinny być typami systemów plików (yaffs2, ext4 lub vfat).

Począwszy od Androida 3.0, plik recovery.fstab zyskuje dodatkowe opcjonalne pole, options . Obecnie jedyną zdefiniowaną opcją jest długość , która pozwala jawnie określić długość partycji. Długość ta jest używana podczas ponownego formatowania partycji (np. partycji danych użytkownika podczas operacji czyszczenia danych/przywracania ustawień fabrycznych lub partycji systemowej podczas instalacji pełnego pakietu OTA). Jeśli wartość długości jest ujemna, rozmiar do sformatowania jest pobierany poprzez dodanie wartości długości do prawdziwego rozmiaru partycji. Na przykład ustawienie „długość=-16384” oznacza, że ​​ostatnie 16 KB tej partycji nie zostanie nadpisane podczas ponownego formatowania tej partycji. Obsługuje takie funkcje, jak szyfrowanie partycji danych użytkownika (gdzie metadane szyfrowania są przechowywane na końcu partycji i nie powinny być nadpisywane).

Uwaga: Pola urządzenie2 i opcje są opcjonalne, co powoduje niejednoznaczność podczas analizy. Jeśli wpis w czwartym polu linii zaczyna się od znaku „/”, uznaje się go za wpis urządzenia2 ; jeśli wpis nie zaczyna się od znaku „/”, jest uważany za pole opcji .

Animacja uruchamiania

Producenci urządzeń mają możliwość dostosowania animacji wyświetlanej podczas uruchamiania urządzenia z systemem Android. Aby to zrobić, utwórz plik .zip zorganizowany i zlokalizowany zgodnie ze specyfikacjami w formacie bootanimation .

W przypadku urządzeń z systemem Android Things możesz przesłać spakowany plik do konsoli Android Things, aby obrazy znalazły się w wybranym produkcie.

Uwaga: te obrazy muszą spełniać wytyczne dotyczące marki Androida.

Interfejs odzyskiwania

Aby obsługiwać urządzenia z różnym dostępnym sprzętem (przyciski fizyczne, diody LED, ekrany itp.), możesz dostosować interfejs odzyskiwania tak, aby wyświetlał stan i umożliwiał dostęp do ręcznie obsługiwanych ukrytych funkcji każdego urządzenia.

Twoim celem jest zbudowanie małej biblioteki statycznej z kilkoma obiektami C++, aby zapewnić funkcjonalność specyficzną dla urządzenia. Domyślnie używany jest plik bootable/recovery/default_device.cpp , który stanowi dobry punkt wyjścia do skopiowania podczas pisania wersji tego pliku dla Twojego urządzenia.

Uwaga: może zostać wyświetlony komunikat Brak polecenia w tym miejscu. Aby przełączyć tekst, przytrzymaj przycisk zasilania i naciśnij przycisk zwiększania głośności. Jeśli Twoje urządzenie nie ma obu przycisków, naciśnij i przytrzymaj dowolny przycisk, aby przełączyć tekst.

device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

Funkcje nagłówka i elementu

Klasa Device wymaga funkcji zwracających nagłówki i elementy, które pojawiają się w ukrytym menu odzyskiwania. Nagłówki opisują sposób obsługi menu (tzn. elementy sterujące umożliwiające zmianę/wybór podświetlonego elementu).

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 };

Uwaga: długie linie są obcinane (nie zawijane), dlatego należy pamiętać o szerokości ekranu urządzenia.

Dostosuj CheckKey

Następnie zdefiniuj implementację RecoveryUI na swoim urządzeniu. W tym przykładzie założono, że urządzenie tardis ma ekran, więc można dziedziczyć z wbudowanej implementacji ScreenRecoveryUI (zobacz instrukcje dla urządzeń bez ekranu ). Jedyną funkcją, którą można dostosować z ScreenRecoveryUI, jest CheckKey() , która wykonuje początkową asynchroniczną obsługę kluczy:

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

KLUCZOWE stałe

Stałe KEY_* są zdefiniowane w linux/input.h . CheckKey() jest wywoływana niezależnie od tego, co dzieje się w dalszej części odzyskiwania: kiedy menu jest wyłączone, kiedy jest włączone, podczas instalacji pakietu, podczas czyszczenia danych użytkownika itp. Może zwrócić jedną z czterech stałych:

  • PRZEŁĄCZNIK . Włącza lub wyłącza wyświetlanie menu i/lub logowania tekstowego
  • PONOWNE URUCHOMIENIE . Natychmiast uruchom ponownie urządzenie
  • IGNORUJ . Zignoruj ​​to naciśnięcie klawisza
  • KOLEJOWAĆ . Umieść w kolejce to naciśnięcie klawisza, aby było używane synchronicznie (tzn. przez system menu odzyskiwania, jeśli wyświetlacz jest włączony)

CheckKey() jest wywoływana za każdym razem, gdy po zdarzeniu naciśnięcia klawisza następuje zdarzenie naciśnięcia klawisza dla tego samego klawisza. (Sekwencja zdarzeń A-dół B-dół B-up A-up powoduje jedynie wywołanie CheckKey(B) .) CheckKey() może wywołać IsKeyPressed() , aby dowiedzieć się, czy inne klawisze są wciśnięte. (W powyższej sekwencji kluczowych zdarzeń, jeśli CheckKey(B) wywołał IsKeyPressed(A) zwróciłby wartość true.)

CheckKey() może utrzymywać stan w swojej klasie; może to być przydatne do wykrywania sekwencji klawiszy. Ten przykład pokazuje nieco bardziej złożoną konfigurację: przełączanie wyświetlacza odbywa się poprzez przytrzymanie przycisku zasilania i naciśnięcie przycisku zwiększania głośności, a urządzenie można natychmiast ponownie uruchomić, naciskając przycisk zasilania pięć razy z rzędu (bez innych klawiszy):

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;
    }
};

Odzyskiwanie ekranuUI

Używając własnych obrazów (ikona błędu, animacja instalacji, paski postępu) za pomocą ScreenRecoveryUI, możesz ustawić zmienną animation_fps , aby kontrolować prędkość animacji w klatkach na sekundę (FPS).

Uwaga: bieżący skrypt interlace-frames.py umożliwia przechowywanie informacji animation_fps w samym obrazie. We wcześniejszych wersjach Androida konieczne było samodzielne ustawienie animation_fps .

Aby ustawić zmienną animation_fps , zastąp funkcję ScreenRecoveryUI::Init() w swojej podklasie. Ustaw wartość, a następnie wywołaj parent Init() , aby zakończyć inicjalizację. Wartość domyślna (20 FPS) odpowiada domyślnym obrazom odzyskiwania; podczas korzystania z tych obrazów nie musisz udostępniać funkcji Init() . Aby uzyskać szczegółowe informacje na temat obrazów, zobacz Obrazy interfejsu odzyskiwania .

Klasa urządzenia

Po implementacji RecoveryUI zdefiniuj klasę urządzenia (podklasę z wbudowanej klasy Device). Powinien utworzyć pojedynczą instancję klasy interfejsu użytkownika i zwrócić ją z funkcji GetUI() :

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

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

    RecoveryUI* GetUI() { return ui; }

Rozpocznij przywracanie

Metoda StartRecovery() jest wywoływana na początku odzyskiwania, po zainicjowaniu interfejsu użytkownika i po przeanalizowaniu argumentów, ale przed podjęciem jakichkolwiek działań. Domyślna implementacja nic nie robi, więc nie musisz podawać tego w swojej podklasie, jeśli nie masz nic do roboty:

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

Dostarczaj menu odzyskiwania i zarządzaj nim

System wywołuje dwie metody w celu uzyskania listy linii nagłówka i listy elementów. W tej implementacji zwraca tablice statyczne zdefiniowane na górze pliku:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

Klawisz menu uchwytu

Następnie udostępnij funkcję HandleMenuKey() , która pobiera naciśnięcie klawisza i aktualną widoczność menu i decyduje, jakie działanie należy podjąć:

   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;
    }

Metoda pobiera kod klucza (który został wcześniej przetworzony i umieszczony w kolejce przez metodę CheckKey() obiektu UI) oraz bieżący stan widoczności menu/dziennika tekstowego. Wartość zwracana jest liczbą całkowitą. Jeśli wartość wynosi 0 lub więcej, jest to pozycja elementu menu, która jest natychmiast wywoływana (patrz metoda InvokeMenuItem() poniżej). W przeciwnym razie może to być jedna z następujących predefiniowanych stałych:

  • kPodświetlUp . Przenieś podświetlenie menu na poprzednią pozycję
  • kPodświetl w dół . Przenieś podświetlenie menu do następnego elementu
  • kInvokeItem . Wywołaj aktualnie podświetlony element
  • kNoAkcja . Nic nie rób z tym naciśnięciem klawisza

Jak wynika z widocznego argumentu, HandleMenuKey() jest wywoływana nawet jeśli menu nie jest widoczne. W przeciwieństwie do CheckKey() funkcja ta nie jest wywoływana podczas wykonywania czynności odzyskiwania, takich jak czyszczenie danych lub instalowanie pakietu — jest wywoływana tylko wtedy, gdy odzyskiwanie jest bezczynne i oczekuje na dane wejściowe.

Mechanizmy trackballowe

Jeśli Twoje urządzenie ma mechanizm wejściowy podobny do trackballa (generuje zdarzenia wejściowe o typie EV_REL i kodzie REL_Y), odzyskiwanie syntetyzuje naciśnięcia klawiszy KEY_UP i KEY_DOWN za każdym razem, gdy urządzenie wejściowe podobne do trackballa zgłosi ruch w osi Y. Wszystko, co musisz zrobić, to zmapować zdarzenia KEY_UP i KEY_DOWN na akcje menu. To mapowanie nie jest wykonywane w przypadku CheckKey() , więc nie można używać ruchów trackballa jako wyzwalaczy ponownego uruchamiania lub przełączania wyświetlania.

Klawisze modyfikujące

Aby sprawdzić, czy klawisze są przytrzymywane jako modyfikatory, wywołaj metodę IsKeyPressed() własnego obiektu interfejsu użytkownika. Na przykład na niektórych urządzeniach naciśnięcie klawiszy Alt-W podczas odzyskiwania rozpoczyna czyszczenie danych niezależnie od tego, czy menu było widoczne, czy nie. Możesz zaimplementować w ten sposób:

   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
        }
        ...
    }

Uwaga: Jeśli widoczne ma wartość fałsz, nie ma sensu zwracać wartości specjalnych, które manipulują menu (przesuwanie podświetlenia, wywoływanie podświetlonego elementu), ponieważ użytkownik nie widzi podświetlenia. W razie potrzeby możesz jednak zwrócić wartości.

Wywołaj element menu

Następnie podaj metodę InvokeMenuItem() , która odwzorowuje pozycje liczb całkowitych w tablicy elementów zwracanych przez GetMenuItems() na akcje. Aby uzyskać tablicę elementów w przykładzie tardis, użyj:

   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;
        }
    }

Ta metoda może zwrócić dowolnego członka wyliczeniabuiltinAction, aby poinformować system o wykonaniu tej akcji (lub element NO_ACTION, jeśli chcesz, aby system nic nie robił). To jest miejsce, w którym możesz zapewnić dodatkową funkcjonalność odzyskiwania wykraczającą poza to, co jest w systemie: dodaj dla niej element w swoim menu, wykonaj go tutaj, gdy zostanie wywołany ten element menu i zwróć NO_ACTION, aby system nie zrobił nic więcej.

WbudowanaAkcja zawiera następujące wartości:

  • BEZ AKCJI . Nic nie robić.
  • PONOWNE URUCHOMIENIE . Wyjdź z odzyskiwania i normalnie uruchom ponownie urządzenie.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD . Zainstaluj pakiet aktualizacji z różnych miejsc. Aby uzyskać szczegółowe informacje, zobacz Ładowanie boczne .
  • WYMAZAĆ PAMIĘĆ PODRĘCZNĄ . Sformatuj ponownie tylko partycję pamięci podręcznej. Potwierdzenie nie jest wymagane, ponieważ jest to stosunkowo nieszkodliwe.
  • WYCZYŚĆ_DANE . Sformatuj ponownie partycje danych użytkownika i pamięci podręcznej, zwane także przywróceniem danych fabrycznych. Przed kontynuowaniem użytkownik proszony jest o potwierdzenie tej czynności.

Ostatnia metoda, WipeData() , jest opcjonalna i jest wywoływana za każdym razem, gdy inicjowana jest operacja czyszczenia danych (albo podczas odzyskiwania za pomocą menu, albo gdy użytkownik zdecydował się na przywrócenie danych fabrycznych z głównego systemu). Ta metoda jest wywoływana przed wyczyszczeniem danych użytkownika i partycji pamięci podręcznej. Jeśli Twoje urządzenie przechowuje dane użytkownika w innym miejscu niż te dwie partycje, powinieneś je tutaj usunąć. Powinieneś zwrócić 0, aby wskazać sukces i inną wartość w przypadku niepowodzenia, chociaż obecnie wartość zwracana jest ignorowana. Dane użytkownika i partycje pamięci podręcznej są czyszczone niezależnie od powodzenia lub niepowodzenia.

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

Zrób urządzenie

Na koniec dołącz szablon na końcu pliku recovery_ui.cpp dla funkcji make_device() , która tworzy i zwraca instancję klasy Device:

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

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

Po ukończeniu pliku recovery_ui.cpp skompiluj go i połącz z odzyskiwaniem na swoim urządzeniu. W Android.mk utwórz bibliotekę statyczną zawierającą tylko ten plik C++:

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)

Następnie w konfiguracji płytki dla tego urządzenia określ bibliotekę statyczną jako wartość TARGET_RECOVERY_UI_LIB.

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

# device-specific extensions to the recovery UI
TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis

Obrazy interfejsu odzyskiwania

Interfejs użytkownika odzyskiwania składa się z obrazów. W idealnym przypadku użytkownicy nigdy nie wchodzą w interakcję z interfejsem użytkownika: podczas normalnej aktualizacji telefon uruchamia się w trybie odzyskiwania, wypełnia pasek postępu instalacji i ponownie uruchamia się do nowego systemu bez interwencji użytkownika. W przypadku problemu z aktualizacją systemu jedyną czynnością, jaką może podjąć użytkownik, jest skontaktowanie się z obsługą klienta.

Interfejs zawierający wyłącznie obrazy eliminuje potrzebę lokalizacji. Jednak od wersji Androida 5.0 aktualizacja może wyświetlać ciąg tekstowy (np. „Instalowanie aktualizacji systemu…”) wraz z obrazem. Aby uzyskać szczegółowe informacje, zobacz Zlokalizowany tekst odzyskiwania .

Android 5.0 i nowsze wersje

Interfejs odzyskiwania systemu Android 5.0 i nowszych wersji wykorzystuje dwa główne obrazy: obraz błędu i animację instalacji .

obraz wyświetlany podczas błędu ota

Rysunek 1. icon_error.png

obraz wyświetlany podczas instalacji OTA

Rysunek 2. icon_installing.png

Animacja instalacji jest reprezentowana jako pojedynczy obraz PNG z różnymi klatkami animacji przeplatanymi wierszami (dlatego Rysunek 2 wydaje się spłaszczony). Na przykład w przypadku animacji składającej się z siedmiu klatek 200 x 200 utwórz pojedynczy obraz o wymiarach 200 x 1400, w którym pierwsza klatka zawiera wiersze 0, 7, 14, 21, ...; druga ramka to wiersze 1, 8, 15, 22, ...; itp. Połączony obraz zawiera fragment tekstu wskazujący liczbę klatek animacji i liczbę klatek na sekundę (FPS). Narzędzie bootable/recovery/interlace-frames.py pobiera zestaw ramek wejściowych i łączy je w niezbędny obraz złożony używany podczas odzyskiwania.

Domyślne obrazy są dostępne w różnych gęstościach i znajdują się w bootable/recovery/res-$DENSITY/images (np. bootable/recovery/res-hdpi/images ). Aby podczas instalacji użyć obrazu statycznego, wystarczy podać obraz icon_installing.png i ustawić liczbę klatek animacji na 0 (ikona błędu nie jest animowana; jest to zawsze obraz statyczny).

Android 4.x i starsze

Interfejs odzyskiwania systemu Android 4.x i wcześniejszych wersji wykorzystuje obraz błędu (pokazany powyżej) i animację instalacji oraz kilka obrazów nakładek:

obraz wyświetlany podczas instalacji OTA

Rysunek 3. icon_installing.png

obraz pokazany jako pierwsza nakładka

Rysunek 4. icon-installing_overlay01.png

obraz pokazany jako siódma nakładka

Rysunek 5. icon_installing_overlay07.png

Podczas instalacji zawartość ekranu jest tworzona poprzez narysowanie obrazu icon_installing.png, a następnie narysowanie na nim jednej z nakładek z odpowiednim przesunięciem. Tutaj nałożone jest czerwone pole, aby podkreślić miejsce umieszczenia nakładki na obrazie podstawowym:

złożony obraz instalacji i pierwszej nakładki

Rysunek 6. Instalowanie klatki animacji 1 (icon_installing.png + icon_installing_overlay01.png)

złożony obraz instalacji i siódmej nakładki

Rysunek 7. Instalowanie ramki animacji 7 (icon_installing.png + icon_installing_overlay07.png)

Kolejne klatki są wyświetlane poprzez rysowanie tylko kolejnego obrazu nakładki na już istniejącym obrazie; obraz bazowy nie jest przerysowywany.

Liczba klatek animacji, żądana prędkość oraz przesunięcia x i y nakładki względem podstawy są ustawiane przez zmienne składowe klasy ScreenRecoveryUI. Jeśli używasz obrazów niestandardowych zamiast obrazów domyślnych, zastąp metodę Init() w swojej podklasie, aby zmienić te wartości dla obrazów niestandardowych (więcej informacji można znaleźć w artykule ScreenRecoveryUI ). Skrypt bootable/recovery/make-overlay.py może pomóc w konwersji zestawu ramek obrazu do postaci „obraz podstawowy + obrazy nakładki” potrzebnej do odzyskiwania, łącznie z obliczeniem niezbędnych przesunięć.

Domyślne obrazy znajdują się w bootable/recovery/res/images . Aby podczas instalacji użyć obrazu statycznego, wystarczy podać obraz icon_installing.png i ustawić liczbę klatek animacji na 0 (ikona błędu nie jest animowana; jest to zawsze obraz statyczny).

Zlokalizowany tekst odzyskiwania

Android 5.x wyświetla ciąg tekstowy (np. „Instalowanie aktualizacji systemu…”) wraz z obrazem. Kiedy główny system uruchamia się w trybie odzyskiwania, przekazuje bieżące ustawienia regionalne użytkownika jako opcję wiersza poleceń do odzyskiwania. W przypadku każdej wiadomości do wyświetlenia odzyskiwanie obejmuje drugi obraz złożony ze wstępnie wyrenderowanymi ciągami tekstowymi dla tej wiadomości w poszczególnych ustawieniach regionalnych.

Przykładowy obraz ciągów tekstowych odzyskiwania:

obraz tekstu odzyskiwania

Rysunek 8. Zlokalizowany tekst komunikatów naprawczych

Tekst odzyskiwania może wyświetlać następujące komunikaty:

  • Instaluję aktualizację systemu...
  • Błąd!
  • Kasowanie... (podczas czyszczenia danych/resetowania do ustawień fabrycznych)
  • Brak polecenia (gdy użytkownik ręcznie uruchamia proces odzyskiwania)

Aplikacja na Androida w bootable/recovery/tools/recovery_l10n/ renderuje lokalizację wiadomości i tworzy obraz złożony. Szczegółowe informacje na temat korzystania z tej aplikacji można znaleźć w komentarzach w bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java .

Gdy użytkownik ręcznie uruchomi odzyskiwanie, ustawienia regionalne mogą nie być dostępne i nie będzie wyświetlany żaden tekst. Nie twórz wiadomości tekstowych krytycznych dla procesu odzyskiwania.

Uwaga: Ukryty interfejs wyświetlający komunikaty dziennika i pozwalający użytkownikowi wybierać akcje z menu jest dostępny tylko w języku angielskim.

Paski postępu

Paski postępu mogą pojawić się pod głównym obrazem (lub animacją). Pasek postępu powstaje poprzez połączenie dwóch obrazów wejściowych, które muszą mieć ten sam rozmiar:

pusty pasek postępu

Rysunek 9. postęp_empty.png

pełny pasek postępu

Rysunek 10. Progress_fill.png

Lewy koniec obrazu wypełniającego jest wyświetlany obok prawego końca pustego obrazu, tworząc pasek postępu. Położenie granicy między dwoma obrazami zmienia się, aby wskazać postęp. Na przykład w przypadku powyższych par obrazów wejściowych wyświetl:

pasek postępu na poziomie 1%

Rysunek 11. Pasek postępu przy 1%>

pasek postępu na poziomie 10%

Rysunek 12. Pasek postępu na poziomie 10%

pasek postępu na poziomie 50%

Rysunek 13. Pasek postępu na poziomie 50%

Możesz udostępnić wersje tych obrazów specyficzne dla urządzenia, umieszczając je (w tym przykładzie) w device/yoyodyne/tardis/recovery/res/images . Nazwy plików muszą odpowiadać nazwom wymienionym powyżej; gdy plik zostanie znaleziony w tym katalogu, system kompilacji użyje go zamiast odpowiedniego obrazu domyślnego. Obsługiwane są tylko pliki PNG w formacie RGB lub RGBA z 8-bitową głębią kolorów.

Uwaga: w systemie Android 5.x, jeśli ustawienia regionalne można odzyskać i jest to język pisany od prawej do lewej (RTL) (arabski, hebrajski itp.), pasek postępu wypełnia się od prawej do lewej.

Urządzenia bez ekranów

Nie wszystkie urządzenia z Androidem mają ekrany. Jeśli Twoje urządzenie jest urządzeniem bezgłowym lub ma interfejs tylko audio, może być konieczne bardziej zaawansowane dostosowanie interfejsu odzyskiwania. Zamiast tworzyć podklasę ScreenRecoveryUI, bezpośrednio podklasuj jej klasę nadrzędną RecoveryUI.

RecoveryUI zawiera metody obsługi operacji interfejsu użytkownika niższego poziomu, takie jak „przełączanie wyświetlania”, „aktualizowanie paska postępu”, „pokazywanie menu”, „zmiana wyboru menu” itp. Możesz je zastąpić, aby zapewnić odpowiedni interfejs dla Twojego urządzenia. Być może Twoje urządzenie ma diody LED, w których możesz używać różnych kolorów lub wzorów migania, aby wskazać stan, a może możesz odtwarzać dźwięk. (Być może nie chcesz w ogóle obsługiwać menu lub trybu „wyświetlania tekstu”; możesz uniemożliwić dostęp do nich za pomocą implementacji CheckKey() i HandleMenuKey() , które nigdy nie włączają wyświetlania ani nie wybierają elementu menu. W tym przypadku , wiele metod RecoveryUI, które musisz podać, może być po prostu pustymi kodami pośredniczącymi.)

Zobacz bootable/recovery/ui.h aby zapoznać się z deklaracją RecoveryUI, aby zobaczyć, jakie metody musisz obsługiwać. RecoveryUI jest abstrakcyjny — niektóre metody są czysto wirtualne i muszą być udostępniane przez podklasy — ale zawiera kod przetwarzający kluczowe dane wejściowe. Możesz to również zastąpić, jeśli Twoje urządzenie nie ma kluczy lub chcesz je przetwarzać inaczej.

Aktualizator

Podczas instalacji pakietu aktualizacji możesz użyć kodu specyficznego dla urządzenia, udostępniając własne funkcje rozszerzeń, które można wywołać z poziomu skryptu aktualizującego. Oto przykładowa funkcja urządzenia tardis:

device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"

Każda funkcja rozszerzająca ma ten sam podpis. Argumenty to nazwa, pod jaką funkcja została wywołana, plik cookie State* , liczba przychodzących argumentów i tablica wskaźników Expr* reprezentujących argumenty. Zwracaną wartością jest nowo przydzielona Value* .

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);
    }

Twoje argumenty nie zostały ocenione w momencie wywołania Twojej funkcji — logika Twojej funkcji określa, które z nich zostaną ocenione i ile razy. Dzięki temu możesz używać funkcji rozszerzeń do implementowania własnych struktur kontrolnych. Call Evaluate() , aby ocenić argument Expr* i zwrócić Value* . Jeśli Evaluate() zwróci NULL, powinieneś zwolnić wszystkie trzymane zasoby i natychmiast zwrócić NULL (spowoduje to przerwanie propagacji w górę stosu edify). W przeciwnym razie przejmujesz na własność zwróconą wartość i jesteś odpowiedzialny za ostateczne wywołanie na niej metody FreeValue() .

Załóżmy, że funkcja potrzebuje dwóch argumentów: klucza o wartości łańcuchowej i obrazu o wartości blob. Można przeczytać takie argumenty:

   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;
    }

Sprawdzanie wartości NULL i zwalnianie wcześniej ocenionych argumentów może być uciążliwe w przypadku wielu argumentów. Funkcja ReadValueArgs() może to ułatwić. Zamiast powyższego kodu mogłeś napisać to:

   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() nie sprawdza typu, więc musisz to zrobić tutaj; wygodniej jest to zrobić za pomocą jednej instrukcji if , kosztem wygenerowania nieco mniej szczegółowego komunikatu o błędzie w przypadku niepowodzenia. Jednak ReadValueArgs() obsługuje ocenę każdego argumentu i zwalnianie wszystkich wcześniej ocenionych argumentów (a także ustawianie przydatnego komunikatu o błędzie), jeśli którakolwiek ocena zakończy się niepowodzeniem. Możesz użyć wygodnej funkcji ReadValueVarArgs() do oceny zmiennej liczby argumentów (zwraca tablicę Value* ).

Po ocenieniu argumentów wykonaj pracę funkcji:

   // 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 ...

Zwracana wartość musi być obiektem Value* ; własność tego obiektu przejdzie na osobę dzwoniącą. Osoba wywołująca przejmuje na własność wszelkie dane wskazane przez tę Value* — w szczególności element danych.

W tym przypadku chcesz zwrócić wartość prawdziwą lub fałszywą, aby wskazać sukces. Zapamiętaj konwencję, że pusty ciąg znaków jest fałszywy , a wszystkie inne ciągi są prawdziwe . Aby zwrócić obiekt Value, musisz mallocować go z kopią stałego łańcucha z malloc, ponieważ osoba wywołująca free() oba. Nie zapomnij wywołać FreeValue() na obiektach, które otrzymałeś, oceniając swoje argumenty!

   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;
}

Wygodna funkcja StringValue() zawija ciąg znaków w nowy obiekt Value. Użyj, aby napisać powyższy kod bardziej zwięźle:

   FreeValue(key);
    FreeValue(image);

    return StringValue(strdup(successful ? "t" : ""));
}

Aby podłączyć funkcje do interpretera edify, podaj funkcję Register_ foo gdzie foo jest nazwą biblioteki statycznej zawierającej ten kod. Wywołaj RegisterFunction() , aby zarejestrować każdą funkcję rozszerzenia. Zgodnie z konwencją nazwij funkcje specyficzne dla urządzenia urządzenie device . whatever , aby uniknąć konfliktów z dodanymi w przyszłości wbudowanymi funkcjami.

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

Możesz teraz skonfigurować plik makefile, aby zbudować bibliotekę statyczną przy użyciu swojego kodu. (Jest to ten sam plik Makefile, który został użyty do dostosowania interfejsu odzyskiwania w poprzedniej sekcji; Twoje urządzenie może mieć tutaj zdefiniowane obie biblioteki statyczne).

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

Nazwa biblioteki statycznej musi odpowiadać nazwie zawartej w niej funkcji Register_ libname .

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Na koniec skonfiguruj kompilację odzyskiwania, aby pobrać bibliotekę. Dodaj swoją bibliotekę do TARGET_RECOVERY_UPDATER_LIBS (który może zawierać wiele bibliotek; wszystkie zostaną zarejestrowane). Jeśli Twój kod zależy od innych bibliotek statycznych, które same w sobie nie są rozszerzeniami edify (tj. nie mają funkcji Register_ libname ), możesz umieścić je w TARGET_RECOVERY_UPDATER_EXTRA_LIBS, aby połączyć je z aktualizatorem bez wywoływania ich (nieistniejącej) funkcji rejestracyjnej. Na przykład, jeśli Twój kod specyficzny dla urządzenia chciał użyć zlib do dekompresji danych, powinieneś dołączyć tutaj libz.

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 +=

Skrypty aktualizacyjne w Twoim pakiecie OTA mogą teraz wywoływać Twoją funkcję jak każdą inną. Aby przeprogramować urządzenie tardis, skrypt aktualizacji może zawierać: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . Wykorzystuje to jednoargumentową wersję wbudowanej funkcji package_extract_file() , która zwraca zawartość pliku wyodrębnionego z pakietu aktualizacji w postaci obiektu BLOB w celu wygenerowania drugiego argumentu nowej funkcji rozszerzenia.

Generowanie pakietów OTA

Ostatnim elementem jest uzyskanie wiedzy narzędzi do generowania pakietów OTA o danych specyficznych dla Twojego urządzenia i wyemitowanie skryptów aktualizacyjnych, które zawierają wywołania funkcji Twojego rozszerzenia.

Najpierw poproś system kompilacji o informację o obiekcie blob danych specyficznym dla urządzenia. Zakładając, że plik danych znajduje się w device/yoyodyne/tardis/tardis.dat , zadeklaruj następujące informacje w pliku AndroidBoard.mk swojego urządzenia:

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

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

Zamiast tego możesz również umieścić go w pliku Android.mk, ale wtedy musi on być chroniony przez kontrolę urządzenia, ponieważ wszystkie pliki Android.mk w drzewie są ładowane bez względu na to, jakie urządzenie jest budowane. (Jeśli twoje drzewo zawiera wiele urządzeń, podczas tworzenia urządzenia tardis chcesz dodać tylko plik tardis.dat.)

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

# an alternative to specifying it in AndroidBoard.mk
ifeq (($TARGET_DEVICE),tardis)
  $(call add-radio-file,tardis.dat)
endif

Ze względów historycznych nazywa się je plikami radiowymi; mogą nie mieć nic wspólnego z radiem urządzenia (jeśli jest obecne). Są to po prostu nieprzezroczyste plamy danych, które system kompilacji kopiuje do plików docelowych .zip używanych przez narzędzia do generowania OTA. Kiedy wykonujesz kompilację, tardis.dat jest przechowywany w pliku target-files.zip jako RADIO/tardis.dat . Możesz wywołać funkcję add-radio-file wiele razy, aby dodać dowolną liczbę plików.

Moduł Pythona

Aby rozszerzyć narzędzia do wydania, napisz moduł Pythona (musi mieć nazwę releasetools.py), do którego narzędzia będą mogły się odwoływać, jeśli są obecne. Przykład:

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"));""")

Osobna funkcja obsługuje przypadek generowania przyrostowego pakietu OTA. W tym przykładzie załóżmy, że musisz przeprogramować tardis tylko wtedy, gdy plik tardis.dat został zmieniony między dwiema kompilacjami.

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"));""")

Funkcje modułu

W module możesz udostępnić następujące funkcje (zaimplementuj tylko te, których potrzebujesz).

FullOTA_Assertions()
Wywoływany w pobliżu początku generowania pełnego OTA. Jest to dobre miejsce na emitowanie asercji na temat bieżącego stanu urządzenia. Nie emituj poleceń skryptowych, które wprowadzają zmiany w urządzeniu.
FullOTA_InstallBegin()
Wywoływana po tym, jak wszystkie potwierdzenia dotyczące stanu urządzenia przeszły, ale przed wprowadzeniem jakichkolwiek zmian. Możesz emitować polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać uruchomione, zanim cokolwiek innego na urządzeniu zostanie zmienione.
FullOTA_InstallEnd()
Wywoływane na końcu generowania skryptu, po wydaniu poleceń skryptu aktualizacji partycji rozruchowej i systemowej. Możesz także emitować dodatkowe polecenia dotyczące aktualizacji specyficznych dla urządzenia.
IncrementalOTA_Assertions()
Podobna do FullOTA_Assertions() ale wywoływana podczas generowania pakietu aktualizacji przyrostowej.
IncrementalOTA_VerifyBegin()
Wywoływana po tym, jak przeszły wszystkie potwierdzenia dotyczące stanu urządzenia, ale przed wprowadzeniem jakichkolwiek zmian. Możesz emitować polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać uruchomione, zanim cokolwiek innego na urządzeniu zostanie zmienione.
IncrementalOTA_VerifyEnd()
Wywoływane na koniec fazy weryfikacji, kiedy skrypt zakończył potwierdzanie, że pliki, które będzie dotykał, mają oczekiwaną zawartość początkową. W tym momencie nic w urządzeniu nie zostało zmienione. Możesz także emitować kod w celu dodatkowej weryfikacji specyficznej dla urządzenia.
IncrementalOTA_InstallBegin()
Wywoływane po sprawdzeniu, czy pliki przeznaczone do załatania mają oczekiwany stan przed , ale przed wprowadzeniem jakichkolwiek zmian. Możesz emitować polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać uruchomione, zanim cokolwiek innego na urządzeniu zostanie zmienione.
IncrementalOTA_InstallEnd()
Podobnie jak w przypadku pełnego pakietu OTA, jest to wywoływane na końcu generowania skryptu, po wyemitowaniu poleceń skryptu w celu aktualizacji partycji rozruchowej i systemowej. Możesz także emitować dodatkowe polecenia dotyczące aktualizacji specyficznych dla urządzenia.

Uwaga: Jeśli urządzenie straci zasilanie, instalacja OTA może rozpocząć się od początku. Bądź przygotowany na radzenie sobie z urządzeniami, na których te polecenia zostały już uruchomione, w całości lub częściowo.

Przekazuj funkcje do obiektów informacyjnych

Przekaż funkcje do pojedynczego obiektu informacyjnego, który zawiera różne przydatne elementy:

  • info.input_zip . (Tylko pełne OTA) Obiekt zipfile.ZipFile dla wejściowych plików docelowych .zip.
  • info.source_zip . (Tylko przyrostowe OTA) Obiekt zipfile.ZipFile dla źródłowych plików docelowych .zip (kompilacja już na urządzeniu podczas instalowania pakietu przyrostowego).
  • info.target_zip . (Tylko przyrostowe OTA) Obiekt zipfile.ZipFile dla docelowych plików docelowych .zip (kompilacja, którą pakiet przyrostowy umieszcza na urządzeniu).
  • info.output_zip . Tworzony jest pakiet; obiekt zipfile.ZipFile otwarty do zapisu. Użyj common.ZipWriteStr(info.output_zip, filename , data ), aby dodać plik do pakietu.
  • info.skrypt . Obiekt skryptowy, do którego można dołączać polecenia. Wywołaj info.script.AppendExtra( script_text ) , aby wyprowadzić tekst do skryptu. Upewnij się, że tekst wyjściowy kończy się średnikiem, aby nie pojawiał się w poleceniach emitowanych później.

Szczegółowe informacje na temat obiektu info można znaleźć w dokumentacji Python Software Foundation dotyczącej archiwów ZIP .

Określ lokalizację modułu

Określ lokalizację skryptu releasetools.py swojego urządzenia w pliku BoardConfig.mk:

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

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Jeśli TARGET_RELEASETOOLS_EXTENSIONS nie jest ustawione, domyślnie jest to katalog $(TARGET_DEVICE_DIR)/../common (w tym przykładzie device/yoyodyne/common ). Najlepiej jest jawnie zdefiniować lokalizację skryptu releasetools.py. Podczas budowania urządzenia tardis skrypt releasetools.py jest dołączany do pliku .zip plików docelowych ( META/releasetools.py ).

Po uruchomieniu narzędzi do wydania ( img_from_target_files lub ota_from_target_files ) skrypt releasetools.py w pliku .zip target-files, jeśli jest obecny, jest preferowany zamiast skryptu z drzewa źródłowego Androida. Możesz także jawnie określić ścieżkę do rozszerzeń specyficznych dla urządzenia za pomocą opcji -s (lub --device_specific ), która ma najwyższy priorytet. Dzięki temu możesz poprawiać błędy i wprowadzać zmiany w rozszerzeniach releasetools oraz stosować te zmiany do starych plików docelowych.

Teraz, gdy uruchomisz ota_from_target_files , automatycznie pobierze on moduł specyficzny dla urządzenia z pliku .zip target_files i użyje go podczas generowania pakietów 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

Alternatywnie możesz określić rozszerzenia specyficzne dla urządzenia, uruchamiając 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

Uwaga: pełną listę opcji można znaleźć w komentarzach ota_from_target_files w build/make/tools/releasetools/ota_from_target_files .

Mechanizm bocznego ładowania

Odzyskiwanie posiada mechanizm bocznego ładowania , umożliwiający ręczne instalowanie pakietu aktualizacji bez pobierania go drogą bezprzewodową przez system główny. Ładowanie boczne jest przydatne do debugowania lub wprowadzania zmian na urządzeniach, na których nie można uruchomić głównego systemu.

Historycznie rzecz biorąc, ładowanie boczne odbywało się poprzez ładowanie pakietów z karty SD urządzenia; w przypadku urządzenia, które nie uruchamia się, pakiet można przenieść na kartę SD za pomocą innego komputera, a następnie włożyć kartę SD do urządzenia. Aby uwzględnić urządzenia z systemem Android bez wymiennej pamięci zewnętrznej, odzyskiwanie obsługuje dwa dodatkowe mechanizmy ładowania bocznego: ładowanie pakietów z partycji pamięci podręcznej i ładowanie ich przez USB za pomocą adb.

Aby wywołać każdy mechanizm sideload, metoda Device::InvokeMenuItem() urządzenia może zwrócić następujące wartości wbudowanej akcji:

  • ZASTOSUJ_EXT . Sideload pakiet aktualizacji z pamięci zewnętrznej (katalog /sdcard ). Twój odzyskiwanie.fstab musi zdefiniować punkt montażu /sdcard . Nie jest to użyteczne na urządzeniach, które naśladują kartę SD z symbolią do /data (lub podobny mechanizm). /data zazwyczaj nie są dostępne do odzyskiwania, ponieważ mogą być szyfrowane. Interfejs użytkownika Recovery wyświetla menu plików .zip w /sdcard i pozwala użytkownikowi wybrać jeden.
  • Apply_Cache . Podobne do załadowania pakietu z /sdcard z tym wyjątkiem, że zamiast tego używany jest katalog /cache (który jest zawsze dostępny do odzyskiwania). Z zwykłego systemu /cache jest zapisana tylko przez uprzywilejowanych użytkowników, a jeśli urządzenie nie jest rozruchowe, nie można w ogóle zapisać katalogu /cache (co czyni ten mechanizm ograniczonej użyteczności).
  • Apply_Adb_sidEload . Pozwala użytkownikowi wysłać pakiet do urządzenia za pomocą kabla USB i narzędzia do tworzenia ADB. Po wywołaniu tego mechanizmu Recovery uruchamia własną mini wersję demona ADBD, aby pozwolić ADB na połączonym komputerze hosta rozmowy z nim. Ta mini wersja obsługuje tylko jedno polecenie: adb sideload filename . Nazwany plik jest wysyłany z urządzenia hosta do urządzenia, który następnie weryfikuje i instaluje go tak, jakby był w lokalnej pamięci.

Kilka zastrzeżeń:

  • Obsługiwany jest tylko transport USB.
  • Jeśli odzyskiwanie działa normalnie (zwykle w przypadku kompilacji Userdebug i ENG), zostanie to zamknięte, gdy urządzenie jest w trybie obciążenia bocznego ADB i zostanie ponownie uruchomione, gdy ADB Bigeload zakończy się otrzymaniem pakietu. Podczas gdy w trybie obciążenia bocznego ADB nie ma poleceń ADB innych niż praca sideload ( logcat , reboot , push , pull , shell itp. Wszystkie zawodzą).
  • Nie można wyjść z trybu obciążenia bocznego ADB na urządzeniu. Aby przerywać, możesz wysłać /dev/null (lub cokolwiek innego, co nie jest prawidłowym pakietem) jako pakiet, a następnie urządzenie nie weryfikuje i zatrzyma procedury instalacji. Metoda CheckKey() wdrożenia RecoveryUI będzie nadal wywoływana dla naciśnięć kluczowych, dzięki czemu możesz dostarczyć kluczową sekwencję, która ponownie uruchamia urządzenie i działa w trybie obciążenia bocznego ADB.