Android关机重启流程

一,概述

重启动作从按键触发中断,linux kernel层给Android framework层返回按键事件进入 framework层,再从 framework层到kernel层执行kernel层关机任务。当然还有非按键触发,比如shell 命令reboot,或者系统异常导致重启,或者直接调用PM的reboot()方法重启。

这里就先从PowerManager说起。


二,重启流程

  1. frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

  2. frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

  3. 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);
    }
设置”sys.shutdown.requested”,记录下mRebootmReason。如果是进入安全模式,则”persist.sys.safemode=1”。

接下来主要关闭一些系统服务:

  1. 发送关机广播
  2. 关闭AMS
  3. 关闭PMS
  4. 关闭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);


五、重启流程

 
  1. aosp/system/core/init/property_service.cpp

  2. aosp/system/core/init/reboot.cpp

  3. aosp/system/core/init/reboot_utils.cpp

  4. 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,也正式开始从用户空间切到到内核空间。

八、内核重启流程

 
  1. kernel/reboot.c

  2. 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。

在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统的关机重启功能是通过多个层面的交互来实现的。首先,从按键触发中断开始,操作系统会将按键事件传递给Android框架层。然后,框架层会将这个事件再次传递回内核层,以执行关机重启任务。除了按键触发外,还可以通过其他方式触发重启,比如使用命令行工具reboot或者直接调用PowerManager的reboot()方法。 在具体的代码实现中,可以通过函数fastboot_register("reboot-bootloader", cmd_reboot_bootloader)将重启到引导加载程序(bootloader)的功能注册到系统中。这样,当执行该命令时,系统就会进入引导加载程序。 关于Android系统的关机重启功能的具体实现,可以参考Android源代码中的PowerManager.java文件,其中包含了与关机重启相关的代码。在该文件中,有一个名为PM.reboot的方法,用于实现系统的重启功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Android关机重启流程](https://blog.csdn.net/mafei852213034/article/details/109818131)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [android 系统重启关机流程分析](https://blog.csdn.net/vzvzvxz/article/details/7768361)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值