IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 12 关机重启流程 -> 正文阅读

[移动开发]Android 12 关机重启流程

文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析

关机流程

Android上层触发关机的入口很多,但最终几乎都是调用ShutdownThread.shutdown来实现。如下是一些常见的调用关机的点:

  • StatusBarManagerService#shutdown, 这个主要是对接SystemUI
  • WindowManagerService#shutdown, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
  • PowerManager#shutdown, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
  • 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
    ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;

以PowerManager#shutdown调用为例,来分析一下关机流程

PowerManager#shutdown

mService实际上是PowerManagerService binder服务的bp端, 执行mService.shutdown实际上是向 PMS binder服务发起调用

/**
 * Turn off the device.
 *
 * @param confirm If true, shows a shutdown confirmation dialog.
 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
 * @param wait If true, this call waits for the shutdown to complete and does not return.
 *
 * @hide
 */
public void shutdown(boolean confirm, String reason, boolean wait) {
    try { // 向 PowerManagerService 发起binder调用
        mService.shutdown(confirm, reason, wait);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

PowerManagerService$BinderService#shutdown

上面的调用将会调用以下代码

/**
 * Shuts down the device.
 *
 * @param confirm If true, shows a shutdown confirmation dialog.
 * @param wait If true, this call waits for the shutdown to complete and does not return.
 */
@Override // Binder call
public void shutdown(boolean confirm, String reason, boolean wait) {
    // 检查执行是否有
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    // 记录 shutdown 事件,会在log中进行打印
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long ident = Binder.clearCallingIdentity();
    try { // 直接给 PowerManagerService 处理
        shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

ShutdownCheckPoints#recordCheckPoint

ShutdownCheckPoints 类是一个专门用来记录关机重启详细信息,如调用者pid,原因

/** Records the pid of the caller process as a shutdown check point. */
public static void recordCheckPoint(int callerProcessId, @Nullable String reason) {
    INSTANCE.recordCheckPointInternal(callerProcessId, reason);
}

@VisibleForTesting
void recordCheckPointInternal(int callerProcessId, @Nullable String reason) {
    recordCheckPointInternal(callerProcessId == Process.myPid()
            ? new SystemServerCheckPoint(mInjector, reason)
            : new BinderCheckPoint(mInjector, callerProcessId, reason));
    // systme log 中打印 shutdown 原因
    Slog.v(TAG, "Binder shutdown checkpoint recorded with pid=" + callerProcessId);
}

PowerManagerService#shutdownOrRebootInternal

这个函数处理包含了关机和重启的处理,通过调用ShutdownThread.shutdown/reboot 实现。

private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
        @Nullable final String reason, boolean wait) {
    if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
        if (!PowerManager.isRebootingUserspaceSupportedImpl()) { // 检查是否支持userspace reboot
            throw new UnsupportedOperationException(
                    "Attempted userspace reboot on a device that doesn't support it");
        }
        UserspaceRebootLogger.noteUserspaceRebootWasRequested();
    }
    if (mHandler == null || !mSystemReady) { // 系统还没准备好
        if (RescueParty.isAttemptingFactoryReset()) { // RescueParty 正执行恢复出厂
            // If we're stuck in a really low-level reboot loop, and a
            // rescue party is trying to prompt the user for a factory data
            // reset, we must GET TO DA CHOPPA!
            // No check point from ShutdownCheckPoints will be dumped at this state.
            PowerManagerService.lowLevelReboot(reason);
        } else { // 给caller 抛出一个异常
            throw new IllegalStateException("Too early to call shutdown() or reboot()");
        }
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { // 重启到安全模式
                    ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                } else if (haltMode == HALT_MODE_REBOOT) { // 重启
                    ShutdownThread.reboot(getUiContext(), reason, confirm);
                } else { // 关机
                    ShutdownThread.shutdown(getUiContext(), reason, confirm);
                }
            }
        }
    };

    // ShutdownThread must run on a looper capable of displaying the UI.
    Message msg = Message.obtain(UiThread.getHandler(), runnable);
    msg.setAsynchronous(true); // 设置异步防止被阻塞
    UiThread.getHandler().sendMessage(msg); // 通过发送消息处理

    // PowerManager.reboot() is documented not to return so just wait for the inevitable.
    if (wait) {  // 阻塞到完成关机重启
        synchronized (runnable) {
            while (true) {
                try {
                    runnable.wait();
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

ShutdownThread#shutdown

confirm 参数表示是否需要确认,若需要确认会弹出一个对话框。

/**
 * Request a clean shutdown, waiting for subsystems to clean up their
 * state etc.  Must be called from a Looper thread in which its UI
 * is shown.
 *
 * @param context Context used to display the shutdown progress dialog. This must be a context
 *                suitable for displaying UI (aka Themable).
 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
 * @param confirm true if user confirmation is needed before shutting down.
 */
public static void shutdown(final Context context, String reason, boolean confirm) {
    mReboot = false;  // 关机或重启标志
    mRebootSafeMode = false;
    mReason = reason;
    shutdownInner(context, confirm);
}

ShutdownThread#shutdownInner

private static void shutdownInner(final Context context, boolean confirm) {
    // ShutdownThread is called from many places, so best to verify here that the context passed
    // in is themed.
    context.assertRuntimeOverlayThemable();

    // ensure that only one thread is trying to power down.
    // any additional calls are just returned
    synchronized (sIsStartedGuard) {
        if (sIsStarted) { // 进行中
            Log.d(TAG, "Request to shutdown already running, returning.");
            return;
        }
    }

    // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
    // this point preserves the system trace of the trigger point of the ShutdownThread.
    ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);

    final int longPressBehavior = context.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
    final int resourceId = mRebootSafeMode
            ? com.android.internal.R.string.reboot_safemode_confirm
            : (longPressBehavior == 2
                    ? com.android.internal.R.string.shutdown_confirm_question
                    : com.android.internal.R.string.shutdown_confirm);

    Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

    if (confirm) { // 需要确认,会弹一个对话框
        final CloseDialogReceiver closer = new CloseDialogReceiver(context);
        if (sConfirmDialog != null) {
            sConfirmDialog.dismiss();
        }
        sConfirmDialog = new AlertDialog.Builder(context)
                .setTitle(mRebootSafeMode
                        ? com.android.internal.R.string.reboot_safemode_title
                        : com.android.internal.R.string.power_off)
                .setMessage(resourceId)
                .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { // 点击是则会进行重启
                    public void onClick(DialogInterface dialog, int which) {
                        beginShutdownSequence(context);
                    }
                })
                .setNegativeButton(com.android.internal.R.string.no, null)
                .create();
        closer.dialog = sConfirmDialog;
        sConfirmDialog.setOnDismissListener(closer);
        sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        sConfirmDialog.show();
    } else { // 直接进行关机流程
        beginShutdownSequence(context);
    }
}

ShutdownThread#beginShutdownSequence

private static void beginShutdownSequence(Context context) {
    synchronized (sIsStartedGuard) {
        if (sIsStarted) {
            Log.d(TAG, "Shutdown sequence already running, returning.");
            return;
        }
        sIsStarted = true;
    }
    // 显示关机对话框
    sInstance.mProgressDialog = showShutdownDialog(context);
    sInstance.mContext = context;
    sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

    // make sure we never fall asleep again
    sInstance.mCpuWakeLock = null;
    try { // 防止机器休眠
        sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
        sInstance.mCpuWakeLock.setReferenceCounted(false);
        sInstance.mCpuWakeLock.acquire();
    } catch (SecurityException e) {
        Log.w(TAG, "No permission to acquire wake lock", e);
        sInstance.mCpuWakeLock = null;
    }

    // also make sure the screen stays on for better user experience
    sInstance.mScreenWakeLock = null;
    if (sInstance.mPowerManager.isScreenOn()) { // 保持屏幕长亮
        try {
            sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
            sInstance.mScreenWakeLock.setReferenceCounted(false);
            sInstance.mScreenWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mScreenWakeLock = null;
        }
    }

    if (SecurityLog.isLoggingEnabled()) {
        SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
    }

    // start the thread that initiates shutdown
    sInstance.mHandler = new Handler() {
    };
    sInstance.start(); // sInstance是ShutdownThread对象 其继承自Thread,开启线程执行关机流程
}

ShutdownThread#run

/**
 * Makes sure we handle the shutdown gracefully.
 * Shuts off power regardless of radio state if the allotted time has passed.
 */
public void run() {
    TimingsTraceLog shutdownTimingLog = newTimingsLog();
    shutdownTimingLog.traceBegin("SystemServerShutdown");
    metricShutdownStart();
    metricStarted(METRIC_SYSTEM_SERVER);

    // Start dumping check points for this shutdown in a separate thread.
    Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
            new File(CHECK_POINTS_FILE_BASENAME));// 将记录写入/data/system/shutdown-checkpoints/checkpoints
    dumpCheckPointsThread.start();

    BroadcastReceiver br = new BroadcastReceiver() {
        @Override public void onReceive(Context context, Intent intent) {
            // We don't allow apps to cancel this, so ignore the result.
            actionDone(); // 结束关机广播处理
        }
    };

    /*
     * Write a system property in case the system_server reboots before we
     * get to the actual hardware restart. If that happens, we'll retry at
     * the beginning of the SystemServer startup.
     */
    {
        String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
        SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
    }

    /*
     * If we are rebooting into safe mode, write a system property
     * indicating so.
     */
    if (mRebootSafeMode) {
        SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
    }

    shutdownTimingLog.traceBegin("DumpPreRebootInfo");
    try {
        Slog.i(TAG, "Logging pre-reboot information...");
        PreRebootLogger.log(mContext);
    } catch (Exception e) {
        Slog.e(TAG, "Failed to log pre-reboot information", e);
    }
    shutdownTimingLog.traceEnd(); // DumpPreRebootInfo

    metricStarted(METRIC_SEND_BROADCAST);
    shutdownTimingLog.traceBegin("SendShutdownBroadcast");
    Log.i(TAG, "Sending shutdown broadcast...");

    // First send the high-level shut down broadcast.
    mActionDone = false;
    Intent intent = new Intent(Intent.ACTION_SHUTDOWN); // 发送关机广播
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    mContext.sendOrderedBroadcastAsUser(intent,
            UserHandle.ALL, null, br, mHandler, 0, null, null);

    final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; // 10s 超时
    synchronized (mActionDoneSync) { // 等待关机广播处理完成,上面调用通知actionDone
        while (!mActionDone) {
            long delay = endTime - SystemClock.elapsedRealtime();
            if (delay <= 0) {
                Log.w(TAG, "Shutdown broadcast timed out");
                break;
            } else if (mRebootHasProgressBar) {
                int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                        BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                sInstance.setRebootProgress(status, null);
            }
            try {
                mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
            } catch (InterruptedException e) {
            }
        }
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
    metricEnded(METRIC_SEND_BROADCAST);

    Log.i(TAG, "Shutting down activity manager...");
    shutdownTimingLog.traceBegin("ShutdownActivityManager");
    metricStarted(METRIC_AM);

    final IActivityManager am =
            IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
    if (am != null) { // ams 处理shutdown
        try {
            am.shutdown(MAX_BROADCAST_TIME);
        } catch (RemoteException e) {
        }
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd();// ShutdownActivityManager
    metricEnded(METRIC_AM);

    Log.i(TAG, "Shutting down package manager...");
    shutdownTimingLog.traceBegin("ShutdownPackageManager");
    metricStarted(METRIC_PM);

    final PackageManagerService pm = (PackageManagerService)
        ServiceManager.getService("package");
    if (pm != null) { // pms 处理shutdown
        pm.shutdown();
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // ShutdownPackageManager
    metricEnded(METRIC_PM);

    // Shutdown radios.
    shutdownTimingLog.traceBegin("ShutdownRadios");
    metricStarted(METRIC_RADIOS);
    shutdownRadios(MAX_RADIO_WAIT_TIME);
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // ShutdownRadios
    metricEnded(METRIC_RADIOS);

    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

        // If it's to reboot to install an update and uncrypt hasn't been
        // done yet, trigger it now.
        uncrypt();
    }

    // Wait for the check points dump thread to finish, or kill it if not finished in time.
    shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
    try {
        dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
    } catch (InterruptedException ex) { // 等待记录写入完成
    }
    shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait

    shutdownTimingLog.traceEnd(); // SystemServerShutdown
    metricEnded(METRIC_SYSTEM_SERVER);
    saveMetrics(mReboot, mReason);
    // Remaining work will be done by init, including vold shutdown
    rebootOrShutdown(mContext, mReboot, mReason); // 继续执行
}

ShutdownThread#rebootOrShutdown

通过PowerManagerService来实现 shutdown/reboot

/**
 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
 * or {@link #shutdown(Context, String, boolean)} instead.
 *
 * @param context Context used to vibrate or null without vibration
 * @param reboot true to reboot or false to shutdown
 * @param reason reason for reboot/shutdown
 */
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
    if (reboot) { // 重启
        Log.i(TAG, "Rebooting, reason: " + reason);
        PowerManagerService.lowLevelReboot(reason);
        Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        reason = null;
    } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
        // vibrate before shutting down
        Vibrator vibrator = new SystemVibrator(context);
        try {
            vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
        } catch (Exception e) {
            // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
            Log.w(TAG, "Failed to vibrate during shutdown.", e);
        }

        // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
        try {
            Thread.sleep(SHUTDOWN_VIBRATE_MS);
        } catch (InterruptedException unused) {
        }
    }
    // Shutdown power
    Log.i(TAG, "Performing low-level shutdown...");
    PowerManagerService.lowLevelShutdown(reason); // 关机
}

PowerManagerService#lowLevelShutdown

此方法通过设置属性sys.powerctl来通知init处理shutdown

/**
 * Low-level function turn the device off immediately, without trying
 * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
 *
 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
 */
public static void lowLevelShutdown(String reason) {
    if (reason == null) {
        reason = "";
    }
    SystemProperties.set("sys.powerctl", "shutdown," + reason);
}

设置属性sys.powerctl值,init的property service将会接受设置的值并进行处理。

HandlePropertySet

init处理设置属性

// @system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr,
                           SocketConnection* socket, std::string* error) {
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
        return ret;
    }

    if (StartsWith(name, "ctl.")) { // 处理 ctl.xxx
        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
    }

    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
    // any process that sets this property to be able to accurately blame the cause of a shutdown.
    if (name == "sys.powerctl") { // 设置的sys.powerctl
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
            // path.
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        //  打印log
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
        if (!value.empty()) {
            DebugRebootLogging();
        }
        if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
            *error = "Userspace reboot is not supported by this device";
            return PROP_ERROR_INVALID_VALUE;
        }
    }

    // If a process other than init is writing a non-empty value, it means that process is
    // requesting that init performs a restorecon operation on the path specified by 'value'.
    // We use a thread to do this restorecon operation to prevent holding up init, as it may take
    // a long time to complete.
    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
        static AsyncRestorecon async_restorecon;
        async_restorecon.TriggerRestorecon(value);
        return PROP_SUCCESS;
    }

    return PropertySet(name, value, error); // 设置属性
}

