当前位置:   article > 正文

Android6.0 关机shutdown & 重启reboot流程分析_android shutdown、reboot原理

android shutdown、reboot原理

android中的重启或者关机操作有很多种情况,包括Power键长按关机/重启、恢复出厂设置重启、低电量关机等等,这些事件经过一系列的判断处理最终都会通过调用PowerManagerService.java类然后调入到ShutdownThread.java类里面,最终都是从这个类的shutdownInner()方法开始的;

shutdownInner()这个方法很长,主要的作用是创建弹出框,根据传入参数confirm来判断是否显示需要用户确认关机或者重启的Dialog,然后调用beginShutdownSequence()方法去开线程执行关机或者重启操作。

  1. static void shutdownInner(final Context context, boolean confirm) {
  2. // ensure that only one thread is trying to power down.
  3. // any additional calls are just returned
  4. synchronized (sIsStartedGuard) {
  5. if (sIsStarted) {
  6. Log.d(TAG, "Request to shutdown already running, returning.");
  7. return;
  8. }
  9. }
  10. boolean showRebootOption = false;
  11. String[] defaultActions = context.getResources().getStringArray(
  12. com.android.internal.R.array.config_globalActionsList);
  13. for (int i = 0; i < defaultActions.length; i++) {
  14. if (defaultActions[i].equals("reboot")) {
  15. showRebootOption = true;
  16. break;
  17. }
  18. }
  19. final int longPressBehavior = context.getResources().getInteger(
  20. com.android.internal.R.integer.config_longPressOnPowerBehavior);
  21. int resourceId = mRebootSafeMode
  22. ? com.android.internal.R.string.reboot_safemode_confirm
  23. : (longPressBehavior == 2
  24. ? com.android.internal.R.string.shutdown_confirm_question
  25. : com.android.internal.R.string.shutdown_confirm);
  26. if (showRebootOption && !mRebootSafeMode) {
  27. resourceId = com.android.internal.R.string.reboot_confirm;
  28. }
  29. Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
  30. if (confirm) {
  31. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
  32. if (sConfirmDialog != null) {
  33. sConfirmDialog.dismiss();
  34. }
  35. sConfirmDialog = new AlertDialog.Builder(context)
  36. .setTitle(mRebootSafeMode
  37. ? com.android.internal.R.string.reboot_safemode_title
  38. : showRebootOption
  39. ? com.android.internal.R.string.reboot_title
  40. : com.android.internal.R.string.power_off)
  41. .setMessage(resourceId)
  42. .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
  43. public void onClick(DialogInterface dialog, int which) {
  44. beginShutdownSequence(context);
  45. }
  46. })
  47. .setNegativeButton(com.android.internal.R.string.no, null)
  48. .create();
  49. closer.dialog = sConfirmDialog;
  50. sConfirmDialog.setOnDismissListener(closer);
  51. sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
  52. sConfirmDialog.show();
  53. } else {
  54. beginShutdownSequence(context);
  55. }
  56. }
beginShutdownSequence()方法的篇幅也比较长:

  1. private static void beginShutdownSequence(Context context) {
  2. synchronized (sIsStartedGuard) {
  3. if (sIsStarted) {
  4. Log.d(TAG, "Shutdown sequence already running, returning.");
  5. return;
  6. }
  7. sIsStarted = true;
  8. }
  9. // Throw up a system dialog to indicate the device is rebooting / shutting down.
  10. ProgressDialog pd = new ProgressDialog(context);
  11. // Path 1: Reboot to recovery and install the update
  12. // Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
  13. // (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
  14. // UI: progress bar
  15. //
  16. // Path 2: Reboot to recovery for factory reset
  17. // Condition: mRebootReason == REBOOT_RECOVERY
  18. // UI: spinning circle only (no progress bar)
  19. //
  20. // Path 3: Regular reboot / shutdown
  21. // Condition: Otherwise
  22. // UI: spinning circle only (no progress bar)
  23. if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
  24. mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
  25. if (mRebootUpdate) {
  26. pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
  27. pd.setMessage(context.getText(
  28. com.android.internal.R.string.reboot_to_update_prepare));
  29. pd.setMax(100);
  30. pd.setProgressNumberFormat(null);
  31. pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  32. pd.setProgress(0);
  33. pd.setIndeterminate(false);
  34. } else {
  35. // Factory reset path. Set the dialog message accordingly.
  36. pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
  37. pd.setMessage(context.getText(
  38. com.android.internal.R.string.reboot_to_reset_message));
  39. pd.setIndeterminate(true);
  40. }
  41. } else {
  42. pd.setTitle(context.getText(com.android.internal.R.string.power_off));
  43. pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
  44. pd.setIndeterminate(true);
  45. }
  46. pd.setCancelable(false);
  47. pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
  48. pd.show();
  49. sInstance.mProgressDialog = pd;
  50. sInstance.mContext = context;
  51. sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
  52. // make sure we never fall asleep again
  53. sInstance.mCpuWakeLock = null;
  54. try {
  55. sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
  56. PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
  57. sInstance.mCpuWakeLock.setReferenceCounted(false);
  58. sInstance.mCpuWakeLock.acquire();
  59. } catch (SecurityException e) {
  60. Log.w(TAG, "No permission to acquire wake lock", e);
  61. sInstance.mCpuWakeLock = null;
  62. }
  63. // also make sure the screen stays on for better user experience
  64. sInstance.mScreenWakeLock = null;
  65. if (sInstance.mPowerManager.isScreenOn()) {
  66. try {
  67. sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
  68. PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
  69. sInstance.mScreenWakeLock.setReferenceCounted(false);
  70. sInstance.mScreenWakeLock.acquire();
  71. } catch (SecurityException e) {
  72. Log.w(TAG, "No permission to acquire wake lock", e);
  73. sInstance.mScreenWakeLock = null;
  74. }
  75. }
  76. // start the thread that initiates shutdown
  77. sInstance.mHandler = new Handler() {
  78. };
  79. sInstance.start();
  80. }

