SystemSuspend service

In Android 9 and lower there is a thread in libsuspend responsible for initiating system suspend. Android 10 introduces an equivalent functionality in a SystemSuspend HIDL service. This service is located in the system image and is served by the Android platform. The logic from libsuspend remains largely the same, except every userspace process blocking the system suspend needs to communicate with SystemSuspend.

libsuspend and libpower

In Android 10, SystemSuspend service replaces libsuspend. libpower was reimplemented to rely on the SystemSuspend service instead of /sys/power/wake[un]lock without changing the C API.

This pseudocode shows how to implement acquire_wake_lock and 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;
}

Execution threads

The SystemSuspend service keeps track of the number of wake locks issued with a suspend counter. It has two threads of execution:

  • The main thread answers binder calls.
  • The suspend thread controls system suspend.

Main thread

The main thread answers requests from clients to allocate new wake locks, incrementing/decrementing the suspend counter.

Suspend thread

The suspend thread performs the following in a loop:

  1. Read from /sys/power/wakeup_count.
  2. Acquire the mutex. This makes sure that the suspend thread doesn't touch the suspend counter while the main thread is trying to increment or decrement it. The main thread is blocked on issuing or removing wake locks when the suspend counter has reached zero and the suspend thread is trying to run.
  3. Wait until the counter is equal to zero.
  4. Write the value read from /sys/power /wakeup_count (from step 1) to this file. If the write fails, return to the beginning of the loop
  5. Start the system suspend by writing mem to /sys/power/state.
  6. Release the mutex.

When a request for a wake lock successfully returns, the suspend thread is blocked.

Figure 1. Suspend thread loop

SystemSuspend API

The SystemSuspend API consists of two interfaces. The HIDL interface is used by native processes to acquire wake locks and the AIDL interface is used for communication between SystemServer and SystemSuspend.

ISystemSuspend HIDL interface


enum WakeLockType : uint32_t {
    PARTIAL,
    FULL
};

interface IWakeLock {
    oneway release();
};

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

Each client that requests a wake lock receives a unique IWakeLock instance. This is different from /sys/power/wake_lock, which allows multiple clients to use the wake lock under the same name. If a client holding an IWakeLock instance terminates, the binder driver and SystemSuspend service cleans it up.

ISuspendControlService AIDL interface

ISuspendControlService is intended to be used only by SystemServer.


interface ISuspendCallback {
     void notifyWakeup(boolean success);
}

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

Leveraging the Android HIDL offers the following benefits:

  • If a suspend-blocking process dies, SystemSuspend can be notified.
  • The thread responsible for system suspend can be given a callback.