Kod na urządzeniu

System odzyskiwania zawiera kilka haka na potrzeby wstawiania kodu związanego z danym urządzeniem, aby aktualizacje OTA mogły również aktualizować części urządzenia inne niż system Android (np. procesor radia lub procesor pasma podstawowego).

W następnych sekcjach i przykładach omawiamy dostosowywanie urządzenia tardis wyprodukowanego przez dostawcę yoyodyne.

Mapa partycji

Od wersji 2.3 Android obsługuje urządzenia flash eMMC oraz system plików ext4, który działa na tych urządzeniach. Obsługuje też urządzenia flash z pamięcią MTD oraz system plików yaffs2 ze starszych wersji.

Plik mapy partycji jest określony przez TARGET_RECOVERY_FSTAB; jest on używany zarówno przez binarny plik binarne narzędzia do odzyskiwania, jak i narzędzia do tworzenia pakietów. Nazwę pliku mapy można określić w polu TARGET_RECOVERY_FSTAB w pliku BoardConfig.mk.

Przykładowy plik mapy partycji może wyglądać tak:

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óre jest opcjonalne, wszystkie punkty zamontowania w tym przykładzie muszą być zdefiniowane (urządzenia mogą też dodawać dodatkowe partycje). Obsługiwane są 5 typów systemów plików:

yaffs2
System plików yaffs2 na urządzeniu flash MTD. „device” musi być nazwą partycji MTD i musi się pojawiać w /proc/mtd.
mtd
Surowa partycja MTD, używana do partycji rozruchowych, takich jak partycja rozruchowa i partycja odzyskiwania. MTD nie jest faktycznie zamontowany, ale punkt zamontowania jest używany jako klucz do zlokalizowania partycji. „device” musi być nazwą partycji MTD w /proc/mtd.
ext4
System plików ext4 na urządzeniu flash eMMC. „device” musi być ścieżką do urządzenia blokady.
emmc
Nieprzetworzone urządzenie blokowe eMMC, używane do partycji rozruchowych, takich jak partycja rozruchowa i partycja odzyskiwania. Podobnie jak w przypadku typu mtd, eMMC nigdy nie jest faktycznie montowane, ale ciąg znaków punktu zamontowania jest używany do znajdowania urządzenia w tabeli.
vfat
System plików FAT na urządzeniu blokowym, zwykle w przypadku pamięci zewnętrznej, takiej jak karta SD. Urządzenie to urządzenie blokowe; urządzenie2 to drugie urządzenie blokowe, które system próbuje zamontować, jeśli zamontowanie urządzenia podstawowego się nie powiedzie (dla zgodności z kartami SD, które mogą, ale nie muszą być sformatowane za pomocą tabeli partycji).

Wszystkie partycje muszą być zamontowane w katalogu głównym (czyli wartość punktu montowania musi zaczynać się od ukośnika i nie może zawierać żadnych innych ukośników). To ograniczenie dotyczy tylko montowania systemów plików podczas przywracania. Główny system może je montować w dowolnym miejscu. Katalogi /boot, /recovery/misc powinny być typu raw (mtd lub emmc), a katalogi /system, /data, /cache/sdcard (jeśli są dostępne) powinny być typu filesystem (yaffs2, ext4 lub vfat).

Od Androida 3.0 plik recovery.fstab zawiera dodatkowe opcjonalne pole options. Obecnie jedyną zdefiniowaną opcją jest length , która umożliwia jawne określenie długości partycji. Ta długość jest używana podczas ponownego formatowania partycji (np. partycji danych użytkownika podczas kasowania danych lub przywracania ustawień fabrycznych albo partycji systemowej podczas instalacji pełnego pakietu OTA). Jeśli wartość długości jest ujemna, rozmiar do sformatowania jest określany przez dodanie wartości długości do rzeczywistego rozmiaru partycji. Na przykład ustawienie „length=-16384” oznacza, że ostatnie 16 KB tej partycji nie zostanie zastąpione podczas jej ponownego formatowania. Umożliwia to korzystanie z takich funkcji jak szyfrowanie partycji userdata (gdzie metadane szyfrowania są przechowywane na końcu partycji i nie powinny być nadpisywane).

Uwaga: pola device2 i options są opcjonalne, co powoduje niejednoznaczność podczas analizowania. Jeśli wpis w czwartym polu wiersza zaczyna się od znaku „/”, jest on uznawany za wpis device2. Jeśli nie zaczyna się od znaku „/”, jest uznawany za pole options.

Animacja uruchamiania

Producenci urządzeń mogą dostosowywać animację wyświetlaną podczas uruchamiania urządzenia z Androidem. Aby to zrobić, utwórz plik .zip zorganizowany i zlokalizowany zgodnie ze specyfikacją bootanimation.

W przypadku urządzeń Android Things możesz przesłać skompresowany plik na konsolę Android Things, aby obrazy były dostępne w wybranym produkcie.

Uwaga: te obrazy muszą być zgodne ze wskazówkami dotyczącymi marki Androida. Wskazówki dotyczące marki znajdziesz w sekcji Android w Partner Marketing Hub.