PropertyChanged

当设置成功后,会调用PropertyChanged

// @system/core/init/init.cpp
void PropertyChanged(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    if (name == "sys.powerctl") { // 处理sys.powerctl属性变化
        trigger_shutdown(value); // 触发关机
    }

    if (property_triggers_enabled) {
        ActionManager::GetInstance().QueuePropertyChange(name, value);
        WakeMainInitThread();
    }

    prop_waiter_state.CheckAndResetWait(name, value);
}

trigger_shutdown在SecondStageMain中定义,是一个lambda表达式

int SecondStageMain(int argc, char** argv) {
    ...
    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
    ...
}

因此接下来调用 ShutdownState#TriggerShutdown

// @system/core/init/init.cpp
void TriggerShutdown(const std::string& command) {
    // We can't call HandlePowerctlMessage() directly in this function,
    // because it modifies the contents of the action queue, which can cause the action queue
    // to get into a bad state if this function is called from a command being executed by the
    // action queue.  Instead we set this flag and ensure that shutdown happens before the next
    // command is run in the main init loop.
    auto lock = std::lock_guard{shutdown_command_lock_};
    shutdown_command_ = command;
    do_shutdown_ = true;
    WakeMainInitThread(); // 唤醒主线程处理
}

在主线程的循环中处理 shutdown command