1、显示一个系统进度dialog表示当前设备正在关机/重启;
2、持CPU锁、屏幕锁,保持设备处于唤醒态、屏幕处于亮屏态;
3、最后启动线程,执行run方法,这个线程指的是ShutThread.java,执行的run()方法,也是这个类的run()方法。

run()方法的篇幅更长,不适合贴代码,接下来分块来分析:

  1. BroadcastReceiver br = new BroadcastReceiver() {
  2. @Override public void onReceive(Context context, Intent intent) {
  3. // We don't allow apps to cancel this, so ignore the result.
  4. actionDone();
  5. }
  6. };
  1. void actionDone() {
  2. synchronized (mActionDoneSync) {
  3. mActionDone = true;
  4. mActionDoneSync.notifyAll();
  5. }
  6. }
  1. // First send the high-level shut down broadcast.
  2. mActionDone = false;
  3. Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
  4. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
  5. mContext.sendOrderedBroadcastAsUser(intent,
  6. UserHandle.ALL, null, br, mHandler, 0, null, null);
  7. final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
  8. synchronized (mActionDoneSync) {
  9. while (!mActionDone) {
  10. long delay = endTime - SystemClock.elapsedRealtime();
  11. if (delay <= 0) {
  12. Log.w(TAG, "Shutdown broadcast timed out");
  13. break;
  14. } else if (mRebootUpdate) {
  15. int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
  16. BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
  17. sInstance.setRebootProgress(status, null);
  18. }
  19. try {
  20. mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
  21. } catch (InterruptedException e) {
  22. }
  23. }
  24. }
1、首先发送有序广播Intent.ACTION_SHUTDOWN;
2、等待Intent.ACTION_SHUTDOWN有序广播全部处理完成,也就是等上面的br.onReveive回调;
3、然后调用actionDone()方法,使得代码可以继续往下执行;

ActivityManagerService执行shutdown()操作,保存一些相关状态(比如battery).

  1. final IActivityManager am =
  2. ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
  3. if (am != null) {
  4. try {
  5. am.shutdown(MAX_BROADCAST_TIME);
  6. } catch (RemoteException e) {
  7. }
  8. }
PackageManagerService执行shutdonw,将当前的package Name写入data/system/package-usage.list文件中;

  1. final PackageManagerService pm = (PackageManagerService)
  2. ServiceManager.getService("package");
  3. if (pm != null) {
  4. pm.shutdown();
  5. }
nfc、电话服务相关、蓝牙等无线模块执行Shutdown;

  1. // Shutdown radios.
  2. shutdownRadios(MAX_RADIO_WAIT_TIME);
中断线程,等待文件系统挂载服务卸载完成后再继续执行;

  1. // Shutdown MountService to ensure media is in a safe state
  2. IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
  3. public void onShutDownComplete(int statusCode) throws RemoteException {
  4. Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
  5. actionDone();
  6. }
  7. };
  8. Log.i(TAG, "Shutting down MountService");
  9. // Set initial variables and time out time.
  10. mActionDone = false;
  11. final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
  12. synchronized (mActionDoneSync) {
  13. try {
  14. final IMountService mount = IMountService.Stub.asInterface(
  15. ServiceManager.checkService("mount"));
  16. if (mount != null) {
  17. mount.shutdown(observer);
  18. } else {
  19. Log.w(TAG, "MountService unavailable for shutdown");
  20. }
  21. } catch (Exception e) {
  22. Log.e(TAG, "Exception during MountService shutdown", e);
  23. }
  24. while (!mActionDone) {
  25. long delay = endShutTime - SystemClock.elapsedRealtime();
  26. if (delay <= 0) {
  27. Log.w(TAG, "Shutdown wait timed out");
  28. break;
  29. } else if (mRebootUpdate) {
  30. int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
  31. (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
  32. MAX_SHUTDOWN_WAIT_TIME);
  33. status += RADIO_STOP_PERCENT;
  34. sInstance.setRebootProgress(status, null);
  35. }
  36. try {
  37. mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
  38. } catch (InterruptedException e) {
  39. }
  40. }
  41. }