Interfejs użytkownika do przywracania

obsługa urządzeń z różnym sprzętem (przyciski fizyczne, diody LED, ekrany itp.); możesz dostosować interfejs odzyskiwania, aby wyświetlać stan i dostęp do ukrytych funkcji obsługiwanych ręcznie w przypadku każdego urządzenia.

Twoim celem jest skompilowanie małej biblioteki statycznej z kilkoma obiektami C++, która umożliwi korzystanie z funkcji związanych z danym urządzeniem. Plik bootable/recovery/default_device.cpp jest używany domyślnie i stanowi dobry punkt wyjścia do skopiowania wersji tego pliku na urządzenie.

Uwaga: możesz zobaczyć komunikat Brak polecenia. Aby przełączyć tekst, przytrzymaj przycisk zasilania i naciśnij przycisk zwiększania głośności. Jeśli urządzenie nie ma obu przycisków, naciśnij dowolny przycisk i przytrzymaj, 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 są widoczne w ukrytym menu odzyskiwania. Nagłówki opisują sposób korzystania z menu (np. elementy sterujące służące do zmiany lub wybrania zaznaczonego 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 wiersze są obcinane (a nie przenoszone), więc pamiętaj o szerokości ekranu urządzenia.

Dostosowywanie klucza weryfikacyjnego

Następnie zdefiniuj implementację interfejsu RecoveryUI na urządzeniu. W tym przykładzie zakładamy, że urządzenie tardis ma ekran, więc możesz odziedziczyć wbudowaną implementację ScreenRecoveryUI (patrz instrukcje dotyczące urządzeń bez ekranu). Jedyną funkcją do dostosowania w interfejsie ScreenRecoveryUI jest CheckKey(), która odpowiada za wstępną asynchroniczną obsługę klucza:

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

Stałe kluczowe

Stałe KEY_* są zdefiniowane w pliku linux/input.h. CheckKey() jest wywoływany niezależnie od tego, co dzieje się w pozostałych częściach funkcji odzyskiwania: gdy menu jest wyłączone, gdy jest włączone, podczas instalacji pakietu, podczas kasowania danych użytkownika itp. Może zwracać jedną z 4 konstant:

  • PRZEKAZUJ. Włączanie i wyłączanie wyświetlania menu lub dziennika tekstowego
  • Uruchom ponownie. Natychmiast uruchom ponownie urządzenie.
  • IGNORUJ. Ignoruj to naciśnięcie klawisza
  • Wstaw do kolejki. Dodanie tego naciśnięcia klawisza do kolejki, aby było ono używane synchronicznie (np.przez system menu odzyskiwania, jeśli wyświetlacz jest włączony).

CheckKey() jest wywoływany za każdym razem, gdy zdarzenie key-down jest poprzedzone zdarzeniem key-up dla tego samego klucza. (sekwencja zdarzeń A-down B-down B-up A-up powoduje tylko wywołanie funkcji CheckKey(B)). CheckKey() może wywołać funkcję IsKeyPressed(), aby sprawdzić, czy inne klawisze są przytrzymane. (W powyższym ciągu kluczowych zdarzeń, gdyby funkcja CheckKey(B) wywołała funkcję IsKeyPressed(A), zwracałaby 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 skomplikowaną konfigurację: ekran można włączyć, przytrzymując przycisk zasilania i naciskając przycisk zwiększania głośności. Urządzenie można natychmiast ponownie uruchomić, naciskając przycisk zasilania 5 razy z rzędu (bez naciskania innych przycisków):

class TardisUI : public ScreenRecoveryUI {
  private:
    int consecutive_power_keys;

  public:
    TardisUI() : consecutive_power_keys(0) {}

    virtual KeyAction CheckKey(int key) {
        if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
            return TOGGLE;
        }
        if (key == KEY_POWER) {
            ++consecutive_power_keys;
            if (consecutive_power_keys >= 5) {
                return REBOOT;
            }
        } else {
            consecutive_power_keys = 0;
        }
        return ENQUEUE;
    }
};

ScreenRecoveryUI

Jeśli używasz własnych obrazów (ikony błędu, animacji instalacji, pasków postępu) w interfejsie ScreenRecoveryUI, możesz ustawić zmienną animation_fps, aby kontrolować szybkość animacji w klatkach na sekundę.

Uwaga: obecny skrypt interlace-frames.py umożliwia przechowywanie informacji animation_fps w samym obrazie. We wcześniejszych wersjach Androida trzeba było samodzielnie ustawić animation_fps.

Aby ustawić zmienną animation_fps, zastąpij funkcję ScreenRecoveryUI::Init() w podklasie. Ustaw wartość, a następnie wywołaj funkcję parent Init() , aby dokończyć inicjowanie. Wartość domyślna (20 FPS) odpowiada domyślnym obrazom odzyskiwania. Podczas korzystania z tych obrazów nie musisz podawać funkcji Init(). Szczegółowe informacje o obrazach znajdziesz w artykule Obrazy interfejsu odzyskiwania.

Klasa urządzenia

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

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

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

    RecoveryUI* GetUI() { return ui; }

StartRecovery

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

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

Dostarczanie menu odzyskiwania i zarządzanie nim

System wywołuje 2 metody, aby uzyskać listę nagłówków i listę produktów. W tej implementacji zwraca on tablice statyczne zdefiniowane u góry pliku:

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

HandleMenuKey

Następnie podaj funkcję HandleMenuKey(), która przyjmuje naciśnięcie klawisza i bieżącą widoczność menu, i decyduje, jakie działanie wykonać:

   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 przyjmuje kod klucza (który został wcześniej przetworzony i wstawiony do kolejki przez metodę CheckKey() obiektu UI) oraz bieżący stan widoczności menu lub dziennika tekstowego. Zwracana wartość jest liczbą całkowitą. Jeśli wartość jest równa 0 lub większa, jest ona traktowana jako pozycja elementu menu, który jest wywoływany natychmiast (patrz metoda InvokeMenuItem() poniżej). W przeciwnym razie może to być jedna z tych wstępnie zdefiniowanych stałych wartości:

  • kHighlightUp. Przesuwanie wyróżnienia menu do poprzedniego elementu
  • kHighlightDown. Przesuwanie wyróżnienia menu do następnego elementu
  • kInvokeItem. Wywołanie aktualnie wyróżnionego elementu
  • kNoAction. Nie rób nic podczas naciskania tego przycisku

Jak sugeruje argument visible, funkcja HandleMenuKey() jest wywoływana nawet wtedy, gdy menu jest niewidoczne. W odróżnieniu od funkcji CheckKey() nie jest wywoływana, gdy funkcja odzyskiwania wykonuje jakąś czynność, np. kasowanie danych lub instalowanie pakietu. Jest wywoływana tylko wtedy, gdy jest nieaktywna i czeka na dane wejściowe.

Mechanizmy kulkowe

Jeśli urządzenie ma mechanizm wejściowy podobny do trackballa (generuje zdarzenia wejściowe z typem EV_REL i kodem REL_Y), podczas odzyskiwania generowane są naciśnięcia klawiszy KEY_UP i KEY_DOWN, gdy urządzenie wejściowe podobne do trackballa wykrywa ruch na osi Y. Wystarczy, że zmapujesz zdarzenia KEY_UP i KEY_DOWN na działania menu. W przypadku CheckKey() nie tak się dzieje, więc nie możesz używać ruchów trackballa jako wyzwalaczy do ponownego uruchamiania lub przełączania wyświetlacza.

Klawisze modyfikujące

Aby sprawdzić, czy klawisze są przytrzymane jako modyfikatory, wywołaj metodę IsKeyPressed() własnego obiektu interfejsu użytkownika. Na przykład na niektórych urządzeniach naciśnięcie Alt+W w trybie odzyskiwania powoduje wyczyszczenie danych niezależnie od tego, czy menu było widoczne. Możesz zastosować taką implementację:

   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 visible ma wartość false, zwracanie wartości specjalnych, które manipulują menu (przesuwanie wyróżnienia, wywoływanie wyróżnionego elementu), nie ma sensu, ponieważ użytkownik nie może zobaczyć wyróżnienia. W razie potrzeby możesz jednak zwrócić wartości.

InvokeMenuItem

Następnie podaj metodę InvokeMenuItem(), która mapuje pozycje całkowite w tablicy elementów zwracanych przez funkcję GetMenuItems() na działania. W przypadku tablicy 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 zwracać dowolny element z enumeracji BuiltinAction, aby wskazać systemowi, jakie działanie ma wykonać (lub element NO_ACTION, jeśli chcesz, aby system nic nie robił). To miejsce, w którym można udostępnić dodatkowe funkcje odzyskiwania, które wykraczają poza to, co jest dostępne w systemie: dodaj pozycję menu, wykonaj ją po wywołaniu tej pozycji menu i zwróć NO_ACTION, aby system nie wykonywał żadnych innych czynności.

BuiltinAction zawiera te wartości:

  • NO_ACTION. Nic nie rób.
  • Uruchom ponownie. Wyjdź z trybu odzyskiwania i normalnie uruchom ponownie urządzenie.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Instalowanie pakietu aktualizacji z różnych miejsc. Więcej informacji znajdziesz w artykule Instalowanie aplikacji z pliku APK.
  • WIPE_CACHE. ponownie sformatować tylko partycję pamięci podręcznej. Nie wymaga to potwierdzenia, ponieważ jest to stosunkowo nieszkodliwe.
  • WIPE_DATA. Sformatuj partycje danych użytkownika i partycję pamięci podręcznej, czyli przywróć dane fabryczne. Zanim użytkownik przejedzie dalej, musi potwierdzić tę czynność.

Ostatnia metoda, WipeData(), jest opcjonalna i jest wywoływana, gdy rozpoczyna się operacja wyczyszczania danych (z menu odzyskiwania lub gdy użytkownik zdecyduje się na przywrócenie ustawień fabrycznych z głównego systemu). Ta metoda jest wywoływana przed wyczyszczeniem partycji danych użytkownika i pamięci podręcznej. Jeśli dane użytkownika są przechowywane na urządzeniu w miejscu innym niż te 2 partycje, należy je usunąć. Warto zwrócić 0, aby wskazać sukces, a inną wartość, aby wskazać niepowodzenie. Obecnie wartość zwracana jest ignorowana. Dane użytkownika i partycje pamięci podręcznej są wymazywane niezależnie od tego, czy zwrócisz wartość true czy false.

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

Marka urządzenia

Na koniec dodaj na końcu pliku recovery_ui.cpp kawałek kodu stałego 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 utworzeniu pliku recovery_ui.cpp skompiluj go i połącz z przywracaniem na urządzeniu. W pliku 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łyty dla tego urządzenia określ stałą bibliotekę 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 odzyskiwania składa się z obrazów. W idealnej sytuacji użytkownicy nigdy nie będą wchodzić w interakcję z interfejsem: Podczas zwykłej aktualizacji telefon uruchamia tryb odzyskiwania, wypełnia pasek postępu instalacji i uruchamia nowy system bez udziału użytkownika. W przypadku problemu z aktualizacją systemu jedynym działaniem, jakie może podjąć użytkownik, jest kontakt z obsługą klienta.

Interfejs składający się tylko z obrazów eliminuje potrzebę lokalizacji. Jednak od Androida 5.0 aktualizacja może wyświetlać tekst (np. „Instalowanie aktualizacji systemu…”) wraz z obrazem. Więcej informacji znajdziesz w artykule Zlokalizowany tekst odzyskiwania.

Android 5.0 lub nowszy

Interfejs odzyskiwania danych w Androidzie 5.0 i nowszych używa 2 głównych obrazów: błędu i animacji instalowania.

obraz wyświetlany podczas błędu ota

Rysunek 1. icon_error.png

obraz wyświetlany podczas instalacji OTA

Rysunek 2.

Animacja instalacji jest reprezentowana jako pojedynczy obraz PNG z różnymi klatkami animacji przeplatanymi w wierszach (dlatego na rysunku 2 obraz jest ściśnięty). Na przykład w przypadku animacji 200 x 200 pikseli składającej się z 7 ramek utwórz jeden obraz 200 x 1400, w którym pierwsza rama to wiersze 0, 7, 14, 21 itd., a druga to wiersze 1, 8, 15, 22 itd. 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 klatek wejściowych i łączy je w niezbędny obraz złożony, który jest używany przez funkcję odzyskiwania.

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

Android 4.x i starsze

W interfejsie odzyskiwania danych w systemie Android 4.x i starszych jest używany obraz błędu (powyżej) oraz animacja instalowania i kilka obrazów nakładających się na siebie:

obraz wyświetlany podczas instalacji OTA

Rysunek 3. icon_installing.png

obraz wyświetlany jako pierwszy nakład.

Rysunek 4. icon-installing_overlay01.png

obraz wyświetlany jako siódma nakładka

Rysunek 5. icon_installing_overlay07.png

Podczas instalacji wyświetlanie na ekranie jest tworzone przez narysowanie obrazu icon_installing.png, a następnie narysowanie jednego z ramek nakładek na wierzchu z odpowiednim przesunięciem. Na obrazie podstawowym nałożono czerwone pole, aby zaznaczyć, gdzie znajduje się nakładka:

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

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

złożony obraz z nakładką instalacji i siódmym

Rysunek 7. Instalowanie klatki animacji 7 (ikona_instalowania.png + ikona_instalowania_overlay07.png)

Kolejne klatki są wyświetlane przez rysowanie tylko kolejnego obrazu nakładki na to, co już jest widoczne; obraz podstawowy nie jest ponownie rysowany.

Liczba klatek w animacji, żądana prędkość oraz przesunięcie w kierunku osi X i Y w stosunku do podstawy są ustawiane przez zmienne członkowskie klasy ScreenRecoveryUI. Jeśli zamiast domyślnych obrazów używasz obrazów niestandardowych, zastąpij metodę Init() w podklasie, aby zmienić te wartości dla obrazów niestandardowych (szczegółowe informacje znajdziesz w artykule ScreenRecoveryUI). Skryptbootable/recovery/make-overlay.py może pomóc w konwertowaniu zbioru klatek obrazu na formę „obraz bazowy + obrazy nakładki”, która jest potrzebna do odzyskiwania, w tym do obliczania niezbędnych przesunięć.

Domyślne obrazy znajdują się w folderze bootable/recovery/res/images. Aby użyć obrazu statycznego podczas instalacji, wystarczy przesłać plik 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. Gdy główny system uruchamia tryb odzyskiwania, przekazuje bieżący język użytkownika jako opcję wiersza poleceń do odzyskiwania. W przypadku każdej wyświetlanej wiadomości odzyskiwanie obejmuje drugi obraz złożony z zarenderowanych ciągów tekstowych w każdej wersji językowej.

Przykładowy obraz tekstów odzyskiwania:

obraz z tekstem dotyczącym przywracania

Rysunek 8. Zlokalizowany tekst wiadomości o przywróceniu

Tekst odzyskiwania może zawierać te komunikaty:

  • Instaluję aktualizację systemu…
  • Błąd!
  • Wymazuję dane… (podczas kasowania danych lub przywracania ustawień fabrycznych)
  • Brak polecenia (gdy użytkownik ręcznie uruchamia tryb odzyskiwania)

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

Gdy użytkownik uruchamia tryb odzyskiwania ręcznie, lokalizacja może być niedostępna i nie wyświetla się żaden tekst. SMS-y nie powinny być kluczowe dla procesu odzyskiwania.

Uwaga: ukryty interfejs, który wyświetla komunikaty z dziennika i pozwala użytkownikowi wybierać działania z menu, jest dostępny tylko w języku angielskim.

Paski postępu

Paski postępu mogą pojawiać się pod głównym obrazem (lub animacją). Pasek postępu jest tworzony przez połączenie 2 obrazów wejściowych, które muszą mieć ten sam rozmiar:

pusty pasek postępu

Rysunek 9. progress_empty.png

pełny pasek postępu

Rysunek 10. progress_fill.png

Lewy koniec obrazu wypełnionego jest wyświetlany obok prawego końca obrazu pustego, aby utworzyć pasek postępu. Położenie granicy między dwoma obrazami zmienia się, aby wskazywać 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 na 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%

Wersje tych obrazów przeznaczone dla poszczególnych urządzeń możesz przesłać, umieszczając je w folderze device/yoyodyne/tardis/recovery/res/images (w tym przykładzie). Nazwy plików muszą być zgodne z wymienionymi powyżej. Jeśli w tym katalogu zostanie znaleziony plik, system kompilacji użyje go zamiast domyślnego obrazu. Obsługiwane są tylko pliki PNG w formacie RGB lub RGBA o 8-bitowej głębokości kolorów.

Uwaga: w Androidzie 5.x, jeśli lokalizacja jest znana w przywracaniu i jest językiem zapisywanym od prawej do lewej (np. 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 urządzenie jest urządzeniem bezprzewodowym lub ma interfejs tylko z dźwiękiem, może być konieczne bardziej rozbudowane dostosowanie interfejsu odzyskiwania. Zamiast tworzyć podklasę ScreenRecoveryUI, bezpośrednio utwórz podklasę klasy nadrzędnej RecoveryUI.

RecoveryUI zawiera metody obsługi operacji interfejsu na niższym poziomie, takich jak „przełączanie wyświetlacza”, „aktualizowanie paska postępu”, „wyświetlanie menu”, „zmiana wyboru menu” itp. Możesz je zastąpić, aby zapewnić odpowiedni interfejs dla urządzenia. Może Twoje urządzenie ma diody LED, których kolory lub wzory mrugania wskazują stan urządzenia. Możesz też odtwarzać dźwięk. (Może nie chcesz obsługiwać menu ani trybu „wyświetlania tekstu”; możesz zablokować dostęp do tych elementów za pomocą implementacji CheckKey()HandleMenuKey(), które nigdy nie włączają wyświetlania ani nie wybierają elementu menu). W takim przypadku wiele metod interfejsu odzyskiwania danych może być pustych.

Aby dowiedzieć się, które metody musisz obsługiwać, zapoznaj się z deklaracją interfejsu użytkownika odzyskiwania danych (bootable/recovery/ui.h). RecoveryUI jest abstrakcyjny – niektóre metody są czysto wirtualne i muszą być udostępnione przez podklasy, ale zawiera kod do przetwarzania kluczowych danych wejściowych. Możesz też zmienić tę wartość, jeśli na urządzeniu nie ma kluczy lub chcesz je przetworzyć w inny sposób.

Aktualizator

Podczas instalacji pakietu aktualizacji możesz użyć kodu specyficznego dla urządzenia, podając własne funkcje rozszerzenia, które można wywołać z poziomu skryptu aktualizatora. Oto przykład funkcji dla urządzenia tardis:

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

#include "edify/expr.h"

Każda funkcja rozszerzenia ma tę samą sygnaturę. Argumenty to nazwa, pod którą wywołano funkcję, plik cookie State*, liczba docierających argumentów oraz tablica wskaźników Expr* reprezentujących argumenty. Wartość zwracana to nowo przydzielone 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);
    }