// @system/core/init/init.cpp
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
    LOG(INFO) << "Got shutdown_command '" << *shutdown_command
              << "' Calling HandlePowerctlMessage()";
    HandlePowerctlMessage(*shutdown_command);
    shutdown_state.set_do_shutdown(false);
}

HandlePowerctlMessage

// @system/core/init/reboot.cpp
void HandlePowerctlMessage(const std::string& command) {
    unsigned int cmd = 0;
    std::vector<std::string> cmd_params = Split(command, ",");
    std::string reboot_target = "";
    bool run_fsck = false;
    bool command_invalid = false;
    bool userspace_reboot = false;

    if (cmd_params[0] == "shutdown") { // 处理关机
        cmd = ANDROID_RB_POWEROFF;
        if (cmd_params.size() >= 2) {
            if (cmd_params[1] == "userrequested") {
                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                // Run fsck once the file system is remounted in read-only mode.
                run_fsck = true;
            } else if (cmd_params[1] == "thermal") {
                // Turn off sources of heat immediately.
                TurnOffBacklight();
                // run_fsck is false to avoid delay
                cmd = ANDROID_RB_THERMOFF;
            }
        }
    } else if (cmd_params[0] == "reboot") { // 处理重启
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            if (reboot_target == "userspace") {
                LOG(INFO) << "Userspace reboot requested";
                userspace_reboot = true;
            }
            // adb reboot fastboot should boot into bootloader for devices not
            // supporting logical partitions.
            if (reboot_target == "fastboot" &&
                !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
                reboot_target = "bootloader";
            }
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (reboot_target == "bootloader") {
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: "
                               << err;
                }
            } else if (reboot_target == "recovery") {
                bootloader_message boot = {};
                if (std::string err; !read_bootloader_message(&boot, &err)) {
                    LOG(ERROR) << "Failed to read bootloader message: " << err;
                }
                // Update the boot command field if it's empty, and preserve
                // the other arguments in the bootloader message.
                if (!CommandIsPresent(&boot)) {
                    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                    if (std::string err; !write_bootloader_message(boot, &err)) {
                        LOG(ERROR) << "Failed to set bootloader message: " << err;
                        return;
                    }
                }
            } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                       reboot_target == "fastboot") {
                std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
                                                                          : reboot_target;
                const std::vector<std::string> options = {
                        "--" + arg,
                };
                std::string err;
                if (!write_bootloader_message(options, &err)) {
                    LOG(ERROR) << "Failed to set bootloader message: " << err;
                    return;
                }
                reboot_target = "recovery";
            }

            // If there are additional parameter, pass them along
            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
                reboot_target += "," + cmd_params[i];
            }
        }
    } else {
        command_invalid = true;
    }
    if (command_invalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return;
    }

    // We do not want to process any messages (queue'ing triggers, shutdown messages, control
    // messages, etc) from properties during reboot.
    StopSendingMessages();

    if (userspace_reboot) {
        HandleUserspaceReboot();
        return;
    }

    LOG(INFO) << "Clear action queue and start shutdown trigger";
    ActionManager::GetInstance().ClearQueue();
    // Queue shutdown trigger first
    ActionManager::GetInstance().QueueEventTrigger("shutdown");
    // Queue built-in shutdown_done
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
        DoReboot(cmd, command, reboot_target, run_fsck); // 关键
        return Result<void>{};
    };
    // 将shutdown封装为<Builtin Action>并添加到队列,之后在主循环被处理,接着DoReboot将被调用
    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");

    EnterShutdown();
}

