Service SystemSuspend

Sous Android 9 ou version antérieure, un thread dans libsuspend est chargé de lancer la suspension du système. Android 10 introduit une fonctionnalité équivalente dans un service HIDL SystemSuspend. Ce service se trouve dans l'image système et est diffusé par la plate-forme Android. La logique de libsuspend reste en grande partie la même, à l'exception de chaque processus d'espace utilisateur bloquant la suspension du système qui doit communiquer avec SystemSuspend.

libsuspend et libpower

Sous Android 10, le service SystemSuspend remplace libsuspend. libpower a été réimplémenté pour s'appuyer sur le service SystemSuspend au lieu de /sys/power/wake[un]lock, sans modifier l'API C.

Ce pseudo-code montre comment implémenter acquire_wake_lock et 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;
}

Threads d'exécution

Le service SystemSuspend suit le nombre de réveils émis avec un compteur de suspension. Il comporte deux threads d'exécution:

  • Le thread principal répond aux appels de liaison.
  • Le thread suspend contrôle la suspension du système.

Thread principal

Le thread principal répond aux requêtes des clients pour allouer de nouveaux wake locks, en incrémentant/décrémentant le compteur de suspension.

Suspendre le thread

Le thread de suspension effectue les opérations suivantes dans une boucle:

  1. Lire à partir de /sys/power/wakeup_count
  2. Acquérir le mutex. Cela garantit que le thread de suspension ne touche pas le compteur de suspension lorsque le thread principal tente de l'incrémenter ou de le diminuer. Le thread principal est bloqué lors de l'émission ou de la suppression de verrous de réveil lorsque le compteur de suspension a atteint zéro et que le thread suspend tente de s'exécuter.
  3. Attendez que le compteur soit égal à zéro.
  4. Écrivez la valeur lue à partir de /sys/power /wakeup_count (à partir de l'étape 1) dans ce fichier. Si l'écriture échoue, revenez au début de la boucle.
  5. Démarrez la suspension du système en écrivant mem dans /sys/power/state.
  6. Libérez le mutex.

Lorsqu'une requête de verrouillage de réveil aboutit, le thread de suspension est bloqué.

Figure 1. Suspendre la boucle de thread

API SystemSuspend

L'API SystemSuspend se compose de deux interfaces. L'interface HIDL est utilisée par les processus natifs pour acquérir des wake locks, et l'interface AIDL est utilisée pour la communication entre SystemServer et SystemSuspend.

Interface HIDL ISystemSuspend


enum WakeLockType : uint32_t {
    PARTIAL,
    FULL
};

interface IWakeLock {
    oneway release();
};

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

Chaque client qui demande un wake lock reçoit une instance IWakeLock unique. Cela diffère de /sys/power/wake_lock, qui permet à plusieurs clients d'utiliser le verrouillage de réveil sous le même nom. Si un client qui détient une instance IWakeLock s'arrête, le pilote de liaison et le service SystemSuspend la nettoient.

Interface AIDL ISuspendControlService

ISuspendControlService n'est destiné qu'à SystemServer.


interface ISuspendCallback {
     void notifyWakeup(boolean success);
}

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

L'utilisation d'Android HIDL présente les avantages suivants:

  • Si un processus bloquant la suspension meurt, SystemSuspend peut en être informé.
  • Un rappel peut être attribué au thread responsable de la suspension du système.