安卓系统面经_Android面经(7/20)系统关机重启流程
牛客高级系列专栏:
安卓(安卓系统开发也要掌握)
- 想通关安卓面试,请看:《150道安卓高频面试题目录及答案链接》
- 想通关安卓系统面试,请看:《140道安卓系统Framework面试题目录及答案链接》
- 想进阶安卓开发,请看:《Android进阶知识体系解析_15大安卓进阶必备知识点》
- 想了解安卓APP完整开发流程,请看:《安卓APP完整开发流程》
- 想掌握安卓App性能优化,请看:《安卓性能优化讲解和实战专栏》
- 想掌握Gradle语法,制作Gradle插件,请看:《安卓Gradle语法解析和实践大全》
嵌入式
- 想通关嵌入式面试,请看: 《111道嵌入式面试题目录及答案链接》
- 想多掌握几个嵌入式项目,请看:《6个嵌入式项目交流分享(附源码)》
- 本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人从嵌入式Linux转Android系统开发过程中对常见安卓系统开发面试题的理解;
- 1份外卖价格助您提高安卓面试准备效率,为您面试保驾护航!!
正文开始⬇
面试题预览
- 简述Android系统关机流程⭐⭐⭐⭐⭐
- 以应用调用Shutdown为例,分析下系统的关机流程?⭐⭐⭐
- 简单说下Android上层都有哪些触发关机的方法?⭐⭐⭐
- 如果你遇到一个系统异常关机的问题,请简述下你的分析思路,以及可以通过获取什么log进行分析?⭐⭐⭐⭐⭐
1 关机流程
Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread.shutdown来实现。如下是一些常见的调用关机的点:
- StatusBarManagerService#shutdown, 这个主要是对接SystemUI
- WindowManagerService#shutdown, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
- PowerManager#shutdown, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
- 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWNACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;
下面以PowerManager#shutdown调用为例,来分析一下关机流程
1.1 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();
}
}
1.2 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);
}
}
1.3 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);
}
1.4 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) {
}
}
}
}
}
1.5 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);
}
1.6 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);
}
}
1.7 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,开启线程执行关机流程
}
1.8 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); // 继续执行
}
1.9 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); // 关机
}
1.10 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.rc的property service将会接受设置的值并进行处理。
1.11 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); // 设置属性
}
1.12 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
1.13 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_guard{shutdown_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 Hand
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2020年研究生毕业后,工作重心由嵌入式Linux转为安卓系统,Android发展已经很多年,网上面向中初级Android系统开发的面经还比较少,也不够集中,因此梳理出本专栏,本专栏收集了本人工作中持续积累的众多安卓系统知识,持续更新中。