cmd 定义如下

// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004

EnterShutdown 用以清除wait prop和 pending exec flag

static void EnterShutdown() {
    LOG(INFO) << "Entering shutdown mode";
    shutting_down = true;
    // Skip wait for prop if it is in progress
    ResetWaitForProp();
    // Clear EXEC flag if there is one pending
    for (const auto& s : ServiceList::GetInstance()) {
        s->UnSetExec();
    }
}

DoReboot

接下来,在shutdown action被处理后,DoReboot被调用

// @system/core/init/reboot.cpp
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
// run_fsck Whether to run fsck after umount is done.
//
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,
                     bool run_fsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;

    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;

    auto shutdown_timeout = 0ms;
    if (!SHUTDOWN_ZERO_TIMEOUT) { // 计算timeout
        constexpr unsigned int shutdown_timeout_default = 6;
        constexpr unsigned int max_thermal_shutdown_timeout = 3;
        auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
                                                                     shutdown_timeout_default);
        if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
            shutdown_timeout_final = max_thermal_shutdown_timeout;
        }
        shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
    }
    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";

    sem_t reboot_semaphore;
    if (sem_init(&reboot_semaphore, false, 0) == -1) {
        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
        LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
        RebootSystem(cmd, reboot_target);
    }

    // Start a thread to monitor init shutdown process
    LOG(INFO) << "Create reboot monitor thread.";
    bool reboot_monitor_run = true;
    // 创建 monitor 线程
    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,
                                      shutdown_timeout, &reboot_monitor_run);
    reboot_monitor_thread.detach();

    // Start reboot monitor thread
    sem_post(&reboot_semaphore);

    // Ensure last reboot reason is reduced to canonical
    // alias reported in bootloader or system boot reason.
    size_t skip = 0;
    std::vector<std::string> reasons = Split(reason, ",");
    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
         reasons[1] == "hard" || reasons[1] == "warm")) {
        skip = strlen("reboot,");
    }
    // 写重启原因到属性和文件
    // /* Android reboot reason stored in this property */
    // #define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
    // #define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY
    PersistRebootReason(reason.c_str() + skip, true);

    // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
    // worry about unmounting it.
    if (!IsDataMounted()) { // data没有挂载,直接调用RebootSystem
        sync();
        RebootSystem(cmd, reboot_target);
        abort();
    }

    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
    const std::set<std::string> to_starts{"watchdogd"};
    std::set<std::string> stop_first;
    for (const auto& s : ServiceList::GetInstance()) {
        if (kDebuggingServices.count(s->name())) {
            // keep debugging tools until non critical ones are all gone.
            s->SetShutdownCritical();
        } else if (to_starts.count(s->name())) {
            if (auto result = s->Start(); !result.ok()) {
                LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
                           << "': " << result.error();
            }
            s->SetShutdownCritical();
        } else if (s->IsShutdownCritical()) {
            // Start shutdown critical service if not started.
            if (auto result = s->Start(); !result.ok()) {
                LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                           << "': " << result.error();
            }
        } else {
            stop_first.insert(s->name());
        }
    }

    // remaining operations (specifically fsck) may take a substantial duration
    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
        TurnOffBacklight();
    }

    // 处理关机动画
    Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
    Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);

        if (do_shutdown_animation) {
            SetProperty("service.bootanim.exit", "0");
            SetProperty("service.bootanim.progress", "0");
            // Could be in the middle of animation. Stop and start so that it can pick
            // up the right mode.
            boot_anim->Stop();
        }

        for (const auto& service : ServiceList::GetInstance()) {
            if (service->classnames().count("animation") == 0) {
                continue;
            }

            // start all animation classes if stopped.
            if (do_shutdown_animation) {
                service->Start();
            }
            service->SetShutdownCritical();  // will not check animation class separately
        }

        if (do_shutdown_animation) {
            boot_anim->Start();
            surface_flinger->SetShutdownCritical();
            boot_anim->SetShutdownCritical();
        }
    }

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdown_timeout > 0ms) {
        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
    }
    // Send SIGKILL to ones that didn't terminate cleanly.
    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
    SubcontextTerminate();
    // Reap subcontext pids.
    ReapAnyOutstandingChildren();

    // 3. send volume abort_fuse and volume shutdown to vold
    Service* vold_service = ServiceList::GetInstance().FindService("vold");
    if (vold_service != nullptr && vold_service->IsRunning()) {
        // Manually abort FUSE connections, since the FUSE daemon is already dead
        // at this point, and unmounting it might hang.
        CallVdc("volume", "abort_fuse");
        CallVdc("volume", "shutdown");
        vold_service->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here 停止 logcat
    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
    // 4. sync, try umount, and optionally run fsck for user shutdown
    {
        Timer sync_timer;
        LOG(INFO) << "sync() before umount...";
        sync();
        LOG(INFO) << "sync() before umount took" << sync_timer;
    }
    // 5. drop caches and disable zram backing device, if exist
    KillZramBackingDevice();

    LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
    // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
    if (auto ret = UnmountAllApexes(); !ret.ok()) {
        LOG(ERROR) << ret.error();
    }
    UmountStat stat =
            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
    // Follow what linux shutdown is doing: one more sync with little bit delay
    {
        Timer sync_timer;
        LOG(INFO) << "sync() after umount...";
        sync();
        LOG(INFO) << "sync() after umount took" << sync_timer;
    }
    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);

    // Send signal to terminate reboot monitor thread.
    reboot_monitor_run = false;
    sem_post(&reboot_semaphore);

    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, reboot_target); /// 执行关机重启
    abort();
}

