Kod dla konkretnego urządzenia

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

W kolejnych sekcjach i przykładach dostosujemy 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, który działa na tych urządzeniach. Obsługuje też urządzenia flash MTD (Memory Technology Device) i 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 odzyskiwania, jak i przez narzędzia do tworzenia pakietów. Nazwę pliku mapy możesz określić w zmiennym 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óry jest opcjonalny, wszystkie punkty montowania w tym przykładzie muszą być zdefiniowane (urządzenia mogą też dodawać dodatkowe partycje). Obsługiwanych jest 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 występować w /proc/mtd.
mtd
Surowa partycja MTD, używana w przypadku partycji rozruchowych, takich jak boot i recovery. MTD nie jest faktycznie montowany, ale punkt montowania 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 blokowego.
emmc
Surowe urządzenie blokowe eMMC, używane w przypadku partycji rozruchowych, takich jak partycja rozruchowa i partycja przywracania. Podobnie jak w przypadku typu mtd, pamięć eMMC nigdy nie jest montowana, ale ciąg punktu montowania jest używany do lokalizowania urządzenia w tabeli.
vfat
System plików FAT na urządzeniu blokowym, zwykle w przypadku pamięci zewnętrznej, np. karty SD. Urządzenie to urządzenie blokowe, a urządzenie2 to drugie urządzenie blokowe, które system próbuje zamontować, jeśli zamontowanie urządzenia głównego się nie powiedzie (dla zgodności z kartami SD, które mogą być sformatowane z tabelą partycji lub bez niej).

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

Od Androida 3.0 plik recovery.fstab ma dodatkowe pole opcjonalne: 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. w przypadku partycji danych użytkownika podczas operacji czyszczenia danych lub przywracania ustawień fabrycznych albo w przypadku partycji systemowej podczas instalacji pełnego pakietu OTA). Jeśli wartość długości jest ujemna, rozmiar do sformatowania jest obliczany przez dodanie wartości długości do rzeczywistego rozmiaru partycji. Na przykład ustawienie „length=-16384” oznacza, że ostatnie 16 tys. B tego obszaru nie zostanie nadpisane podczas ponownego formatowania. Obsługuje to funkcje takie jak szyfrowanie partycji danych użytkownika (gdzie metadane szyfrowania są przechowywane na końcu partycji, która nie powinna być nadpisywana).

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 traktowany jako wpis device2. Jeśli wpis nie zaczyna się od znaku „/”, jest traktowany jako pole options.

Animacja rozruchu

Producenci urządzeń mogą dostosowywać animację wyświetlaną podczas uruchamiania urządzenia z Androidem. Aby to zrobić, utwórz plik ZIP zorganizowany i umieszczony zgodnie ze specyfikacjami w formacie animacji rozruchowej.

W przypadku urządzeń z Androidem Things możesz przesłać spakowany plik w konsoli Androida Things, aby obrazy zostały uwzględnione w wybranym produkcie.

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

Interfejs odzyskiwania

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

Twoim celem jest utworzenie małej biblioteki statycznej z kilkoma obiektami C++, która będzie udostępniać funkcje specyficzne dla urządzenia. Plik bootable/recovery/default_device.cpp jest używany domyślnie i stanowi dobry punkt wyjścia do skopiowania podczas pisania wersji tego pliku na urządzenie.