开始重启/关机;

rebootOrShutdown(mContext, mReboot, mRebootReason);
至此ShutdownThread.java类的run()方法分析完成,接下来调用rebootOrShutdown()方法;

  1. /**
  2. * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
  3. * or {@link #shutdown(Context, boolean)} instead.
  4. *
  5. * @param context Context used to vibrate or null without vibration
  6. * @param reboot true to reboot or false to shutdown
  7. * @param reason reason for reboot
  8. */
  9. public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
  10. if (reboot) {
  11. Log.i(TAG, "Rebooting, reason: " + reason);
  12. PowerManagerService.lowLevelReboot(reason);
  13. Log.e(TAG, "Reboot failed, will attempt shutdown instead");
  14. } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
  15. // vibrate before shutting down
  16. Vibrator vibrator = new SystemVibrator(context);
  17. try {
  18. vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
  19. } catch (Exception e) {
  20. // Failure to vibrate shouldn't interrupt shutdown. Just log it.
  21. Log.w(TAG, "Failed to vibrate during shutdown.", e);
  22. }
  23. // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
  24. try {
  25. Thread.sleep(SHUTDOWN_VIBRATE_MS);
  26. } catch (InterruptedException unused) {
  27. }
  28. }
  29. // Shutdown power
  30. Log.i(TAG, "Performing low-level shutdown...");
  31. PowerManagerService.lowLevelShutdown();
  32. }
判断是否是重启,如果是重启的话则调用:

PowerManagerService.lowLevelReboot(reason);
是关机命令的话,线程会先sleep500ms等待手机震动500ms完成,再执行:

PowerManagerService.lowLevelShutdown();
也就是说最终关机操作还是会回到PowerManagerService.java中:

先来看lowLevelShutdown()方法:
  1. /**
  2. * Low-level function turn the device off immediately, without trying
  3. * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
  4. */
  5. public static void lowLevelShutdown() {
  6. SystemProperties.set("sys.powerctl", "shutdown");
  7. }
使用系统属性服务类SystemProperties传入cmd命令触发shutdown的相关操作,到这里java层的关机流程结束,进入native层;

再来看lowLevelReboot()方法:

  1. /**
  2. * Low-level function to reboot the device. On success, this
  3. * function doesn't return. If more than 20 seconds passes from
  4. * the time a reboot is requested, this method returns.
  5. *
  6. * @param reason code to pass to the kernel (e.g. "recovery"), or null.
  7. */
  8. public static void lowLevelReboot(String reason) {
  9. if (reason == null) {
  10. reason = "";
  11. }
  12. if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
  13. // If we are rebooting to go into recovery, instead of
  14. // setting sys.powerctl directly we'll start the
  15. // pre-recovery service which will do some preparation for
  16. // recovery and then reboot for us.
  17. SystemProperties.set("ctl.start", "pre-recovery");
  18. } else {
  19. SystemProperties.set("sys.powerctl", "reboot," + reason);
  20. }
  21. try {
  22. Thread.sleep(20 * 1000L);
  23. } catch (InterruptedException e) {
  24. Thread.currentThread().interrupt();
  25. }
  26. Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
  27. }
首先判断重启原因,根据reason的不同,传入不同的命令参数进入native层,开始重启操作且必须在20s内完成;
至此,Java层reboot & shutdown流程分析结束。


reboot & shutdown流程总结:
1、不管是reboot还是shutdown最终都会调入到ShutdownThread.java类里的shutdownInner()方法;
2、ShutdownThread.java继承于Thread.java,这个类的核心方法还是run()方法;
3、在run()方法内,发起有序shutdown广播;执行ActivityManagerService,PackageManagerService,nfc、电话服务相关、蓝牙等无线模块,文件系统挂载服务等的shutdown工作;
4、shutdown操作会导致设备震动500ms;
5、java层的最终实现是在PowerManagerService.java,从这里进入native层;通过系统属性类SystemProperties.java触发shutdown & reboot的相关操作。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/945222
推荐阅读
相关标签
  

闽ICP备14008679号