RebootSystem

// @system/core/init/reboot_utils.cpp
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";

    if (!IsRebootCapable()) {
        // On systems where init does not have the capability of rebooting the
        // device, just exit cleanly.
        exit(0);
    }

    switch (cmd) {
        case ANDROID_RB_POWEROFF: // 关机
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2: // 重启
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF:
            if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
                LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
            } else {
                reboot(RB_POWER_OFF);
            }
            break;
    }
    // In normal case, reboot should not return.
    PLOG(ERROR) << "reboot call returned";
    abort();
}

接着调用bionic 的 reboot 函数:

// @bionic/libc/bionic/reboot.cpp
#include <unistd.h>
#include <sys/reboot.h>

extern "C" int __reboot(int, int, int, void*);

int reboot(int mode) {
  // 调用 linux reboot 函数
  return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, nullptr);
}

C++层关机入口

在C++代码里面可以直接调用 android_reboot 来实现关机重启

// @system/core/libcutils/include/cutils/android_reboot.h
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004

/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"

/* Android reboot reason stored in this property */
#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
#define LAST_REBOOT_REASON_FILE "/metadata/bootstat/" LAST_REBOOT_REASON_PROPERTY

/* Reboot or shutdown the system.
 * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
 * Due to that, process calling this should have proper selinux permission
 * to write to the property or the call will fail.
 */