Uwaga: w tym miejscu może się pojawić komunikat Brak polecenia. Aby przełączać 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 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 produktu

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 (np. elementy sterujące do zmiany lub wyboru wyróżnionego 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 (nie zawijane), więc pamiętaj o szerokości ekranu urządzenia.

Dostosowywanie klucza sprawdzania

Następnie zdefiniuj implementację RecoveryUI na urządzeniu. W tym przykładzie zakłada się, że urządzenie tardis ma ekran, więc możesz dziedziczyć z wbudowanej implementacji ScreenRecoveryUI (instrukcje dotyczące urządzeń bez ekranu). Jedyną funkcją, którą można dostosować w interfejsie ScreenRecoveryUI, jest CheckKey(), która obsługuje początkowe asynchroniczne przetwarzanie kluczy:

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

Stałe KEY

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

  • PRZEŁĄCZ Włączanie i wyłączanie wyświetlania menu lub dziennika tekstu
  • REBOOT Natychmiastowe ponowne uruchomienie urządzenia
  • IGNORUJ Ignoruj to naciśnięcie klawisza
  • ENQUEUE Dodaj to naciśnięcie klawisza do kolejki, aby było przetwarzane synchronicznie (czyli 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 zwolnienia tego samego klawisza. (Sekwencja zdarzeń A-down B-down B-up A-up powoduje wywołanie tylko funkcji CheckKey(B)). CheckKey() can call IsKeyPressed(), aby sprawdzić, czy inne klawisze są przytrzymywane. (W powyższej sekwencji kluczowych zdarzeń, gdyby CheckKey(B) wywołało IsKeyPressed(A), zwróciłoby wartość „prawda”).

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ę: wyświetlacz jest włączany i wyłączany przez przytrzymanie przycisku zasilania i naciśnięcie przycisku zwiększania głośności, a urządzenie można natychmiast ponownie uruchomić, naciskając 5 razy z rzędu przycisk zasilania (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 (ikona błędu, animacja instalacji, paski postępu) w przypadku ScreenRecoveryUI, możesz ustawić zmienną animation_fps, aby kontrolować szybkość 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 klasie podrzędnej. Ustaw wartość, a następnie wywołaj funkcję parent Init() , aby zakończyć inicjowanie. Wartość domyślna (20 kl./s) odpowiada domyślnym obrazom przywracania. Jeśli ich używasz, nie musisz podawać funkcji Init(). Więcej informacji o obrazach znajdziesz w sekcji Obrazy interfejsu odzyskiwania.

Klasa urządzenia

Po zaimplementowaniu interfejsu RecoveryUI zdefiniuj klasę urządzenia (podklasę wbudowanej klasy Device). Powinna utworzyć pojedynczą instancję klasy interfejsu i zwrócić ją 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 przywracania, po zainicjowaniu interfejsu i przeanalizowaniu argumentów, ale przed podjęciem jakichkolwiek działań. Domyślna implementacja nie wykonuje żadnych działań, więc nie musisz jej podawać w klasie podrzędnej, jeśli nie masz nic do zrobienia:

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

Dostarczanie menu regeneracyjnego i zarządzanie nim

System wywołuje 2 metody, aby uzyskać listę wierszy nagłówka i listę elementów. W tej implementacji zwraca statyczne tablice 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, a następnie 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 przyjmuje kod klucza (który został wcześniej przetworzony i umieszczony w kolejce przez metodę CheckKey() obiektu interfejsu) oraz bieżący stan widoczności menu lub dziennika tekstu. Wartość zwracana jest liczbą całkowitą. Jeśli wartość jest równa 0 lub większa, jest 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 predefiniowanych stałych:

  • kHighlightUp. Przesuń podświetlenie menu na poprzedni element.
  • kHighlightDown. Przesuwanie zaznaczenia menu do następnego elementu
  • kInvokeItem Wywołaj obecnie wyróżniony element
  • kNoAction. Nie wykonuj żadnej czynności po naciśnięciu tego klawisza

Jak wynika z argumentu visible, funkcja HandleMenuKey() jest wywoływana nawet wtedy, gdy menu nie jest widoczne. W przeciwieństwie do CheckKey() nie jest wywoływana, gdy przywracanie wykonuje takie czynności jak czyszczenie danych czy instalowanie pakietu. Jest wywoływana tylko wtedy, gdy przywracanie jest w stanie bezczynności i czeka na dane wejściowe.

Mechanizmy trackballa

Jeśli urządzenie ma mechanizm wejściowy podobny do trackballa (generuje zdarzenia wejściowe typu EV_REL i kodu REL_Y), mechanizm odzyskiwania syntetyzuje naciśnięcia klawiszy KEY_UP i KEY_DOWN, gdy urządzenie wejściowe podobne do trackballa zgłasza ruch w osi Y. Wystarczy, że przypiszesz zdarzenia KEY_UP i KEY_DOWN do działań w menu. To mapowanie nie występuje w przypadku CheckKey(), więc nie możesz używać ruchów trackballa jako wyzwalaczy ponownego uruchamiania ani włączania i wyłączania wyświetlacza.

Klawisze modyfikujące

Aby sprawdzić, czy klawisze są przytrzymywane jako modyfikatory, wywołaj metodę IsKeyPressed() własnego obiektu interfejsu. Na przykład na niektórych urządzeniach naciśnięcie Alt-W w trybie odzyskiwania powodowało wyczyszczenie danych niezależnie od tego, czy menu było widoczne. Możesz to zrobić 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 wartość visible to „false”, nie ma sensu zwracać specjalnych wartości, które manipulują menu (przesuwają wyróżnienie, wywołują wyróżniony element), ponieważ użytkownik nie widzi wyróżnienia. W razie potrzeby możesz jednak przywrócić te wartości.

InvokeMenuItem

Następnie podaj metodę InvokeMenuItem(), która mapuje pozycje całkowite w tablicy elementów zwracanej przez 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 wyliczenia BuiltinAction, aby poinformować system o wykonaniu danego działania (lub element NO_ACTION, jeśli chcesz, aby system nic nie robił). W tym miejscu możesz udostępnić dodatkowe funkcje odzyskiwania, które nie są dostępne w systemie: dodaj element do menu, wykonaj go tutaj, gdy zostanie wywołany, i zwróć NO_ACTION, aby system nie wykonywał żadnych innych działań.

BuiltinAction zawiera te wartości:

  • NO_ACTION Nic nie rób.
  • REBOOT Wyjdź z trybu odzyskiwania i uruchom urządzenie ponownie w normalny sposób.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Zainstaluj pakiet aktualizacji z różnych miejsc. Więcej informacji znajdziesz w sekcji Instalowanie aplikacji z innych źródeł.
  • WIPE_CACHE Sformatuj tylko partycję pamięci podręcznej. Potwierdzenie nie jest wymagane, ponieważ jest to stosunkowo nieszkodliwe.
  • WIPE_DATA Ponowne sformatowanie partycji userdata i cache, czyli przywrócenie danych fabrycznych. Zanim użytkownik będzie mógł kontynuować, musi potwierdzić tę czynność.

Ostatnia metoda, WipeData(), jest opcjonalna i jest wywoływana za każdym razem, gdy rozpoczyna się operacja wymazywania danych (z poziomu odzyskiwania za pomocą menu lub gdy użytkownik zdecyduje się przywrócić dane fabryczne z głównego systemu). Ta metoda jest wywoływana przed wyczyszczeniem danych użytkownika i partycji pamięci podręcznej. Jeśli urządzenie przechowuje dane użytkownika w innych miejscach niż te 2 partycje, usuń je w tym miejscu. W przypadku powodzenia należy zwrócić wartość 0, a w przypadku niepowodzenia – inną wartość. Obecnie wartość zwracana jest ignorowana. Partycje danych użytkownika i pamięci podręcznej są czyszczone niezależnie od tego, czy zwracany jest wynik powodzenia czy niepowodzenia.

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

Marka urządzenia

Na koniec dodaj na końcu pliku recovery_ui.cpp kod standardowy 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 trybem odzyskiwania na urządzeniu. W pliku Android.mk utwórz bibliotekę statyczną, która zawiera 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 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 odzyskiwania składa się z obrazów. Najlepiej, aby użytkownicy nigdy nie wchodzili w interakcję z interfejsem: Podczas normalnej aktualizacji telefon uruchamia się w trybie odzyskiwania, wypełnia pasek postępu instalacji i uruchamia się ponownie w nowym systemie bez interwencji użytkownika. W przypadku problemu z aktualizacją systemu jedyną czynnością, jaką może wykonać użytkownik, jest skontaktowanie się z działem obsługi klienta.

Interfejs oparty wyłącznie na obrazach eliminuje potrzebę lokalizacji. Jednak od Androida 5.0 aktualizacja może wyświetlać ciąg tekstu (np. „Instalowanie aktualizacji systemu…”) wraz z obrazem. Więcej informacji znajdziesz w sekcji Zlokalizowany tekst odzyskiwania.

Android 5.0 i nowsze

Interfejs odzyskiwania w Androidzie 5.0 i nowszym korzysta z 2 głównych obrazów: obrazu błędu i animacji instalacji.

Obraz wyświetlany podczas błędu aktualizacji OTA

Rysunek 1. icon_error.png

Obraz wyświetlany podczas instalacji OTA

Rysunek 2. icon_installing.png

Animacja instalacji jest przedstawiona jako pojedynczy obraz PNG z różnymi klatkami animacji przeplatanymi wierszami (dlatego rysunek 2 wygląda na ściśnięty). Na przykład w przypadku animacji 200 x 200 pikseli składającej się z 7 klatek utwórz jeden obraz o wymiarach 200 x 1400 pikseli, w którym pierwsza klatka to wiersze 0, 7, 14, 21 itd., druga klatka to wiersze 1, 8, 15, 22 itd. Połączony obraz zawiera fragment tekstu, który wskazuje 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 używany do 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 podać obraz icon_installing.png i ustawić liczbę klatek w animacji na 0 (ikona błędu nie jest animowana, zawsze jest obrazem statycznym).

Android 4.x i starsze

Interfejs odzyskiwania w Androidzie 4.x i starszych wersjach korzysta z obrazu błędu (pokazanego powyżej) oraz animacji instalacji i kilku obrazów nakładanych:

Obraz wyświetlany podczas instalacji OTA

Rysunek 3. icon_installing.png

obraz wyświetlany jako pierwsza
nakładka

Rysunek 4. icon-installing_overlay01.png

obraz wyświetlany jako siódma nakładka,

Rysunek 5. icon_installing_overlay07.png

Podczas instalacji wyświetlacz jest tworzony przez narysowanie obrazu icon_installing.png, a następnie narysowanie na nim jednej z ramek nakładki z odpowiednim przesunięciem. Czerwony kwadrat pokazuje, gdzie nakładka jest umieszczona na obrazie bazowym:

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

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

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

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

Kolejne ramki są wyświetlane przez narysowanie tylko następnego obrazu nakładki na to, co już jest widoczne. Obraz podstawowy nie jest rysowany ponownie.

Liczba klatek w animacji, żądana szybkość oraz przesunięcia nakładki w osiach X i Y względem podstawy są ustawiane przez zmienne składowe klasy ScreenRecoveryUI. Jeśli zamiast obrazów domyślnych używasz obrazów niestandardowych, zastąp metodę Init() w klasie podrzędnej, 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 przekształceniu zestawu klatek obrazubootable/recovery/make-overlay.py w formę „obraz podstawowy + obrazy nakładki” potrzebną do odzyskiwania, w tym w obliczeniu niezbędnych przesunięć.

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

Zlokalizowany tekst odzyskiwania

Android 5.x wyświetla ciąg tekstu (np. „Instalowanie aktualizacji systemu…”) wraz z obrazem. Gdy główny system uruchamia się w trybie odzyskiwania, przekazuje bieżące ustawienia regionalne użytkownika jako opcję wiersza poleceń do trybu odzyskiwania. Aby wyświetlić każdą wiadomość, proces odzyskiwania obejmuje drugi obraz złożony z wstępnie wyrenderowanymi ciągami tekstowymi dla tej wiadomości w każdej wersji językowej.

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

Obraz z tekstem dotyczącym odzyskiwania

Rysunek 8. Zlokalizowany tekst wiadomości dla użytkowników blokujących reklamy

W SMS-ie w celu odzyskania konta mogą się pojawić te komunikaty:

  • Instaluję aktualizację systemu…
  • Błąd!
  • Wymazuję… (podczas czyszczenia 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 wersje językowe 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 ręcznie uruchomi tryb odzyskiwania, język może być niedostępny i nie będzie wyświetlany żaden tekst. Nie sprawiaj, aby SMS-y były kluczowe w procesie odzyskiwania.

Uwaga: ukryty interfejs, który wyświetla komunikaty dziennika i umożliwia użytkownikowi wybieranie działań 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 powstaje z połączenia 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:

pasek postępu na poziomie 1%

Rysunek 11. Pasek postępu na poziomie 1%>

pasek postępu na poziomie 10%

Rysunek 12. Pasek postępu na poziomie 10%

pasek postępu w 50%

Rysunek 13. Pasek postępu w 50%

Możesz podać wersje tych obrazów przeznaczone na konkretne urządzenia, umieszczając je w folderze (w tym przykładzie) device/yoyodyne/tardis/recovery/res/images. Nazwy plików muszą być zgodne z podanymi powyżej. Jeśli w tym katalogu zostanie znaleziony plik, system kompilacji użyje go zamiast odpowiedniego obrazu domyślnego. Obsługiwane są tylko pliki PNG w formacie RGB lub RGBA o 8-bitowej głębi kolorów.

Uwaga: w Androidzie 5.x, jeśli język jest znany w trybie odzyskiwania i jest językiem zapisanym od prawej do lewej (np. arabskim lub hebrajskim), pasek postępu wypełnia się od prawej do lewej.

Urządzenia bez ekranu

Nie wszystkie urządzenia z Androidem mają ekrany. Jeśli urządzenie jest urządzeniem bez monitora lub ma interfejs tylko audio, może być konieczne bardziej rozbudowane dostosowanie interfejsu odzyskiwania. Zamiast tworzyć podklasę ScreenRecoveryUI, utwórz bezpośrednio podklasę jej klasy nadrzędnej RecoveryUI.

RecoveryUI ma metody obsługi operacji interfejsu użytkownika niższego poziomu, 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 swojego urządzenia. Być może urządzenie ma diody LED, które mogą świecić w różnych kolorach lub migać w różnych wzorach, aby wskazywać stan, albo może odtwarzać dźwięk. (Możesz w ogóle nie obsługiwać menu ani trybu „wyświetlanie tekstu”. Możesz uniemożliwić dostęp do nich za pomocą implementacji CheckKey()HandleMenuKey(), które nigdy nie włączają wyświetlania ani nie wybierają elementu menu. W takim przypadku wiele metod RecoveryUI, które musisz udostępnić, może być pustymi elementami zastępczymi).

W deklaracji RecoveryUI znajdziesz bootable/recovery/ui.hmetody, które musisz obsługiwać. Klasa RecoveryUI jest abstrakcyjna – niektóre metody są czysto wirtualne i muszą być dostarczane przez podklasy – ale zawiera kod do przetwarzania danych wejściowych kluczy. Możesz też to zmienić, jeśli urządzenie nie ma klawiszy lub chcesz je przetwarzać w inny sposób.

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ć ze skryptu aktualizatora. 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 rozszerzenia ma ten sam podpis. Argumentami są nazwa, pod którą wywołano funkcję, State* plik cookie, liczba argumentów przychodzących i tablica wskaźników Expr* reprezentujących argumenty. Wartością zwracaną jest nowo przydzielony 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 – logika 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 sterujących. Call Evaluate() do obliczenia argumentu Expr* i zwrócenia wartości Value*. Jeśli Evaluate() zwraca wartość NULL, należy zwolnić wszystkie używane zasoby i natychmiast zwrócić wartość NULL (spowoduje to propagowanie przerwań w górę stosu edify). W przeciwnym razie stajesz się właścicielem zwróconej wartości i ponosisz odpowiedzialność za ostateczne wywołanie na niej funkcji FreeValue().

Załóżmy, że funkcja wymaga 2 argumentów: klucza w postaci ciągu znaków i obrazu w postaci obiektu blob. Możesz przeczytać argumenty takie jak te:

   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 obliczonych argumentów może być uciążliwe w przypadku wielu argumentów. Może to ułatwić funkcja ReadValueArgs(). Zamiast powyższego kodu możesz 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 sprawdza typów, więc musisz to zrobić tutaj. Wygodniej jest to zrobić za pomocą jednej instrukcji if, ale kosztem nieco mniej szczegółowego komunikatu o błędzie w przypadku niepowodzenia. Funkcja ReadValueArgs() obsługuje jednak ocenę każdego argumentu i zwalnianie wszystkich wcześniej ocenionych argumentów (a także ustawianie przydatnego komunikatu o błędzie), jeśli któraś z ocen się nie powiedzie. Możesz użyć funkcji ReadValueVarArgs() do oceny zmiennej liczby argumentów (zwraca ona tablicę wartości Value*).

Po obliczeniu 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 przejmuje własność wszystkich danych, na które wskazuje ten wskaźnik Value*, a 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 zgodnie z konwencją pusty ciąg znaków ma wartość false, a wszystkie inne ciągi znaków mają wartość true. Musisz przydzielić pamięć dla obiektu Value za pomocą funkcji malloc, a także przydzielić pamięć dla kopii stałego ciągu znaków, ponieważ wywołujący free() obie te wartości. Nie zapomnij wywołać funkcji FreeValue() na obiektach, które zostały utworzone w wyniku 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 pomocnicza StringValue() opakowuje ciąg znaków w nowy obiekt Value. Aby zapisać powyższy kod w bardziej zwięzły sposób:

   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. Wywołaj funkcję RegisterFunction(), aby zarejestrować każdą funkcję rozszerzenia. Zgodnie z konwencją nadawaj funkcjom specyficznym dla urządzenia nazwy device.whatever, aby uniknąć konfliktów z przyszłymi funkcjami wbudowanymi.

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

Teraz możesz skonfigurować plik makefile tak, aby tworzył bibliotekę statyczną z Twoim kodem. (Jest to ten sam plik makefile, który został użyty do dostosowania 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 biblioteki statycznej musi być zgodna z nazwą funkcji Register_libname, którą zawiera.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Na koniec skonfiguruj kompilację odzyskiwania, aby pobrać bibliotekę. Dodaj bibliotekę do zmiennej TARGET_RECOVERY_UPDATER_LIBS (może ona zawierać wiele bibliotek, które 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 wymienić w TARGET_RECOVERY_UPDATER_EXTRA_LIBS, aby połączyć je z programem aktualizującym bez wywoływania ich (nieistniejącej) funkcji rejestracji. Jeśli na przykład kod specyficzny dla urządzenia ma używać biblioteki zlib do dekompresji danych, musisz ją tu uwzględnić.

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 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 1 argumentem, która zwraca zawartość pliku wyodrębnionego z pakietu aktualizacji jako obiekt blob, aby utworzyć drugi argument nowej funkcji rozszerzenia.

Generowanie pakietu OTA

Ostatnim elementem jest udostępnienie narzędziom do generowania pakietów OTA informacji o danych specyficznych dla urządzenia i generowanie skryptów aktualizatora, które zawierają wywołania funkcji rozszerzeń.

Najpierw spraw, aby system kompilacji znał konkretny plik binarny danych urządzenia. Załóżmy, że plik danych znajduje się w device/yoyodyne/tardis/tardis.dat. W takim przypadku zadeklaruj w pliku AndroidBoard.mk urządzenia:

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 sprawdzenie urządzenia, ponieważ wszystkie pliki Android.mk w drzewie są ładowane niezależnie od tego, jakie urządzenie jest budowane. (Jeśli drzewo zawiera wiele urządzeń, plik tardis.dat powinien być dodawany tylko podczas tworzenia urządzenia 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

Z przyczyn historycznych są one nazywane plikami radiowymi, ale mogą nie mieć nic wspólnego z radiem urządzenia (jeśli jest dostępne). Są to po prostu nieprzejrzyste bloki danych, które system kompilacji kopiuje do pliku ZIP target-files używanego przez narzędzia do generowania aktualizacji OTA. Podczas kompilacji plik tardis.dat jest zapisywany w pliku target-files.zip jako RADIO/tardis.dat. Możesz wywołać tę funkcjęadd-radio-file wiele razy, aby dodać dowolną liczbę plików.

Moduł Pythona

Aby rozszerzyć narzędzia do wydawania, napisz moduł Pythona (musi mieć nazwę releasetools.py), do którego narzędzia mogą się odwoływać, jeśli jest obecny. 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 generowanie przyrostowego pakietu OTA. Załóżmy, że w tym przykładzie musisz przeprogramować TARDIS tylko wtedy, gdy plik tardis.dat zmienił się 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ć te funkcje (wdrażaj tylko te, których potrzebujesz).

FullOTA_Assertions()
Wywoływana na początku generowania pełnej aktualizacji OTA. To dobre miejsce na emitowanie asercji dotyczących bieżącego stanu urządzenia. Nie wysyłaj poleceń skryptu, które wprowadzają zmiany na urządzeniu.
FullOTA_InstallBegin()
Wywoływana po przejściu wszystkich asercji dotyczących stanu urządzenia, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać wykonane, zanim na urządzeniu zostaną wprowadzone jakiekolwiek inne zmiany.
FullOTA_InstallEnd()
Wywoływana na końcu generowania skryptu, po wyemitowaniu poleceń skryptu służących do aktualizacji partycji rozruchowej i systemowej. Możesz też wysyłać 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 przejściu wszystkich asercji dotyczących stanu urządzenia, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać wykonane, zanim na urządzeniu zostaną wprowadzone jakiekolwiek inne zmiany.
IncrementalOTA_VerifyEnd()
Wywoływana na końcu fazy weryfikacji, gdy skrypt potwierdzi, że pliki, których będzie używać, mają oczekiwaną zawartość początkową. Na tym etapie nic na urządzeniu nie zostało zmienione. Możesz też wygenerować kod do dodatkowych weryfikacji na konkretnych urządzeniach.
IncrementalOTA_InstallBegin()
Wywoływana po sprawdzeniu, czy pliki do poprawienia mają oczekiwany stan przed, ale przed wprowadzeniem jakichkolwiek zmian. Możesz wysyłać polecenia dotyczące aktualizacji specyficznych dla urządzenia, które muszą zostać wykonane, zanim na urządzeniu zostaną wprowadzone jakiekolwiek inne zmiany.
IncrementalOTA_InstallEnd()
Podobnie jak w przypadku pełnego pakietu OTA, jest on wywoływany na końcu generowania skryptu, po wyemitowaniu poleceń skryptu do aktualizacji partycji rozruchowej i systemowej. Możesz też wysyłać dodatkowe polecenia dotyczące aktualizacji na konkretnych urządzeniach.

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

Przekazywanie funkcji do obiektów informacji

Przekaż funkcje do jednego obiektu informacji, który zawiera różne przydatne elementy:

  • info.input_zip (Tylko pełne aktualizacje OTA) Obiekt zipfile.ZipFile dla wejściowego pliku ZIP target-files.
  • info.source_zip (Tylko przyrostowe aktualizacje OTA) Obiekt zipfile.ZipFile dla źródłowego pliku ZIP target-files (kompilacja już zainstalowana na urządzeniu w momencie instalacji pakietu przyrostowego).
  • info.target_zip. (Tylko przyrostowe aktualizacje OTA) Obiekt zipfile.ZipFile dla docelowego pliku ZIP target-files (kompilacja, którą pakiet przyrostowy umieszcza na urządzeniu).
  • info.output_zip Pakiet jest tworzony; obiekt zipfile.ZipFile został otwarty do zapisu. Użyj common.ZipWriteStr(info.output_zip, filename, data), aby dodać plik do pakietu.
  • info.script. Obiekt skryptu, do którego możesz dołączać polecenia. Wywołaj funkcję Call info.script.AppendExtra(script_text), aby wygenerować tekst w skrypcie. 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 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 zmienna TARGET_RELEASETOOLS_EXTENSIONS nie jest ustawiona, domyślnie przyjmuje wartość katalogu $(TARGET_DEVICE_DIR)/../common (w tym przykładzie device/yoyodyne/common ). Najlepiej jest wyraźnie określić lokalizację skryptu releasetools.py. Podczas tworzenia urządzenia TARDIS skrypt releasetools.py jest umieszczany w pliku target-files.zip (META/releasetools.py ).

Gdy uruchamiasz narzędzia do publikowania (img_from_target_files lub ota_from_target_files), skrypt releasetools.py w pliku target-files .zip, jeśli jest obecny, ma pierwszeństwo przed skryptem z drzewa źródłowego Androida. Możesz też wyraźnie określić ścieżkę do rozszerzeń specyficznych dla urządzenia 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 do starych plików docelowych.

Teraz, gdy uruchomisz ota_from_target_files, automatycznie pobierze on moduł dostosowany do urządzenia z pliku target_files .zip 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

Możesz też określić rozszerzenia dla konkretnych urządzeń, gdy uruchamiasz polecenie 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 ota_from_target_files komentarzach w  build/make/tools/releasetools/ota_from_target_files.

Mechanizm instalowania z nieoficjalnych źródeł

Tryb odzyskiwania ma mechanizm instalacji z boku, który umożliwia ręczne zainstalowanie pakietu aktualizacji bez pobierania go bezprzewodowo przez główny system. Instalowanie aplikacji z nieoficjalnych źródeł jest przydatne do debugowania lub wprowadzania zmian na urządzeniach, na których nie można uruchomić głównego systemu.

W przeszłości instalacja z zewnątrz odbywała się przez wczytywanie pakietów z karty SD urządzenia. W przypadku urządzenia, które się nie uruchamia, pakiet można umieścić na karcie SD za pomocą innego komputera, a następnie włożyć kartę SD do urządzenia. W przypadku urządzeń z Androidem bez wymiennej pamięci zewnętrznej przywracanie obsługuje 2 dodatkowe mechanizmy instalowania z innego urządzenia: wczytywanie pakietów z partycji pamięci podręcznej i wczytywanie ich przez USB za pomocą ADB.

Aby wywołać każdy mechanizm wczytywania z boku, metoda Device::InvokeMenuItem() na urządzeniu może zwracać te wartości BuiltinAction:

  • APPLY_EXT Zainstaluj pakiet aktualizacji z pamięci zewnętrznej (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ą linku symbolicznego do /data (lub podobnego mechanizmu). /data zwykle nie jest dostępny do odzyskania, ponieważ może być zaszyfrowany. W interfejsie odzyskiwania wyświetla się menu plików ZIP w usłudze /sdcard, z którego użytkownik może wybrać jeden plik.
  • APPLY_CACHE Podobnie jak w przypadku wczytywania pakietu z /sdcard, z tą różnicą, że zamiast tego używany jest katalog /cache (który jest zawsze dostępny w trybie odzyskiwania). W normalnym systemie katalog /cache może być zapisywany tylko przez użytkowników z uprawnieniami, a jeśli urządzenie nie może się uruchomić, nie można w ogóle zapisywać w katalogu /cache (co sprawia, ż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 deweloperskiego adb. Gdy ten mechanizm zostanie wywołany, przywracanie uruchomi własną miniaturową wersję demona adbd, aby umożliwić komunikację z nim za pomocą adb na podłączonym komputerze hosta. Ta wersja mini obsługuje tylko jedno 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ługiwany jest tylko transport USB.
  • Jeśli tryb odzyskiwania działa normalnie (zwykle w przypadku kompilacji userdebug i eng), zostanie on wyłączony, gdy urządzenie będzie w trybie adb sideload, i ponownie uruchomiony po zakończeniu odbierania pakietu przez adb sideload. W trybie przesyłania bocznego adb działa tylko polecenie sideload ( logcat, reboot, push, pull, shell itp. nie działają).
  • Nie możesz wyjść z trybu wczytywania adb na urządzeniu. Aby przerwać proces, możesz wysłać /dev/null (lub dowolny inny element, który nie jest prawidłowym pakietem), a następnie urządzenie nie będzie w stanie go zweryfikować i zatrzyma procedurę instalacji. Metoda CheckKey() w implementacji RecoveryUI będzie nadal wywoływana w przypadku naciśnięć klawiszy, więc możesz podać sekwencję klawiszy, która spowoduje ponowne uruchomienie urządzenia i będzie działać w trybie adb sideload.