cover: ""
copyright_author: zsl
copyright_author_href: https://github.com/zslxiu
copyright_url: "https://wayawbott0.f.mioffice.cn/docx/doxk46H9DPPTQcGFAJRtwdgNkxd"
copyright_info: "此文章版权归 zsl 所有,如有转载,请注明来自原作者"


{% tip success %}
Code 基于 Android U ssi、kernel-5.10
{% endtip %}

0. 重启类型

本文整理的重启流程有:

  • adb reboot
  • power key reboot(弹框确认 or 直接重启)
  • panic reboot
    整体框架流程见 总流程图。具体代码流程见 重启前流程处理、重启后 reason 传递。

1. 总流程图

2. 重启前流程梳理

2.1 adb reboot

执行 adb reboot 后经过 adb client 端和 server 端的一些交互后

  • 电脑侧client端,packages/modules/adb/client/main.cpp的main();
  • 内调commandline.cpp的adb_commandline();
  • 转到adb_connect_command();
  • 内调adb_client.cpp的adb_connect();
  • 内调adb_io.cpp的SendProtocolString(),获取 server端的 fd句柄;
  • 手机侧server端,services.cpp的service_to_fd();
  • 内调services.cpp的daemon_service_to_fd()
  • 最终通过 daemon_service_to_fd() 判断参数后调用到 reboot_device(),在 reboot_device() 里面执行 /system/bin/reboot。
// packages/modules/adb/daemon/services.cpp
unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
    ADB_LOG(Service) << "transport " << transport->serial_name() << " opening service " << name;

#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
    if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
        return execute_abb_command(name);
    }
#endif

#if defined(__ANDROID__)
    if (name.starts_with("framebuffer:")) {
        return create_service_thread("fb", framebuffer_service);
    } else if (android::base::ConsumePrefix(&name, "remount:")) {
        std::string cmd = "/system/bin/remount ";
        cmd += name;
        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
    } else if (android::base::ConsumePrefix(&name, "reboot:")) {
        return reboot_device(std::string(name));
    } else if (name.starts_with("root:")) {
        return create_service_thread("root", restart_root_service);
    } else if (name.starts_with("unroot:")) {
        return create_service_thread("unroot", restart_unroot_service);
    } else if (android::base::ConsumePrefix(&name, "backup:")) {
        std::string cmd = "/system/bin/bu backup ";
        cmd += name;
        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
    } else if (name.starts_with("restore:")) {
        return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
    } else if (name.starts_with("disable-verity:")) {
        return StartSubprocess("/system/bin/disable-verity", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
    } else if (name.starts_with("enable-verity:")) {
        return StartSubprocess("/system/bin/enable-verity", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
    } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
        std::string str(name);

[[maybe_unused]] static unique_fd reboot_device(const std::string& name) {
#if defined(__ANDROID_RECOVERY__)
    if (!__android_log_is_debuggable()) {
        auto reboot_service = [name](unique_fd fd) {
            std::string reboot_string = android::base::StringPrintf("reboot,%s", name.c_str());
            if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
                WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
                return;
            }
            while (true) pause();
        };
        return create_service_thread("reboot", reboot_service);
    }
#endif
    // Fall through
    std::string cmd = "/system/bin/reboot ";
    cmd += name;
    return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
}

StartSubprocess("/system/bin/reboot ")
--> ForkAndExec(); 系统调用fork(),fork 子进程执行函数 execle(),执行 /system/bin/reboot;
/system/bin/reboot 由 system/core/reboot/reboot.c 编译生成。

// system/core/reboot/reboot.c 
int main(int argc, char* argv[]) {
   int ret;
   size_t prop_len;
   char property_val[PROPERTY_VALUE_MAX];
   static const char reboot[] = "reboot";
   const char* cmd = reboot;
   char* optarg = "";

   opterr = 0;
   do {
       int c;

       c = getopt(argc, argv, "p");

       if (c == -1) {
           break;
       }

       switch (c) {
       case 'p':
           cmd = "shutdown";
           break;
       case '?':
           fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
           exit(EXIT_FAILURE);
       }
   } while (1);

   if(argc > optind + 1) {
       fprintf(stderr, "%s: too many arguments\n", argv[0]);
       exit(EXIT_FAILURE);
   }

   if (argc > optind)
       optarg = argv[optind];
   if (!optarg || !optarg[0]) optarg = "shell";

   prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
   if (prop_len >= sizeof(property_val)) {
       fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
       exit(EXIT_FAILURE);
   }

   ret = property_set(ANDROID_RB_PROPERTY, property_val);
   if (ret < 0) {
       perror(cmd);
       exit(EXIT_FAILURE);
   }

   // Don't return early. Give the reboot command time to take effect
   // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
   if (cmd == reboot) {
       while (1) {
           pause();
       }
   }

   fprintf(stderr, "Done\n");
   return 0;
}

调用到 property_set(ANDROID_RB_PROPERTY, property_val);
ANDROID_RB_PROPERTY = sys.powerctl
property_set(ANDROID_RB_PROPERTY, property_val); --> __system_property_set(key, value)

后续prop 处理流程见 重启前 prop 处理流程

2.2 Power key reboot

开机后先注册输入监听事件,长按power键时,kernel层会发出一个事件上来,该事件最终被InputDispatcher.handleReceiveCallback监听到。

xref: lc-s-flame-vendor/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
     std::scoped_lock _l(mLock);
     sp<Connection> connection = getConnectionLocked(connectionToken);
     if (connection == nullptr) {
         ALOGW("Received looper callback for unknown input channel token %p.  events=0x%x",
               connectionToken.get(), events);
         return 0; // remove the callback
     }
 
     bool notify;
     if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
         if (!(events & ALOOPER_EVENT_INPUT)) {
             ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
                   "events=0x%x",
                   connection->getInputChannelName().c_str(), events);
             return 1;
         }
 
         nsecs_t currentTime = now();
         bool gotOne = false;
         status_t status = OK;
         // 在一个无限循环中,从 connection->inputPublisher 
         // 接收消费者响应,处理不同类型的响应,例如完成信号或时间轴信息。
         for (;;) {
             Result<InputPublisher::ConsumerResponse> result =
                     connection->inputPublisher.receiveConsumerResponse();
             if (!result.ok()) {
                 status = result.error().code();
                 break;
             }
 
             if (std::holds_alternative<InputPublisher::Finished>(*result)) {
                 const InputPublisher::Finished& finish =
                         std::get<InputPublisher::Finished>(*result);
                 // 处理完成信号
                 finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
                                           finish.consumeTime);
             } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
                 if (shouldReportMetricsForConnection(*connection)) {
                     const InputPublisher::Timeline& timeline =
                             std::get<InputPublisher::Timeline>(*result);
                     mLatencyTracker
                             .trackGraphicsLatency(timeline.inputEventId,
                                                   connection->inputChannel->getConnectionToken(),
                                                   std::move(timeline.graphicsTimeline));
                 }
             }
             gotOne = true;
         }
         if (gotOne) {
             runCommandsLockedInterruptible();
             if (status == WOULD_BLOCK) {
                 return 1;
             }
         }
 
         notify = status != DEAD_OBJECT || !connection->monitor;
         if (notify) {
             ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%s(%d)",
                   connection->getInputChannelName().c_str(), statusToString(status).c_str(),
                   status);
         }
     } else {
         // Monitor channels are never explicitly unregistered.
         // We do it automatically when the remote endpoint is closed so don't warn about them.
         const bool stillHaveWindowHandle =
                 getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr;
         notify = !connection->monitor && stillHaveWindowHandle;
         if (notify) {
              ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  events=0x%x",
                    connection->getInputChannelName().c_str(), events);
          }
      }
  
      // Remove the channel.
      removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
      return 0; // remove the callback
  }

