[Android5.1]关机工作流程
Posted 迷途小书童Eric
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Android5.1]关机工作流程相关的知识,希望对你有一定的参考价值。
这篇文章主要分析一下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。代码为:
<!-- Control the behavior when the user long presses the power button.
0 - Nothing
1 - Global actions menu
2 - Power off (with confirmation)
3 - Power off (without confirmation)
-->
<integer name="config_longPressOnPowerBehavior">1</integer>
可以看到,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<Action>();
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
.......
可见,关机对话框中的每个列表项都抽象为一个Action,Action中定义了动作方法。并存放在一个ArrayList<Action>对象mItems中。关机对话框中的列表项有很多种,定义如下:
/* 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<String> addedKeys = new ArraySet<String>();
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完成最后的关机工作,并关闭手机电源。
以上是关于[Android5.1]关机工作流程的主要内容,如果未能解决你的问题,请参考以下文章
[Android5.1]开机动画desc.txt描述文件的分析