android在长按Power键(手机或pad)或按下电源键(电视)会进入待机状态,下面分析一下非STR待机流程。
待机分STR和非STR待机。
STR待机是为了实现快速开关机,提高开机速度。android自身支持STR,OEM可以根据自身需要进行深度定制。STR待机时,PM芯片仍然供电,其他断电,当STR开机时,从PM芯片唤醒,回复到STR待机前状态,从而提高开机速度。
非STR待机即正常待机,待机后会彻底断电,这里只分析android层的行为。
1 PhoneWindowManager处理逻辑
按键在给到WindowManagerService之前,会先给到PhoneWindowManager进行处理。
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
//KeyEvent入队
...
case KeyEvent.KEYCODE_POWER: {
Log.d(TAG,"KEYCODE_POWER.....");
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
在key Down时,执行interceptPowerKeyDown逻辑,这段逻辑会处理正常待机流程。 在key Up时,执行interceptPowerKeyUp逻辑,这段逻辑出来STR待机流程。
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
//获取wakeLock
mPowerKeyWakeLock.acquire();
}
...
// Latch power key state to detect screenshot chord.
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
//截图,STR和非STR待机都会执行截图,截图为了在待机时画面看起来很和谐
interceptScreenshotChord();
}
...
// When interactive, we're already awake.
// Wait for a long press or for the button to be released to decide what to do.
if (hasLongPressOnPowerBehavior()) { //如果不是LONG_PRESS_POWER_NOTHING
if (enable_str == false) { //非STR
if (mShortPressOnPowerBehavior < 0) { //如果等于0,重新赋值
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
}
//发送MSG_POWER_LONG_PRESS,处理待机事件
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
//是否延迟待机
mHandler.sendMessageDelayed(msg,
(mShortPressOnPowerBehavior == SHORT_PRESS_POWER_SHUT_DOWN) ?
0 : ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
config_shortPressOnPowerBehavior默认值是1,在frameworks\base\core\res\res\values\config.xml中有定义:
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
2 - Really go to sleep (don't doze)
3 - Really go to sleep and go home (don't doze)
-->
<integer name="config_shortPressOnPowerBehavior">1</integer>
1表示会弹出一个系统Dialog提示用户选择重启还是关机。 2表示直接待机,不会提示用户。 3看字面意思是会先回到home再去待机,也不会给用户提示。 case MSG_POWER_LONG_PRESS:
powerLongPress();
对于handler消息的处理会call powerLongPress函数,具体实现如下:
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
showGlobalActionsInternal();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
}
}
在我现在的平台,在overlay的config.xml配置config_shortPressOnPowerBehavior等于2,即会直接待机,不会走showGlobalActionsInternal。
2 WindowManagerService处理逻辑
WindowManagerService有实现WindowManagerPolicy.WindowManagerFuncs函数,所以在PhoneWindowManager中调用mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);时会call到WindowManagerService的shutdown函数。
// Called by window manager policy. Not exposed externally.
@Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, confirm);
}
看起来WindowManagerService只是一个封装,最后call到ShutdownThread的shutdown函数。 这里的confirm参数根据resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF来看是等于true.
3 ShutdownThread处理逻辑
ShutDownThread是一个Thread,对于shutdown函数的实现如下:
/**
* Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI
* is shown.
*
* @param context Context used to display the shutdown progress dialog.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
看函数的解释是必须要在有Looper并且可以显示UI的thread去call这个函数。
static void shutdownInner(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
final int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
}
根据上面提到的confirm参数是true,那么会走上面的逻辑,但是上面的逻辑会显示dialog,在我的平台,待机是不会显示dialog的,需要加打印确定下这个值是否为true. 在confirm为false或点击Dialog的OK按钮,会调用beginShutdownSequence函数,开始进入待机时序。
private static void beginShutdownSequence(Context context) {
...
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
sInstance.start();
}
// static instance of this thread
private static final ShutdownThread sInstance = new ShutdownThread(); 调用ShutdownThread的start函数,执行run函数(线程):
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
public void run() {
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// We don't allow apps to cancel this, so ignore the result.
actionDone();
}
};
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
* the beginning of the SystemServer startup.
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
} catch (RemoteException e) {
}
}
Log.i(TAG, "Shutting down package manager...");
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
}
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
// Shutdown MountService to ensure media is in a safe state
IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
public void onShutDownComplete(int statusCode) throws RemoteException {
Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
actionDone();
}
};
Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
mActionDone = false;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
try {
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.checkService("mount"));
if (mount != null) {
mount.shutdown(observer);
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
}
while (!mActionDone) {
long delay = endShutTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
rebootOrShutdown(mReboot, mRebootReason);
}
主要的逻辑: 1)发送待机广播 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,UserHandle.ALL, null, br, mHandler, 0, null, null); 这里感觉不应该用Order的方式,否则会导致有些app无法收到SHUTDOWN广播。 2)ActivityManagerService shutdown 3)PackageManagerService shutdown 4)Shutdown radios
/**
* Do not call this directly. Use {@link #reboot(Context, String, boolean)}
* or {@link #shutdown(Context, boolean)} instead.
*
* @param reboot true to reboot or false to shutdown
* @param reason reason for reboot
*/
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}
如果是重启,会call到PowerManagerService.lowLevelReboot(reason); 这里是关机,会call到PowerManagerService.lowLevelShutdown();
4 PowerManagerService处理逻辑
PowerManagerService的实现很简单,直接call到JNI层。
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
nativeShutdown();
}
5 PowerManagerService JNI层处理逻辑
PowerManagerService JNI对应的文件: com_android_server_power_PowerManagerService.cpp
static void nativeShutdown(JNIEnv *env, jclass clazz) {
if (gPowerModule && gPowerModule->shutdown) {
gPowerModule->shutdown(gPowerModule);
}
}
直接call到Power HAL层
6 Power HAL层处理逻辑
Power HAL层对应的文件power.cpp struct power_module HAL_MODULE_INFO_SYM = {
.. .shutdown = power_shutdown
} power_shutdown函数实现call到我司中间层的enterSleepMode函数(基于保密原则,无法公开代码)
Android部分待机流程分析完毕,1-3步是android标准流程,4-6步是我司客制化部分,但是流程应该都是大同小异。