int android_reboot(unsigned cmd, int flags, const char* arg);

android_reboot

// @system/core/libcutils/android_reboot.cpp
int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
    int ret;
    const char* restart_cmd = NULL;
    char* prop_value;

    switch (cmd) {
        case ANDROID_RB_RESTART:  // deprecated
        case ANDROID_RB_RESTART2:
            restart_cmd = "reboot";
            break;
        case ANDROID_RB_POWEROFF:
            restart_cmd = "shutdown";
            break;
        case ANDROID_RB_THERMOFF:
            restart_cmd = "shutdown,thermal";
            break;
    }
    if (!restart_cmd) return -1;
    if (arg && arg[0]) {
        ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
    } else {
        ret = asprintf(&prop_value, "%s", restart_cmd);
    }
    if (ret < 0) return -1;
    // 设置属性sys.powerctl, 和上层一样都会触发init执行关机
    ret = property_set(ANDROID_RB_PROPERTY, prop_value);
    free(prop_value);
    return ret;
}

关机流程总结

Java层:

ShutdownThread#shutdown -> PowerManagerService#lowLevelShutdown -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot

C++层

android_reboot -> 设置属性"sys.powerctl" -> init处理关机 -> bionic reboot -> linux __reboot

重启流程

Android上层重启的入口与关机流程类似,只是调用的方法从shutdown变成了 reboot,也是调用 ShutdownThread.reboot 来实现。如下是一些常见的调用关机的点:

  • StatusBarManagerService#reboot, 这个主要是对接SystemUI
  • WindowManagerService#reboot, 以WindowManagerFuncs接口提供给系统其他模块使用,诸如GlobalActions、PhoneWindowManager。
  • PowerManager#reboot, 以binder服务形式提供给客户端调用,需要持有android.permission.REBOOT权限。
  • 通过action启动ShutdownActivity请求关机重启,需要权限 android.permission.SHUTDOWN
    ACTION_REQUEST_SHUTDOWN = “com.android.internal.intent.action.REQUEST_SHUTDOWN”;

ShutdownThread#reboot

/**
 * Request a clean shutdown, waiting for subsystems to clean up their
 * state etc.  Must be called from a Looper thread in which its UI
 * is shown.
 *
 * @param context Context used to display the shutdown progress dialog. This must be a context
 *                suitable for displaying UI (aka Themable).
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 * @param confirm true if user confirmation is needed before shutting down.
 */
public static void reboot(final Context context, String reason, boolean confirm) {
    mReboot = true;
    mRebootSafeMode = false;
    mRebootHasProgressBar = false;
    mReason = reason;
    shutdownInner(context, confirm); // 和关机类似,最终调用rebootOrShutdown
}

rebootOrShutdown

重启情况是通过PowerManagerService.lowLevelReboot进行重启

public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
    if (reboot) {
        Log.i(TAG, "Rebooting, reason: " + reason);
        PowerManagerService.lowLevelReboot(reason);
        Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        reason = null;
    } ...

PowerManagerService#lowLevelReboot

/**
 * Low-level function to reboot the device. On success, this
 * function doesn't return. If more than 20 seconds passes from
 * the time a reboot is requested, this method returns.
 *
 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
 */
