Zawieszenie systemu

W Androidzie 9 i starszych jest wątek w libsuspend odpowiedzialny za inicjowanie zawieszania systemu. Android 10 wprowadza równoważną funkcję w usłudze SystemSuspend HIDL. Ta usługa znajduje się w pliku obrazu systemu i jest obsługiwana przez platformę Android. Logika z libsuspend pozostaje w dużej mierze taka sama, z tym że każdy proces w przestrzeni użytkownika blokujący zawieszenie systemu musi komunikować się z SystemSuspend.

libsuspend i libpower

W Androidzie 10 usługa SystemSuspend zastępuje usługę libsuspend. Interfejs libpower został ponownie zaimplementowany, aby używać usługi SystemSuspend zamiast /sys/power/wake[un]lock bez zmiany interfejsu C API.

Ten pseudokod pokazuje, jak zaimplementować funkcje acquire_wake_lock i release_wake_lock.


static std::unordered_map<std::string, sp<IWakeLock>> gWakeLockMap;

int acquire_wake_lock(int, const char* id) {
    ...
    if (!gWakeLockMap[id]) {
        gWakeLockMap[id] = suspendService->acquireWakeLock(WakeLockType::PARTIAL, id);
    }
    ...
    return 0;
}

int release_wake_lock(const char* id) {
    ...
    if (gWakeLockMap[id]) {
        auto ret = gWakeLockMap[id]->release();
        gWakeLockMap[id].clear();
        return 0;
    }
    ...
    return -1;
}

Wątek wykonania

Usługa SystemSuspend śledzi liczbę blokad aktywacji wydanych z licznikiem zawieszenia. Ma 2 wątki wykonania:

  • Wątek główny odpowiada na wywołania Binder.
  • Wątek suspend kontroluje zawieszenie systemu.

Wątek główny

Wątek główny odpowiada na żądania klientów dotyczące przydzielenia nowych blokad aktywacji, zwiększając lub zmniejszając licznik zawieszenia.

Zawieszanie wątku

Wątek zawieszenia wykonuje w pętli te czynności:

  1. Czytaj z /sys/power/wakeup_count.
  2. Uzyskaj mutex. Dzięki temu wątek zawieszenia nie dotyka licznika zawieszenia, gdy wątek główny próbuje go zwiększyć lub zmniejszyć. Wątek główny jest blokowany podczas wydawania lub usuwania blokad budzenia, gdy licznik zawieszenia osiągnął wartość 0, a wątek zawieszenia próbuje się uruchomić.
  3. Zaczekaj, aż licznik osiągnie wartość 0.
  4. Zapisz w tym pliku wartość odczytaną z pliku /sys/power /wakeup_count (z kroku 1). Jeśli zapis się nie powiedzie, wróć na początek pętli.
  5. Aby zawiesić system, wpisz mem do /sys/power/state.
  6. Zwolnij semafor.

Gdy żądanie blokady aktywacji zostanie pomyślnie zwrócone, wątek zawieszenia jest zablokowany.

Rysunek 1. Wstrzymanie pętli wątku

SystemSuspend API

Interfejs SystemSuspend API składa się z 2 interfejsów. Interfejs HIDL jest używany przez natywne procesy do uzyskiwania blokad budzenia, a interfejs AIDL jest używany do komunikacji między SystemServerem a SystemSuspend.

Interfejs ISystemSuspend HIDL


enum WakeLockType : uint32_t {
    PARTIAL,
    FULL
};

interface IWakeLock {
    oneway release();
};

interface ISystemSuspend {
    acquireWakeLock(WakeLockType type, string debugName)
        generates (IWakeLock lock);
};

Każdy klient, który poprosi o blokadę aktywacji, otrzyma unikalną instancję IWakeLock. Jest to inne niż /sys/power/wake_lock, które pozwala wielu klientom używać blokady aktywacji o tej samej nazwie. Jeśli klient, który posiada instancję IWakeLock, zostanie zamknięty, usługa binder i SystemSuspend ją oczyszczą.

Interfejs AIDL ISuspendControlService

Interfejs ISuspendControlService jest przeznaczony tylko dla SystemServer.


interface ISuspendCallback {
     void notifyWakeup(boolean success);
}

interface ISuspendControlService {
    boolean enableAutosuspend();
    boolean registerCallback(ISuspendCallback callback);
    boolean forceSuspend();
}

Korzystanie z Androida HIDL zapewnia te korzyści:

  • Jeśli proces blokowania zawieszenia się zatrzyma, SystemSuspend może zostać o tym powiadomiony.
  • Wątek odpowiedzialny za zawieszanie systemu może zostać wywołany ponownie.