Argumenty nie są oceniane w momencie wywołania funkcji – to logika funkcji określa, które z nich są oceniane i ile razy. Dzięki temu możesz używać funkcji rozszerzeń do implementowania własnych struktur sterujących. Call Evaluate() do oceny argumentu Expr* , zwracającego wartość Value*. Jeśli Evaluate()zwraca NULL, należy zwolnić wszystkie zatrzymywane zasoby i natychmiast zwrócić NULL (to powoduje propagowanie anulowań w górę stosu edify). W przeciwnym razie przejmujesz własność zwróconej wartości i odpowiadasz za jej wywołanie.FreeValue()

Załóżmy, że funkcja wymaga 2 argumentów: key o typie string i image o typie blob. Argumenty mogą wyglądać tak:

   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ć żmudne w przypadku wielu argumentów. Funkcja ReadValueArgs() może ułatwić to zadanie. Zamiast kodu powyżej można napisać:

   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 wykonuje sprawdzania typu, więc musisz to zrobić tutaj. Wygodniej jest to zrobić za pomocą jednego instrukcji if, ale w efekcie w przypadku niepowodzenia pojawi się nieco mniej szczegółowy komunikat o błędzie. Funkcja ReadValueArgs() obsługuje jednak ocenę każdego argumentu i zwolnienie wszystkich wcześniej ocenionych argumentów (a także ustawienie przydatnego komunikatu o błędzie), jeśli którykolwiek z testów zakończy się niepowodzeniem. Do obliczania zmiennej liczby argumentów możesz użyć funkcji pomocniczej ReadValueVarArgs() (zwraca ona tablicę Value*).

