W Androidzie 9 i starszych wersjach za inicjowanie zawieszenia systemu odpowiada wątek w libsuspend. Android 10 wprowadza równoważną funkcję w usłudze HIDL SystemSuspend.
Ta usługa znajduje się w obrazie systemu i jest obsługiwana przez platformę Android.
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 usługą SystemSuspend.
libsuspend i libpower
W Androidzie 10 usługa SystemSuspend zastępuje libsuspend. Interfejs libpower został ponownie zaimplementowany, aby korzystać z usługi SystemSuspend zamiast /sys/power/wake[un]lock bez zmiany interfejsu C API.
Ten pseudokod pokazuje, jak wdrożyć 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 wybudzania wydanych za pomocą licznika 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
Główny wątek odpowiada na żądania klientów dotyczące przydzielania nowych blokad wybudzania, zwiększając lub zmniejszając licznik zawieszenia.
Zawieszanie wątku
Wątek zawieszania wykonuje w pętli te czynności:
- Czytaj od
/sys/power/wakeup_count. - Uzyskaj dostęp do muteksu. Dzięki temu wątek zawieszający nie będzie modyfikować licznika zawieszeń, gdy wątek główny będzie próbował go zwiększyć lub zmniejszyć. Wątek główny jest blokowany podczas wydawania lub usuwania blokad wybudzania, gdy licznik zawieszenia osiągnie zero, a wątek zawieszenia próbuje się uruchomić.
- Poczekaj, aż licznik osiągnie wartość zero.
- Zapisz w tym pliku wartość odczytaną z
/sys/power /wakeup_count(z kroku 1). Jeśli zapis się nie powiedzie, wróć na początek pętli. - Aby rozpocząć zawieszanie systemu, wpisz
memw/sys/power/state. - Zwolnij mutex.
Gdy żądanie blokady uśpienia zostanie rozpatrzone pozytywnie, wątek zawieszenia zostanie zablokowany.
SystemSuspend API
Interfejs SystemSuspend API składa się z 2 interfejsów. Interfejs HIDL jest używany przez procesy natywne do uzyskiwania blokad wybudzania, a interfejs AIDL służy do komunikacji między SystemServer a SystemSuspend.
Interfejs HIDL ISystemSuspend
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ę uśpienia, otrzyma unikalną instancję IWakeLock. Różni się to od /sys/power/wake_lock, które umożliwia wielu klientom korzystanie z blokady uśpienia pod tą samą nazwą. Jeśli klient korzystający z instancji IWakeLock zostanie zamknięty, sterownik Binder i usługa SystemSuspend usuną tę instancję.
Interfejs ISuspendControlService AIDL
Interfejs ISuspendControlService jest przeznaczony do używania tylko przez serwer systemowy.
interface ISuspendCallback {
void notifyWakeup(boolean success);
}
interface ISuspendControlService {
boolean enableAutosuspend();
boolean registerCallback(ISuspendCallback callback);
boolean forceSuspend();
}
Korzystanie z interfejsu HIDL w Androidzie ma te zalety:
- Jeśli proces blokujący zawieszenie zostanie zakończony, można o tym powiadomić SystemSuspend.
- Wątek odpowiedzialny za zawieszenie systemu może otrzymać wywołanie zwrotne.