Andriod系统关机流程-从按下关机键到完成关机任务
一、
正常关机一般就是长按电源键,然后系统弹出个对话框,选择Power off则开始关机。电源键是系统级别的按键,所以对按键事件的响应不在某一个app中,而是在PhoneWindowManager的dispatchUnhandledKey方法中。
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java:
/** {@inheritDoc} */
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
// Note: This method is only called if the initial down was unhandled.
...
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
...
}
系统按键的处理逻辑被下放到了interceptFallback方法中。
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
通过分析interceptFallback方法的源码,发现电源按键的处理逻辑在interceptKeyBeforeQueueing方法中,所以继续看一下interceptKeyBeforeQueueing方法。
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
if (!mSystemBooted) {
//如果没有boot,则不做处理
// If we have not yet booted, don't let key events do anything.
return 0;
}
/// M: If USP service freeze display, disable power key
if (interceptKeyBeforeHandling(event)) {
//如果USP服务冻结显示,禁用电源键
return 0;
}
/// M: power-off alarm, disable power_key @{
if (KeyEvent.KEYCODE_POWER == event.getKeyCode() && mIsAlarmBoot) {
//断电警告,禁用电源键
return 0;
}
/// @}
...
//add by liuhao for home key to wakeup
if ( keyCode == KeyEvent.KEYCODE_HOME && !isScreenOn()) {
//添加用home键唤醒手机的功能
policyFlags |= WindowManagerPolicy.FLAG_WAKE;
}
...
// Handle special keys.
switch (keyCode) {
...
case KeyEvent.KEYCODE_POWER: {
//关于电源按键的处理逻辑
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
/* 360OS begin */
/* add for FEATURE_SYS_SCREENRECORD FEATURE_SYSTEMUI_MISTOUCH_PREVENTION */
if (interactive && mQikuPhoneWindowManager != null && !mQikuPhoneWindowManager.ismPowerKeyTriggered()
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
long keyPowerForMistouchAndScreenRecord =
mQikuPhoneWindowManager.qiku_keyPowerForMistouchAndScreenRecord(SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS,event.getDownTime());
if(keyPowerForMistouchAndScreenRecord == -1){
break;
}
mQikuPhoneWindowManager.qiku_interceptScreenshotLog(mScreenshotChordEnabled,SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS);
}
/* 360OS end */
interceptPowerKeyDown(event, interactive);
//当电源按下时调用了interceptPowerKeyDown,在这之前360OS做了自定义
} else {
//360OS begin
if (SystemProperties.getBoolean("gesture.enable_sos_launch", false) ) {
touchToSos();
}
//360OS end
/* 360OS begin */
/* add for FEATURE_SYS_SCREENRECORD */
if (null != mQikuPhoneWindowManager) {
mQikuPhoneWindowManager.qiku_cancelPendingStartAndsetmPowerKeyTriggered();
}
/* 360OS end */
interceptPowerKeyUp(event, interactive, canceled);
//当电源键抬起时调用了interceptPowerKeyUp,在这之前360OS也做了自定义
}
break;
}
...
}
}
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
//add by topwise liudongming begin
String bootReason = SystemProperties.get("sys.boot.reason.ext");
boolean ret = (bootReason != null && bootReason.equals("1")) ? true : false;
if (!ret) {
//add by topwise liudongming end
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
// mPowerKeyWakeLock为PARTIAL_WAKE_LOCK级别的锁
mPowerKeyWakeLock.acquire();
//将调用到PowerManagerService的acquire WakeLock流程
}
// Cancel multi-press detection timeout.
//处理多次按power键的场景
//每次power up时,发送MSG_POWER_DELAYED_PRESS的延迟消息
//如果延迟消息被处理,说明一次完整的Power键处理结束(按下去,弹起来)
//在延迟消息被处理前,再次按power键,就检测到多次点击了
if (mPowerKeyPressCounter != 0) {
mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
}
// Detect user pressing the power button in panic when an application has
// taken over the whole screen.
当应用程序接管整个屏幕时,检测用户在紧急情况下按下电源按钮。误触?
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));
if (panic) {
mHandler.post(mHiddenNavPanic);
}
// Latch power key state to detect screenshot chord.
//如果当前是亮屏状态,且满足触发截屏的条件,触发截屏功能
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
//如果有电话呼入
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
//设置电话响铃静音
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
//如果正在接听电话,配置了Power键挂断电话,当前是亮屏状态
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
//按Power按键挂断正在接听的电话
}
}
GestureLauncherService gestureService = LocalServices.getService(GestureLauncherService.class);
boolean gesturedServiceIntercepted = false;
if (gestureService != null) {
//手势对应的服务,尝试拦截处理Power键动作事件
//360OS begin
if (!SystemProperties.getBoolean("gesture.enable_sos_launch", false)) {
gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, mTmpBoolean);
}
//360OS end
if (mTmpBoolean.value && mGoingToSleep) {
mCameraGestureTriggeredDuringGoingToSleep = true;
}
}
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
//如果电源按键事件还没有被处理,开始处理短按、长按、多次按下事件
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
//之前是否进行处理的标志位
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.
if (hasLongPressOnPowerBehavior()) {
//1、判断是否支持长按行为
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
//2、发送MSG_POWER_LONG_PRESS消息
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
//3、发送delay消息,给用户一些时间长按power键显示关机dialog, 同时delay时间也可以通过config.xml中配置
}
} else {
//当前不是亮屏状态
wakeUpFromPowerKey(event.getDownTime());
//唤醒系统
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
//1、判断支持长按行为,并且支持息屏长按
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
//2、发送MSG_POWER_LONG_PRESS消息
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
mBeganFromNonInteractive = true;
//3、设置从息屏状态开始的标识为true
} else {
final int maxCount = getMaxMultiPressPowerCount();
//多次按下计数,默认返回1
if (maxCount <= 1) {
//息屏时,按下power键(不弹起),仅消耗掉该事件
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
//设置从息屏状态开始的标识为true
}
}
}
}
}
}
在if(interactive)分支,我们发送一个一个异步消息,并且msg的what为MSG_POWER_LONG_PRESS,即长按电源事件的异步消息。看一下mHandler的handleMessage方法对该what消息的处理逻辑。
private class PolicyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_POWER_LONG_PRESS:
powerLongPress();
break;
...
}
}
}
当msg的what为MSG_POWER_LONG_PRESS时我们调用了powerLongPress方法,这个方法应该就是处理电源按键长按的逻辑。
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
//根据长按行为behavior来分情况处理
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
//长按Power键不作处理
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
//为全局动作,显示关机Dialog,正常流程
mPowerKeyHandled = true;
标识按下电源键的事件已经进行处理
if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
showGlobalActionsInternal();
//显示全局的dialog
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;
}
}
正常关机流程调用showGlobalActionsInternal()方法,弹出选择要进行的操作界面。
void showGlobalActionsInternal() {
qiku_showGlobalActionsInternal();
}
/* 360OS begin */
void qiku_showGlobalActionsInternal(){
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
//请求ActivityManagerNative关闭系统所有窗口
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
//keyguard是否在显示
/* 360OS begin */
mQikuPhoneWindowManager.qiku_showGlobalActionsInternal(mContext, mWindowManagerFuncs, keyguardShowing, isDeviceProvisioned());
//360OS定制的方法,目的也是显示关机对话框
/* 360OS end */
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);
//通知power发生了一次用户时间, 让用户可以看到dialog
}
}
/* 360OS end */
首先调用了sendCloseSystemWindows方法,该方法用于关机系统弹窗,比如输入法,壁纸等。关键函数在于360OS定制的一个函数,源生安卓在这里先创建并初始化GlobalActions,然后调用了GlobalActions的方法:
if (mGlobalActions == null) {
//初始化GlobalActions
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
其作用是显示关机对话框,360OS源码没法查看,但是可以肯定的是360OS定制了自己的一些UI,显示关机对话框是其必然会有的过程,所以这里根据源生Android的代码继续进行分析。
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java:
/**
* Show the global actions dialog (creating if necessary)
* @param keyguardShowing True if keyguard is showing
*/
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null) {
//已经显示对话框
mDialog.dismiss();
mDialog = null;
// Show delayed, so that the dismiss of the previous dialog completes
mHandler.sendEmptyMessage(MESSAGE_SHOW);
} else {
handleShow();
//如果不存在mDialog, 就调用handleShow处理
}
}
在showDialog方法中我们首先判断mDialog是否为空,若为空则发送msg的what为MESSAGE_SHOW的异步消息,否则调用handleShow方法。
private void handleShow() {
awakenIfNecessary();
mDialog = createDialog();
//创建mDialog对象
prepareDialog();
//准备dialog
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
// 对话框视图只有一个item,当手单按时回调onPress()函数
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
//调用onPress函数
} else {
//否则显示关机对话框,设置对话框参数属性
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("GlobalActions");
mDialog.getWindow().setAttributes(attrs);
mDialog.show();
//显示dialog
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
}
方法体中调用了createDialog方法,创建了GlobalActionsDialog类型的mDialog。
/**
* Create the global actions dialog.
* @return A new dialog.
*/
private GlobalActionsDialog createDialog() {
...
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
R.drawable.ic_lock_airplane_mode_off,
R.string.global_actions_toggle_airplane_mode,
R.string.global_actions_airplane_mode_on_status,
R.string.global_actions_airplane_mode_off_status) {
...
};
//飞行模式
onAirplaneModeChanged();
//飞行模式改变
mItems = new ArrayList
();
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
//获取action list记录在defaultActions数组中,可能包含飞行模式、关机、静音、重启等
ArraySet addedKeys = new ArraySet();
for (int i = 0; i < defaultActions.length; i++) {
//遍历defaultActions列表
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
//如果action已经在addedKeys列表中就不再添加了
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
//对action list中action进行匹配,如果能匹配上就创建对应对象放入mItems列表中。关机
mItems.add(new PowerAction());
//*封装关机操作
} else if ("reboot".equals(actionKey)){
//重启
mItems.add(new RebootAction());
//*封装重启操作
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
//飞行模式
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
//上报bug
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(new BugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
//静音模式
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
}
} else if ( ... ) {
...
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
// Add here so we don't add more than one.
addedKeys.add(actionKey);
//最后将actionKey放入列表, 不重复处理
}
mAdapter = new MyAdapter();
//创建适配器
AlertParams params = new AlertParams(mContext);
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
//创建dialog
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
触摸对话框外部,对话框消失
//给对话框列表注册监听事件
dialog.getListView().setItemsCanFocus(true);
dialog.getListView().setLongClickable(true);
dialog.getListView().setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position,
long id) {
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
return ((LongPressAction) action).onLongPress();
}
return false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.setOnDismissListener(this);
return dialog;
//将新建的dialog返回
}
首先获得操作列表,可能包含:飞行模式、关机、静音模式、重启等等;然后轮训操作列表,并添加相应的Action;最后将这个操作列表保存到Dialog的adapter中并返回该dialog;然后回到handleShow方法,在得到返回的dialog之后调用了dialog的show方法,这样就显示出了电源长按操作界面。其中PowerAction和RebootAction分别封装了关机和重启操作。
private final class PowerAction extends SinglePressAction implements LongPressAction {
...
@Override
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown(false /* confirm */);
}
}
在PowerAction类的成员函数onPress方法中调用了mWindowManagerFuncs.showdown方法执行关机操作。这里的mWindowManagerFuncs成员变量是在GlobalActions的构造方法中赋值的。
/**
* @param context everything needs a context :(
*/
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
...
mWindowManagerFuncs = windowManagerFuncs;
...
}
源生安卓代码在创建并初始化GlobalActions的时候直接传递了PhoneWindowManager的成员变量mWindowManagerFuncs。
if (mGlobalActions == null) {
//初始化GlobalActions
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
而mWindowManagerFuncs变量是在PhoneWindowManager的init方法中被初始化。
/** {@inheritDoc} */
@Override
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs) {
...
mWindowManagerFuncs = windowManagerFuncs;
...
}
再次查找到PhoneWindowManager的init方法是在WindowManagerService中被调用。
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java:
...
final WindowManagerPolicy mPolicy = new PhoneWindowManager();
//mPolicy是PhoneWindowManager的一个实例
...
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
/// M: ANR mechanism for Message History/Queue for system server @{
if ("eng".equals(Build.TYPE)) {
Looper.myLooper().setMessageLogging(
ANRAppManager.getDefault(
new ANRAppFrameworks())
.newMessageLogger(false, Thread.currentThread().getName()));
}
/// M: ANR mechanism for Message History/Queue for system server @}
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
//可以发现这里的init方法中传递的就是一个WindowManagerService的实例
}
}, 0);
}
...
那么在PowerAction的onPress方法中调用的mWindowManagerFuncs.shutdown方法,实际上调用的就是WindowManagerService的shutdown方法。
// Called by window manager policy. Not exposed externally.
@Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}
很简单地直接调用了ShutdownThread的shutdown方法。表明真正执行关机线程的是ShutdownThread.shutdown。
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java:
/**
* 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 reason code to pass to android_reboot() (e.g. "userrequested"), or null.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, String reason, boolean confirm) {
mReboot = false;
//标识不是重启
mRebootSafeMode = false;
//也不是安全模式重启
mReason = reason;
//记录关机原因
...
shutdownInner(context, confirm);
//具体关机操作
}
设置ShutdownThread的全局属性后将具体的操作下发到了shutdownInner方法中。
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);
//获取长按资源id
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();
}
bConfirmForAnimation = confirm;
Log.d(TAG, "PowerOff dialog doesn't exist. Create it first");
sConfirmDialog = new AlertDialog.Builder(context)
//需要再次确认关机, 创建dialog
.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);
}
}
对于参数context和confirm,context用来用来显示关机进程的对话,confirm如果是true表示关机之前需要显示确认关机的对话框。
longPressBehavior和resourceId是用来记录一些标志。在咱们的源码中,confirm为false,则直接执行beginShutdownSequence(),开始进入关机流程。
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
//确保单一进程操作
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
// Throw up a system dialog to indicate the device is rebooting / shutting down.
/* 360OS begin, use no 360os dialog style */
//ProgressDialog pd = new ProgressDialog(context);
ProgressDialog pd = new ProgressDialog(context, 0, true);
//初始化一个Process的dialog,用于显示关机进度
/* 360OS end */
// Path 1: Reboot to recovery for update
// Condition: mReason == REBOOT_RECOVERY_UPDATE
//
// Path 1a: uncrypt needed
// Condition: if /cache/recovery/uncrypt_file exists but
// /cache/recovery/block.map doesn't.
// UI: determinate progress bar (mRebootHasProgressBar == True)
//
// * Path 1a is expected to be removed once the GmsCore shipped on
// device always calls uncrypt prior to reboot.
//
// Path 1b: uncrypt already done
// UI: spinning circle only (no progress bar)
//
// Path 2: Reboot to recovery for factory reset
// Condition: mReason == REBOOT_RECOVERY
// UI: spinning circle only (no progress bar)
//
// Path 3: Regular reboot / shutdown
// Condition: Otherwise
// UI: spinning circle only (no progress bar)
if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
//重启进入recovery模式进行系统升级
// We need the progress bar if uncrypt will be invoked during the
// reboot, which might be time-consuming.
mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
&& !(RecoverySystem.BLOCK_MAP_FILE.exists());
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
if (mRebootHasProgressBar) {
pd.setMax(100);
pd.setProgress(0);
pd.setIndeterminate(false);
pd.setProgressNumberFormat(null);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_prepare));
} else {
pd.setIndeterminate(true);
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_reboot));
}
} else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
//重启进入recovery模式进行恢复出厂设置
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message));
pd.setIndeterminate(true);
} else {
//正常关机
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
}
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
//关机的ProgressDialog各项参数设置完成,进入启动关机线程
// start the thread that initiates shutdown
sInstance.mContext = context;
//sInstance是ShutdownThread静态类实例,context继续用来显示关机进程的对话框
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
sInstance.mHandler = new Handler() {
};
beginAnimationTime = 0;
//开始设置关机动画
boolean mShutOffAnimation = configShutdownAnimation(context);
int screenTurnOffTime = getScreenTurnOffTime(context);
synchronized (mEnableAnimatingSync) {
if (mEnableAnimating) {
if (mShutOffAnimation) {
//显示关机动画
Log.d(TAG, "mIBootAnim.isCustBootAnim() is true");
bootanimCust(context);
} else {
//不显示关机动画
pd.show();
//那么展示关机ProgressDialog
sInstance.mProgressDialog = pd;
}
sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime);
}
}
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null;
//防止手机进入休眠状态
try {
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
//PARTIAL_WAKE_LOCK:保持CPU运转,屏幕和键盘灯有可能是关闭的
sInstance.mCpuWakeLock.setReferenceCounted(false);
sInstance.mCpuWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mCpuWakeLock = null;
}
// also make sure the screen stays on for better user experience
sInstance.mScreenWakeLock = null;
//保证用户体验,保持屏幕、键盘的亮度
if (sInstance.mPowerManager.isScreenOn()) {
try {
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
//FULL_WAKE_LOCK:保持CPU运转,保持屏幕高亮显示,键盘灯也保持亮度
sInstance.mScreenWakeLock.setReferenceCounted(false);
sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mScreenWakeLock = null;
}
}
if (sInstance.getState() != Thread.State.NEW || sInstance.isAlive()) {
//启动关机线程异常的处理
if (sInstance.mShutdownFlow == IPO_SHUTDOWN_FLOW) {
Log.d(TAG, "ShutdownThread exists already");
checkShutdownFlow();
synchronized (mShutdownThreadSync) {
mShutdownThreadSync.notify();
}
} else {
Log.e(TAG, "Thread state is not normal! froce to shutdown!");
delayForPlayAnimation();
//unmout data/cache partitions while performing shutdown
sInstance.mPowerManager.goToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_SHUTDOWN, 0);
PowerManagerService.lowLevelShutdown(mReason);
}
} else {
sInstance.start();
//启动shutdown线程
}
}
参数context用来显示关机进程的对话框;sInstance是ShutdownThread静态类实例,也是this thread的实例;ProgressDialog pd用来启动一个系统进度对话框,pd的成员函数setTitle()、setMessage()、setIndeterminate()、setCancelable()会根据context内容对对话框属性进行设置,使用show()来显示;通过sInstance.mCpuWakeLock来获得cpuwakelock,使系统不会休眠;通过sInstance.mScreenWakeLock获得screenwakelock使屏幕长亮;最后启动shutdown线程sInstance.start()。
/**
* 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() {
//关机预处理
checkShutdownFlow();
while (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
mShutdownManager.saveStates(mContext);
mShutdownManager.enterShutdown(mContext);
switchToLauncher();
running();
//正式启动关机线程
}
if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
mShutdownManager.enterShutdown(mContext);
switchToLauncher();
running();
//正式启动关机线程
}
}
private void running() {
command = SystemProperties.get("sys.ipo.pwrdncap");
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();
//接收关机广播,不允许app取消该线程
}
};
/*
* 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") + (mReason != null ? mReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
//设置系统属性值,以防止我们进入实际的硬件restart之前system_server reboots
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
//设置系统属性值,表明是否rebooting进入到safe mode
}
...
if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
//最大广播时间内(10s)关闭ActivityManager
} catch (RemoteException e) {
}
}
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
}
}
Log.i(TAG, "Shutting down package manager...");
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
//关闭PackageManager
}
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
// Shutdown radios.
Log.i(TAG, "Shutting down radios...");
shutdownRadios(MAX_RADIO_WAIT_TIME);
//最大收音等待时间(12s)内关闭收音机
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
}
if ((mShutdownFlow == IPO_SHUTDOWN_FLOW) && (command.equals("1") || command.equals("3"))) {
Log.i(TAG, "bypass MountService!");
} else {
// 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");
if (statusCode < 0) {
mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
}
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);
//关闭MountService
} 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");
if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
Log.d(TAG, changeToNormalMessage + ": MountService");
mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
}
break;
} else if (mRebootHasProgressBar) {
int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
(MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
MAX_SHUTDOWN_WAIT_TIME);
status += RADIO_STOP_PERCENT;
sInstance.setRebootProgress(status, null);
}
try {
mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
} catch (InterruptedException e) {
}
}
}
Log.i(TAG, "MountService shut done...");
}
...
rebootOrShutdown(mContext, mReboot, mReason);
//最终执行重启或关机的函数
}
在关机过程中发送完关机广播后,依次关闭ActivityManager Service、PackageManager Service、Radio、MountService。最后执行的shutdown流程的函数rebootOrShutdown()。
/**
* Do not call this directly. Use {@link #reboot(Context, String, boolean)}
* or {@link #shutdown(Context, boolean)} instead.
*
* @param context Context used to vibrate or null without vibration
* @param reboot true to reboot or false to shutdown
* @param reason reason for reboot/shutdown
*/
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
reason = null;
} else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator(context);
//关机前震动
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(reason);
//调用底层方法关闭电源
}
二、
PowerManagerService.lowLevelShutdown函数调用后开始进入JNI层和kernel层。
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java:
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*
* @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
*/
public static void lowLevelShutdown(String reason) {
if (reason == null) {
reason = "";
}
SystemProperties.set("sys.powerctl", "shutdown," + reason);
}
这是一个通往下层JNI的函数。
frameworks\base\core\java\android\os\SystemProperties.java:
/**
* Set the value for the given key.
* @throws IllegalArgumentException if the key exceeds 32 characters
* @throws IllegalArgumentException if the value exceeds 92 characters
*/
public static void set(String key, String val) {
if (key.length() > PROP_NAME_MAX) {
throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
if (val != null && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("val.length > " +
PROP_VALUE_MAX);
}
native_set(key, val);
//底层方法
}
判断参数,抛出异常后,native_set方法实际操作通过JNI调用底层cpp文件对应的接口SystemProperties_set。
frameworks\base\core\jni\android_os_SystemProperties.cpp:
static void SystemProperties_set(JNIEnv *env, jobject clazz,
jstring keyJ, jstring valJ)
{
int err;
const char* key;
const char* val;
if (keyJ == NULL) {
jniThrowNullPointerException(env, "key must not be null.");
return ;
}
key = env->GetStringUTFChars(keyJ, NULL);
if (valJ == NULL) {
val = ""; /* NULL pointer not allowed here */
} else {
val = env->GetStringUTFChars(valJ, NULL);
}
err = property_set(key, val);
//调用函数int property_set
env->ReleaseStringUTFChars(keyJ, key);
if (valJ != NULL) {
env->ReleaseStringUTFChars(valJ, val);
}
if (err < 0) {
ALOGE("setproperty key=%s value=%s err=%d\n", key, val, err);
jniThrowException(env, "java/lang/RuntimeException",
"failed to set system property");
}
}
先检查传入参数的合法性使用err = property_set(key, val),根据err的值输出log。
system\core\init\property_service.cpp:
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
}
return rc;
}
static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
if (selinux_reload_policy() != 0) {
ERROR("Failed to reload policy\n");
}
} else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
}
prop_info* pi = (prop_info*) __system_property_find(name);
//找到对应name的系统属性字段
if(pi != 0) {
/* ro.* properties may NEVER be modified once set */
if(!strncmp(name, "ro.", 3)) {
#ifdef MTK_INIT
ERROR("PropSet Error:[%s:%s] ro.* properties may NEVER be modified once set\n", name, value);
#endif
return -1;
}
__system_property_update(pi, value, valuelen);
//更新系统属性字段
} else {
int rc = __system_property_add(name, namelen, value, valuelen);
//添加系统属性字段
if (rc < 0) {
#ifdef MTK_INIT
ERROR("Failed to set '%s'='%s'\n", name, value);
#endif
return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
if (strncmp("net.", name, strlen("net.")) == 0) {
if (strcmp("net.change", name) == 0) {
#ifdef MTK_INIT
INFO("PropSet [%s:%s] Done\n", name, value);
#endif
return 0;
}
/*
* The 'net.change' property is a special property used track when any
* 'net.*' property name is updated. It is _ONLY_ updated here. Its value
* contains the last updated 'net.*' property.
*/
property_set("net.change", name);
} else if (persistent_properties_loaded &&
strncmp("persist.", name, strlen("persist.")) == 0) {
/*
* Don't write properties to disk until after we have read all default properties
* to prevent them from being overwritten by default values.
*/
write_persistent_property(name, value);
}
property_changed(name, value);
//改变属性值
#ifdef MTK_INIT
INFO("PropSet [%s:%s] Done\n", name, value);
#endif
return 0;
}
使用了多个函数进行判断、预处理。最后调用函数改变系统属性字段值。
system\core\init\init.cpp:
void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled)
ActionManager::GetInstance().QueuePropertyTrigger(name, value);
}
改变系统属性字段值,添加到触发器。
可以发现,系统通过Android Property机制来进行关机操作。关机操作相关的属性被组织成简单的键值对,通过修改属性值,相关的触发器来触发相应的操作。通过adb连接手机到终端,实际验证输入命令adb shell,然后设置属性值,setprop sys.powerctl shutdown,观察发现手机完成了关机操作。