Po ocenie argumentów wykonaj działanie 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 zostanie przekazana do wywołującego. Wywołujący staje się właścicielem wszystkich danych wskazywanych przez ten argument Value*, w szczególności elementu danych.

W tym przypadku chcesz zwrócić wartość „prawda” lub „fałsz”, aby wskazać, czy operacja się powiodła. Pamiętaj, że pusty ciąg znaków to false, a wszystkie inne ciągi znaków to true. Musisz za pomocą malloc zaalokować obiekt Value z zaalokowanymi kopiami ciągu znaków stałej, ponieważ wywołujący będzie free() oba. Pamiętaj, aby wywołać metodę FreeValue() obiektów, które zostały utworzone na podstawie oceny argumentów.

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

Funkcja ułatwiająca StringValue() otacza ciąg znaków nowym obiektem Value. Aby zapisać powyższy kod w bardziej zwięzły sposób, użyj:

   FreeValue(key);
    FreeValue(image);

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

Aby podłączyć funkcje do interpretera edify, podaj funkcję Register_foo, gdzie foo to nazwa biblioteki statycznej zawierającej ten kod. Aby zarejestrować każdą funkcję rozszerzenia, wywołaj funkcję RegisterFunction(). Zgodnie z konwencją nazwy funkcji związanych z urządzeniem należy nadawać w postaci device.whatever, aby uniknąć konfliktów z dodanymi w przyszłości funkcjami wbudowanymi.

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