总结来说,finishDispatchCycleLocked 方法主要用于完成调度周期,包括记录调试信息(如果启用了调试宏)、检查连接状态以避免无效操作,并通知其他系统组件当前调度周期的完成情况。

xref: lc-s-flame-vendor/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cp
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                  const sp<Connection>& connection, uint32_t seq,
                                                  bool handled, nsecs_t consumeTime) {
  #if DEBUG_DISPATCH_CYCLE
      ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
            connection->getInputChannelName().c_str(), seq, toString(handled));
  #endif
  
      if (connection->status == Connection::STATUS_BROKEN ||
          connection->status == Connection::STATUS_ZOMBIE) {
          return;
      }
  
      // Notify other system components and prepare to start the next dispatch cycle.
      onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
  }
 xref: lc-s-flame-vendor/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cp
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                      const sp<Connection>& connection, uint32_t seq,
                                                      bool handled, nsecs_t consumeTime) {
      std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
              &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
      commandEntry->connection = connection;
      commandEntry->eventTime = currentTime;
      commandEntry->seq = seq;
      commandEntry->handled = handled;
      commandEntry->consumeTime = consumeTime;
      postCommandLocked(std::move(commandEntry));
  }

根据事件类型 (KEYMOTION) 分别调用相应的处理函数 (afterKeyEventLockedInterruptibleafterMotionEventLockedInterruptible) 进行事件后处理。

 xref: lc-s-flame-vendor/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cp
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
      sp<Connection> connection = commandEntry->connection;
      const nsecs_t finishTime = commandEntry->eventTime;
      uint32_t seq = commandEntry->seq;
      const bool handled = commandEntry->handled;
  
      // Handle post-event policy actions.
      std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
      if (dispatchEntryIt == connection->waitQueue.end()) {
          return;
      }
      DispatchEntry* dispatchEntry = *dispatchEntryIt;
      const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
      if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
          ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
                ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
      }
      if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
          mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
                                             connection->inputChannel->getConnectionToken(),
                                             dispatchEntry->deliveryTime, commandEntry->consumeTime,
                                             finishTime);
      }
  
      //按键事件
      bool restartEvent;
      if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
          KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
          restartEvent =
                  afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
      } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
          MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
          restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
                                                             handled);
      } else {
          restartEvent = false;
      }
  
      // Dequeue the event and start the next cycle.
      // Because the lock might have been released, it is possible that the
      // contents of the wait queue to have been drained, so we need to double-check
      // a few things.
      dispatchEntryIt = connection->findWaitQueueEntry(seq);
      if (dispatchEntryIt != connection->waitQueue.end()) {
          dispatchEntry = *dispatchEntryIt;
          connection->waitQueue.erase(dispatchEntryIt);
          const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
          mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
          if (!connection->responsive) {
              connection->responsive = isConnectionResponsive(*connection);
              if (connection->responsive) {
                  // The connection was unresponsive, and now it's responsive.
                  processConnectionResponsiveLocked(*connection);
              }
          }
          traceWaitQueueLength(*connection);
          if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
              connection->outboundQueue.push_front(dispatchEntry);
              traceOutboundQueueLength(*connection);
          } else {
              releaseDispatchEntry(dispatchEntry);
          }
      }
  
      // Start the next dispatch cycle for this connection.
      startDispatchCycleLocked(now(), connection);
  }
  

Key event触发之后的中断事件处理入口:

 xref: lc-s-flame-vendor/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cp
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                                         DispatchEntry* dispatchEntry,
                                                         KeyEntry& keyEntry, bool handled) {
      if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
          if (!handled) {
              // Report the key as unhandled, since the fallback was not handled.
              mReporter->reportUnhandledKey(keyEntry.id);
          }
          return false;
      }
  
      // Get the fallback key state.
      // Clear it out after dispatching the UP.
      int32_t originalKeyCode = keyEntry.keyCode;
      int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
      if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
          connection->inputState.removeFallbackKey(originalKeyCode);
      }
  
      if (handled || !dispatchEntry->hasForegroundTarget()) {
          // If the application handles the original key for which we previously
          // generated a fallback or if the window is not a foreground window,
          // then cancel the associated fallback key, if any.
          if (fallbackKeyCode != -1) {
              // Dispatch the unhandled key to the policy with the cancel flag.
  #if DEBUG_OUTBOUND_EVENT_DETAILS
              ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
                    "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
                    keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
  #endif
              KeyEvent event = createKeyEvent(keyEntry);
              event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
  
              mLock.unlock();
  
              mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
                                            keyEntry.policyFlags, &event);
  
              mLock.lock();
  
              // Cancel the fallback key.
              if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
                  CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                             "application handled the original non-fallback key "
                                             "or is no longer a foreground target, "
                                             "canceling previously dispatched fallback key");
                  options.keyCode = fallbackKeyCode;
                  synthesizeCancelationEventsForConnectionLocked(connection, options);
              }
              connection->inputState.removeFallbackKey(originalKeyCode);
          }
      } else {
      ......
      return false;
  }
xref: /lc-s-flame-vendor/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp 
 bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
          const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
      ATRACE_CALL();
      // Policy:
      // - Ignore untrusted events and do not perform default handling.
      bool result = false;
      if (policyFlags & POLICY_FLAG_TRUSTED) {
          JNIEnv* env = jniEnv();
          ScopedLocalFrame localFrame(env);
  
          // Note: tokenObj may be null.
          jobject tokenObj = javaObjectForIBinder(env, token);
          jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
          if (keyEventObj) {
          //回调到JAVA层InputManagerService.java
              jobject fallbackKeyEventObj = env->CallObjectMethod(mServiceObj,
                      gServiceClassInfo.dispatchUnhandledKey,
                      tokenObj, keyEventObj, policyFlags);
              if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
                  fallbackKeyEventObj = nullptr;
              }
              android_view_KeyEvent_recycle(env, keyEventObj);
              env->DeleteLocalRef(keyEventObj);
  
              if (fallbackKeyEventObj) {
                  // Note: outFallbackKeyEvent may be the same object as keyEvent.
                  if (!android_view_KeyEvent_toNative(env, fallbackKeyEventObj,
                          outFallbackKeyEvent)) {
                      result = true;
                  }
                  android_view_KeyEvent_recycle(env, fallbackKeyEventObj);
                  env->DeleteLocalRef(fallbackKeyEventObj);
              }
          } else {
              ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
          }
      }
      return result;
  }

