Android系统关机重启流程(一)

借鉴参考:
http://gityuan.com/2016/07/09/android-reboot/#28-sdtrebootorshutdown
https://blog.csdn.net/anlory/article/details/85010689

一,概述

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

这里就先从PowerManager说起。

二,重启流程

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
frameworks/base/core/java/android/os/PowerManager.java
2.1 PM.reboot

[->PowerManager.java]

public void reboot(String reason) {
    try {
        mService.reboot(false, reason, true); // 调用PowerManagerService->reboot
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
2.2 BinderService.reboot

[-> PowerManagerService.java]

private final class BinderService extends IPowerManager.Stub {
        /**
         * Reboots the device.
         *
         * @param confirm If true, shows a reboot confirmation dialog.
         * @param reason The reason for the reboot, or null if none.
         * @param wait If true, this call waits for the reboot to complete and does not return.
         */     
     
     @Override // Binder call
         public void reboot(boolean confirm, String reason, boolean wait) {
         //检查权限,reboot权限和reason是recovery时检查recovery权限
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
         if (PowerManager.REBOOT_RECOVERY.equals(reason)
             || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
         }
 
         final long ident = Binder.clearCallingIdentity();
         try {
             shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 }

此时参数为shutdownOrRebootInternal(false, false, reason, true);

shutdown=false:代表重启;
confirm=false:代表直接重启,无需弹出询问提示框;
reason 可能为recovery、bootloader、shutdown等
wait=true:代表阻塞等待重启操作完成。

2.3 PMS.shutdownOrRebootInternal

[-> PowerManagerService.java]

  private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
            final String reason, boolean wait) {
        ...
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    //根据haltMode 区分,reboot,shutdown,rebootsafemode
                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                    } else if (haltMode == HALT_MODE_REBOOT) {
                        ShutdownThread.reboot(getUiContext(), reason, confirm);
                    } else {
                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
                    }
                }
            }
        };
 
        // ShutdownThread must run on a looper capable of displaying the UI.
        //// ShutdownThread必须运行在一个展现UI的looper
        Message msg = Message.obtain(UiThread.getHandler(), runnable);
        msg.setAsynchronous(true);
        UiThread.getHandler().sendMessage(msg);
 
        // PowerManager.reboot() is documented not to return so just wait for the inevitable.
        if (wait) {
            //等待ShutdownThread执行完毕
            synchronized (runnable) {
                while (true) {
                    try {
                        runnable.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
    }
 
2.4 SDT.reboot

[->ShutdownThread.java]

public static void reboot(final Context context, String reason, boolean confirm) {
    mReboot = true;
    mRebootSafeMode = false;
    mRebootHasProgressBar = false;
    mReason = reason;
    shutdownInner(context, confirm);
}

mReboot为true则代表重启操作,值为false则代表关机操作。

2.5 SDT.shutdownInner

[->ShutdownThread.java]

 private static void shutdownInner(final Context context, boolean confirm) {
        // ShutdownThread is called from many places, so best to verify here that the context passed
        // in is themed.
        context.assertRuntimeOverlayThemable();
 
        // ensure that only one thread is trying to power down.
        // any additional calls are just returned
        ////确保只有唯一的线程执行shutdown/reboot操作
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Request to shutdown already running, returning.");
                return;
            }
        }
 
        final int longPressBehavior = context.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
        final int resourceId = mRebootSafeMode
                ? com.android.internal.R.string.reboot_safemode_confirm
                : (longPressBehavior == 2
                        ? com.android.internal.R.string.shutdown_confirm_question
                        : com.android.internal.R.string.shutdown_confirm);
 
        /*  longPressBehavior 并没有什么意义,只配置长按power键时弹窗power off选项还是语音助手。
        -- Control the behavior when the user long presses the power button.
            0 - Nothing
            1 - Global actions menu
            2 - Power off (with confirmation)
            3 - Power off (without confirmation)
            4 - Go to voice assist
        */
        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
 
        if (confirm) {
            //这里是走弹出关机/重启提示框
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            sConfirmDialog = new AlertDialog.Builder(context)
                    .setTitle(mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.no, null)
                    .create();
            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            sConfirmDialog.show();

        } else {
            //本次confirm=false ,直接走这里
            beginShutdownSequence(context);
        }
    }
2.6 SDT.beginShutdownSequence

[->ShutdownThread.java]

private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return; //shutdown操作正在执行,则直接返回
            }
            sIsStarted = true;
        }
  sInstance.mProgressDialog = showShutdownDialog(context);
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
 
        // make sure we never fall asleep again
        //确保系统不会进入休眠状态
        sInstance.mCpuWakeLock = null;
        try {
            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mCpuWakeLock = null;
        }
 
        // also make sure the screen stays on for better user experience
        //当处理亮屏状态,则获取亮屏锁,提供用户体验
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }
 
        if (SecurityLog.isLoggingEnabled()) {
            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
        }
 
        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        //启动线程来执行shutdown初始化
        sInstance.start(); //接下来会启动shutdownThread.run
    }
   