Teraz możesz skonfigurować plik makefile, aby wygenerować stałą bibliotekę z Twoim kodem. (To jest ten sam plik makefile, który służy do dostosowywania interfejsu odzyskiwania w poprzedniej sekcji. Na urządzeniu mogą być 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 statycznej biblioteki musi być zgodna z nazwą zawartej w niej funkcji Register_libname.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Na koniec skonfiguruj wersję odzyskiwania, aby pobrać bibliotekę. Dodaj bibliotekę do TARGET_RECOVERY_UPDATER_LIBS (może ona zawierać wiele bibliotek; wszystkie zostaną zarejestrowane). Jeśli Twój kod zależy od innych bibliotek statycznych, które nie są rozszerzeniami edify (np. nie mają funkcji Register_libname), możesz je podać w pliku TARGET_RECOVERY_UPDATER_EXTRA_LIBS, aby połączyć je z updaterem bez wywoływania (nieistniejącej) funkcji rejestracji. Jeśli na przykład kod dla konkretnego urządzenia ma używać zlib do dekompresji danych, należy tutaj uwzględnić 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 aktualizatora w pakiecie OTA mogą teraz wywoływać Twoją funkcję tak samo jak każdą inną. Aby przeprogramować urządzenie Tardis, skrypt aktualizacji może zawierać:tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) Używa ona wersji funkcji wbudowanej package_extract_file() z jednym argumentem, która zwraca zawartość pliku wyodrębnionego z pakietu aktualizacji jako blob, aby utworzyć drugi argument nowej funkcji rozszerzenia.