事件是从native层的InputDispatcher.cpp一直传到java层,在native层主要做了keyevent的封装,循环等待下一次事件的分发。
由native层com_android_server_input_InputManagerService.cpp的NativeInputManager::dispatchUnhandledKey回调private KeyEvent dispatchUnhandledKey。

 xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
 private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
     return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
 }

mWindowManagerCallbacks是在SystemServer.startOtherServices得到InputManagerCallback并设置的,所以dispatchUnhandledKey是在InputManagerCallback.java

xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
    /**
      * Provides an opportunity for the window manager policy to process a key that
      * the application did not handle.
      */
     @Override
     public KeyEvent dispatchUnhandledKey(
             IBinder focusedToken, KeyEvent event, int policyFlags) {
         return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
     }

直接看它的调用栈:
PhoneWindowManager.dispatchUnhandledKey-->PhoneWindowManager.interceptFallback --> PhoneWindowManager.interceptKeyBeforeDispatching -->PhoneWindowManager.interceptPowerKeyDown -->PhoneWindowManager.powerLongPress (此处区分是直接重启还是弹框重启)-->PhoneWindowManager. showGlobalActionsInternal-->GlobalActions.showDialog -->LegacyGlobalActions.showDialog(弹框重启)

2.3 长按键10s直接重启

长按键涉及底层按键时间配置:

// vendor/qcom/non-hlos-sm4450/zprojects/HMI_C3F_COMMON/BOOT.MXF.2.0/boot_images/boot/Settings/Clarence/Core/PMIC/pm.dtsi

18     s2-kpdpwr {
19       enable = <PM_TRUE>;
20       reset-type = <PM_HARD_RESET>;
21       s1-ms = <4480>;
22       s2-ms = <2000>;
23     };
24 
25     s2-kpdpwr-resin {
26       enable = <PM_TRUE>;
27       reset-type = <PM_WARM_RESET>;
28       s1-ms = <4480>;
29       s2-ms = <500>;
30     };

2.4 ui弹窗选择重启

 private void powerLongPress(long eventTime) {
   //获取用户的behavior
   final int behavior = getResolvedLongPressOnPowerBehavior();
   Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
           + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);

   switch (behavior) {
       case LONG_PRESS_POWER_NOTHING:
           break;
       case LONG_PRESS_POWER_GLOBAL_ACTIONS:
           mPowerKeyHandled = true;
           performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                   "Power - Long Press - Global Actions");
           showGlobalActions();
           break;
       case LONG_PRESS_POWER_SHUT_OFF:
       case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM://直接重启,无需按键确认
           mPowerKeyHandled = true;
           performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                   "Power - Long Press - Shut Off");
           sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
           //mWindowManagerFuncs是WindowManagerService的实例
           mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
           break;
       case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
           mPowerKeyHandled = true;
           performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                   "Power - Long Press - Go To Voice Assist");
           // Some devices allow the voice assistant intent during setup (and use that intent
           // to launch something else, like Settings). So we explicitly allow that via the
           // config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
           launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
           break;
       case LONG_PRESS_POWER_ASSISTANT:
           mPowerKeyHandled = true;
           performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
                   "Power - Long Press - Go To Assistant");
           final int powerKeyDeviceId = Integer.MIN_VALUE;
           launchAssistAction(null, powerKeyDeviceId, eventTime,
                   AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
           break;
   }

  xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 
   
   // Called by window manager policy.  Not exposed externally.  
   @Override  public void shutdown(boolean confirm) {
     // Pass in the UI context, since ShutdownThread requires it (to show UI).
      ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
            PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
  }
  xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
  /**
	* 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);
	}

最后走到LegacyGlobalActions类,该类是关机提示框的实现类,提示框的显示、逻辑处理都是在此实现,对话框显示的不仅仅是关机/重启,还有飞行模式、截图、锁定等,不同的手机开发商定制的功能也不同,对话框的创建逻辑

xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java 
    /**
      * Create the global actions dialog.
      * @return A new dialog.
      */
     private ActionsDialog createDialog() {
         .......
         ArraySet<String> addedKeys = new ArraySet<String>();
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
                 // If we already have added this, don't add it again.
                 continue;
             }
             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                 mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                 mItems.add(mAirplaneModeOn);
             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
                 if (Settings.Global.getInt(mContext.getContentResolver(),
                         Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
                     mItems.add(new BugReportAction());
                 }
             } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
                 if (mShowSilentToggle) {
                     mItems.add(mSilentModeAction);
                 }
             } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
                 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
                     addUsersToMenu(mItems);
                 }
             } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
                 mItems.add(getSettingsAction());
             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                 mItems.add(getLockdownAction());
             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
                 mItems.add(getVoiceAssistAction());
             } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
                 mItems.add(getAssistAction());
             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                 mItems.add(new RestartAction(mContext, mWindowManagerFuncs));
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
             // Add here so we don't add more than one.
             addedKeys.add(actionKey);
         }
 
         if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
             mItems.add(getEmergencyAction());
         }
 
         mAdapter = new ActionsAdapter(mContext, mItems,
                 () -> mDeviceProvisioned, () -> mKeyguardShowing);
 
         AlertController.AlertParams params = new AlertController.AlertParams(mContext);
         params.mAdapter = mAdapter;
         params.mOnClickListener = this;
         params.mForceInverseBackground = true;
 
         ActionsDialog dialog = new ActionsDialog(mContext, params);
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
 
         dialog.getListView().setItemsCanFocus(true);
         dialog.getListView().setLongClickable(true);
         dialog.getListView().setOnItemLongClickListener(
                 new AdapterView.OnItemLongClickListener() {
                     @Override
                     public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                             long id) {
                         final Action action = mAdapter.getItem(position);
                         if (action instanceof LongPressAction) {
                             return ((LongPressAction) action).onLongPress();
                         }
                         return false;
                     }
         });
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
         dialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
 
         dialog.setOnDismissListener(this);
 
         return dialog;
     }

对话框Item的点击事件,点击事件后的onPress()是RestartAction里的onPress()

 xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/policy/LegacyGlobalActions.java
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
            dialog.dismiss();
        }
        mAdapter.getItem(which).onPress();
    }
    
 xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/policy/RestartAction.java 
    @Override   
    public void onPress() {
        mWindowManagerFuncs.reboot(false /* confirm */);
     }
     
  xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java 
          @Override    
          public void reboot(boolean confirm) {
                 // Pass in the UI context, since ShutdownThread requires it (to show UI).
                 ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(),
                 PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
     }