public static void lowLevelReboot(String reason) {
    if (reason == null) {
        reason = "";
    }

    // If the reason is "quiescent", it means that the boot process should proceed
    // without turning on the screen/lights.
    // The "quiescent" property is sticky, meaning that any number
    // of subsequent reboots should honor the property until it is reset.
    if (reason.equals(PowerManager.REBOOT_QUIESCENT)) { // 息屏重启
        sQuiescent = true;
        reason = "";
    } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
        sQuiescent = true;
        reason = reason.substring(0,
                reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
    }

    if (reason.equals(PowerManager.REBOOT_RECOVERY)
            || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) { // 进入 recovery
        reason = "recovery";
    }

    if (sQuiescent) {
        // Pass the optional "quiescent" argument to the bootloader to let it know
        // that it should not turn the screen/lights on.
        reason = reason + ",quiescent";
    }
    // 设置sys.powerctl
    SystemProperties.set("sys.powerctl", "reboot," + reason);
    try {
        Thread.sleep(20 * 1000L);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}

如同关机流程,设置sys.powerctl然后init会执行重启。

如何分析关机重启问题

了解了上面的流程,分析问题应该不难,查看关键的log即可。下面以关机为例来分析,对于关机的机器来说,通常插上充电器是会进入关机充电状态,有关机充电动画。而某些情况实际是发生了死机,插上充电器没有充电动画,一般需要长按10+s开机。

查看main log

主要看下ShutdownThread log

01-22 15:54:05.117339  2174  2174 D ShutdownThread: Notifying thread to start shutdown longPressBehavior=1
01-22 15:54:05.181086  2174 25190 I ShutdownThread: Sending shutdown broadcast...
01-22 15:54:06.122875  2174 25190 I ShutdownThread: Shutting down activity manager...
01-22 15:54:06.233849  2174 25190 I ShutdownThread: Shutting down package manager...
01-22 15:54:06.242261  2174 25296 W ShutdownThread: Turning off cellular radios...
01-22 15:54:06.256316  2174 25296 I ShutdownThread: Waiting for Radio...
01-22 15:54:06.558647  2174 25296 I ShutdownThread: Radio turned off.
01-22 15:54:06.558735  2174 25296 I ShutdownThread: Radio shutdown complete.
01-22 15:54:07.118474  2174 25190 I ShutdownThread: Shutdown critical subsyslist is :modem :
01-22 15:54:07.118492  2174 25190 I ShutdownThread: Waiting for a maximum of 10000ms
01-22 15:54:07.608521  2174 25190 I ShutdownThread: Vendor subsystem(s) shutdown successful
01-22 15:54:07.608561  2174 25190 I ShutdownThread: Performing low-level shutdown...

查看system log

可以查看ShutdownCheckPoints,看看是那个进程发起的,比如这里的 pid=3393 就是 systemui 触发的关机。

01-22 15:54:05.114927  2174 12696 V ShutdownCheckPoints: Binder shutdown checkpoint recorded with pid=3393
01-22 15:54:05.117219  2174  2174 V ShutdownCheckPoints: System server shutdown checkpoint recorded

查看 sys.boot.reason

结合开机后的开机原因,可以获取更多信息,比如下面的一些原因

// 通常是用户操作
[sys.boot.reason]: [shutdown,userrequested]
// 这种通常是低电导致,可以看下kernel的healthd 查看下电量上报信息
[sys.boot.reason]: [shutdown,battery]

重启关机的原因有很多,如下定义在PowerManager

/// frameworks/base/core/java/android/os/PowerManager.java
/**
 * The value to pass as the 'reason' argument to reboot() to reboot into
 * recovery mode for tasks other than applying system updates, such as
 * doing factory resets.
 * <p>
 * Requires the {@link android.Manifest.permission#RECOVERY}
 * permission (in addition to
 * {@link android.Manifest.permission#REBOOT}).
 * </p>
 * @hide
 */
public static final String REBOOT_RECOVERY = "recovery";

/**
 * The value to pass as the 'reason' argument to reboot() to reboot into
 * recovery mode for applying system updates.
 * <p>
 * Requires the {@link android.Manifest.permission#RECOVERY}
 * permission (in addition to
 * {@link android.Manifest.permission#REBOOT}).
 * </p>
 * @hide
 */
public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";

/**
 * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
 * the device.
 * @hide
 */
public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";

/**
 * The 'reason' value used when rebooting in safe mode
 * @hide
 */
public static final String REBOOT_SAFE_MODE = "safemode";

/**
 * The 'reason' value used for rebooting userspace.
 * @hide
 */
@SystemApi
public static final String REBOOT_USERSPACE = "userspace";

/**
 * The 'reason' value used when rebooting the device without turning on the screen.
 * @hide
 */
public static final String REBOOT_QUIESCENT = "quiescent";

/**
 * The value to pass as the 'reason' argument to android_reboot().
 * @hide
 */
public static final String SHUTDOWN_USER_REQUESTED = "userrequested";

/**
 * The value to pass as the 'reason' argument to android_reboot() when battery temperature
 * is too high.
 * @hide
 */
public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";

/**
 * The value to pass as the 'reason' argument to android_reboot() when device temperature
 * is too high.
 * @hide
 */
public static final String SHUTDOWN_THERMAL_STATE = "thermal";

/**
 * The value to pass as the 'reason' argument to android_reboot() when device is running
 * critically low on battery.
 * @hide
 */
public static final String SHUTDOWN_LOW_BATTERY = "battery";

除了上面的原因,重启原因还包括如下

rescueparty / RescueParty  --- RescueParty机制重启的原因
rollback_staged_install    --- crashes回滚机制重启

healthd 信息

查看kernel log查看healthd的电量信息

// l=0 电量值
// t=25.0  电池温度℃
// chg=  充电状态,为空是没有进行充电
05-19 17:49:59.253565  1200  1200 W healthd : battery l=0 v=7892 t=25.0 h=2 st=3 c=320 fc=13320000 cc=5 chg=

healthd log 打印

/// system/core/healthd/BatteryMonitor.cpp
void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
                               const struct healthd_config& healthd_config) {
    char dmesgline[256];
    size_t len;
    const HealthInfo_1_0& props = health_info.legacy.legacy;
    if (props.batteryPresent) {
        snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                 props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
                 abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
                 props.batteryHealth, props.batteryStatus);

        len = strlen(dmesgline);
        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                            props.batteryCurrent);
        }

        if (!healthd_config.batteryFullChargePath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                            props.batteryFullCharge);
        }

        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                            props.batteryCycleCount);
        }
    } else {
        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
    }

    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
             props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
             props.chargerWirelessOnline ? "w" : "");

    KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}

查看 PMIC PON log