Generowanie pakietu OTA

Ostatnim elementem jest przekazanie narzędziom do generowania pakietów OTA informacji o danych dotyczących Twojego urządzenia i wydanie skryptów aktualizacyjnych, które zawierają wywołania funkcji rozszerzenia.

Najpierw system kompilacji musi wiedzieć o blobie danych na urządzenie. Zakładając, że plik danych znajduje się w katalogu device/yoyodyne/tardis/tardis.dat, w pliku AndroidBoard.mk urządzenia zadeklaruj:

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

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

Możesz też umieścić go w pliku Android.mk, ale wtedy musi być chroniony przez sprawdzanie urządzenia, ponieważ wszystkie pliki Android.mk w drzewie są ładowane niezależnie od tego, na jakie urządzenie jest kompilowany pakiet. (jeśli drzewo zawiera wiele urządzeń, do tworzenia urządzenia tardis należy 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

Nazwa „pliki radiowe” jest używana ze względów historycznych. Nie muszą one mieć nic wspólnego z radiem w urządzeniu (jeśli jest dostępne). To po prostu nieprzezroczyste bloki danych, które system kompilacji kopiuje do plików docelowych .zip używanych przez narzędzia do generowania OTA. Gdy wykonasz kompilację, plik tardis.dat zostanie zapisany w pliku target-files.zip jako RADIO/tardis.dat. Możesz wywołać funkcję add-radio-file dowolną liczbę razy, aby dodać dowolną liczbę plików.

Moduł Pythona

Aby rozszerzyć narzędzia do publikowania, napisz moduł Pythona (musi mieć nazwę releasetools.py), do którego narzędzia mogą 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"));""")