RestartAction.onPress()直接调用了mWindowManagerFuncs.reboot(false),mWindowManagerFuncs是WindowManagerService的实例,直接看它里面的reboot函数,该函数直接调用了ShutdownThread.reboot
ShutdownThread是重启/关机的类,重启/关机的逻辑主要在此类实现。
先看看此类的基本结构,此类是final类,不能被其他类继承,也不能被其他类覆盖,属于线程类,继承Thread。
ShutdownThread.reboot进入到了shutdownInner然后调用ShutdownThread.beginShutdownSequence,在这里主要做了显示关机进度对话框、保持屏幕打开,启动线程。

 xref: /lc-s-flame-vendor/frameworks/base/core/java/android/os/PowerManager.java 
 /**
       * Reboot the device.  Will not return if the reboot is successful.
       * <p>
       * Requires the {@link android.Manifest.permission#REBOOT} permission.
       * </p>
       * <p>
       * If the {@code reason} string contains ",quiescent", then the screen stays off during reboot
       * and is not turned on again until the user triggers the device to wake up (for example,
       * by pressing the power key).
       * This behavior applies to Android TV devices launched on Android 11 (API level 30) or higher.
       * </p>
       *
       * @param reason code to pass to the kernel (e.g., "recovery") to
       *               request special boot modes, or null.
       * @throws UnsupportedOperationException if userspace reboot was requested on a device that
       *                                       doesn't support it.
       */
      @RequiresPermission(permission.REBOOT)
      public void reboot(@Nullable String reason) {
          if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
              throw new UnsupportedOperationException(
                      "Attempted userspace reboot on a device that doesn't support it");
          }
          try {
              mService.reboot(false, reason, true);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java  
          /**
           * 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
                       params://false,reason,true
          public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
              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);
              }
  
              ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
              final long ident = Binder.clearCallingIdentity();
              try {//params:HALT_MODE_REBOOT,false, reason, true
                  shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
              } finally {
                  Binder.restoreCallingIdentity(ident);
              }
          }
xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 
             //params:HALT_MODE_REBOOT,false, reason, true
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()) {
                  throw new UnsupportedOperationException(
                          "Attempted userspace reboot on a device that doesn't support it");
              }
              UserspaceRebootLogger.noteUserspaceRebootWasRequested();
          }
          if (mHandler == null || !mSystemReady) {
              if (RescueParty.isAttemptingFactoryReset()) {
                  // 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 {
                  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) {
                      }
                  }
              }
          }
      }
xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
     /**
       * 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.
       */
       //params:getUiContext(), reason, false[不进行弹框确认]
      public static void reboot(final Context context, String reason, boolean confirm) {
          mReboot = true;
          mRebootSafeMode = false;
          mRebootHasProgressBar = false;
          mReason = reason;
          shutdownInner(context, confirm);
      }
xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java 
                    //params:getUiContext(), reason, false[不进行弹框确认]
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);
          }
      }
 xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
 // UIcontext
 private static void beginShutdownSequence(Context context) {
          synchronized (sIsStartedGuard) {
              if (sIsStarted) {//shutdown操作正在执行,则直接返回
                  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() {
          };
          // static instance of this thread 
          // private static final ShutdownThread sInstance = new ShutdownThread();
          //启动线程来执行shutdown初始化
          sInstance.start(); ------>.start会调用ShutdownThread的run()方法
xref: /lc-s-flame-vendor/fameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
 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));
          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) {
              //如果需要重启进入安全模式,则设置"persist.sys.safemode"=1
              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;
          synchronized (mActionDoneSync) {
              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) {
              try {
              //关闭AMS
                  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
              pm.shutdown();
          }
          if (mRebootHasProgressBar) {
              sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
          }
          shutdownTimingLog.traceEnd(); // ShutdownPackageManager
          metricEnded(METRIC_PM);
          //关闭radios
          // 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);
      }
 /**
       * 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) {
          String subsysProp;
          subsysProp = SystemProperties.get("vendor.peripheral.shutdown_critical_list",
                          "ERROR");
          //If we don't have the shutdown critical subsystem list we can't
          //really do anything. Proceed with full system shutdown.
          if (!subsysProp.equals("ERROR")) {
                  Log.i(TAG, "Shutdown critical subsyslist is :"+subsysProp+": ");
                  Log.i(TAG, "Waiting for a maximum of " +
                             (VENDOR_SUBSYS_MAX_WAIT_MS) + "ms");
                  String[] subsysList = subsysProp.split(" ");
                  int wait_count = 0;
                  boolean okToShutdown = true;
                  String subsysState;
                  int subsysListLength = subsysList.length;
                  do {
                          okToShutdown = true;
                          for (int i = 0; i < subsysListLength; i++) {
                                  subsysState =
                                          SystemProperties.get(
                                                          "vendor.peripheral." +
                                                          subsysList[i] +
                                                          ".state",
                                                          "ERROR");
                                  if(subsysState.equals("ONLINE"))  {
                                          //We only want to delay shutdown while
                                          //one of the shutdown critical
                                          //subsystems still shows as 'ONLINE'.
                                          okToShutdown = false;
                                  }
                          }
                          if (okToShutdown == false) {
                                  SystemClock.sleep(VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS);
                                  wait_count++;
                          }
                  } while (okToShutdown != true &&
                                  wait_count < (VENDOR_SUBSYS_MAX_WAIT_MS/VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS));
                  if (okToShutdown != true) {
                          for (int i = 0; i < subsysList.length; i++) {
                                  subsysState =
                                          SystemProperties.get(
                                                          "vendor.peripheral." +
                                                          subsysList[i] +
                                                          ".state",
                                                          "ERROR");
                                  if(subsysState.equals("ONLINE"))  {
                                          Log.w(TAG, "Subsystem " + subsysList[i]+
                                                     "did not shut down within timeout");
                                  }
                          }
                  } else {
                          Log.i(TAG, "Vendor subsystem(s) shutdown successful");
                  }
          }
          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);
      }
 xref: /lc-s-flame-vendor/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
     /**
       * 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";
          }
  
          //设置属性为sys.powerctl + reboot + reason
          SystemProperties.set("sys.powerctl", "reboot," + reason);
          try {
              Thread.sleep(20 * 1000L);
          } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
          }
          Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
      }

SystemProperties.set 调用到 native_set,native_set 绑定函数 SystemProperties_set ,最终调用到 __system_property_set。

int register_android_os_SystemProperties(JNIEnv *env)
{
    const JNINativeMethod method_table[] = {
        { "native_get",
          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
          (void*) SystemProperties_getSS },
        { "native_get_int", "(Ljava/lang/String;I)I",
          (void*) SystemProperties_get_integral<jint> },
        { "native_get_long", "(Ljava/lang/String;J)J",
          (void*) SystemProperties_get_integral<jlong> },
        { "native_get_boolean", "(Ljava/lang/String;Z)Z",
          (void*) SystemProperties_get_boolean },
        { "native_find",
          "(Ljava/lang/String;)J",
          (void*) SystemProperties_find },
        { "native_get",
          "(J)Ljava/lang/String;",
          (void*) SystemProperties_getH },
        { "native_get_int", "(JI)I",
          (void*) SystemProperties_get_integralH<jint> },
        { "native_get_long", "(JJ)J",
          (void*) SystemProperties_get_integralH<jlong> },
        { "native_get_boolean", "(JZ)Z",
          (void*) SystemProperties_get_booleanH },
        { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
          (void*) SystemProperties_set },
        { "native_add_change_callback", "()V",
          (void*) SystemProperties_add_change_callback },
        { "native_report_sysprop_change", "()V",
          (void*) SystemProperties_report_sysprop_change },
    };
    return RegisterMethodsOrDie(env, "android/os/SystemProperties",
                                method_table, NELEM(method_table));
}

void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
                          jstring valJ)
{
    ScopedUtfChars key(env, keyJ);
    if (!key.c_str()) {
        return;
    }
    std::optional<ScopedUtfChars> value;
    if (valJ != nullptr) {
        value.emplace(env, valJ);
        if (!value->c_str()) {
            return;
        }
    }
    bool success;
#if defined(__BIONIC__)
    success = !__system_property_set(key.c_str(), value ? value->c_str() : "");
#else
    success = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");
#endif
    if (!success) {
        jniThrowException(env, "java/lang/RuntimeException",
                          "failed to set system property (check logcat for reason)");
    }
}

后续prop 处理流程见 重启前 prop 处理流程

2.5 panic reboot

我们通过各种类型 panic 的 panic task 的栈数据进行回溯,会发现 Kernel panic 最后都会调用 panic,在 panic 里会调用到 emergency_restart,再调用 machine_restart。

// kernel_platform/common/kernel/panic.c 
void panic(const char *fmt, ...)

        if (panic_timeout != 0) {
                /*
                 * This will not be a clean reboot, with everything
                 * shutting down.  But if there is a chance of
                 * rebooting the system it will be rebooted.
                 */
                if (panic_reboot_mode != REBOOT_UNDEFINED)
                        reboot_mode = panic_reboot_mode;
                emergency_restart();
        }

