Zawieszenie systemu

W Androidzie 9 i starszych jest wątek w libsuspend odpowiedzialny za zawieszanie systemu. Android 10 wprowadza równoważną funkcję w usłudze SystemSuspend HIDL. Ta usługa znajduje się w obrazie systemu i jest obsługiwana przez platformę Androida. Logika z libsuspend pozostaje w dużej mierze taka sama, z tym wyjątkiem, ż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 interfejsu /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ątki 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.
  • Zawieszanie powoduje zawieszenie wątku w systemie.

Wątek główny

Wątek główny odpowiada na żądania od klientów, aby przydzielić nowe blokady uśpienia, zwiększając/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. Przejęcie muteksa. 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 nakładania lub usuwania blokad uśpienia, gdy licznik zawieszenia osiągnie 0, a wątek zawieszenia próbuje 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. Rozpocznij zawieszanie systemu, wpisując mem w /sys/power/state.
  6. Zwolnij semafor.

Po zwróceniu żądania blokady uśpienia wątek zawieszenia wątku jest blokowany.

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 ISuspensionControlService AIDL

Interfejs ISuspendControlService jest przeznaczony do używania tylko przez 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 zawiesza się, można powiadomić o tym fakcie.
  • Wątek odpowiedzialny za zawieszanie systemu może zostać wywołany ponownie.