Wygenerowanie przyrostowego pakietu OTA obsługuje osobna funkcja. Załóżmy, że musisz przeprogramować tardis tylko wtedy, gdy plik tardis.dat zmienił się między dwoma wersjami.

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ć te funkcje (wdróż tylko te, których potrzebujesz):

FullOTA_Assertions()
Wywołanie następuje na początku generowania pełnego OTA. To dobre miejsce na emitowanie stwierdzeń dotyczących bieżącego stanu urządzenia. Nie emituj poleceń skryptu, które wprowadzają zmiany na urządzeniu.
FullOTA_InstallBegin()
Wywoływany po przejściu wszystkich stwierdzeń dotyczących stanu urządzenia, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji dotyczących konkretnego urządzenia, które muszą zostać wykonane przed zmianą czegokolwiek innego na urządzeniu.
FullOTA_InstallEnd()
Jest wywoływany pod koniec generowania skryptu, po emisji poleceń skryptu dotyczących aktualizacji partycji rozruchowej i systemowej. Możesz też wysyłać dodatkowe polecenia do aktualizacji dotyczących konkretnych urządzeń.
IncrementalOTA_Assertions()
Podobny do FullOTA_Assertions(), ale wywoływany podczas generowania pakietu aktualizacji przyrostowej.
IncrementalOTA_VerifyBegin()
Wywoływany po wykonaniu wszystkich stwierdzeń dotyczących stanu urządzenia, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji dotyczących konkretnego urządzenia, które muszą zostać wykonane przed zmianą czegokolwiek innego na urządzeniu.
IncrementalOTA_VerifyEnd()
Wywoływany pod koniec fazy weryfikacji, gdy skrypt potwierdził, że pliki, z którymi ma pracować, mają oczekiwaną zawartość początkową. Na tym etapie nic na urządzeniu nie zostało zmienione. Możesz też wygenerować kod dla dodatkowych weryfikacji dotyczących konkretnych urządzeń.
IncrementalOTA_InstallBegin()
Wywoływany po zweryfikowaniu, że pliki, które mają być zaktualizowane, mają oczekiwany stan przed, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji dotyczących konkretnego urządzenia, które muszą zostać wykonane przed zmianą czegokolwiek innego na urządzeniu.
IncrementalOTA_InstallEnd()
Tak jak w przypadku pełnego pakietu OTA, ta funkcja jest wywoływana na końcu generowania skryptu, po emisji poleceń skryptu dotyczących aktualizacji partycji rozruchowej i systemowej. Możesz też wysyłać dodatkowe polecenia dotyczące aktualizacji dotyczących konkretnego urządzenia.

Uwaga: jeśli urządzenie straci zasilanie, instalacja OTA może się rozpocząć od początku. Przygotuj się na obsługę urządzeń, na których te polecenia zostały już wykonane w pełni lub częściowo.

Przekazywanie funkcji do obiektów informacji

Przekazywanie funkcji do jednego obiektu informacji zawierającego różne przydatne elementy:

  • info.input_zip. (dotyczy tylko pełnych OTA) Obiekt zipfile.ZipFile dla wejścia target-files .zip.
  • info.source_zip. (Tylko w przypadku przyrostowych OTA) Obiekt zipfile.ZipFile zawierający pliki docelowe źródłowe .zip (wersja już na urządzeniu, gdy instalowany jest przyrostowy pakiet).
  • info.target_zip. (Tylko w przypadku OTAs) Obiekt zipfile.ZipFile dla docelowego pliku .zip z plikami docelowymi (kompilacja pakietu przyrostowego umieszcza na urządzeniu).
  • info.output_zip. tworzony pakiet; obiekt zipfile.ZipFile otwarty do zapisu. Aby dodać plik do pakietu, użyj polecenia common.ZipWriteStr(info.output_zip, filename, data).
  • info.script. Obiekt skryptu, do którego możesz dołączać polecenia. Wywołaj funkcję info.script.AppendExtra(script_text), aby wstawić tekst do skryptu. Upewnij się, że tekst wyjściowy kończy się średnikiem, aby nie kolidował z poleceniami emitowanymi później.

Szczegółowe informacje o obiekcie info znajdziesz w dokumentacji stowarzyszenia Python Software Foundation dotyczącej archiwów ZIP.

Określanie lokalizacji modułu

W pliku BoardConfig.mk określ lokalizację skryptu releasetools.py na urządzeniu:

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

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Jeśli opcja TARGET_RELEASETOOLS_EXTENSIONS nie jest ustawiona, domyślnie jest to katalog $(TARGET_DEVICE_DIR)/../common (device/yoyodyne/common w tym przykładzie). Najlepiej wyraźnie określić lokalizację skryptu releasetools.py. Podczas kompilowania urządzenia TARDIS skrypt releasetools.py jest dołączany do pliku target-files.zip (META/releasetools.py ).