emergency_restart ---> machine_emergency_restart

// kernel_platform/common/kernel/reboot.c
void emergency_restart(void)
{
        kmsg_dump(KMSG_DUMP_EMERG);
        machine_emergency_restart();
}
EXPORT_SYMBOL_GPL(emergency_restart);

machine_emergency_restart ---> machine_restart

// kernel_platform/common/include/asm-generic/emergency-restart.h 
static inline void machine_emergency_restart(void)
{
        machine_restart(NULL);
}

后续流程见 重启前 kernel 部分 reboot 流程

2.6 重启前 prop 处理流程

property_set 和 SystemProperties.set 最终都会调用到 __system_property_set
这边要注意根据头文件确认 __system_property_set 函数为:

// bionic/libc/bionic/system_property_set.cpp
int __system_property_set(const char* key, const char* value) {
  if (key == nullptr) return -1;
  if (value == nullptr) value = "";

  if (g_propservice_protocol_version == 0) {
    detect_protocol_version();
  }

  if (g_propservice_protocol_version == kProtocolVersion1) {
    // Old protocol does not support long names or values
    if (strlen(key) >= PROP_NAME_MAX) return -1;
    if (strlen(value) >= PROP_VALUE_MAX) return -1;

    prop_msg msg;
    memset(&msg, 0, sizeof msg);
    msg.cmd = PROP_MSG_SETPROP;
    strlcpy(msg.name, key, sizeof msg.name);
    strlcpy(msg.value, value, sizeof msg.value);

    return send_prop_msg(&msg);
  } else {
    // New protocol only allows long values for ro. properties only.
    if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
    // Use proper protocol
    PropertyServiceConnection connection;
    if (!connection.IsValid()) {
      errno = connection.GetLastError();
      async_safe_format_log(
          ANDROID_LOG_WARN, "libc",
          "Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
          errno, strerror(errno));
      return -1;
    }

    SocketWriter writer(&connection);
    if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
      errno = connection.GetLastError();
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
                            key, value, errno, strerror(errno));
      return -1;
    }

    int result = -1;
    if (!connection.RecvInt32(&result)) {
      errno = connection.GetLastError();
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
                            key, value, errno, strerror(errno));
      return -1;
    }

    if (result != PROP_SUCCESS) {
      async_safe_format_log(ANDROID_LOG_WARN, "libc",
                            "Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
                            result);
      return -1;
    }

    return 0;
  }
}

PropertyServiceConnection connection;
SocketWriter writer(&connection);
这边连接的是 property_service socket。

static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";

class PropertyServiceConnection {
 public:
  PropertyServiceConnection() : last_error_(0) {
    socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
    if (socket_.get() == -1) {
      last_error_ = errno;
      return;
    }

    const size_t namelen = strlen(property_service_socket);
    sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
    addr.sun_family = AF_LOCAL;
    socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;

    if (TEMP_FAILURE_RETRY(connect(socket_.get(),
                                   reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
      last_error_ = errno;
      socket_.reset();
    }
}

这边连接上 init SecondStageMain() 中创建的 property_service socket。

int SecondStageMain(int argc, char** argv) {

     StartPropertyService(&property_fd);

 void StartPropertyService(int* epoll_socket) {
     InitPropertySet("ro.property_service.version", "2");
 
     int sockets[2];
     if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
         PLOG(FATAL) << "Failed to socketpair() between property_service and init";
     }
     *epoll_socket = from_init_socket = sockets[0];
     init_socket = sockets[1];
     StartSendingMessages();
 
     if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                    /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
                                    /*gid=*/0, /*socketcon=*/{});
         result.ok()) {
         property_set_fd = *result;
     } else {
         LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
     }
 
     listen(property_set_fd, 8);
 
     auto new_thread = std::thread{PropertyServiceThread};
     property_service_thread.swap(new_thread);
 
     auto async_persist_writes =
             android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
 
     if (async_persist_writes) {
         persist_write_thread = std::make_unique<PersistWriteThread>();
     }
 }

property_set_fd = *result;
listen(property_set_fd, 8); 监听 socket 请求改动 prop
PropertyServiceThread epoll 监听 property_set_fd 句柄变动来调用实际的处理函数 handle_property_set_fd
以上socket的连接和epoll的注册可以说明为什么property_set()为什么被监测到,并走到后续流程

 static void PropertyServiceThread() {
     Epoll epoll;
     if (auto result = epoll.Open(); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
     if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
         !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
     if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
     while (true) {
         auto epoll_result = epoll.Wait(std::nullopt);
         if (!epoll_result.ok()) {
             LOG(ERROR) << epoll_result.error();
         }
     }
 }

static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
        return;
    }

    ucred cr;
    socklen_t cr_size = sizeof(cr);
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
        return;
    }

    SocketConnection socket(s, cr);
    uint32_t timeout_ms = kDefaultSocketTimeout;

    uint32_t cmd = 0;
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
        socket.SendUint32(PROP_ERROR_READ_CMD);
        return;
    }

    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
            return;
        }

        const auto& cr = socket.cred();
        std::string error;
        auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }

        std::string source_context;
        if (!socket.GetSourceContext(&source_context)) {
            PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
            return;
        }

        // HandlePropertySet takes ownership of the socket if the set is handled asynchronously.
        const auto& cr = socket.cred();
        std::string error;
        auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
        if (!result) {
            // Result will be sent after completion.
            return;
        }
        if (*result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }
        socket.SendUint32(*result);
        break;
      }

    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}