2.7 SDT.run

[->ShutdownThread.java]

 /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio state if the allotted time has passed.
     */
    public void run() {
        TimingsTraceLog shutdownTimingLog = newTimingsLog();
        shutdownTimingLog.traceBegin("SystemServerShutdown");
        metricShutdownStart();//用来统计关机时间
        metricStarted(METRIC_SYSTEM_SERVER);
 
        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };
 
        /*
         * Write a system property in case the system_server reboots before we
         * get to the actual hardware restart. If that happens, we'll retry at
         * the beginning of the SystemServer startup.
         */
        {
            String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
            //设置属性"sys.shutdown.requested"的值为reason
        }
 
        /*
         * If we are rebooting into safe mode, write a system property
         * indicating so.
         */
        if (mRebootSafeMode) {
            //如果需要重启进入安全模式,则设置"persist.sys.safemode"=1
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }
 
        metricStarted(METRIC_SEND_BROADCAST);
        shutdownTimingLog.traceBegin("SendShutdownBroadcast");
        Log.i(TAG, "Sending shutdown broadcast...");
 
        // First send the high-level shut down broadcast.
        //1. 发送关机广播
        mActionDone = false;
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendOrderedBroadcastAsUser(intent,
                UserHandle.ALL, null, br, mHandler, 0, null, null);
 
        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            //循环等待,超时或者mActionDone都会结束该循环
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                } else if (mRebootHasProgressBar) {
                    int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootHasProgressBar) {//设置reboot进程条
            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
        metricEnded(METRIC_SEND_BROADCAST);
        
        Log.i(TAG, "Shutting down activity manager...");
        shutdownTimingLog.traceBegin("ShutdownActivityManager");
        metricStarted(METRIC_AM);
        //2. 关闭AMS
        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd();// ShutdownActivityManager
        metricEnded(METRIC_AM);
 
        Log.i(TAG, "Shutting down package manager...");
        shutdownTimingLog.traceBegin("ShutdownPackageManager");
        metricStarted(METRIC_PM);
        //3. 关闭PMS 
        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // ShutdownPackageManager
        metricEnded(METRIC_PM);
 
        // Shutdown radios.
        //4. 关闭radios
        shutdownTimingLog.traceBegin("ShutdownRadios");
        metricStarted(METRIC_RADIOS);
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);

        }
        shutdownTimingLog.traceEnd(); // ShutdownRadios
        metricEnded(METRIC_RADIOS);
 
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
 
            // If it's to reboot to install an update and uncrypt hasn't been
            // done yet, trigger it now.
            uncrypt();
        }
 
        shutdownTimingLog.traceEnd(); // SystemServerShutdown
        metricEnded(METRIC_SYSTEM_SERVER);
        saveMetrics(mReboot, mReason);
        // Remaining work will be done by init, including vold shutdown
        //关机剩下得动作由init完成,包括void,下一节分析
        rebootOrShutdown(mContext, mReboot, mReason);
    }

设置”sys.shutdown.requested”,记录下mReboot和mReason。如果是进入安全模式,则”persist.sys.safemode=1”。

