_前段时间盆友问我怎么实现关机功能,于是我就开始捣鼓,第一个想法就是发送关机广播,然而遇到了些问题,一是获取不到关机权限,二是Intent.ACTION_REQUEST_SHUT_DOWN和Intent.SHUTDOWN这两个属性不对上层开放。一条路行不通就换条走吧,既然没对上层提供shutdown()函数,那就用反射的方法来取,结果还是因为没有关机权限而无法调用shutdown()函数。无奈只好决定来看下源码,分析关机流程。这里主要是分析正常的关机流程。画了个简单的调用流程图。
接下来一步步分析。我们正常关机一般就是长按关机键,然后系统弹出个关机对话框
选择Power Off则开启关机。
长按关机键(KEYCODE_POWER),PhoneWindowManager.java拦截该事件,做出处理
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
if (!mSystemBooted) {
// If we have not yet booted, don't let key events do anything.
return 0;
}
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int keyCode = event.getKeyCode();
switch(keycode){
case KeyEvent.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;
}
case KeyEvent.KEYCODE_SLEEP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!mPowerManager.isInteractive()) {
useHapticFeedback = false; // suppress feedback if already non-interactive
}
if (down) {
sleepPress(event.getEventTime());
} else {
sleepRelease(event.getEventTime());
}
break;
}
case KeyEvent.KEYCODE_WAKEUP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
break;
}
}
}
down事件:
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
//如果电源键尚未处理,则检测是短按,长按,或多按操作中的哪一种,再决定做下一步处理。
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mScreenshotChordVolumeUpKeyTriggered;
if (!mPowerKeyHandled) {
//手机处于激活(亮屏)状态
if (interactive) {
// When interactive, we're already awake.
// Wait for a long press or for the button to be released to decide what to do.
//检查是否是长按操作
//hasLongPressOnPowerBehavior=getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
if (hasLongPressOnPowerBehavior()) {
//true
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
//发送消息通知主线程处理长按操作
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
}
//手机待机状态
else {
//唤醒手机
wakeUpFromPowerKey(event.getDownTime());
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
mBeganFromNonInteractive = true;
} else {
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
}
主线程接收到长按的消息并做出下一步处理
private class PolicyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_POWER_LONG_PRESS:
//调用powerPress()函数
powerLongPress();
break;
}
}
}
那接着来看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;
}
}
这里根据长按的行为behavior来分情况处理的。
情况一:LONG_PRESS_POWER_NOTHING:不处理
情况二:LONG_PRESS_POWER_GLOBAL_ACTIONS:正常关机流程
void showGlobalActionsInternal() {
//请求ActivityManagerNative关闭系统所有窗口
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
//初始化GlobalActions
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
//显示关机对话框
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
if (keyguardShowing) {
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
(1)sendCloseSystemWindows();//发送请求关闭系统的对话框
void sendCloseSystemWindows(String reason) {
PhoneWindow.sendCloseSystemWindows(mContext, reason);
}
调用的是PhoneWindow.java的方法,如下:
public static void sendCloseSystemWindows(Context context, String reason) {
if (ActivityManagerNative.isSystemReady()) {
try {
ActivityManagerNative.getDefault().closeSystemDialogs(reason);
} catch (RemoteException e) {
}
}
}
继续调用ActivityManagerNative.java的方法,如下:
public void closeSystemDialogs(String reason) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.o