这篇文章主要分析一下Android5.1系统的关机流程。
当我们长按电源键时,按键消息会传递给PhoneWindowManager中的interceptKeyBeforeQueueing()函数处理。该函数代码如下:
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
......
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;
}
......
}
......
}
这个函数会对一些特殊KeyEvent进行处理,比如KeyEvent.KEYCODE_ENDCALL、KeyEvent.KEYCODE_CAMERA等等,当然也包括电源键按下。
代码中,‘down’用来标识动作是按下还是抬起,定义如下:
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
由于是按下电源键,因此down=1,会进一步调用interceptPowerKeyDown()处理。
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// Hold a wake lock until the power key is released.
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
// Cancel multi-press detection timeout.
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,
event.getDownTime(), isImmersiveMode(mLastSystemUiFlags));
if (panic) {
mHandler.post(mRequestTransientNav);
}
// 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) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
// 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;
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()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
} else {
wakeUpFromPowerKey(event.getDownTime());
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
可以看到,由于有很多情况下电源键都会被按下,比如,来电时按电源键静音或者挂电话、屏幕截图等。该函数会依次执行这些操作。最后,如果还没有执行,则会根据短按或长按来执行不同的操作。
接下来会判断"interactive",该参数表示屏幕是否已经唤醒,如果没有唤醒,则为false,函数会调用wakeUpFromPowerKey唤醒屏幕;如果已唤醒,则为true,程序会使用Handler机制发送一个“MSG_POWER_LONG_PRESS”的消息,消息处理代码如下:
private class PolicyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ENABLE_POINTER_LOCATION:
enablePointerLocation();
break;
case MSG_DISABLE_POINTER_LOCATION:
disablePointerLocation();
break;
......
case MSG_POWER_LONG_PRESS:
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值,做出不同的操作。该值是通过调用getResolvedLongPressOnPowerBehavior获取,代码如下:
private int getResolvedLongPressOnPowerBehavior() {
if (FactoryTest.isLongPressOnPowerOffEnabled()) {
return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
}
return mLongPressOnPowerBehavior;
}
由于不是出厂测试,所以会返回mLongPressOnPowerBehavior。mLongPressOnPowerBehavior的值是PhoneWindowManager::init()中赋值的。代码如下:
@Override
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs) {
......
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
......
}
进一步,mLongPressOnPowerBehavior的值是通过配置文件设置的,配置文件路径为:frameworks/base/core/res/res/values/config.xml。代码为:
1
可以看到,mLongPressOnPowerBehavior=1。
一步一步往上回到powerLongPress函数,由于各case项的定义如下:
static final int LONG_PRESS_POWER_NOTHING = 0;
static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
因此会执行如下语句:
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
showGlobalActionsInternal();
break;
即进一步执行showGlobalActionsInternal:
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
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);
}
}
该函数首先向ActivityManagerService请求关闭所有的窗口,然后调用GlobalActions::showDialog()。showDialog()代码如下:
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();
}
}
该函数最后调用handleShow显示关机对话框。
private void handleShow() {
awakenIfNecessary();
mDialog = createDialog(); //创建关机对话框
prepareDialog(); //对话框显示前的准备工作
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("GlobalActions");
mDialog.getWindow().setAttributes(attrs);
mDialog.show(); //显示关机对话框
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
}
首先分析一下creatDialog()函数,由于程序代码比较长,下面分块介绍,代码如下:
private GlobalActionsDialog createDialog() {
// Simple toggle style if there's no vibrator, otherwise use a tri-state
......
mItems = new ArrayList();
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
.......
}
可见,关机对话框中的每个列表项都抽象为一个Action,Action中定义了动作方法。并存放在一个ArrayList/* Valid settings for global actions keys.
* see config.xml config_globalActionList */
private static final String GLOBAL_ACTION_KEY_POWER = "power"; //关机
private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane"; //飞行模式
private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport"; //bug报告
private static final String GLOBAL_ACTION_KEY_SILENT = "silent"; //静默模式
private static final String GLOBAL_ACTION_KEY_USERS = "users"; //列出所有用户,针对一个系统多个用户
private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings"; //打开设置窗口
private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown"; //锁定手机
当然,关机对话框中不可能这些列表项都包括,开发者可以根据产品需求,选择合适的列表项。那如何设置呢?接着往下看代码:
ArraySet addedKeys = new ArraySet();
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
mItems.add(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(getBugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
mItems.add(getLockdownAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
// Add here so we don't add more than one.
addedKeys.add(actionKey);
}
字符串数组defaultActions[]中保存了要显示的列表项,然后逐个判断,如果存在,则新建一个对应的Action,并加入到mItems中。
现在,接着讲creatDialog()函数剩下的代码:
mAdapter = new MyAdapter(); //创建适配器,用于存放关机对话框要显示的数据
AlertParams params = new AlertParams(mContext); //
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
/* 创建关机对话框 */
GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
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;
可以看到,定义了一个Adapter,当单击列表项目的某一项时,会调用Adapter中的对应项来响应该事件。
这样关机对话框就创建完成了,再回到上面的handleShow()函数,最终调用mDialog.show(),关机对话框就呈现在屏幕上了。由于关机对话框中的每个列表项对应一个Action,从creatDialog()函数中可知,关机对应的Action为PowerAction()。代码如下:
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(com.android.internal.R.drawable.ic_lock_power_off, //关机图标
R.string.global_action_power_off); //关机字样
}
@Override
public boolean onLongPress() { //长按执行的动作
mWindowManagerFuncs.rebootSafeMode(true);
return true;
}
......
@Override
public void onPress() { //单击执行的动作
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown(false);
}
}
可见,当单击对话框中的"关机"时,会执行WindowManagerFuncs对象mWindowManagerFuncs的成员函数shutdown(),正式进入关机流程。
那mWindowManagerFuncs从哪来的呢?是创建GlobalActino对象时候通过构造函数的参数传进来的。其实mWindowManagerFuncs就是一个WindowManagerService对象,然后在创建PhoneWindowManager时候传进来,进一步传递给GlobalAction。那shutdown()的实现自然是WindowManagerService::shutdown(),代码如下:
// Called by window manager policy. Not exposed externally.
@Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, confirm);
}
可见,WindowManagerService进一步调用了ShutdownThread中的shutdown函数:
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
可见,进一步调用了shutdownInner(),代码如下:
static void shutdownInner(final Context context, boolean confirm) {
//该函数只能被一个线程调用,如果已经被调用了,则其他线程再调用的话,会直接返回
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);
//confirm用来标识是否需要显示确认对话框,若为true,则显示
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); //不显示确认对话框,则直接进入关机流程
}
}
经历千三万水,最终调用了beginShutdownSequence()来正式开始关机流程:
private static void beginShutdownSequence(Context context) {
//该函数只能被一个线程调用,如果已经被调用了,则其他线程再调用的话,会直接返回
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
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);
//pd.show();
shutdownTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_TIME;
//显示关机动画
String[] bootcmd = {"bootanimation", "shutdown"} ;
try {
Log.i(TAG, "exec the bootanimation ");
SystemProperties.set("service.bootanim.exit", "0");
Runtime.getRuntime().exec(bootcmd);
} catch (Exception e){
Log.e(TAG,"bootanimation command exe err!");
}
//初始化关机线程ShutdownThread
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null;
try {
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + "-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");
sInstance.mScreenWakeLock.setReferenceCounted(false);
sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mScreenWakeLock = null;
}
}
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
//启动关机线程ShutdownThread
sInstance.start();
}
该函数主要做了两件事:第一,执行命令:“bootanimation shutdown",显示关机动画。这个具体后面再讲。第二,初始化关机线程,然后启动关机线程。线程执行函数代码如下:
public void run() {
//创建一个广播接收器,用于接收关机广播,如果收到则设置mActionDone为true
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();
}
};
//设置属性"sys.shutdown.requested",用于记录关机原因
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
//如果是重启进入安全模式,则设置属性"persist.sys.safemode"值为1
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
Slog.d(TAG, "*****Sending shutdown 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);
//等待10s,直到前面定义的广播接收器收到关机广播,或者超时
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) {
}
}
}
//关闭ActivityManager,超时时间为10s
Log.i(TAG, "Shutting down activity manager...");
Slog.d(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) {
}
}
//关闭PackageManagerService
Log.i(TAG, "Shutting down package manager...");
Slog.d(TAG, "*****Shutting down package manager...");
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
}
//关闭无线电话,超时时间为12s
mContext.sendBroadcast(new Intent(TelephonyIntents.ACTION_SHUTDOWN_SPRD));
shutdownRadios(MAX_RADIO_WAIT_TIME);
Slog.d(TAG, "*****after shutdownRadios");
if(!SystemProperties.getBoolean("persist.sys.volte.enable", false)){
Slog.d(TAG, "*****before shutdownIccs");
shutdownIccs(MAX_ICC_WAIT_TIME);
Slog.d(TAG, "*****end shutdownIccs");
}
//关闭挂载服务,超时时间为20s
Slog.d(TAG, "*****before IMountShutdownObserver.Stub");
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");
Slog.d(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"));
Slog.d(TAG, "*****IMountService.Stub.asInterface");
if (mount != null) {
Slog.d(TAG, "*****before mount.shutdown");
mount.shutdown(observer);
Slog.d(TAG, "*****end mount shutdown");
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
Slog.d(TAG, "*****Exception during MountService shutdown");
}
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) {
}
}
}
//继续执行其他的关机工作
Slog.d(TAG, "***** before rebootOrShutdown");
rebootOrShutdown(mReboot, mRebootReason);
Slog.d(TAG, "***** end rebootOrShutdown");
}
继续调用rebootOrShutdown()完成剩下的关机工作:
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) { //如果是重启,则调用PowerManagerService::lowLevelReboot重启系统
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) { //如果关机,先震动500ms
// 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);
}
//由于震动是个异步过程,因此在这里需要等500ms,指导震动完成.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
//关闭电源
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}
public static void lowLevelShutdown() {
SystemProperties.set("sys.powerctl", "shutdown");
}
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);
}
SystemProperties::native_set()是native方法,JNI方法位置:\frameworks\base\core\jni\android_os_SystemProperties.cpp
static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)
{
......
err = property_set(key, val);
......
}
可见,最终调用property_set将属性"sys.powerctl"的值设置为”shutdown“。这样就到了init进程。sys.powerctl在init.rc中的定义如下:
on property:sys.powerctl=*
powerctl ${sys.powerctl}
init进程得到这个事件后,会执行do_powerctl函数,路径为:system/core/init/builtins.c。
int do_powerctl(int nargs, char **args)
{
......
if (strncmp(command, "shutdown", 8) == 0) {
cmd = ANDROID_RB_POWEROFF;
len = 8;
} else if (strncmp(command, "reboot", 6) == 0) {
cmd = ANDROID_RB_RESTART2;
len = 6;
} else {
ERROR("powerctl: unrecognized command '%s'\n", command);
return -EINVAL;
}
.......
return android_reboot(cmd, 0, reboot_target);
}
最终,调用android_reboot完成最后的关机工作,并关闭手机电源。