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:
- Read from
/sys/power/wakeup_count
. - 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.
- Wait until the counter is equal to zero.
- 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 - Start the system suspend by writing
mem
to/sys/power/state
. - Release the mutex.
When a request for a wake lock successfully returns, the suspend thread is blocked.
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.