一,概述
重启动作从按键触发中断,linux kernel层给Android framework层返回按键事件进入 framework层,再从 framework层到kernel层执行kernel层关机任务。当然还有非按键触发,比如shell 命令reboot,或者系统异常导致重启,或者直接调用PM的reboot()方法重启。
这里就先从PowerManager说起。
二,重启流程
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
frameworks/base/core/java/android/os/PowerManager.java
2.1 PM.reboot
[->PowerManager.java]
public void reboot(String reason) {
try {
mService.reboot(false, reason, true); // 调用PowerManagerService->reboot
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
2.2 BinderService.reboot
[-> PowerManagerService.java]
private final class BinderService extends IPowerManager.Stub {
/**
* Reboots the device.
*
* @param confirm If true, shows a reboot confirmation dialog.
* @param reason The reason for the reboot, or null if none.
* @param wait If true, this call waits for the reboot to complete and does not return.
*/
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
//检查权限,reboot权限和reason是recovery时检查recovery权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
if (PowerManager.REBOOT_RECOVERY.equals(reason)
|| PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
}
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
此时参数为shutdownOrRebootInternal(false, false, reason, true);
- shutdown=false:代表重启;
- confirm=false:代表直接重启,无需弹出询问提示框;
- reason 可能为recovery、bootloader、shutdown等
- wait=true:代表阻塞等待重启操作完成。
2.3 PMS.shutdownOrRebootInternal
[-> PowerManagerService.java]
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
final String reason, boolean wait) {
...
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
//根据haltMode 区分,reboot,shutdown,rebootsafemode
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.
ShutdownThread必须运行在一个展现UI的looper
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) {
//等待ShutdownThread执行完毕
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
}
2.4 SDT.reboot
[->ShutdownThread.java]
public static void reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootHasProgressBar = false;
mReason = reason;
shutdownInner(context, confirm);
}
mReboot为true则代表重启操作,值为false则代表关机操作。
2.5 SDT.shutdownInner
[->ShutdownThread.java]
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
确保只有唯一的线程执行shutdown/reboot操作
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);
/* longPressBehavior 并没有什么意义,只配置长按power键时弹窗power off选项还是语音助手。
-- 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)
4 - Go to voice assist
*/
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 {
//本次confirm=false ,直接走这里
beginShutdownSequence(context);
}
}
2.6 SDT.beginShutdownSequence
[->ShutdownThread.java]
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return; //shutdown操作正在执行,则直接返回
}
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() {
};
//启动线程来执行shutdown初始化
sInstance.start(); //接下来会启动shutdownThread.run
}
2.7 SDT.run
[->ShutdownThread.java]
/**
* 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);
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);
//设置属性"sys.shutdown.requested"的值为reason
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
//如果需要重启进入安全模式,则设置"persist.sys.safemode"=1
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
metricStarted(METRIC_SEND_BROADCAST);
shutdownTimingLog.traceBegin("SendShutdownBroadcast");
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
//1. 发送关机广播
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;
synchronized (mActionDoneSync) {
//循环等待,超时或者mActionDone都会结束该循环
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) {//设置reboot进程条
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);
//2. 关闭AMS
final IActivityManager am =
IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
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);
//3. 关闭PMS
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();
}
if (mRebootHasProgressBar) {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
shutdownTimingLog.traceEnd(); // ShutdownPackageManager
metricEnded(METRIC_PM);
// Shutdown radios.
//4. 关闭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();
}
shutdownTimingLog.traceEnd(); // SystemServerShutdown
metricEnded(METRIC_SYSTEM_SERVER);
saveMetrics(mReboot, mReason);
// Remaining work will be done by init, including vold shutdown
//关机剩下得动作由init完成,包括void,下一节分析
rebootOrShutdown(mContext, mReboot, mReason);
}
mReboot
mReason
接下来主要关闭一些系统服务:
- 发送关机广播
- 关闭AMS
- 关闭PMS
- 关闭radios
之后就需要进入重启/关机流程,由init 进程处理。
2.7.1 AMS.shutdown
通过AMP.shutdown,通过binder调用到AMS.shutdown.
[->ActivityManagerService.java]
@Override
public boolean shutdown(int timeout) {
权限检测
if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SHUTDOWN);
}
boolean timedout = false;
synchronized(this) {
mShuttingDown = true;
mStackSupervisor.prepareForShutdownLocked();
//禁止WMS继续处理Event
updateEventDispatchingLocked();
//调用ASS处理shutdown操作
timedout = mStackSupervisor.shutdownLocked(timeout);
}
mAppOpsService.shutdown();
if (mUsageStatsService != null) {
mUsageStatsService.prepareShutdown();
}
mBatteryStatsService.shutdown();
synchronized (this) {
mProcessStats.shutdownLocked();
notifyTaskPersisterLocked(null, true);
}
return timedout;
}
此处timeout为MAX_BROADCAST_TIME=10s
2.7.2 PMS.shutdown
public void shutdown() {
mPackageUsage.write(true);
}
此处mPackageUsage数据类型是PMS的内部类PackageUsage。
private class PackageUsage {
void write(boolean force) {
if (force) {
writeInternal();
return;
}
...
}
}
对于force=true,接下来调用writeInternal方法。
private class PackageUsage {
private void writeInternal() {
synchronized (mPackages) {
synchronized (mFileLock) {
//file是指/data/system/package-usage.list
AtomicFile file = getFile();
FileOutputStream f = null;
try {
//将原来的文件记录到package-usage.list.bak
f = file.startWrite();
BufferedOutputStream out = new BufferedOutputStream(f);
FileUtils.setPermissions(file.getBaseFile().getPath(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
StringBuilder sb = new StringBuilder();
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg.mLastPackageUsageTimeInMills == 0) {
continue;
}
sb.setLength(0);
sb.append(pkg.packageName);
sb.append(' ');
sb.append((long)pkg.mLastPackageUsageTimeInMills);
sb.append('\n');
out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
}
out.flush();
//将文件内容同步到磁盘,并删除.bak文件
file.finishWrite(f);
} catch (IOException e) {
if (f != null) {
file.failWrite(f);
}
Log.e(TAG, "Failed to write package usage times", e);
}
}
}
mLastWritten.set(SystemClock.elapsedRealtime());
}
}
/data/system/package-usage.list文件中每一行记录一条package及其上次使用时间(单位ms)。
由于IO操作的过程中,写入文件并非立刻就会真正意义上写入物理磁盘,以及在写入文件的过程中还可能中断或者出错等原因的考虑,采用的策略是先将老的文件package-usage.list,重命为增加后缀package-usage.list.bak;然后再往package-usage.list文件写入新的数据,数据写完之后再执行sync操作,将内存数据彻底写入物理磁盘,此时便可以安全地删除原来的package-usage.list.bak文件。
2.7.3 ST.shutdownRadios
[-> ShutdownThread.java]
private void shutdownRadios(final int timeout) {
// If a radio is wedged, disabling it may hang so we do this work in another thread,
// just in case.
final long endTime = SystemClock.elapsedRealtime() + timeout;
final boolean[] done = new boolean[1];
Thread t = new Thread() {
public void run() {
TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
boolean radioOff;
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
try {
radioOff = phone == null || !phone.needMobileRadioShutdown();
if (!radioOff) {
Log.w(TAG, "Turning off cellular radios...");
metricStarted(METRIC_RADIO);
phone.shutdownMobileRadios();
}
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException during radio shutdown", ex);
radioOff = true;
}
Log.i(TAG, "Waiting for Radio...");
long delay = endTime - SystemClock.elapsedRealtime();
while (delay > 0) {
if (mRebootHasProgressBar) {
int status = (int)((timeout - delay) * 1.0 *
(RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
status += PACKAGE_MANAGER_STOP_PERCENT;
sInstance.setRebootProgress(status, null);
}
if (!radioOff) {
try {
radioOff = !phone.needMobileRadioShutdown();
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException during radio shutdown", ex);
radioOff = true;
}
if (radioOff) {
Log.i(TAG, "Radio turned off.");
metricEnded(METRIC_RADIO);
shutdownTimingsTraceLog
.logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
}
}
if (radioOff) {
Log.i(TAG, "Radio shutdown complete.");
done[0] = true;
break;
}
SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS);
delay = endTime - SystemClock.elapsedRealtime();
}
}
};
t.start();
try {
t.join(timeout);
} catch (InterruptedException ex) {
}
if (!done[0]) {
Log.w(TAG, "Timed out waiting for Radio shutdown.");
}
}
创建新的线程来处理NFC, Radio and Bluetooth这些射频相关的模块的shutdown过程。每间隔500ms,check一次,直到nfc、bluetooth、radio全部关闭或者超时(MAX_RADIO_WAIT_TIME=12s)才会退出循环。
2.8 SDT.rebootOrShutdown
[-> ShutdownThread.java]
/**
* 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
//关闭电源 [见流程2.10]
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown(reason);
}
对于重启原因:
logcat会直接输出Rebooting, reason: ;
如果重启失败,则会输出Reboot failed; ,- 果无法重启,则会尝试直接关机。
2.9 PMS.lowLevelReboot
public static void lowLevelShutdown(String reason) {
if (reason == null) {
reason = "";
}
SystemProperties.set("sys.powerctl", "shutdown," + reason);
}
/**
* 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)) {
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";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
try {
Thread.sleep(20 * 1000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}
- 当reboot原因是“recovery”,则设置属性sys.powerctl
=reboot,recovery
; - 当其他情况,则设置属性”sys.powerctl=reboot,[reason]”。
- 当关机时,则设置属性”sys.powerctl=shutdown,[reason]”。
到此,framework层面的重启就流程基本介绍完了,那么接下来就要进入属性服务,即设置sys.powerctl=reboot,
。
三,总结
先用一句话总结,从最开始的PM.reboot(),经过层层调用,最终重启的核心方法等价于调用SystemProperties.set(“sys.powerctl”, “reboot,” + reason); 也就意味着调用下面命令,也能重启手机:
adb shell setprop sys.powerctl reboot
后续,还会进一步上面命令的执行流程,如何进入native,如何进入kernel来完成重启的,以及PM.reboot如何触发的。
四、回顾
先回顾下上部分得分析,从最开始的PM.reboot(),经过层层调用,最终调用
SystemProperties.set(“sys.powerctl”, “reboot,” + reason);
五、重启流程
aosp/system/core/init/property_service.cpp
aosp/system/core/init/reboot.cpp
aosp/system/core/init/reboot_utils.cpp
aosp/system/core/init/init.cpp
PM.reboot最终也就是setprop sys.powerctl,那么谁来监听sys.powerctl 值呢,肯定是init了,接下来就也就引入本节得重点,
init监听 sys.powerctl 处理关机后半部流程
5.1 HandlePropertySet
[->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, std::string* 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.
//此时会记录到logcat中,那个进程设置sys.powerctl得以及原因
if (name == "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(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
}
...
return PropertySet(name, value, error); //设置属性值
}
5.2 HandlePropertySet
[->property_service.cpp]
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
...
property_changed(name, value);// 会通知init property值改变
return PROP_SUCCESS;
}
5.3 property_changed
[->init.cpp]
void property_changed(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") { //当init检测到某个进程设置 sys.powerctl时,会把do_shutdown 置true,init主循环会执行关机动作 见2.3节
// Despite the above comment, we can't call HandlePowerctlMessage() 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.
// TODO: once property service is removed from init, this will never happen from a builtin,
// but rather from a callback from the property service socket, in which case this hack can
// go away.
shutdown_command = value;
do_shutdown = true;
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
ResetWaitForProp();
}
}
}
5.4 SecondStageMain
[->init.cpp]
int SecondStageMain(int argc, char** argv) {
...
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) { //执行关机重启流程
shutting_down = true;
}
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
if (auto result = epoll.Wait(epoll_timeout); !result) {
LOG(ERROR) << result.error();
}
}
return 0;
}
5.5 HandlePowerctlMessage
[->reboot.cpp]
bool 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;
if (cmd_params.size() > 3) {
command_invalid = true;
} else 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];
// 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)) {//会把bootloader写到kernel,重启使用
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
}
} 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)) {//会把recovery写到kernel,重启使用
LOG(ERROR) << "Failed to set bootloader message: " << err;
return false;
}
reboot_target = "recovery";
}
// If there is an additional parameter, pass it along
if ((cmd_params.size() == 3) && cmd_params[2].size()) {
reboot_target += "," + cmd_params[2];
}
}
} else {
command_invalid = true;
}
if (command_invalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return false;
}
LOG(INFO) << "Clear action queue and start shutdown trigger";
ActionManager::GetInstance().ClearQueue();
// Queue shutdown trigger first
ActionManager::GetInstance().QueueEventTrigger("shutdown");//处理init.rc中shutdown部分,依次关闭各个模块
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
DoReboot(cmd, command, reboot_target, run_fsck); //reboot 流程核心,见下节
return Success();
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");//设置中shutdown_done部分,shutdown执行完会执行DoReboot
// 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();
}
return true;
}
5.6 DoReboot
[->reboot.cpp]
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
// empty string.
// runFsck Whether to run fsck after umount is done.
//
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) {
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
// 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,");
}
property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);//设置reboot 重启原因到persist.sys.boot.reason,重启后可以查看
sync();
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
auto shutdown_timeout = 0ms;
if (!SHUTDOWN_ZERO_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";
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
const std::set<std::string> to_starts{"watchdogd"};
for (const auto& s : ServiceList::GetInstance()) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
if (auto result = s->Start(); !result) {
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) {
LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
<< "': " << result.error();
}
}
}
// remaining operations (specifically fsck) may take a substantial duration
if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
TurnOffBacklight();
}
Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
// will not check animation class separately
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count("animation")) service->SetShutdownCritical();
}
}
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
//终止除shutdown关键之外的所有服务。 等待完成
if (shutdown_timeout > 0ms) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Terminate();
}
int service_count = 0;
// Only wait up to half of timeout here
auto termination_wait_timeout = shutdown_timeout / 2;
while (t.duration() < termination_wait_timeout) {
ReapAnyOutstandingChildren();
service_count = 0;
for (const auto& s : ServiceList::GetInstance()) {
// Count the number of services running except shutdown critical.
// Exclude the console as it will ignore the SIGTERM signal
// and not exit.
// Note: SVC_CONSOLE actually means "requires console" but
// it is only used by the shell.
if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
service_count++;
}
}
if (service_count == 0) {
// All terminable services terminated. We can exit early.
break;
}
// Wait a bit before recounting the number or running services.
std::this_thread::sleep_for(50ms);
}
LOG(INFO) << "Terminating running services took " << t
<< " with remaining services:" << service_count;
}
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
//2. 关闭所有得服务
for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (!s->IsShutdownCritical()) s->Stop();
}
SubcontextTerminate();
ReapAnyOutstandingChildren();
// 3. send volume shutdown to vold
// 3. 关闭vold
Service* voldService = ServiceList::GetInstance().FindService("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
voldService->Stop();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
// logcat stopped here
for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
if (kill_after_apps.count(s->name())) s->Stop();
}
// 4. sync, try umount, and optionally run fsck for user shutdown
//4. 同步,卸载分区,
{
Timer sync_timer;
LOG(INFO) << "sync() before umount...";
sync();
LOG(INFO) << "sync() before umount took" << sync_timer;
}
UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// 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);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget); //重启系统
abort();
}
5.7 RebootSystem
[->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);
}
//下面就是reboot的system call进入内核空间了:
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF); //调用reboot函数执行关机
break;
case ANDROID_RB_RESTART2:
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());//调用syscall函数执行重启
break;
case ANDROID_RB_THERMOFF:
reboot(RB_POWER_OFF);//调用reboot函数执行重启
break;
}
// In normal case, reboot should not return.
PLOG(ERROR) << "reboot call returned";
abort();
}
六、总结
一句话总结,从 设置属性sys.powerctrl,最终重启调用libc库得 reboot或syscall, 也就意味这reboot下一步流程到达内核空间,
七、回顾
前面我们从最开始的PM.reboot(),经过层层调用,最终调用libc库得reboot和syscall,也正式开始从用户空间切到到内核空间。
八、内核重启流程
kernel/reboot.c
arch/arm64/kernel/process.c
8.1 SYSCALL_DEFINE4(reboot
内核空间reboot入口
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
...
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF: //power off
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2: // reboot
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
#ifdef CONFIG_KEXEC_CORE
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif
#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif
default:
ret = -EINVAL;
break;
}
mutex_unlock(&reboot_mutex);
return ret;
}
8.2 kernel_restart
/**
* kernel_restart - reboot the system
* @cmd: pointer to buffer containing command to execute for restart
* or %NULL
*
* Shutdown everything and perform a clean reboot.
* This is not safe to call in interrupt context.
*/
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
void kernel_restart_prepare(char *cmd)
/* 内核通知链,
void kernel_restart_prepare(char *cmd)
{
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); 锁定内核reboot通知链,我们可以监听reboot_notifier_list获取重启原因
system_state = SYSTEM_RESTART;
usermodehelper_disable();
device_shutdown();
}
*/
migrate_to_reboot_cpu();
syscore_shutdown();
if (!cmd)// lastkmsg 可以搜索一下关键词获取重启原因
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART); // 打印堆栈
machine_restart(cmd);
}
8.3 machine_restart
/*
* Restart requires that the secondary CPUs stop performing any activity
* while the primary CPU resets the system. Systems with multiple CPUs must
* provide a HW restart implementation, to ensure that all CPUs reset at once.
* This is required so that any code running after reset on the primary CPU
* doesn't have to co-ordinate with other CPUs to ensure they aren't still
* executing pre-reset code, and using RAM that the primary CPU's code wishes
* to use. Implementing such co-ordination would be essentially impossible.
*/
void machine_restart(char *cmd)
{
/* Disable interrupts first */
//关闭中断
local_irq_disable();
smp_send_stop();
/*
* UpdateCapsule() depends on the system being reset via
* ResetSystem().
*/
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_reboot(reboot_mode, NULL);
/* Now call the architecture specific reboot code. */
//现在调用体系结构特定的重启代码。由于我是在aosp上下载得代码,不包含具体芯片部分得操作,因此重启流程到此结束,
if (arm_pm_restart)
arm_pm_restart(reboot_mode, cmd);
else
do_kernel_restart(cmd);
/*
void do_kernel_restart(char *cmd)
{
atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);//通知所有注册restart_handler_list得模块
}
*/
/*
* Whoops - the architecture was unable to reboot.
*/
printk("Reboot failed -- System halted\n");
while (1);
}
arm_pm_restart = mdesc->restart; //arm_pm_restart
针对插单卡,开数据业务情况下radio关机流程分析。
先贴一下ShutdownThread的log:
05-24 03:48:17.226 1354 1354 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
05-24 03:48:17.298 1354 4205 I ShutdownThread: Sending shutdown broadcast...
05-24 03:48:17.422 1354 4205 I ShutdownThread: Shutting down activity manager...
05-24 03:48:17.500 1354 4205 I ShutdownThread: Shutting down package manager...
05-24 03:48:17.511 1354 4213 W ShutdownThread: Turning off cellular radios...
05-24 03:48:17.516 1354 4213 I ShutdownThread: Waiting for Radio...
05-24 03:48:18.422 1354 4213 I ShutdownThread: Radio turned off.
05-24 03:48:18.422 1354 4213 I ShutdownThread: Radio shutdown complete.
05-24 03:48:19.226 1354 4205 I ShutdownThread: Shutdown critical subsyslist is :modem :
05-24 03:48:19.226 1354 4205 I ShutdownThread: Waiting for a maximum of 10000ms
05-24 03:48:19.227 1354 4205 I ShutdownThread: Vendor subsystem(s) shutdown successful
05-24 03:48:19.734 1354 4205 I ShutdownThread: Performing low-level shutdown...
每一步的耗时时间:
05-24 03:48:17.422 1354 4205 D ShutdownTiming: SendShutdownBroadcast took to complete: 124ms
05-24 03:48:17.500 1354 4205 D ShutdownTiming: ShutdownActivityManager took to complete: 77ms
05-24 03:48:17.509 1354 4205 D ShutdownTiming: ShutdownPackageManager took to complete: 9ms
05-24 03:48:18.422 1354 4213 D ShutdownTiming: ShutdownRadio took to complete: 911ms
05-24 03:48:18.422 1354 4205 D ShutdownTiming: ShutdownRadios took to complete: 914ms
//在rebootorShutdown函数前的每一步耗时
05-24 03:48:18.422 1354 4205 D ShutdownTiming: SystemServerShutdown took to complete: 1133ms
ShutdownThread.shutdown()
Notifying thread to start shutdown longPressBehavior=1 打印出现在shutdownInner函数中。当重启或关机时会分别触发shutdown或reboot函数。最终都会走到shutdownInner函数。shutdownInner函数会触发ShutdownThread 中static实例的start函数。因为其继承自thread,所以会触发run函数。
关机画面
shutdownInner中调用beginShutdownSequence
确保此函数只进入一次。
接着调用showShutdwonDialog()判断是否去显示关机动画。
然后触发ShutdownThread.run()
ShutdownThread.run()
发送广播
注意其在调用sendOrderedBraodcastAsUser的参数br,即在发送广播的同时,br会成为此广播的最后一个接受者。br的作用是为了确保所有上层应用都收到关机广播再走下面的流程。
等待广播发送完毕
br最后收到广播,将mActionDone设置为true。 run函数中轮询mActionDone,为true时退出,走下面的流程。
关闭ActivityManager和PackageManager
关闭radio
调用shutdownRadio函数
shutdownRadio函数启动一个thread执行关闭radio的操作。
shutdownRadio
首先判断radio是否需要shutdown,若是则调用phone.shutdownMobileRadios来关闭radio。
然后轮询needMobileRadioShutdown()状态。如果radiooff则走下一步。
关闭radio
接下来针对关闭radio 重点分析一下流程。
判断是否需要关闭radio
从上面截图可以知道,就是判断RIL.java的mState变量值。如果是RADIO_UNAVAILABLE则认为不需要关机,否则需要关机。
mState状态如何转变为UNAVAILABLE
有两个情况会变为UNAVAILBALBE:
radioIndication.java 被通知radioStateChanged时
对应log:
05-24 03:48:19.179 2128 2287 D RILJ : [UNSL]< UNSOL_RESPONSE_RADIO_STATE_CHANGED radioStateChanged: RADIO_UNAVAILABLE [SUB0]
05-24 03:48:19.184 2128 2287 D RILJ : [UNSL]< UNSOL_RESPONSE_RADIO_STATE_CHANGED radioStateChanged: RADIO_UNAVAILABLE [SUB1]
RIL_REQUEST_SHUTDOWN请求返回时
详细流程如下:
RILC 返回response时通过RadioResponse的对应接口返回。
对应RIL_REQUEST_SHUTDOWN是requestShutdownResponse().
在responseVoid函数中,先通过RIL.java的processResponse做一些处理。
然后通过sendMessageResponse通知AP上层的发送方,命令执行情况。
最后通过processResponseDone来打印收到的response信息
对应log:
05-24 03:48:18.120 2128 2128 D RILJ : [4151]> RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.124 2128 2128 D RILJ : [4152]> RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.134 2128 2287 D RILJ : [4151]< RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.149 2128 2287 D RILJ : [4152]< RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.212 2128 2128 D RILJ : [4153]> RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:18.214 2128 2128 D RILJ : [4154]> RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:18.374 2128 2287 D RILJ : [4153]< RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:19.108 2128 2287 D RILJ : [4154]< RIL_REQUEST_SHUTDOWN [SUB0]
关闭radio
对应log:
05-24 03:48:17.513 SST : [0] mDeviceShuttingDown=true, mDesiredPowerState=false, getRadioState=RADIO_ON, mPowerOffDelayNeed=true, mAlarmSwitch=false, mRadioDisabledByCarrier=false
05-24 03:48:17.515 SST : [1] mDeviceShuttingDown=true, mDesiredPowerState=false, getRadioState=RADIO_ON, mPowerOffDelayNeed=true, mAlarmSwitch=false, mRadioDisabledByCarrier=false
当插单卡,开数据业务,接着调用poweOffRadioSafely()
开数据业务情况下poweOffRadioSafely流程
调用流程如下:
ShutDownThread.shutdownRadios()-》phone.shutdownMobileRadios (ITelephone–>PhoneInterfaceManager)->PhoneInterfaceManager.shutdownRadiosUsingID()->Phone.shutdownRadio()->ServiceStateTracker.requestShutDown()->setPowerStateToDesired()->powerOffRadioSafely->DcTracker.clearUpAllConnections() 同事registerForAllDataDisconnected(EVENT_All_DATA_DISCONNECTED)
即使 插单卡,只要有数据业务,两个sub都会调用dcTracker.cleanUpAllConnections()。 无卡的那个sub还会关注EVENT_All_DATA_DISCONNECTED。
在cleanUpAllConnections()中,针对每一个APN调用一次cleanUpConnection。mDisconnectPendingCount 在cleanUpConnection ()中会自加1.
cleanUpConnection 中tearDown apn。 mDisconnectPendingCount 自加1. 关闭成功后收到EVENT_DISCONNECT_DONE.
无卡sub 的cleanUpAllConnections中,直接调用notifyAllDataDisconnected().
数据业务sub data断开后触发onDisconnectDone,mDisconnectPendingCount 减1至0后,也调用notifyAllDataDisconnected。
数据业务卡是在onDisconnectDone中触发processPendingRadioPowerOffAfterDataOff().进而调用hangupAndPowerOff。
非数据业务卡,或无卡sub,在SST(serviceStateTracker) 处理EVENT_ALL_DATA_DISCONNTECD时调用hangupAndPowerOff
无卡sub等待EVENT_All_DATA_DISCONNECTED
log:
//不插卡,或无数据业务的卡。等待另一张卡关数据业务
05-24 03:48:17.516 2128 2412 D SST : [1] Data is active on DDS. Wait for all data disconnect
//两个sub 都等待30s,都会设置mPendingPowerOffAfterDataOff
05-24 03:48:17.514 2128 2412 D SST : [0] Wait upto 30s for data to disconnect, then turn off radio.
05-24 03:48:17.516 2128 2412 D SST : [1] Wait upto 30s for data to disconnect, then turn off radio.
//两个sub,一个无卡mDisconnectPendingCount 为0,一个volte和data两个apn,所以mDisconnectPendingCount = 2
05-24 03:48:17.515 2128 2128 D QtiDCT : [0]cleanUpConnection: tearing down using gen#1apnContext={mApnType=default mState=CONNECTED mWaitingApns={[[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true}
05-24 03:48:17.515 2128 2128 D QtiDCT : [0]cleanUpConnection: tearing down using gen#1apnContext={mApnType=ims mState=CONNECTED mWaitingApns={[[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true}
05-24 03:48:17.523 2128 2128 D QtiDCT : [0]cleanUpConnection: mDisconnectPendingCount = 2
05-24 03:48:17.535 2128 2128 D QtiDCT : [1]cleanUpConnection: mDisconnectPendingCount = 0
//使能数据业务的sub log,分别关闭数据和volte的apn
//QtiDcTracker.java继承自DcTracker.java,由于多态,调用基本是DcTracker的函数
05-24 03:48:17.568 2128 2128 D QtiDCT : [0]getValidApnContext (onDisconnectDone) on {mApnType=ims mState=DISCONNECTING mWaitingApns={[[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true} got 1 vs 1
05-24 03:48:17.568 2128 2128 D QtiDCT : [0]onDisconnectDone: EVENT_DISCONNECT_DONE apnContext={mApnType=ims mState=DISCONNECTING mWaitingApns={[[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] China Mobile (IMS), 2466, 46002, ims, , , , , , -1, ims, IPV4V6, IPV4V6, true, 0, 0, 2, true, 1023, 0, 300, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true}
05-24 03:48:17.583 2128 2128 D QtiDCT : [0]onDisconnectDone: not retrying
05-24 03:48:17.600 2128 2128 D QtiDCT : [0]getValidApnContext (onDisconnectDone) on {mApnType=default mState=DISCONNECTING mWaitingApns={[[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true} got 1 vs 1
05-24 03:48:17.600 2128 2128 D QtiDCT : [0]onDisconnectDone: EVENT_DISCONNECT_DONE apnContext={mApnType=default mState=DISCONNECTING mWaitingApns={[[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0]} mApnSetting={[ApnSettingV5] APN_NAME_CMNET, 2463, 46002, cmnet, , , , , , -1, default | net | supl, IPV4V6, IPV4V6, true, 0, 0, 0, false, 0, 0, 0, 0, , , false, 0, 0} mReason=radioTurnedOff mDataEnabled=true mDependencyMet=true}
05-24 03:48:17.606 2128 2128 D SST : [0] Process pending request to turn radio off.
05-24 03:48:17.608 2128 2128 D QtiDCT : [0]onDisconnectDone: radio will be turned off, no retries
//数据业务sub调用hangupAndPowerOff
05-24 03:48:17.606 2128 2128 D SST : [0] Process pending request to turn radio off.
//非数据业务sub调用hangupAndPowerOff
05-24 03:48:17.610 2128 2128 D SST : [1] EVENT_ALL_DATA_DISCONNECTED, turn radio off now.
hangupAndPoweroff
最终是向ril发送RIL_REQUEST_RADIO_POWER
qcril_qmi_nas_request_power
对应log:
05-24 03:48:17.608 2128 2128 D RILJ : [4127]> RADIO_POWER on = false [SUB0]
05-24 03:48:17.612 2128 2128 D RILJ : [4128]> RADIO_POWER on = false [SUB1]
05-24 03:48:17.997 2128 2287 D RILJ : [4128]< RADIO_POWER [SUB1]
05-24 03:48:18.003 2128 2287 D RILJ : [4127]< RADIO_POWER [SUB0]
05-24 03:48:18.117 2128 2128 D SST : [1] EVENT_RADIO_POWER_OFF_DONE
05-24 03:48:18.210 2128 2128 D SST : [0] EVENT_RADIO_POWER_OFF_DONE
成功power radio后的流程:
SST会收到EVENT_RADIO_POWER_OFF_DONE。
调用requestShutDown。又会走到ril。
里面涉及到一个shutdown的状态机。
这个状态机如何触发ril内部模块shutdown呢?以内部的关卡状态为例。
在这里将UimCardPowerReqMsg命令发出。
最终UimModule模块处理。
最终发送出去,由UimModemEndpointModule处理:
最终通过qmi发送QMI_UIM_POWER_DOWN_REQ命令给modem。真是一个弯弯曲曲的流程。
RIL_REQUEST_SHUTDOWN执行完毕
不管ril内部的复杂流程。当执行完后,会通知上层么?
从代码看,调用是没有提供参数。所以不会通知上层。但是后续radio状态机会发生变化。
并且RIL_REQUEST_SHUTDOWN 处理完毕后 ,RIl.java 会处理。也会将Radio状态设置为unavailable。所以发送shutdown后,终端在radio还没有到unavailabe时,ril层就提前转到unavialable了。
05-24 03:48:18.120 2128 2128 D RILJ : [4151]> RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.124 2128 2128 D RILJ : [4152]> RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.134 2128 2287 D RILJ : [4151]< RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.149 2128 2287 D RILJ : [4152]< RIL_REQUEST_SHUTDOWN [SUB1]
05-24 03:48:18.212 2128 2128 D RILJ : [4153]> RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:18.214 2128 2128 D RILJ : [4154]> RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:18.374 2128 2287 D RILJ : [4153]< RIL_REQUEST_SHUTDOWN [SUB0]
// radio state 变为 unavialable是 03:48:19.175,但是由于RIL_REQUEST_SHUTDOWN 03:48:18.374时已经都执行完了。所以ShutdownThread的轮询认为radio turned off。在shutdownRadios中出现如下打印
05-24 03:48:18.422 1354 4213 I ShutdownThread: Radio turned off.
05-24 03:48:18.422 1354 4213 I ShutdownThread: Radio shutdown complete.
05-24 03:48:19.108 2128 2287 D RILJ : [4154]< RIL_REQUEST_SHUTDOWN [SUB0]
05-24 03:48:18.001 1208 1269 D RILC : radioStateChangedInd: radioState 0
05-24 03:48:18.006 1193 1255 D RILC : radioStateChangedInd: radioState 0
05-24 03:48:19.175 1193 1255 D RILC : radioStateChangedInd: radioState 1
05-24 03:48:19.182 1208 1269 D RILC : radioStateChangedInd: radioState 1
最后一步 rebootOrShutdown
QCRIL执行RIL_REQUEST_SHUTDOWN 的内部状态机
这个内部状态机曾经发送过变化。之所以关注到这个代码的改动,是因为比较不同机器的ShutdownTiming: ShutdownRadio took to complete的时间打印在终端同样插卡开数据volte的情况下,稳定的存在差异。最后发现主要差异在RIL_REQUEST_SHUTDOWN 执行时间不同。新平台机器的时间要长。 最后对比了日志,发现在状态2至状态3时,新平台有500ms差异,而老平台没有。看了代码才发现,状态名变了,等待的事件变了(从ims service的ims-reg状态,到ims pdp connected状态)。 这个变化是为了确保ims从网络侧de-registered。