HandlePropertySet,真正的处理函数。
set property msg 分为两类处理:

  1. msg name以“ctl.”为起始的msg 通过 SendControlMessage() 处理,主要是启动、停止、重启服务。
  2. 修改其它 prop 时会调用 property_set,然后通过 bionic 的__system_property_set 函数来实现。
std::optional<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.")) {
        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") {
        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());
        }

// MIUI ADD: Init_DebugEnhance
        android::base::unique_fd fd_kmsg_cpp(TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)));
        if(fd_kmsg_cpp != -1) {
            std::string logString("<3> Received sys.powerctl='" + value + "' from pid: " + std::to_string(cr.pid)
                                + process_log_string );
            WriteStringToFd(logString, fd_kmsg_cpp);
        }
        LOG(ERROR) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
// END Init_DebugEnhance

        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, socket, error);
}

PropertySet

static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
                                           SocketConnection* socket, std::string* error) {
    size_t valuelen = value.size();

    if (!IsLegalPropertyName(name)) {
        *error = "Illegal property name";
        return {PROP_ERROR_INVALID_NAME};
    }

    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
        *error = result.error().message();
        return {PROP_ERROR_INVALID_VALUE};
    }

    prop_info* pi = (prop_info*)__system_property_find(name.c_str());
    if (pi != nullptr) {
// MIUI ADD: Init_LoadCustProp
#ifdef MIUI_INIT_HOOK
        if (!ShouldSkipCheck(name.c_str())) {
#endif
// END Init_LoadCustProp
            // ro.* properties are actually "write-once".
            if (StartsWith(name, "ro.")) {
                *error = "Read-only property was already set";
                return {PROP_ERROR_READ_ONLY_PROPERTY};
            }

// MIUI ADD: Init_LoadCustProp
#ifdef MIUI_INIT_HOOK
        }
#endif
// END Init_LoadCustProp
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return {PROP_ERROR_SET_FAILED};
        }
    }

    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
        if (persist_write_thread) {
            persist_write_thread->Write(name, value, std::move(*socket));
            return {};
        }
        WritePersistentProperty(name, value);
    }

    NotifyPropertyChange(name, value);
    return {PROP_SUCCESS};
}

void NotifyPropertyChange(const std::string& name, const std::string& value) {
    // If init hasn't started its main loop, then it won't be handling property changed messages
    // anyway, so there's no need to try to send them.
    auto lock = std::lock_guard{accept_messages_lock};
    if (accept_messages) {
        PropertyChanged(name, value);
    }
}

PropertyChanged

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") {
        trigger_shutdown(value);
    }

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

    prop_waiter_state.CheckAndResetWait(name, value);
}

int SecondStageMain(int argc, char** argv) {

     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };

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

    std::optional<std::string> CheckShutdown() __attribute__((warn_unused_result)) {
        auto lock = std::lock_guard{shutdown_command_lock_};
        if (do_shutdown_ && !IsShuttingDown()) {
            do_shutdown_ = false;
            return shutdown_command_;
        }
        return {};
    }

上面的 PropertyChanged() 调用到 trigger_shutdown(),而 trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };,所以实际上调用的是 TriggerShutdown()
TriggerShutdown() 改到 shutdown_command 和 do_shutdown_。
在 init SecondStageMain 里有个循环,CheckShutdown() 根据 do_shutdown_ && !IsShuttingDown() 判断是否返回 shutdown_command_

int SecondStageMain(int argc, char** argv) {

     while (true) {
         // By default, sleep until something happens. Do not convert far_future into
         // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
         // is 1ns.
         const boot_clock::time_point far_future = boot_clock::time_point::max();
         boot_clock::time_point next_action_time = far_future;
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
             LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                       << "' Calling HandlePowerctlMessage()";
             HandlePowerctlMessage(*shutdown_command);
         }

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;

// MIUI ADD: Recovery_StabilityEnhance
#ifdef MIUI_MONITOR_RECOVERY
    save_system_time_file();
#endif
// END Recovery_StabilityEnhance

    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 == "quiescent") {
                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-quiescent", 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";
            }

// MIUI ADD: PORTING_LogEnhance
#ifdef MIUI_MONITOR_RECOVERY
            if (cmd_params[1] == "recovery" || cmd_params[1] == "exaid" || cmd_params[1] == "RescueParty") {
                save_reboot_log();
            }
#endif
// END PORTING_LogEnhance

            // 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>{};
    };
    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");

    EnterShutdown();
}

DoReboot

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) {
        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, reason);
    }

    // Start a thread to monitor init shutdown process
    LOG(INFO) << "Create reboot monitor thread.";
    bool reboot_monitor_run = true;
    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,");
    }
    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("*")) {
        sync();
        RebootSystem(cmd, reboot_target, reason);
        abort();
    }

    bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
    // 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 (do_shutdown_animation) {
            continue;
        } 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 (!do_shutdown_animation && (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()) {

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

        // MIUI ADD: CUST_ThemeResourcesCustom
        // fixup bootanim's start_order
        // In miui version, boot anim service is restarted in the process of shutdown,
        // then make a latest start order. But the service stopped in the opposite order
        // that they are started, so there is a big gap between boot anim service and
        // surfaceflinger, cause a corner case of splash screen issue. Now we set a
        // fixed start order close to surfaceflinger.
        if (surface_flinger->start_order() > 0) {
            boot_anim->set_start_order(surface_flinger->start_order() + 1);
        }
        // END CUST_ThemeResourcesCustom
    }

    // 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
    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.
    if (IsDataMounted("f2fs")) {
        uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;
        unique_fd fd(TEMP_FAILURE_RETRY(open("/data", O_RDONLY)));
        int ret = ioctl(fd.get(), F2FS_IOC_SHUTDOWN, &flag);
        if (ret) {
            PLOG(ERROR) << "Shutdown /data: ";
        } else {
            LOG(INFO) << "Shutdown /data";
        }
    }
    RebootSystem(cmd, reboot_target, reason);
    abort();
}

RebootSystem

RebootSystem(unsigned int cmd, const std::string& rebootTarget, const std::string& reboot_reason) {
    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)) {
                std::string reason = "shutdown,thermal";
                if (!reboot_reason.empty()) reason = reboot_reason;

                LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                        LINUX_REBOOT_CMD_RESTART2, reason.c_str());
            } else {
                reboot(RB_POWER_OFF);
            }
            break;
    }
    // In normal case, reboot should not return.
    PLOG(ERROR) << "reboot call returned";
    abort();
}

Syscall 系统调用到函数 SYSCALL_DEFINE4。
系统调用流程:
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
SYSCALL_DEFINE4(reboot 展开后其实是 __arm64_sys_reboot)

