[Android稳定性] 第019篇 [原理篇] QCOM 常见 reboot 类型流程梳理

Code 基于 Android U ssi、kernel-5.10

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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 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 编译生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 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监听到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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 方法主要用于完成调度周期,包括记录调试信息(如果启用了调试宏)、检查连接状态以避免无效操作,并通知其他系统组件当前调度周期的完成情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
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) 进行事件后处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 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触发之后的中断事件处理入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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。

1
2
3
4
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

1
2
3
4
5
6
7
8
9
10
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直接重启

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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弹窗选择重启

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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;
}

1
2
3
4
5
6
7
8
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 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类,该类是关机提示框的实现类,提示框的显示、逻辑处理都是在此实现,对话框显示的不仅仅是关机/重启,还有飞行模式、截图、锁定等,不同的手机开发商定制的功能也不同,对话框的创建逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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,在这里主要做了显示关机进度对话框、保持屏幕打开,启动线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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) {
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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

1
2
3
4
5
6
7
// 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

1
2
3
4
5
// 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 函数为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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()为什么被监测到,并走到后续流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
 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 函数来实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 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

1
2
3
4
5
6
7
8
9
10
11
12
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 写入寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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

1
2
3
4
5
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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;
}

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

3. 重启后的reason的传递

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 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 里。

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