android 关机重启流程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 关机重启流程相关的知识,希望对你有一定的参考价值。
参考技术A https://developer.android.com/intl/zh-CN/reference/android/os/PowerManager.html在PowerManager的API文档中,给出了一个关机/重启接口:
public void reboot (String reason)
对于这个接口的描述很简单,就是几句话。
接口的作用就是重启设备,而且,就算重启成功了也没有返回值。
需要包含REBOOT权限,也就是android.permission.REBOOT
唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。
1.frameworks/base/core/java/android/os/PowerManager.java
2.frameworks/base/core/java/android/os/IPowerManager.aidl
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
---------------------》
6.system/core/libcutils/android_reboot.c
7.bionic/libc/unistd/reboot.c
8.__reboot通过syscall来到内核
9.kernel/sys.c
frameworks/base/core/java/android/os/PowerManager.java
mService为IPowerManager Binder接口服务。
frameworks/base/core/java/android/os/IPowerManager.aidl
frameworks/base/services/java/com/android/server/PowerManagerService.java
frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。
重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。
frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。
如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。
frameworks/base/services/java/com/android/server/PowerManagerService.java
frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。
system/core/libcutils/android_reboot.c
以reboot recovery为例,arg即为recovery,所在在第五步的时候会传入ANDROID_RB_RESTART2。到了android_reboot函数中,会看到这样的定义#ifdef RECOVERY_PRE_COMMAND,即属于重启前会执行的命令,如果定义了就会执行。
下面也是做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是通过调用emergency_remount()强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。
接下来才是对参数的解析处理:
1)普通重启 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;
2)关机 ANDROID_RB_POWEROFF, 无需reason,直接调用reboot进行关机;
3)带参数的特殊重启 ANDROID_RB_RESTART2, reason 将为默认值 -1
这里又出现一个#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。
最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:
reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL )
__reboot通过syscall来到内核bionic/libc/arch-arm/syscalls/__reboot.S
其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。
已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h中可以找到内核对__NR_reboot的syscall函数映射,即
同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。
include/linux/syscalls.h
与__reboot的调用参数一致。
进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),对比__reboot的参数,能够符合。究竟是不是这个函数?
同样在include/linux/syscalls.h文件中,能够找到这样几个定义:
而pm_power_off为空的话,就把用户的关机命令转换为挂起:
arch/arm/kernel/process.c
pm_power_off = msm_pm_power_off;
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。
bionic/libc/include/sys/reboot.h 中可以看到android定义的启动方式
RESTART
POWER_OFF
RESTART2
对框架进行赋值,qcom 平台 845上已经不是这函数,自己查找
arm_pm_restart = msm_pm_restart;
下面是qcom 实现,每个平台不同
可以在跟踪这个流程的过程中会发现,确实是有存在关机的相关接口的。那么关机该怎么用呢?
frameworks/base/services/java/com/android/serverBatteryService.java
重启方式: 最后就是设定寄存器,Uboot 解析不同寄存器的值进入不同的启动模式
recovery 如果传下来的字符串是recovery那么,就在RTC寄存器里设置某个特定值,当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了。
Ref: https://blog.csdn.net/leerobin83/article/details/7162751
上面主要讲到流程,在实际开发中, 主动调用系统开机关机如何做
(Ref: https://blog.csdn.net/luzhenrong45/article/details/42092007 )
一. 发送系统广播方式
二. 通过init.rc启动系统服务来运行sh文件
三. Runtime调用Linux-shell
四 . PowerManager reboot以及反射调用PowerManagerService shutdown
五.使用ShutdownThread (尝试不成功,但想法觉得可行)
Intent.java位于源码/frameworks/base/core/java/android/content/Intent.java下面
脚本方式,实际都是基于指令的
使用PowerManager 或ShutdownThread 都是基于关机流程
Android 12 关机重启流程
文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析
关机流程
Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread.shutdown来实现。如下是一些常见的调用关机的点:
- StatusBarManagerService#shutdown, 这个主要是对接SystemUI
- WindowManagerService#shutdown, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
- PowerManager#shutdown, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
- 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;
以PowerManager#shutdown调用为例,来分析一下关机流程
PowerManager#shutdown
mService实际上是PowerManagerService binder服务的bp端, 执行mService.shutdown实际上是向 PMS binder服务发起调用
/**
* Turn off the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
* @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
* @param wait If true, this call waits for the shutdown to complete and does not return.
*
* @hide
*/
public void shutdown(boolean confirm, String reason, boolean wait)
try // 向 PowerManagerService 发起binder调用
mService.shutdown(confirm, reason, wait);
catch (RemoteException e)
throw e.rethrowFromSystemServer();
PowerManagerService$BinderService#shutdown
上面的调用将会调用以下代码
/**
* Shuts down the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
* @param wait If true, this call waits for the shutdown to complete and does not return.
*/
@Override // Binder call
public void shutdown(boolean confirm, String reason, boolean wait)
// 检查执行是否有
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
// 记录 shutdown 事件,会在log中进行打印
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long ident = Binder.clearCallingIdentity();
try // 直接给 PowerManagerService 处理
shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
finally
Binder.restoreCallingIdentity(ident);
ShutdownCheckPoints#recordCheckPoint
ShutdownCheckPoints 类是一个专门用来记录关机重启详细信息,如调用者pid,原因
/** Records the pid of the caller process as a shutdown check point. */
public static void recordCheckPoint(int callerProcessId, @Nullable String reason)
INSTANCE.recordCheckPointInternal(callerProcessId, reason);
@VisibleForTesting
void recordCheckPointInternal(int callerProcessId, @Nullable String reason)
recordCheckPointInternal(callerProcessId == Process.myPid()
? new SystemServerCheckPoint(mInjector, reason)
: new BinderCheckPoint(mInjector, callerProcessId, reason));
// systme log 中打印 shutdown 原因
Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);
PowerManagerService#shutdownOrRebootInternal
这个函数处理包含了关机和重启的处理,通过调用ShutdownThread.shutdown/reboot 实现。
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
@Nullable final String reason, boolean wait)
if (PowerManager.REBOOT_USERSPACE.equals(reason))
if (!PowerManager.isRebootingUserspaceSupportedImpl()) // 检查是否支持userspace reboot
throw new UnsupportedOperationException(
"Attempted userspace reboot on a device that doesn't support it");
UserspaceRebootLogger.noteUserspaceRebootWasRequested();
if (mHandler == null || !mSystemReady) // 系统还没准备好
if (RescueParty.isAttemptingFactoryReset()) // RescueParty 正执行恢复出厂
// If we're stuck in a really low-level reboot loop, and a
// rescue party is trying to prompt the user for a factory data
// reset, we must GET TO DA CHOPPA!
// No check point from ShutdownCheckPoints will be dumped at this state.
PowerManagerService.lowLevelReboot(reason);
else // 给caller 抛出一个异常
throw new IllegalStateException("Too early to call shutdown() or reboot()");
Runnable runnable = new Runnable()
@Override
public void run()
synchronized (this)
if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) // 重启到安全模式
ShutdownThread.rebootSafeMode(getUiContext(), confirm);
else if (haltMode == HALT_MODE_REBOOT) // 重启
ShutdownThread.reboot(getUiContext(), reason, confirm);
else // 关机
ShutdownThread.shutdown(getUiContext(), reason, confirm);
;
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(UiThread.getHandler(), runnable);
msg.setAsynchronous(true); // 设置异步防止被阻塞
UiThread.getHandler().sendMessage(msg); // 通过发送消息处理
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
if (wait) // 阻塞到完成关机重启
synchronized (runnable)
while (true)
try
runnable.wait();
catch (InterruptedException e)
ShutdownThread#shutdown
confirm 参数表示是否需要确认,若需要确认会弹出一个对话框。
/**
* 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. This must be a context
* suitable for displaying UI (aka Themable).
* @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
private static void shutdownInner(final Context context, boolean confirm)
// ShutdownThread is called from many places, so best to verify here that the context passed
// in is themed.
context.assertRuntimeOverlayThemable();
// 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;
// Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
// this point preserves the system trace of the trigger point of the ShutdownThread.
ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);
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);
ShutdownThread#beginShutdownSequence
private static void beginShutdownSequence(Context context)
synchronized (sIsStartedGuard)
if (sIsStarted)
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
sIsStarted = true;
// 显示关机对话框
sInstance.mProgressDialog = showShutdownDialog(context);
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;
if (SecurityLog.isLoggingEnabled())
SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
// start the thread that initiates shutdown
sInstance.mHandler = new Handler()
;
sInstance.start(); // sInstance是ShutdownThread对象 其继承自Thread,开启线程执行关机流程
ShutdownThread#run
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio state if the allotted time has passed.
*/
public void run()
TimingsTraceLog shutdownTimingLog = newTimingsLog();
shutdownTimingLog.traceBegin("SystemServerShutdown");
metricShutdownStart();
metricStarted(METRIC_SYSTEM_SERVER);
// Start dumping check points for this shutdown in a separate thread.
Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
new File(CHECK_POINTS_FILE_BASENAME));// 将记录写入/data/system/shutdown-checkpoints/checkpoints
dumpCheckPointsThread.start();
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") + (mReason != null ? mReason : "");
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");
shutdownTimingLog.traceBegin("DumpPreRebootInfo");
try
Slog.i(TAG, "Logging pre-reboot information...");
PreRebootLogger.log(mContext);
catch (Exception e)
Slog.e(TAG, "Failed to log pre-reboot information", e);
shutdownTimingLog.traceEnd(); // DumpPreRebootInfo
metricStarted(METRIC_SEND_BROADCAST);
shutdownTimingLog.traceBegin("SendShutdownBroadcast");
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 | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; // 10s 超时
synchronized (mActionDoneSync) // 等待关机广播处理完成,上面调用通知actionDone
while (!mActionDone)
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0)
Log.w(TAG, "Shutdown broadcast timed out");
break;
else if (mRebootHasProgressBar)
int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
sInstance.setRebootProgress(status, null);
try
mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
catch (InterruptedException e)
if (mRebootHasProgressBar)
sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
metricEnded(METRIC_SEND_BROADCAST);
Log.i(TAG, "Shutting down activity manager...");
shutdownTimingLog.traceBegin("ShutdownActivityManager");
metricStarted(METRIC_AM);
final IActivityManager am =
IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
if (am != null) // ams 处理shutdown
try
am.shutdown(MAX_BROADCAST_TIME);
catch (RemoteException e)
if (mRebootHasProgressBar)
sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
shutdownTimingLog.traceEnd();// ShutdownActivityManager
metricEnded(METRIC_AM);
Log.i(TAG, "Shutting down package manager...");
shutdownTimingLog.traceBegin("ShutdownPackageManager");
metricStarted(METRIC_PM);
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) // pms 处理shutdown
pm.shutdown();
if (mRebootHasProgressBar)
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
shutdownTimingLog.traceEnd(); // ShutdownPackageManager
metricEnded(METRIC_PM);
// Shutdown radios.
shutdownTimingLog.traceBegin("ShutdownRadios");
metricStarted(METRIC_RADIOS);
shutdownRadios(MAX_RADIO_WAIT_TIME);
if (mRebootHasProgressBar)
sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
shutdownTimingLog.traceEnd(); // ShutdownRadios
metricEnded(METRIC_RADIOS);
if (mRebootHasProgressBar)
sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
// If it's to reboot to install an update and uncrypt hasn't been
// done yet, trigger it now.
uncrypt();
// Wait for the check points dump thread to finish, or kill it if not finished in time.
shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
try
dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
catch (InterruptedException ex) // 等待记录写入完成
shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait
shutdownTimingLog.traceEnd(); // SystemServerShutdown
metricEnded(METRIC_SYSTEM_SERVER);
saveMetrics(mReboot, mReason);
// Remaining work will be done by init, including vold shutdown
rebootOrShutdown(mContext, mReboot, mReason); // 继续执行
ShutdownThread#rebootOrShutdown
通过PowerManagerService来实现 shutdown/reboot
/**
* Do not call this directly. Use @link #reboot(Context, String, boolean)
* or @link #shutdown(Context, String, 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
此方法通过设置属性sys.powerctl来通知init处理shutdown
/**
* 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);
设置属性sys.powerctl值,init的property service将会接受设置的值并进行处理。
HandlePropertySet
init处理设置属性
// @system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr,
SocketConnection* socket, std::string* error)
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS)
return ret;
if (StartsWith(name, "ctl.")) // 处理 ctl.xxx
return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") // 设置的sys.powerctl
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
std::string process_cmdline;
std::string process_log_string;
if (ReadFileToString(cmdline_path, &process_cmdline))
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process
// path.
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
// 打印log
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
if (!value.empty())
DebugRebootLogging();
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false))
*error = "Userspace reboot is not supported by this device";
return PROP_ERROR_INVALID_VALUE;
// If a process other than init is writing a non-empty value, it means that process is
// requesting that init performs a restorecon operation on the path specified by 'value'.
// We use a thread to do this restorecon operation to prevent holding up init, as it may take
// a long time to complete.
if (name == kRestoreconProperty && cr.pid != 1 && !value.empty())
static AsyncRestorecon async_restorecon;
async_restorecon.TriggerRestorecon(value);
return PROP_SUCCESS;
return PropertySet(name, value, error); // 设置属性
PropertyChanged
当设置成功后,会调用PropertyChanged
// @system/core/init/init.cpp
void PropertyChanged(const std::string& name, const std::string& value)
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") // 处理sys.powerctl属性变化
trigger_shutdown(value); // 触发关机
if (property_triggers_enabled)
ActionManager::GetInstance().QueuePropertyChange(name, value);
WakeMainInitThread();
prop_waiter_state.CheckAndResetWait(name, value);
trigger_shutdown在SecondStageMain中定义,是一个lambda表达式
int SecondStageMain(int argc, char** argv)
...
trigger_shutdown = [](const std::string& command) shutdown_state.TriggerShutdown(command); ;
...
因此接下来调用 ShutdownState#TriggerShutdown
// @system/core/init/init.cpp
void TriggerShutdown(const std::string& command)
// We can't call HandlePowerctlMessage() directly in this function,
// because it modifies the contents of the action queue, which can cause the action queue
// to get into a bad state if this function is called from a command being executed by the
// action queue. Instead we set this flag and ensure that shutdown happens before the next
// command is run in the main init loop.
auto lock = std::lock_guardshutdown_command_lock_;
shutdown_command_ = command;
do_shutdown_ = true;
WakeMainInitThread(); // 唤醒主线程处理
在主线程的循环中处理 shutdown command
// @system/core/init/init.cpp
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command)
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
HandlePowerctlMessage
// @system/core/init/reboot.cpp
void HandlePowerctlMessage(const std::string& command)
unsigned int cmd = 0;
std::vector<std::string> cmd_params = Split(command, ",");
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
bool userspace_reboot = false;
if (cmd_params[0] == "shutdown") // 处理关机
cmd = ANDROID_RB_POWEROFF;
if (cmd_params.size() >= 2)
if (cmd_params[1] == "userrequested")
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
run_fsck = true;
else if (cmd_params[1] == "thermal")
// Turn off sources of heat immediately.
TurnOffBacklight();
// run_fsck is false to avoid delay
cmd = ANDROID_RB_THERMOFF;
else if (cmd_params[0] == "reboot") // 处理重启
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2)
reboot_target = cmd_params[1];
if (reboot_target == "userspace")
LOG(INFO) << "Userspace reboot requested";
userspace_reboot = true;
// adb reboot fastboot should boot into bootloader for devices not
// supporting logical partitions.
if (reboot_target == "fastboot" &&
!android::base::GetBoolProperty("ro.boot.dynamic_partitions", false))
reboot_target = "bootloader";
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader")
std::string err;
if (!write_reboot_bootloader(&err))
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
else if (reboot_target == "recovery")
bootloader_message boot = ;
if (std::string err; !read_bootloader_message(&boot, &err))
LOG(ERROR) << "Failed to read bootloader message: " << err;
// Update the boot command field if it's empty, and preserve
// the other arguments in the bootloader message.
if (!CommandIsPresent(&boot))
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
if (std::string err; !write_bootloader_message(boot, &err))
LOG(ERROR) << "Failed to set bootloader message: " << err;
return;
else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot")
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
: reboot_target;
const std::vector<std::string> options =
"--" + arg,
;
std::string err;
if (!write_bootloader_message(options, &err))
LOG(ERROR) << "Failed to set bootloader message: " << err;
return;
reboot_target = "recovery";
// If there are additional parameter, pass them along
for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i)
reboot_target += "," + cmd_params[i];
else
command_invalid = true;
if (command_invalid)
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return;
// We do not want to process any messages (queue'ing triggers, shutdown messages, control
// messages, etc) from properties during reboot.
StopSendingMessages();
if (userspace_reboot)
HandleUserspaceReboot();
return;
LOG(INFO) << "Clear action queue and start shutdown trigger";
ActionManager::GetInstance().ClearQueue();
// Queue shutdown trigger first
ActionManager::GetInstance().QueueEventTrigger("shutdown");
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&)
DoReboot(cmd, command, reboot_target, run_fsck); // 关键
return Result<void>;
;
// 将shutdown封装为<Builtin Action>并添加到队列,之后在主循环被处理,接着DoReboot将被调用
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
EnterShutdown();
cmd 定义如下
// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004
EnterShutdown 用以清除wait prop和 pending exec flag
static void EnterShutdown()
LOG(INFO) << "Entering shutdown mode";
shutting_down = true;
// Skip wait for prop if it is in progress
ResetWaitForProp();
// Clear EXEC flag if there is one pending
for (const auto& s : ServiceList::GetInstance())
s->UnSetExec();
DoReboot
接下来,在shutdown action被处理后,DoReboot被调用
// @system/core/init/reboot.cpp
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
// run_fsck Whether to run fsck after umount is done.
//
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
bool run_fsck)
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
auto shutdown_timeout = 0ms;
if (!SHUTDOWN_ZERO_TIMEOUT) // 计算timeout
constexpr unsigned int shutdown_timeout_default = 6;
constexpr unsigned int max_thermal_shutdown_timeout = 3;
auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
shutdown_timeout_default);
if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout)
shutdown_timeout_final = max_thermal_shutdown_timeout;
shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
sem_t reboot_semaphore;
if (sem_init(&reboot_semaphore, false, 0) == -1)
// These should never fail, but if they do, skip the graceful reboot and reboot immediately.
LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
RebootSystem(cmd, reboot_target);
// Start a thread to monitor init shutdown process
LOG(INFO) << "Create reboot monitor thread.";
bool reboot_monitor_run = true;
// 创建 monitor 线程
std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
shutdown_timeout, &reboot_monitor_run);
reboot_monitor_thread.detach();
// Start reboot monitor thread
sem_post(&reboot_semaphore);
// Ensure last reboot reason is reduced to canonical
// alias reported in bootloader or system boot reason.
size_t skip = 0;
std::vector<std::string> reasons = Split(reason, ",");
if (reasons.size() >= 2 && reasons[0] == "reboot" &&
(reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
reasons[1] == "hard" || reasons[1] == "warm"))
skip = strlen("reboot,");
// 写重启原因到属性和文件
// /* Android reboot reason stored in this property */
// #define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
// #define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
PersistRebootReason(reason.c_str() + skip, true);
// If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
// worry about unmounting it.
if (!IsDataMounted()) // data没有挂载,直接调用RebootSystem
sync();
RebootSystem(cmd, reboot_target);
abort();
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts"watchdogd";
std::set<std::string> stop_first;
for (const auto& s : ServiceList::GetInstance())
if (kDebuggingServices.count(s->name()))
// keep debugging tools until non critical ones are all gone.
s->SetShutdownCritical();
else if (to_starts.count(s->name()))
if (auto result = s->Start(); !result.ok())
LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
<< "': " << result.error();
s->SetShutdownCritical();
else if (s->IsShutdownCritical())
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok())
LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
<< "': " << result.error();
else
stop_first.insert(s->name());
// remaining operations (specifically fsck) may take a substantial duration
if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)
TurnOffBacklight();
// 处理关机动画
Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning())
bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
if (do_shutdown_animation)
SetProperty("service.bootanim.exit", "0");
SetProperty("service.bootanim.progress", "0");
// Could be in the middle of animation. Stop and start so that it can pick
// up the right mode.
boot_anim->Stop();
for (const auto& service : ServiceList::GetInstance())
if (service->classnames().count("animation") == 0)
continue;
// start all animation classes if stopped.
if (do_shutdown_animation)
service->Start();
service->SetShutdownCritical(); // will not check animation class separately
if (do_shutdown_animation)
boot_anim->Start();
surface_flinger->SetShutdownCritical();
boot_anim->SetShutdownCritical();
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
if (shutdown_timeout > 0ms)
StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
// Send SIGKILL to ones that didn't terminate cleanly.
StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
SubcontextTerminate();
// Reap subcontext pids.
ReapAnyOutstandingChildren();
// 3. send volume abort_fuse and volume shutdown to vold
Service* vold_service = ServiceList::GetInstance().FindService("vold");
if (vold_service != nullptr && vold_service->IsRunning())
// Manually abort FUSE connections, since the FUSE daemon is already dead
// at this point, and unmounting it might hang.
CallVdc("volume", "abort_fuse");
CallVdc("volume", "shutdown");
vold_service->Stop();
else
LOG(INFO) << "vold not running, skipping vold shutdown";
// logcat stopped here 停止 logcat
StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
// 4. sync, try umount, and optionally run fsck for user shutdown
Timer sync_timer;
LOG(INFO) << "sync() before umount...";
sync();
LOG(INFO) << "sync() before umount took" << sync_timer;
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
// 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
if (auto ret = UnmountAllApexes(); !ret.ok())
LOG(ERROR) << ret.error();
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
Timer sync_timer;
LOG(INFO) << "sync() after umount...";
sync();
LOG(INFO) << "sync() after umount took" << sync_timer;
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
// Send signal to terminate reboot monitor thread.
reboot_monitor_run = false;
sem_post(&reboot_semaphore);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, reboot_target); /// 执行关机重启
abort();
RebootSystem
// @system/core/init/reboot_utils.cpp
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget)
LOG(INFO) << "Reboot ending, jumping to kernel";
if (!IsRebootCapable())
// On systems where init does not have the capability of rebooting the
// device, just exit cleanly.
exit(0);
switch (cmd)
case ANDROID_RB_POWEROFF: // 关机
reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2: // 重启
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
break;
case ANDROID_RB_THERMOFF:
if (android::base::GetBoolProperty("ro.thermal_warmreset", false))
LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
else
reboot(RB_POWER_OFF);
break;
// In normal case, reboot should not return.
PLOG(ERROR) << "reboot call returned";
abort();
接着调用bionic 的 reboot 函数:
// @bionic/libc/bionic/reboot.cpp
#include <unistd.h>
#include <sys/reboot.h>
extern "C" int __reboot(int, int, int, void*);
int reboot(int mode)
// 调用 linux reboot 函数
return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, nullptr);
C++层关机入口
在C++代码里面可以直接调用 android_reboot 来实现关机重启
// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004
/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"
/* Android reboot reason stored in this property */
#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
#define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
* Due to that, process calling this should have proper selinux permission
* to write to the property or the call will fail.
*/
int android_reboot(unsigned cmd, int flags, const char* arg);
android_reboot
// @system/core/libcutils/android_reboot.cpp
int android_reboot(unsigned cmd, int /*flags*/, const char* arg)
int ret;
const char* restart_cmd = NULL;
char* prop_value;
switch (cmd)
case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
restart_cmd = "reboot";
break;
case ANDROID_RB_POWEROFF:
restart_cmd = "shutdown";
break;
case ANDROID_RB_THERMOFF:
restart_cmd = "shutdown,thermal";
break;
if (!restart_cmd) return -1;
if (arg && arg[0])
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
else
ret = asprintf(&prop_value, "%s", restart_cmd);
if (ret < 0) return -1;
// 设置属性sys.powerctl, 和上层一样都会触发init执行关机
ret = property_set(ANDROID_RB_PROPERTY, prop_value);
free(prop_value);
return ret;
关机流程总结
Java层:
ShutdownThread#shutdown -> PowerManagerService#lowLevelShutdown -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot
C++层
android_reboot -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot
重启流程
Android上层重启的入口与关机流程类似,只是调用的方法从shutdown变成了 reboot,也是调用 ShutdownThread.reboot 来实现。如下是一些常见的调用关机的点:
- StatusBarManagerService#reboot, 这个主要是对接SystemUI
- WindowManagerService#reboot, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
- PowerManager#reboot, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
- 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;
ShutdownThread#reboot
/**
* 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. This must be a context
* suitable for displaying UI (aka Themable).
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void reboot(final Context context, String reason, boolean confirm)
mReboot = true;
mRebootSafeMode = false;
mRebootHasProgressBar = false;
mReason = reason;
shutdownInner(context, confirm); // 和关机类似,最终调用rebootOrShutdown
rebootOrShutdown
重启情况是通过PowerManagerService.lowLevelReboot进行重启
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;
...
PowerManagerService#lowLevelReboot
/**
* Low-level function to reboot the device. On success, this
* function doesn't return. If more than 20 seconds passes from
* the time a reboot is requested, this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/
public static void lowLevelReboot(String reason)
if (reason == null)
reason = "";
// If the reason is "quiescent", it means that the boot process should proceed
// without turning on the screen/lights.
// The "quiescent" property is sticky, meaning that any number
// of subsequent reboots should honor the property until it is reset.
if (reason.equals(PowerManager.REBOOT_QUIESCENT)) // 息屏重启
sQuiescent = true;
reason = "";
else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT))
sQuiescent = true;
reason = reason.substring(0,
reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
if (reason.equals(PowerManager.REBOOT_RECOVERY)
|| reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) // 进入 recovery
reason = "recovery";
if (sQuiescent)
// Pass the optional "quiescent" argument to the bootloader to let it know
// that it should not turn the screen/lights on.
reason = reason + ",quiescent";
// 设置sys.powerctl
SystemProperties.set("sys.powerctl", "reboot," + reason);
try
Thread.sleep(20 * 1000L);
catch (InterruptedException e)
Thread.currentThread().interrupt();
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
如同关机流程,设置sys.powerctl然后init会执行重启。
如何分析关机重启问题
了解了上面的流程,分析问题应该不难,查看关键的log即可。下面以关机为例来分析,对于关机的机器来说,通常插上充电器是会进入关机充电状态,有关机充电动画。而某些情况实际是发生了死机,插上充电器没有充电动画,一般需要长按10+s开机。
查看main log
主要看下ShutdownThread log
01-22 15:54:05.117339 2174 2174 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
01-22 15:54:05.181086 2174 25190 I ShutdownThread: Sending shutdown broadcast...
01-22 15:54:06.122875 2174 25190 I ShutdownThread: Shutting down activity manager...
01-22 15:54:06.233849 2174 25190 I ShutdownThread: Shutting down package manager...
01-22 15:54:06.242261 2174 25296 W ShutdownThread: Turning off cellular radios...
01-22 15:54:06.256316 2174 25296 I ShutdownThread: Waiting for Radio...
01-22 15:54:06.558647 2174 25296 I ShutdownThread: Radio turned off.
01-22 15:54:06.558735 2174 25296 I ShutdownThread: Radio shutdown complete.
01-22 15:54:07.118474 2174 25190 I ShutdownThread: Shutdown critical subsyslist is :modem :
01-22 15:54:07.118492 2174 25190 I ShutdownThread: Waiting for a maximum of 10000ms
01-22 15:54:07.608521 2174 25190 I ShutdownThread: Vendor subsystem(s) shutdown successful
01-22 15:54:07.608561 2174 25190 I ShutdownThread: Performing low-level shutdown...
查看system log
可以查看ShutdownCheckPoints,看看是那个进程发起的,比如这里的 pid=3393 就是 systemui 触发的关机。
01-22 15:54:05.114927 2174 12696 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=3393
01-22 15:54:05.117219 2174 2174 V ShutdownCheckPoints: System server shutdown checkpoint recorded
查看 sys.boot.reason
结合开机后的开机原因,可以获取更多信息,比如下面的一些原因
// 通常是用户操作
[sys.boot.reason]: [shutdown,userrequested]
// 这种通常是低电导致,可以看下kernel的healthd 查看下电量上报信息
[sys.boot.reason]: [shutdown,battery]
重启关机的原因有很多,如下定义在PowerManager
/// frameworks/base/core/java/android/os/PowerManager.java
/**
* The value to pass as the 'reason' argument to reboot() to reboot into
* recovery mode for tasks other than applying system updates, such as
* doing factory resets.
* <p>
* Requires the @link android.Manifest.permission#RECOVERY
* permission (in addition to
* @link android.Manifest.permission#REBOOT).
* </p>
* @hide
*/
public static final String REBOOT_RECOVERY = "recovery";
/**
* The value to pass as the 'reason' argument to reboot() to reboot into
* recovery mode for applying system updates.
* <p>
* Requires the @link android.Manifest.permission#RECOVERY
* permission (in addition to
* @link android.Manifest.permission#REBOOT).
* </p>
* @hide
*/
public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
/**
* The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
* the device.
* @hide
*/
public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";
/**
* The 'reason' value used when rebooting in safe mode
* @hide
*/
public static final String REBOOT_SAFE_MODE = "safemode";
/**
* The 'reason' value used for rebooting userspace.
* @hide
*/
@SystemApi
public static final String REBOOT_USERSPACE = "userspace";
/**
* The 'reason' value used when rebooting the device without turning on the screen.
* @hide
*/
public static final String REBOOT_QUIESCENT = "quiescent";
/**
* The value to pass as the 'reason' argument to android_reboot().
* @hide
*/
public static final String SHUTDOWN_USER_REQUESTED = "userrequested";
/**
* The value to pass as the 'reason' argument to android_reboot() when battery temperature
* is too high.
* @hide
*/
public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";
/**
* The value to pass as the 'reason' argument to android_reboot() when device temperature
* is too high.
* @hide
*/
public static final String SHUTDOWN_THERMAL_STATE = "thermal";
/**
* The value to pass as the 'reason' argument to android_reboot() when device is running
* critically low on battery.
* @hide
*/
public static final String SHUTDOWN_LOW_BATTERY = "battery";
除了上面的原因,重启原因还包括如下
rescueparty / RescueParty --- RescueParty机制重启的原因
rollback_staged_install --- crashes回滚机制重启
healthd 信息
查看kernel log查看healthd的电量信息
// l=0 电量值
// t=25.0 电池温度℃
// chg= 充电状态,为空是没有进行充电
05-19 17:49:59.253565 1200 1200 W healthd : battery l=0 v=7892 t=25.0 h=2 st=3 c=320 fc=13320000 cc=5 chg=
healthd log 打印
/// system/core/healthd/BatteryMonitor.cpp
void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
const struct healthd_config& healthd_config)
char dmesgline[256];
size_t len;
const HealthInfo_1_0& props = health_info.legacy.legacy;
if (props.batteryPresent)
snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
props.batteryHealth, props.batteryStatus);
len = strlen(dmesgline);
if (!healthd_config.batteryCurrentNowPath.isEmpty())
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrent);
if (!healthd_config.batteryFullChargePath.isEmpty())
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullCharge);
if (!healthd_config.batteryCycleCountPath.isEmpty())
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
else
len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : "");
KLOG_WARNING(LOG_TAG, "%s\\n", dmesgline);
查看 PMIC PON log
而有些情况是看不到上面的log,通常可能是异常关机,可以查看开机后的 kernel 的 PMIC PON log
01-01 08:00:21.958147 418 418 I : PMIC PON log: Fundamental Reset: PON_PBL_STATUS=XVDD, DVDD
01-01 08:00:21.958160 418 418 I : PMIC PON log: FAULT_REASON2=RESTART_PON
01-01 08:00:21.958212 418 418 I : PMIC PON log: PON Trigger: USB_CHARGER
01-01 08:00:21.958262 418 418 I : PMIC PON log: Reset Trigger: PS_HOLD
01-01 08:00:21.958273 418 418 I : PMIC PON log: Reset Type: HARD_RESET
01-01 08:00:21.958304 418 418 I : PMIC PON log: PON Trigger: KPDPWR_N
01-01 08:00:21.958315 418 418 I : PMIC PON log: PON Trigger: HARD_RESET
一些常见场景
如下包含一些关机重启的场景的触发点
低电
/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfNoPowerLocked()
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
if (shouldShutdownLocked())
mHandler.post(new Runnable()
@Override
public void run()
if (mActivityManagerInternal.isSystemReady())
// 使用intent启动ShutdownActivity来执行关机
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra(Intent.EXTRA_REASON,
PowerManager.SHUTDOWN_LOW_BATTERY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
);
高温
/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfOverTempLocked()
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) // 温度超标关机, 默认临界68度
mHandler.post(new Runnable()
@Override
public void run()
if (mActivityManagerInternal.isSystemReady())
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.putExtra(Intent.EXTRA_REASON,
PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
);
Thermal
/// @frameworks/base/services/core/java/com/android/server/power/ThermalManagerService.java
private void shutdownIfNeeded(Temperature temperature)
if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN)
return;
final PowerManager powerManager = getContext().getSystemService(PowerManager.class);
switch (temperature.getType())
case Temperature.TYPE_CPU:
// Fall through
case Temperature.TYPE_GPU:
// Fall through
case Temperature.TYPE_NPU:
// Fall through
case Temperature.TYPE_SKIN: // 设置硬件高温
powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
break;
case Temperature.TYPE_BATTERY: // 电池高温
powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
break;
status bar关机重启
这个通常是从systemui发起
/// frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
/**
* Allows the status bar to shutdown the device.
*/
@Override
public void shutdown()
enforceStatusBarService();
String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long identity = Binder.clearCallingIdentity();
try
mNotificationDelegate.prepareForPossibleShutdown();
// ShutdownThread displays UI, so give it a UI context.
mHandler.post(() ->
ShutdownThread.shutdown(getUiContext(), reason, false));
finally
Binder.restoreCallingIdentity(identity);
/**
* Allows the status bar to reboot the device.
*/
@Override
public void reboot(boolean safeMode)
enforceStatusBarService();
String reason = safeMode
? PowerManager.REBOOT_SAFE_MODE
: PowerManager.SHUTDOWN_USER_REQUESTED;
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long identity = Binder.clearCallingIdentity();
try
mNotificationDelegate.prepareForPossibleShutdown();
mHandler.post(() ->
// ShutdownThread displays UI, so give it a UI context.
if (safeMode)
ShutdownThread.rebootSafeMode(getUiContext(), true);
else
ShutdownThread.reboot(getUiContext(), reason, false);
);
finally
Binder.restoreCallingIdentity(identity);
Rollback重启
@WorkerThread
private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
BroadcastReceiver listener, @Nullable VersionedPackage logPackage)
assertInWorkerThread();
PackageInstaller packageInstaller =
mContext.getPackageManager().getPackageInstaller();
List<RollbackInfo> recentRollbacks =
rollbackManager.getRecentlyCommittedRollbacks();
for (int i = 0; i < recentRollbacks.size(); i++)
RollbackInfo recentRollback = recentRollbacks.get(i);
int sessionId = recentRollback.getCommittedSessionId();
if ((rollbackId == recentRollback.getRollbackId())
&& (sessionId != PackageInstaller.SessionInfo.INVALID_ID))
PackageInstaller.SessionInfo sessionInfo =
packageInstaller.getSessionInfo(sessionId);
if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId))
mContext.unregisterReceiver(listener);
saveStagedRollbackId(rollbackId, logPackage);
WatchdogRollbackLogger.logEvent(logPackage,
FrameworkStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
else if (sessionInfo.isStagedSessionFailed()
&& markStagedSessionHandled(rollbackId))
WatchdogRollbackLogger.logEvent(logPackage,
FrameworkStatsLog
以上是关于android 关机重启流程的主要内容,如果未能解决你的问题,请参考以下文章