Dois eventos principais desencadeiam a entrada em suspensão (após executar operações necessárias antes de dormir):
- Evento da tecla Power: invoca
goToSleepFromNative()no PowerManagerService via JNI. - Timeout: definido nas configurações (Ajustes → Tela → Suspensão).
O fluxo de suspensão no PowerManagerService é iulstrado abaixo:
Ambos os caminhos evetnualmente chamam updatePowerStateLocked(), que, após atualizar flags e enviar notificações, invoca updateSuspendBlockerLocked() para liberar os locks de suspensão.
Processamento do Timeout no PowerManagerService
private void atualizarResumoAtividadeUsuarioLocked(long agora, int sujeira) {
if ((sujeira & (SUJEIRA_ATIVIDADE_USUARIO | SUJEIRA_VIGILIA | SUJEIRA_CONFIGURACOES)) != 0) {
mHandler.removeMessages(MSG_TIMEOUT_ATIVIDADE_USUARIO);
long proximoTimeout = 0;
if (mVigilia != VIGILIA_DORMINDO) {
final int timeoutTelaDesligada = obterTimeoutTelaDesligadaLocked();
final int duracaoDim = obterDuracaoDimLocked(timeoutTelaDesligada);
mResumoAtividadeUsuario = 0;
if (mUltimaAtividadeUsuario >= mUltimoDespertar) {
proximoTimeout = mUltimaAtividadeUsuario + timeoutTelaDesligada - duracaoDim;
if (agora < proximoTimeout) {
mResumoAtividadeUsuario |= ATIVIDADE_TELA_CLARA;
} else {
proximoTimeout = mUltimaAtividadeUsuario + timeoutTelaDesligada;
if (agora < proximoTimeout) {
mResumoAtividadeUsuario |= ATIVIDADE_TELA_DIM;
}
}
}
if (mResumoAtividadeUsuario == 0 && mUltimaAtividadeSemMudancaLuz >= mUltimoDespertar) {
proximoTimeout = mUltimaAtividadeSemMudancaLuz + timeoutTelaDesligada;
if (agora < proximoTimeout && mRequisicaoExibicao.estadoTela != ESTADO_TELA_DESLIGADA) {
mResumoAtividadeUsuario = (mRequisicaoExibicao.estadoTela == ESTADO_TELA_CLARA) ?
ATIVIDADE_TELA_CLARA : ATIVIDADE_TELA_DIM;
}
}
if (mResumoAtividadeUsuario != 0) {
Message msg = mHandler.obtainMessage(MSG_TIMEOUT_ATIVIDADE_USUARIO);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, proximoTimeout);
}
} else {
mResumoAtividadeUsuario = 0;
}
}
}
Manipulação do evento MSG_TIMEOUT_ATIVIDADE_USUARIO:
private final class PowerManagerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_TIMEOUT_ATIVIDADE_USUARIO:
lidarComTimeoutAtividadeUsuario();
break;
}
}
}
private void lidarComTimeoutAtividadeUsuario() {
mSujeira |= SUJEIRA_ATIVIDADE_USUARIO;
atualizarEstadoEnergiaLocked();
}
Aquisição e Liberação dos Locks de Suspensão no PowerManagerService
private void atualizarEstadoEnergiaLocked() {
if (!mSistemaPronto || mSujeira == 0) {
return;
}
// Fase 0: Atualizações básicas de estado.
// Fase 1: Atualiza vigília.
// Fase 2: Atualiza sonhos e estado da tela.
// Fase 3: Envia notificações, se necessário.
// Fase 4: Atualiza bloqueador de suspensão.
atualizarBloqueadorSuspensaoLocked();
}
private void atualizarBloqueadorSuspensaoLocked() {
final boolean precisaBloqueadorWakeLock = ((mResumoWakeLock & WAKE_LOCK_CPU) != 0);
final boolean precisaBloqueadorTela = precisaBloqueadorTela();
// Adquire bloqueadores se necessário
if (precisaBloqueadorWakeLock && !mSegurandoBloqueadorWakeLock) {
mBloqueadorWakeLock.adquirir();
mSegurandoBloqueadorWakeLock = true;
}
if (precisaBloqueadorTela && !mSegurandoBloqueadorTela) {
mBloqueadorTela.adquirir();
mSegurandoBloqueadorTela = true;
}
// Libera bloqueadores se necessário
if (!precisaBloqueadorWakeLock && mSegurandoBloqueadorWakeLock) {
mBloqueadorWakeLock.liberar();
mSegurandoBloqueadorWakeLock = false;
}
if (!precisaBloqueadorTela && mSegurandoBloqueadorTela) {
mBloqueadorTela.liberar();
mSegurandoBloqueadorTela = false;
}
}
private final class BloqueadorSuspensaoImpl implements SuspendBlocker {
private final String mNome;
private int mContadorReferencia;
public BloqueadorSuspensaoImpl(String nome) {
mNome = nome;
}
@Override
public void adquirir() {
synchronized (this) {
mContadorReferencia++;
if (mContadorReferencia == 1) {
nativoAdquirirBloqueadorSuspensao(mNome);
}
}
}
@Override
public void liberar() {
synchronized (this) {
mContadorReferencia--;
if (mContadorReferencia == 0) {
nativoLiberarBloqueadorSuspensao(mNome);
}
}
}
}
A aquisição e liberação dos locks de suspensão são realizadsa através de chamadas JNI que escrevem nos arquivos /sys/power/wake_lock e /sys/power/wake_unlock:
// Interface JNI
// (com_android_server_power_PowerManagerService.cpp)
static void nativoAdquirirBloqueadorSuspensao(JNIEnv *env, jclass clazz, jstring nomeStr) {
ScopedUtfChars nome(env, nomeStr);
adquirir_wake_lock(PARTIAL_WAKE_LOCK, nome.c_str());
}
// Definição dos arquivos a serem acessados
// (power.c)
const char *const NOVOS_CAMINHOS[] = {
"/sys/power/wake_lock",
"/sys/power/wake_unlock",
};
// Inicialização dos descritores de arquivo
static inline void inicializar_fds(void) {
if (g_inicializado == 0) {
if (abrir_descriptores_arquivo(NOVOS_CAMINHOS) < 0)
abrir_descriptores_arquivo(CAMINHOS_ANTIGOS);
g_inicializado = 1;
}
}
static int abrir_descriptores_arquivo(const char * const caminhos[]) {
int i;
for (i=0; i<QUANTIDADE_FDS; i++) {
int fd = open(caminhos[i], O_RDWR);
if (fd < 0) {
fprintf(stderr, "erro fatal ao abrir \"%s\"\n", caminhos[i]);
g_erro = errno;
return -1;
}
g_fds[i] = fd;
}
g_erro = 0;
return 0;
}
// id é o nome do lock; após abrir, escreve no dispositivo
int adquirir_wake_lock(int lock, const char* id) {
inicializar_fds();
if (g_erro) return g_erro;
int fd;
if (lock == PARTIAL_WAKE_LOCK) {
fd = g_fds[ADQUIRIR_WAKE_LOCK_PARCIAL];
} else {
return EINVAL;
}
return write(fd, id, strlen(id));
}