接下来主要关闭一些系统服务:
发送关机广播
关闭AMS
关闭PMS
关闭radios
之后就需要进入重启/关机流程,由init 进程处理。

2.7.1 AMS.shutdown

通过AMP.shutdown,通过binder调用到AMS.shutdown.
[->ActivityManagerService.java]

 @Override
    public boolean shutdown(int timeout) {
        ////权限检测
        if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.SHUTDOWN);
        }
 
        boolean timedout = false;
 
        synchronized(this) {
            mShuttingDown = true;
            mStackSupervisor.prepareForShutdownLocked();
            //禁止WMS继续处理Event
            updateEventDispatchingLocked();
            //调用ASS处理shutdown操作
            timedout = mStackSupervisor.shutdownLocked(timeout);
        }
 
        mAppOpsService.shutdown();
        if (mUsageStatsService != null) {
            mUsageStatsService.prepareShutdown();
        }
        mBatteryStatsService.shutdown();
        synchronized (this) {
            mProcessStats.shutdownLocked();
            notifyTaskPersisterLocked(null, true);
        }
 
        return timedout;
    }
   

此处timeout为MAX_BROADCAST_TIME=10s

2.7.2 PMS.shutdown
public void shutdown() {
    mPackageUsage.write(true);
}
此处mPackageUsage数据类型是PMS的内部类PackageUsage。

private class PackageUsage {
    void write(boolean force) {
        if (force) {
            writeInternal();
            return;
        }
        ...
    }
}
对于force=true,接下来调用writeInternal方法。