// kernel_platform/common/kernel/reboot.c
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
                void __user *, arg)
{
        struct pid_namespace *pid_ns = task_active_pid_ns(current);
        char buffer[256];
        int ret = 0;

        /* We only trust the superuser with rebooting the system. */
        if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
                return -EPERM;

        /* For safety, we require "magic" arguments. */
        if (magic1 != LINUX_REBOOT_MAGIC1 ||
                        (magic2 != LINUX_REBOOT_MAGIC2 &&
                        magic2 != LINUX_REBOOT_MAGIC2A &&
                        magic2 != LINUX_REBOOT_MAGIC2B &&
                        magic2 != LINUX_REBOOT_MAGIC2C))
                return -EINVAL;

        /*
         * If pid namespaces are enabled and the current task is in a child
         * pid_namespace, the command is handled by reboot_pid_ns() which will
         * call do_exit().
         */
        ret = reboot_pid_ns(pid_ns, cmd);
        if (ret)
                return ret;

        /* Instead of trying to make the power_off code look like
         * halt when pm_power_off is not set do it the easy way.
         */
        if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
                cmd = LINUX_REBOOT_CMD_HALT;

        mutex_lock(&system_transition_mutex);
        switch (cmd) {
        case LINUX_REBOOT_CMD_RESTART:
                kernel_restart(NULL);
                break;

        case LINUX_REBOOT_CMD_CAD_ON:
                C_A_D = 1;
                break;

        case LINUX_REBOOT_CMD_CAD_OFF:
                C_A_D = 0;
                break;

        case LINUX_REBOOT_CMD_HALT:
                kernel_halt();
                do_exit(0);
                panic("cannot halt");

        case LINUX_REBOOT_CMD_POWER_OFF:
                kernel_power_off();
                do_exit(0);
                break;

        case LINUX_REBOOT_CMD_RESTART2:
                ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
                if (ret < 0) {
                        ret = -EFAULT;
                        break;
                }
                buffer[sizeof(buffer) - 1] = '\0';

                kernel_restart(buffer);
                break;

#ifdef CONFIG_KEXEC_CORE
        case LINUX_REBOOT_CMD_KEXEC:
                ret = kernel_kexec();
                break;
#endif

#ifdef CONFIG_HIBERNATION
        case LINUX_REBOOT_CMD_SW_SUSPEND:
                ret = hibernate();
                break;
#endif

        default:
                ret = -EINVAL;
                break;
        }
        mutex_unlock(&system_transition_mutex);
        return ret;
}

2.7 重启前kernel部分reboot流程

kernel_restart

void kernel_restart(char *cmd)
{
        kernel_restart_prepare(cmd);
        migrate_to_reboot_cpu();
        syscore_shutdown();
        if (!cmd)
                pr_emerg("Restarting system\n");
        else
                pr_emerg("Restarting system with command '%s'\n", cmd);
        kmsg_dump(KMSG_DUMP_SHUTDOWN);
        machine_restart(cmd);
}

其中 kernel_restart_prepare() 中 blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); 调用下面两个关键的通知链函数,用于设置 dload mode 和 reboot reason:
qcom_dload_reboot
用于判断重启后是正常启动还是 进 edl 或者 900e

static int qcom_dload_reboot(struct notifier_block *this, unsigned long event,
                              void *ptr)
{
        char *cmd = ptr;
        struct qcom_dload *poweroff = container_of(this, struct qcom_dload,
                                                     reboot_nb);

        /* Clean shutdown, disable dump mode to allow normal restart */
        if (!poweroff->in_panic)
                set_download_mode(QCOM_DOWNLOAD_NODUMP);

        if (cmd) {
                if (!strcmp(cmd, "edl"))
                        set_download_mode(QCOM_DOWNLOAD_EDL);
                else if (!strcmp(cmd, "qcom_dload"))
                        msm_enable_dump_mode(true);
        }

        if (current_download_mode != QCOM_DOWNLOAD_NODUMP)
                reboot_mode = REBOOT_WARM;

        return NOTIFY_OK;
}

qcom_reboot_reason_reboot
重启前将 static struct poweroff_reason reasons[] 对应的 flag 值写入寄存器:
qcom_reboot_reason_reboot() 中循环遍历 cmd 判断和数组对应的 cmd 是否相等,将相应的 flag 值写入寄存器
panic_prep_restart() 对应 panic 流程,直接将 0x21 写入寄存器

static struct poweroff_reason reasons[] = {
        { "recovery",                        0x01 },
        { "bootloader",                        0x02 },
        { "rtc",                        0x03 },
        { "dm-verity device corrupted",        0x04 },
        { "dm-verity enforcing",        0x05 },
        { "keys clear",                        0x06 },
        { "ffu",                        0x41 },
        { "panic",                        0x21 },
        { NULL,                                0x20 },
        {}
};

static int qcom_reboot_reason_reboot(struct notifier_block *this,
                                     unsigned long event, void *ptr)
{
       char *cmd = ptr;
       struct qcom_reboot_reason *reboot = container_of(this,
               struct qcom_reboot_reason, reboot_nb);
       struct poweroff_reason *reason;

       if (!cmd) {
               nvmem_cell_write(reboot->nvmem_cell,
                                &reasons[RESTART_REASON_NORMAL].pon_reason,
                                sizeof(reasons[RESTART_REASON_NORMAL].pon_reason));
               return NOTIFY_OK;
       }
       for (reason = reasons; reason->cmd; reason++) {
               if (!strcmp(cmd, reason->cmd)) {
                       nvmem_cell_write(reboot->nvmem_cell,
                                        &reason->pon_reason,
                                        sizeof(reason->pon_reason));
                       return NOTIFY_OK;
               }
       }
        nvmem_cell_write(reboot->nvmem_cell,
                        &reason->pon_reason,
                        sizeof(reason->pon_reason));

        return NOTIFY_OK;
}

static int panic_prep_restart(struct notifier_block *this,
                              unsigned long event, void *ptr)
{
        struct qcom_reboot_reason *reboot = container_of(this,
                struct qcom_reboot_reason, panic_nb);
        nvmem_cell_write(reboot->nvmem_cell,
                        &reasons[RESTART_REASON_PANIC].pon_reason,
                        sizeof(reasons[RESTART_REASON_PANIC].pon_reason));
        return NOTIFY_DONE;
}

machine_restart

// kernel_platform/common/arch/arm64/kernel/process.c
void machine_restart(char *cmd)
{
        /* Disable interrupts first */
        local_irq_disable();
        smp_send_stop();

        /*
         * UpdateCapsule() depends on the system being reset via
         * ResetSystem().
         */
        if (efi_enabled(EFI_RUNTIME_SERVICES))
                efi_reboot(reboot_mode, NULL);

        /* Now call the architecture specific reboot code. */
        do_kernel_restart(cmd);

        /*
         * Whoops - the architecture was unable to reboot.
         */
        printk("Reboot failed -- System halted\n");
        while (1);
}

do_kernel_restart

// kernel_platform/common/kernel/reboot.c
void do_kernel_restart(char *cmd)
{
        atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}

atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
根据 restart_handler_list 的注册以及是否编译和加 log 判断,这条通知链包含如下函数:
restart_wdog_handler

