赞
踩
重启动作从按键触发中断,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
[->PowerManager.java]
- public void reboot(String reason) {
- try {
- mService.reboot(false, reason, true); // 调用PowerManagerService->reboot
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
[-> 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);
[-> 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) {
- }
- }
- }
- }
- }

[->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则代表关机操作。
[->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);
- }

设置”sys.shutdown.requested”,记录下mReboot
和mReason
。如果是进入安全模式,则”persist.sys.safemode=1”。
接下来主要关闭一些系统服务:
之后就需要进入重启/关机流程,由init 进程处理。
通过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
- 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文件。
[-> 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)才会退出循环。
[-> 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; ,- 果无法重启,则会尝试直接关机。
- 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
;到此,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 处理关机后半部流程
- // 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); //设置属性值
- }

- static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
- ...
- property_changed(name, value);// 会通知init property值改变
- return PROP_SUCCESS;
- }
[->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();
- }
- }
- }

[->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;
- }

[->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;
- }

[->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();
- }

[->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
内核空间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;
- }

- /**
- * 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);
- }

- /*
- * 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()
注意其在调用sendOrderedBraodcastAsUser的参数br,即在发送广播的同时,br会成为此广播的最后一个接受者。br的作用是为了确保所有上层应用都收到关机广播再走下面的流程。
br最后收到广播,将mActionDone设置为true。 run函数中轮询mActionDone,为true时退出,走下面的流程。
调用shutdownRadio函数
shutdownRadio函数启动一个thread执行关闭radio的操作。
首先判断radio是否需要shutdown,若是则调用phone.shutdownMobileRadios来关闭radio。
然后轮询needMobileRadioShutdown()状态。如果radiooff则走下一步。
接下来针对关闭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]
详细流程如下:
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]
对应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()
调用流程如下:
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.

最终是向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
SST会收到EVENT_RADIO_POWER_OFF_DONE。
调用requestShutDown。又会走到ril。
里面涉及到一个shutdown的状态机。
这个状态机如何触发ril内部模块shutdown呢?以内部的关卡状态为例。
在这里将UimCardPowerReqMsg命令发出。
最终UimModule模块处理。
最终发送出去,由UimModemEndpointModule处理:
最终通过qmi发送QMI_UIM_POWER_DOWN_REQ命令给modem。真是一个弯弯曲曲的流程。
不管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
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。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。