private class PackageUsage {
    private void writeInternal() {
        synchronized (mPackages) {
            synchronized (mFileLock) {
                //file是指/data/system/package-usage.list
                AtomicFile file = getFile();
                FileOutputStream f = null;
                try {
                    //将原来的文件记录到package-usage.list.bak
                    f = file.startWrite();
                    BufferedOutputStream out = new BufferedOutputStream(f);
                    FileUtils.setPermissions(file.getBaseFile().getPath(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
                    StringBuilder sb = new StringBuilder();
                    for (PackageParser.Package pkg : mPackages.values()) {
                        if (pkg.mLastPackageUsageTimeInMills == 0) {
                            continue;
                        }
                        sb.setLength(0);
                        sb.append(pkg.packageName);
                        sb.append(' ');
                        sb.append((long)pkg.mLastPackageUsageTimeInMills);
                        sb.append('\n');
                        out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
                    }
                    out.flush();
                    //将文件内容同步到磁盘,并删除.bak文件
                    file.finishWrite(f);
                } catch (IOException e) {
                    if (f != null) {
                        file.failWrite(f);
                    }
                    Log.e(TAG, "Failed to write package usage times", e);
                }
            }
        }
        mLastWritten.set(SystemClock.elapsedRealtime());
    }
}

/data/system/package-usage.list文件中每一行记录一条package及其上次使用时间(单位ms)。

由于IO操作的过程中,写入文件并非立刻就会真正意义上写入物理磁盘,以及在写入文件的过程中还可能中断或者出错等原因的考虑,采用的策略是先将老的文件package-usage.list,重命为增加后缀package-usage.list.bak;然后再往package-usage.list文件写入新的数据,数据写完之后再执行sync操作,将内存数据彻底写入物理磁盘,此时便可以安全地删除原来的package-usage.list.bak文件。

2.7.3 ST.shutdownRadios

[-> ShutdownThread.java]

  private void shutdownRadios(final int timeout) {
        // If a radio is wedged, disabling it may hang so we do this work in another thread,
        // just in case.
        final long endTime = SystemClock.elapsedRealtime() + timeout;
        final boolean[] done = new boolean[1];
        Thread t = new Thread() {
            public void run() {
                TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
                boolean radioOff;
 
                final ITelephony phone =
                        ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
 
                try {
                    radioOff = phone == null || !phone.needMobileRadioShutdown();
                    if (!radioOff) {
                        Log.w(TAG, "Turning off cellular radios...");
                        metricStarted(METRIC_RADIO);
                        phone.shutdownMobileRadios();
                    }
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during radio shutdown", ex);
                    radioOff = true;
                }
 
                Log.i(TAG, "Waiting for Radio...");
 
                long delay = endTime - SystemClock.elapsedRealtime();
                while (delay > 0) {
                    if (mRebootHasProgressBar) {
                        int status = (int)((timeout - delay) * 1.0 *
                                (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
                        status += PACKAGE_MANAGER_STOP_PERCENT;
                        sInstance.setRebootProgress(status, null);
                    }
 
                    if (!radioOff) {
                        try {
                            radioOff = !phone.needMobileRadioShutdown();
                        } catch (RemoteException ex) {
                            Log.e(TAG, "RemoteException during radio shutdown", ex);
                            radioOff = true;
                        }
                        if (radioOff) {
                            Log.i(TAG, "Radio turned off.");
                            metricEnded(METRIC_RADIO);
                            shutdownTimingsTraceLog
                                    .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
                        }
                    }
 
                    if (radioOff) {
                        Log.i(TAG, "Radio shutdown complete.");
                        done[0] = true;
                        break;
                    }
                    SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS);
                    delay = endTime - SystemClock.elapsedRealtime();
                }
            }
        };
 
        t.start();
        try {
            t.join(timeout);
        } catch (InterruptedException ex) {
        }
        if (!done[0]) {
            Log.w(TAG, "Timed out waiting for Radio shutdown.");
        }
    }

创建新的线程来处理NFC, Radio and Bluetooth这些射频相关的模块的shutdown过程。每间隔500ms,check一次,直到nfc、bluetooth、radio全部关闭或者超时(MAX_RADIO_WAIT_TIME=12s)才会退出循环。

2.8 SDT.rebootOrShutdown

[-> ShutdownThread.java]

  /**
     * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
     * or {@link #shutdown(Context, String, boolean)} instead.
     *
     * @param context Context used to vibrate or null without vibration
     * @param reboot true to reboot or false to shutdown
     * @param reason reason for reboot/shutdown
     */
    public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }
 
            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }
        // Shutdown power
        //关闭电源 [见流程2.10]
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

对于重启原因:
logcat会直接输出Rebooting, reason: ;
如果重启失败,则会输出Reboot failed; ,- 果无法重启,则会尝试直接关机。

2.9 PMS.lowLevelReboot
public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "shutdown," + reason);
    }
 
    /**
     * Low-level function to reboot the device. On success, this
     * function doesn't return. If more than 20 seconds passes from
     * the time a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }
 
        // If the reason is "quiescent", it means that the boot process should proceed
        // without turning on the screen/lights.
        // The "quiescent" property is sticky, meaning that any number
        // of subsequent reboots should honor the property until it is reset.
        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = "";
        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = reason.substring(0,
                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
        }
 
        if (reason.equals(PowerManager.REBOOT_RECOVERY)
                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            reason = "recovery";
        }
 
        if (sQuiescent) {
            // Pass the optional "quiescent" argument to the bootloader to let it know
            // that it should not turn the screen/lights on.
            reason = reason + ",quiescent";
        }
 
        SystemProperties.set("sys.powerctl", "reboot," + reason);
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }

当reboot原因是“recovery”,则设置属性sys.powerctl=reboot,recovery;
当其他情况,则设置属性”sys.powerctl=reboot,[reason]”。
当关机时,则设置属性”sys.powerctl=shutdown,[reason]”。
到此,framework层面的重启就流程基本介绍完了,那么接下来就要进入属性服务,即getprop sys.powerctl reboot,

三,总结

先用一句话总结,从最开始的PM.reboot(),经过层层调用,最终重启的核心方法等价于调用SystemProperties.set(“sys.powerctl”, “reboot,” + reason); 也就意味着调用下面命令,也能重启手机:

adb shell setprop sys.powerctl reboot

后续,还会进一步上面命令的执行流程,如何进入native,如何进入kernel来完成重启的,以及PM.reboot如何触发的。

你可能感兴趣的:(Android系统关机重启流程(一))