// kernel_platform/msm-kernel/drivers/soc/qcom/qcom_wdt_core.c
static int restart_wdog_handler(struct notifier_block *this,
                               unsigned long event, void *ptr)
{
        struct msm_watchdog_data *wdog_dd = container_of(this,
                                struct msm_watchdog_data, restart_blk);
        if (WDOG_BITE_ON_PANIC && wdog_dd->in_panic) {
                /*
                 * Trigger a watchdog bite here and if this fails,
                 * device will take the usual restart path.
                 */
                pr_info("Triggering late bite\n");
                qcom_wdt_trigger_bite();
        }
        return NOTIFY_DONE;
}

psci_sys_reset

// kernel_platform/common/drivers/firmware/psci/psci.c
static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
                          void *data)
{
        if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
            psci_system_reset2_supported) {
                /*
                 * reset_type[31] = 0 (architectural)
                 * reset_type[30:0] = 0 (SYSTEM_WARM_RESET)
                 * cookie = 0 (ignored by the implementation)
                 */
                invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0);
        } else {
                invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
        }

        return NOTIFY_DONE;
}

{% tip success %}
注意:关于这条restart_handler_list的内核通知链,在高通项目中是存在一些差异的。有的项目会在msm-poweroff驱动中注册这条通知链,当do_kernel_restart触发通知链时,msm-poweroff也会执行相应的处理函数,从笔者公司的这个项目来看,会往一个地址写0,暂时不知道有什么作用?
{% endtip %}

3. 重启后的reason的传递

重启后 ABL 阶段 GetRebootReason() 从寄存器中获取 flag 值,用于判断 switch (BootReason) 对应 boot mode。

// bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.c
STATIC EFI_STATUS GetPmicPONReason(VOID)
{
  EFI_STATUS Status = EFI_SUCCESS;
  EFI_PM_PON_REASON_TYPE PONReason;
  EFI_PM_POFF_REASON_TYPE POFFReason;
  EFI_QCOM_PMIC_PON_PROTOCOL *PmicPonProtocol;
  BOOLEAN WarmRtStatus;
  BOOLEAN IsColdBoot;
  Status = gBS->LocateProtocol(&gQcomPmicPonProtocolGuid, NULL,
      (VOID **) &PmicPonProtocol);
  if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "Error locating pmic pon protocol: %d\n", Status));
    return Status;
  }
  /* Passing 0 for PMIC device Index since the protocol infers internally */
  Status = PmicPonProtocol->GetPonReason(0, &PONReason);
  DEBUG((EFI_D_INFO, "PON Reason is %d\n",PONReason));
  if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "Error getting pon reason: %d\n", Status));
    return Status;
  }
  /* Passing 0 for PMIC device Index since the protocol infers internally */
  Status = PmicPonProtocol->GetPoffReason(0, &POFFReason);
  if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "Error getting poff reason: %d\n", Status));
    return Status;
  }
  Status = PmicPonProtocol->WarmResetStatus(0, &WarmRtStatus);
  if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "Error getting warm reset status: %d\n", Status));
    return Status;
  }
  IsColdBoot = !WarmRtStatus;
    DEBUG((EFI_D_INFO, " PON Reason is %d cold_boot:%d\n",
        PONReason, IsColdBoot));
  pureason |= *((uint8_t *)(&PONReason));
  //pureason |= *(uint8_t *)&PONReason;
  if (!IsColdBoot)
    pureason |= WARM_RST;
  pdreason |= *((uint8_t *)(&POFFReason));
  DEBUG((EFI_D_ERROR, "LinuxLoader-GetPmicPONReason: pureason=0x%x, pdreason=0x%x\n", pureason, pdreason));
    return Status;
}

  switch (BootReason) {
  case FASTBOOT_MODE:
    BootIntoFastboot = TRUE;
    pureason |= RESTART_EVENT_NORMAL;
    DEBUG ((EFI_D_ERROR, "FASTBOOT_MODE  : pureason=0x%x\n", pureason));
    goto_fastboot_reason = REASON_REBOOT_CMD;
    break;
  case RECOVERY_MODE:
    BootIntoRecovery = TRUE;
    pureason |= RESTART_EVENT_NORMAL;
    DEBUG ((EFI_D_ERROR, "RECOVERY_MODE : pureason=0x%x\n", pureason));
    break;
  case ALARM_BOOT:
    BootReasonAlarm = TRUE;
    pureason |= RESTART_EVENT_NORMAL;
    DEBUG ((EFI_D_ERROR, "ALARM_BOOT : pureason=0x%x\n", pureason));
    break;
  case DM_VERITY_ENFORCING:
    // write to device info
    Status = EnableEnforcingMode (TRUE);
    if (Status != EFI_SUCCESS)
      goto stack_guard_update_default;
    pureason |= RESTART_EVENT_DVE;
    DEBUG ((EFI_D_ERROR, "DM_VERITY_ENFORCING : pureason=0x%x\n", pureason));
    break;
  case DM_VERITY_LOGGING:
    /* Disable MDTP if it's Enabled through Local Deactivation */
    Status = MdtpDisable ();
    if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
      DEBUG ((EFI_D_ERROR, "MdtpDisable Returned error: %r\n", Status));
      goto stack_guard_update_default;
    }
    pureason |= RESTART_EVENT_DVL;
    DEBUG ((EFI_D_ERROR, "DM_VERITY_LOGGING : pureason=0x%x\n", pureason));
    // write to device info
    Status = EnableEnforcingMode (FALSE);
    if (Status != EFI_SUCCESS)
      goto stack_guard_update_default;

    break;
  case DM_VERITY_KEYSCLEAR:
    Status = ResetDeviceState ();
    if (Status != EFI_SUCCESS) {
      DEBUG ((EFI_D_ERROR, "VB Reset Device State error: %r\n", Status));
      goto stack_guard_update_default;
    }
    pureason |= RESTART_EVENT_DVK;
    DEBUG ((EFI_D_ERROR, "DM_VERITY_KEYSCLEAR : pureason=0x%x\n", pureason));
    break;
  case NORMAL_MODE:
    pureason |= RESTART_EVENT_OTHER;
    DEBUG ((EFI_D_ERROR, "RESTART_EVENT_OTHER NORMAL_MODE : pureason=0x%x\n", pureason));
    break;
  case REBOOT_MODE:
    pureason |= RESTART_EVENT_NORMAL;
    DEBUG ((EFI_D_ERROR, "REBOOT_MODE : pureason=0x%x\n", pureason));
    break;
  case FASTBOOT_REBOOT_MODE:
    pureason |= RESTART_EVENT_FASTBOOT;
    DEBUG ((EFI_D_ERROR, "FASTBOOT_REBOOT_MODE : pureason=0x%x\n", pureason));
    break;
  case KPANIC_MODE:
    pureason |= RESTART_EVENT_KPANIC;
    DEBUG ((EFI_D_ERROR, "KPANIC_MODE : pureason=0x%x\n", pureason));
    break;

pureason 为全局变量,赋值完成后在 UpdateCmdLine() 里添加到 cmdline 里。

// bootable/bootloader/edk2/QcomModulePkg/Library/BootLib/UpdateCmdLine.c
   AsciiSPrint (PuReason, sizeof (PuReason),
                " bootinfo.pureason=0x%x", pureason);
   CmdLineLen += AsciiStrLen(PuReason);