而有些情况是看不到上面的log,通常可能是异常关机,可以查看开机后的 kernel 的 PMIC PON log

01-01 08:00:21.958147   418   418 I         : PMIC PON log: Fundamental Reset: PON_PBL_STATUS=XVDD, DVDD
01-01 08:00:21.958160   418   418 I         : PMIC PON log: FAULT_REASON2=RESTART_PON
01-01 08:00:21.958212   418   418 I         : PMIC PON log: PON Trigger: USB_CHARGER
01-01 08:00:21.958262   418   418 I         : PMIC PON log: Reset Trigger: PS_HOLD
01-01 08:00:21.958273   418   418 I         : PMIC PON log: Reset Type: HARD_RESET
01-01 08:00:21.958304   418   418 I         : PMIC PON log: PON Trigger: KPDPWR_N
01-01 08:00:21.958315   418   418 I         : PMIC PON log: PON Trigger: HARD_RESET

一些常见场景

如下包含一些关机重启的场景的触发点

低电

/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfNoPowerLocked() {
    // shut down gracefully if our battery is critically low and we are not powered.
    // wait until the system has booted before attempting to display the shutdown dialog.
    if (shouldShutdownLocked()) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mActivityManagerInternal.isSystemReady()) {
                    // 使用intent启动ShutdownActivity来执行关机
                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.putExtra(Intent.EXTRA_REASON,
                            PowerManager.SHUTDOWN_LOW_BATTERY);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                }
            }
        });
    }
}

高温

/// @frameworks/base/services/core/java/com/android/server/BatteryService.java
private void shutdownIfOverTempLocked() {
    // shut down gracefully if temperature is too high (> 68.0C by default)
    // wait until the system has booted before attempting to display the
    // shutdown dialog.
    if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) { // 温度超标关机, 默认临界68度
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mActivityManagerInternal.isSystemReady()) {
                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.putExtra(Intent.EXTRA_REASON,
                            PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                }
            }
        });
    }
}

Thermal

/// @frameworks/base/services/core/java/com/android/server/power/ThermalManagerService.java
private void shutdownIfNeeded(Temperature temperature) {
    if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
        return;
    }
    final PowerManager powerManager = getContext().getSystemService(PowerManager.class);
    switch (temperature.getType()) {
        case Temperature.TYPE_CPU:
            // Fall through
        case Temperature.TYPE_GPU:
            // Fall through
        case Temperature.TYPE_NPU:
            // Fall through
        case Temperature.TYPE_SKIN: // 设置硬件高温
            powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
            break;
        case Temperature.TYPE_BATTERY: // 电池高温
            powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
            break;
    }
}

status bar关机重启

这个通常是从systemui发起

/// frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
/**
 * Allows the status bar to shutdown the device.
 */
@Override
public void shutdown() {
    enforceStatusBarService();
    String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long identity = Binder.clearCallingIdentity();
    try {
        mNotificationDelegate.prepareForPossibleShutdown();
        // ShutdownThread displays UI, so give it a UI context.
        mHandler.post(() ->
                ShutdownThread.shutdown(getUiContext(), reason, false));
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

/**
 * Allows the status bar to reboot the device.
 */
@Override
public void reboot(boolean safeMode) {
    enforceStatusBarService();
    String reason = safeMode
            ? PowerManager.REBOOT_SAFE_MODE
            : PowerManager.SHUTDOWN_USER_REQUESTED;
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long identity = Binder.clearCallingIdentity();
    try {
        mNotificationDelegate.prepareForPossibleShutdown();
        mHandler.post(() -> {
            // ShutdownThread displays UI, so give it a UI context.
            if (safeMode) {
                ShutdownThread.rebootSafeMode(getUiContext(), true);
            } else {
                ShutdownThread.reboot(getUiContext(), reason, false);
            }
        });
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

Rollback重启

@WorkerThread
private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
        BroadcastReceiver listener, @Nullable VersionedPackage logPackage) {
    assertInWorkerThread();
    PackageInstaller packageInstaller =
            mContext.getPackageManager().getPackageInstaller();
    List<RollbackInfo> recentRollbacks =
            rollbackManager.getRecentlyCommittedRollbacks();
    for (int i = 0; i < recentRollbacks.size(); i++) {
        RollbackInfo recentRollback = recentRollbacks.get(i);
        int sessionId = recentRollback.getCommittedSessionId();
        if ((rollbackId == recentRollback.getRollbackId())
                && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
            PackageInstaller.SessionInfo sessionInfo =
                    packageInstaller.getSessionInfo(sessionId);
            if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
                mContext.unregisterReceiver(listener);
                saveStagedRollbackId(rollbackId, logPackage);
                WatchdogRollbackLogger.logEvent(logPackage,
                        FrameworkStatsLog
                        .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                        "");
            } else if (sessionInfo.isStagedSessionFailed()
                    && markStagedSessionHandled(rollbackId)) {
                WatchdogRollbackLogger.logEvent(logPackage,
                        FrameworkStatsLog
                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
                        "");
                mContext.unregisterReceiver(listener);
            }
        }
    }

    // Wait for all pending staged sessions to get handled before rebooting.
    if (isPendingStagedSessionsEmpty()) {
        mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
    }
}

还有更多场景就不一一列出了。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:20:14  更:2022-05-24 18:21:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 22:42:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码