Podczas uruchamiania narzędzi do publikowania (img_from_target_files lub ota_from_target_files) skrypt releasetools.py w pliku target-files .zip (jeśli jest obecny) jest preferowany przed skryptem z drzewa źródłowego Androida. Możesz też wyraźnie określić ścieżkę do rozszerzeń dla poszczególnych urządzeń za pomocą opcji -s (lub --device_specific), która ma najwyższy priorytet. Umożliwia to poprawianie błędów i wprowadzanie zmian w rozszerzeniach releasetools oraz stosowanie tych zmian w starych plikach docelowych.

Teraz, gdy uruchomisz ota_from_target_files, automatycznie pobierze moduł dla danego urządzenia z pliku target_files .zip i użyje go do wygenerowania 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

Możesz też określić rozszerzenia dla poszczególnych urządzeń podczas wykonywania polecenia 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 znajdziesz w komentarzach ota_from_target_files w sekcji build/make/tools/releasetools/ota_from_target_files.

Mechanizm instalowania z nieoficjalnych źródeł

Odzyskiwanie ma mechanizm sideloadingu umożliwiający ręczną instalację pakietu aktualizacji bez pobierania go przez system główny. Sideloading jest przydatny do debugowania lub wprowadzania zmian na urządzeniach, na których nie można uruchomić głównego systemu.

W przeszłości sideloading był wykonywany przez ładowanie pakietów z karty SD urządzenia. W przypadku urządzenia, które się nie uruchamia, pakiet można wgrać na kartę SD za pomocą innego komputera, a następnie włożyć kartę SD do urządzenia. Aby uwzględnić urządzenia z Androidem bez wymiennej pamięci zewnętrznej, funkcja przywracania obsługuje 2 dodatkowe mechanizmy instalowania z innego urządzenia: instalowanie pakietów z partycji pamięci podręcznej i instalowanie ich przez USB za pomocą narzędzia adb.

Aby wywołać każdy mechanizm sideload, metoda Device::InvokeMenuItem() urządzenia może zwracać te wartości BuiltinAction:

  • APPLY_EXT. Zainstaluj pakiet aktualizacji z zewnętrznej pamięci (katalog /sdcard). Plik recovery.fstab musi definiować punkt podłączania /sdcard . Nie można go używać na urządzeniach, które emulują kartę SD za pomocą symbolicznego linku do /data (lub za pomocą podobnego mechanizmu). Plik /data zwykle nie jest dostępny do odzyskania, ponieważ może być zaszyfrowany. Interfejs odzyskiwania wyświetla menu plików ZIP w /sdcard i pozwala użytkownikowi wybrać jeden z nich.
  • APPLY_CACHE. Jest to podobne do wczytywania pakietu z poziomu /sdcard, z tym że zamiast tego używany jest katalog /cache (który jest zawsze dostępny do odzyskania). Z poziomu zwykłego systemu tylko użytkownicy uprzywilejowani mogą zapisywać dane w katalogu /cache . Jeśli urządzenie nie może się uruchomić, w katalogu /cache nie można zapisywać danych wcale (co powoduje, że ten mechanizm ma ograniczone zastosowanie).
  • APPLY_ADB_SIDELOAD. Umożliwia użytkownikowi wysyłanie pakietu na urządzenie za pomocą kabla USB i narzędzia do programowania adb. Gdy ten mechanizm zostanie wywołany, odzyskiwanie uruchamia własną miniwersję demona adbd, aby umożliwić aplikacji adb na połączonym komputerze hosta komunikację z tym demonem. Ta miniwersja obsługuje tylko 1 polecenie: adb sideload filename. Nazwany plik jest wysyłany z komputera hosta na urządzenie, które następnie go weryfikuje i instaluje tak, jakby znajdował się w pamięci lokalnej.

Kilka uwag:

  • Obsługiwana jest tylko transmisja przez USB.
  • Jeśli podczas odzyskiwania uruchamiasz adbd normalnie (zwykle w przypadku kompilacji userdebug i eng), zostanie on wyłączony, gdy urządzenie będzie w trybie sideload adb, a następnie uruchomione, gdy adb sideload zakończy pobieranie pakietu. W trybie ładowania z użyciem adb żadne polecenia adb inne niż sideload nie działają ( logcat, reboot, push, pull, shell itp.).
  • Nie można wyjść z tego trybu na urządzeniu. Aby przerwać proces, możesz wysłać jako pakiet /dev/null (lub cokolwiek innego, co nie jest prawidłowym pakietem). W takim przypadku urządzenie nie będzie mogło zweryfikować pakietu i zatrzyma procedurę instalacji. Metoda CheckKey() implementacji RecoveryUI będzie nadal wywoływana w przypadku naciśnięć klawiszy, aby umożliwić podanie sekwencji klawiszy, która uruchamia ponowne uruchamianie urządzenia i działa w trybie sideload adb.