android 关机流程详细分析。前面有提到过android事件上报流程,InputReaderThread 从EventHub读到按键事件后,交给InputDispatcher 往上上报,我们从这里跟踪一下长按power键关键流程,
frameworks/native/services/inputflinger/InputDispatcher.cpp
1
2
3
4
5
|
void
InputDispatcher::notifyKey(
const
NotifyKeyArgs* args) {
…
mPolicy->interceptKeyBeforeQueueing(&event,
/*byref*/
policyFlags);
…
}
|
这里的mPolicy是NativeInputManager对象.
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
void
NativeInputManager::interceptKeyBeforeQueueing(
const
KeyEvent* keyEvent, uint32_t& policyFlags) {
…
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
886
jint wmActions;
887
if
(keyEventObj) {
888
wmActions = env->CallIntMethod(mServiceObj,
889
gServiceClassInfo.interceptKeyBeforeQueueing,
890
keyEventObj, policyFlags);
891
if
(checkAndClearExceptionFromCallback(env,
"interceptKeyBeforeQueueing"
)) {
892
wmActions =
0
;
893
}
894
android_view_KeyEvent_recycle(env, keyEventObj);
895
env->DeleteLocalRef(keyEventObj);
896
}
else
{
897
ALOGE(
"Failed to obtain key event object for interceptKeyBeforeQueueing."
);
898
wmActions =
0
;
899
}
…
}
|
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
1
2
3
|
// Native callback.
1871
private
int
interceptKeyBeforeQueueing(KeyEvent event,
int
policyFlags) {
1872
return
mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
|
1
|
}
|
这个mWindowManagerCallbacks 是通过下面的接口设置的。
1
|
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
|
frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
1
2
3
|
public
int
interceptKeyBeforeQueueing(KeyEvent event,
int
policyFlags) {
return
mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
|
这里的mService就是WindowManagerService。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
1
|
final
WindowManagerPolicy mPolicy =
new
PhoneWindowManager();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
int
interceptKeyBeforeQueueing(KeyEvent event,
int
policyFlags) {
@Override
public
int
interceptKeyBeforeQueueing(KeyEvent event,
int
policyFlags) {
case
KeyEvent.KEYCODE_POWER: {
5815
result &= ~ACTION_PASS_TO_USER;
5816
isWakeKey =
false
;
// wake-up will be handled separately
5817
if
(down) {
5818
interceptPowerKeyDown(event, interactive);
5819
}
else
{
5820
interceptPowerKeyUp(event, interactive, canceled);
5821
}
5822
break
;
5823
}
}
|
1
|
private
void
interceptPowerKeyDown(KeyEvent event,
boolean
interactive) {
|
1
2
3
4
5
6
7
8
9
10
11
|
...
if
(interactive) {
1087
// When interactive, we're already awake.
1088
// Wait for a long press or for the button to be released to decide what to do.
1089
if
(hasLongPressOnPowerBehavior()) {
1090
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
1091
msg.setAsynchronous(
true
);
1092
mHandler.sendMessageDelayed(msg,
1093
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
1094
}
1095
}
|
1
|
}
|
1
2
3
4
5
6
7
8
9
10
|
private
class
PolicyHandler
extends
Handler {
...
746
@Override
747
public
void
handleMessage(Message msg) {
748
switch
(msg.what) {
case
MSG_POWER_LONG_PRESS:
790
powerLongPress();
791
break
;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private
void
powerLongPress() {
1255
final
int
behavior = getResolvedLongPressOnPowerBehavior();
1256
switch
(behavior) {
1257
case
LONG_PRESS_POWER_NOTHING:
1258
break
;
1259
case
LONG_PRESS_POWER_GLOBAL_ACTIONS:
1260
mPowerKeyHandled =
true
;
1261
if
(!performHapticFeedbackLw(
null
, HapticFeedbackConstants.LONG_PRESS,
false
)) {
1262
performAuditoryFeedbackForAccessibilityIfNeed();
1263
}
1264
showGlobalActionsInternal();
1265
break
;
1273
}
1274
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
showGlobalActionsInternal() {
1389
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
1390
if
(mGlobalActions ==
null
) {
1391
mGlobalActions =
new
GlobalActions(mContext, mWindowManagerFuncs);
1392
}
1393
final
boolean
keyguardShowing = isKeyguardShowingAndNotOccluded();
1394
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
1395
if
(keyguardShowing) {
1396
// since it took two seconds of long press to bring this up,
1397
// poke the wake lock so they have some time to see the dialog.
1398
mPowerManager.userActivity(SystemClock.uptimeMillis(),
false
);
1399
}
1400
}
|
showDialog(keyguardShowing, isDeviceProvisioned());会弹出选择界面。
frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
1
2
3
4
5
6
7
8
9
10
11
12
|
public
void
showDialog(
boolean
keyguardShowing,
boolean
isDeviceProvisioned) {
172
mKeyguardShowing = keyguardShowing;
173
mDeviceProvisioned = isDeviceProvisioned;
174
if
(mDialog !=
null
) {
175
mDialog.dismiss();
176
mDialog =
null
;
177
// Show delayed, so that the dismiss of the previous dialog completes
178
mHandler.sendEmptyMessage(MESSAGE_SHOW);
179
}
else
{
180
handleShow();
181
}
182
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
private
void
handleShow() {
197
awakenIfNecessary();
198
mDialog = createDialog();
199
prepareDialog();
200
201
// If we only have 1 item and it's a simple press action, just do this action.
202
if
(mAdapter.getCount() ==
1
203
&& mAdapter.getItem(
0
)
instanceof
SinglePressAction
204
&& !(mAdapter.getItem(
0
)
instanceof
LongPressAction)) {
205
((SinglePressAction) mAdapter.getItem(
0
)).onPress();
206
}
else
{
207
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
208
attrs.setTitle(
"GlobalActions"
);
209
mDialog.getWindow().setAttributes(attrs);
210
mDialog.show();
211
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
212
}
213
}
|
1
2
3
4
5
6
7
8
|
private
GlobalActionsDialog createDialog() {
if
(GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
281
mItems.add(
new
PowerAction());
282
}
else
if
(GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
283
mItems.add(mAirplaneModeOn);
284
}
}
|
1
2
3
4
5
6
7
|
private
final
class
PowerAction
extends
SinglePressAction
implements
LongPressAction {
@Override
376
public
void
onPress() {
377
// shutdown by making sure radio and power are handled accordingly.
378
mWindowManagerFuncs.shutdown(
false
/* confirm */
);
379
}
380
}
|
如果点击关机按钮,就会走到onPress(),调用WindowManagerservice的shutdown接口。
1
2
3
4
5
|
// Called by window manager policy. Not exposed externally.
5823
@Override
5824
public
void
shutdown(
boolean
confirm) {
5825
ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
5826
}
|
1
2
3
4
5
6
|
public
static
void
shutdown(
final
Context context, String reason,
boolean
confirm) {
133
mReboot =
false
;
134
mRebootSafeMode =
false
;
135
mReason = reason;
136
shutdownInner(context, confirm);
137
}
|
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
|
static
void
shutdownInner(
final
Context context,
boolean
confirm) {
...
159
if
(confirm) {
160
final
CloseDialogReceiver closer =
new
CloseDialogReceiver(context);
161
if
(sConfirmDialog !=
null
) {
162
sConfirmDialog.dismiss();
163
}
164
sConfirmDialog =
new
AlertDialog.Builder(context)
165
.setTitle(mRebootSafeMode
166
? com.android.internal.R.string.reboot_safemode_title
167
: com.android.internal.R.string.power_off)
168
.setMessage(resourceId)
169
.setPositiveButton(com.android.internal.R.string.yes,
new
DialogInterface.OnClickListener() {
170
public
void
onClick(DialogInterface dialog,
int
which) {
171
beginShutdownSequence(context);
172
}
173
})
174
.setNegativeButton(com.android.internal.R.string.no,
null
)
175
.create();
176
closer.dialog = sConfirmDialog;
177
sConfirmDialog.setOnDismissListener(closer);
178
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
179
sConfirmDialog.show();
180
}
else
{
181
beginShutdownSequence(context);
182
}
183
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private
static
void
beginShutdownSequence(Context context) {
...
276
if
(PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
...
301
}
else
{
302
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
303
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
304
pd.setIndeterminate(
true
);
305
}
306
pd.setCancelable(
false
);
307
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
308
309
pd.show();
310
...
341
// start the thread that initiates shutdown
342
sInstance.mHandler =
new
Handler() {
343
};
344
sInstance.start();
345
}
|
1
|
private
static
final
ShutdownThread sInstance =
new
ShutdownThread();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
358
public
void
run() {
359
...
384
Log.i(TAG,
"Sending shutdown broadcast..."
);
385
386
// First send the high-level shut down broadcast.
387
mActionDone =
false
;
388
Intent intent =
new
Intent(Intent.ACTION_SHUTDOWN);
389
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
390
mContext.sendOrderedBroadcastAsUser(intent,
391
UserHandle.ALL,
null
, br, mHandler,
0
,
null
,
null
);
392
393
...
497
rebootOrShutdown(mContext, mReboot, mReason);
498
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
static
void
rebootOrShutdown(
final
Context context,
boolean
reboot, String reason) {
644
if
(reboot) {
645
Log.i(TAG,
"Rebooting, reason: "
+ reason);
646
PowerManagerService.lowLevelReboot(reason);
647
Log.e(TAG,
"Reboot failed, will attempt shutdown instead"
);
648
reason =
null
;
649
}
else
if
(SHUTDOWN_VIBRATE_MS >
0
&& context !=
null
) {
...
664
}
665
666
// Shutdown power
667
Log.i(TAG,
"Performing low-level shutdown..."
);
668
PowerManagerService.lowLevelShutdown(reason);
669
}
|
最后又跑到PowerManagerService 的lowLevelShutdown()。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
1
2
3
4
5
6
|
public
static
void
lowLevelShutdown(String reason) {
2787
if
(reason ==
null
) {
2788
reason =
""
;
2789
}
2790
SystemProperties.set(
"sys.powerctl"
,
"shutdown,"
+ reason);
2791
}
|
很多人疑惑SystemProperties.set("sys.powerctl", "shutdown,"+ reason);又跑到了哪里?
首先sys.powerctl 在init.rc中有配置,它是一类特殊的property,可以认为是command。
它对应的处理函数定义在/system/core/init/builtins.cpp中。
1
2
3
4
|
{
"mount"
, {
3
, kMax, do_mount}},
{
"umount"
, {
1
,
1
, do_umount}},
{
"powerctl"
, {
1
,
1
, do_powerctl}},
{
"restart"
, {
1
,
1
, do_restart}},
|
所以接着会调到do_powerctl(),注意传下来的参数有shutdown,所以cmd是ANDROID_RB_POWEROFF,reboot_target是上面传的reason字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
static
int
do_powerctl(
const
std::vector
696
const
char
* command = args[
1
].c_str();
697
int
len =
0
;
698
unsigned
int
cmd =
0
;
699
const
char
*reboot_target =
""
;
700
void
(*callback_on_ro_remount)(
const
struct mntent*) = NULL;
701
702
if
(strncmp(command,
"shutdown"
,
8
) ==
0
) {
703
cmd = ANDROID_RB_POWEROFF;
704
len =
8
;
705
}
else
if
(strncmp(command,
"reboot"
,
6
) ==
0
) {
706
cmd = ANDROID_RB_RESTART2;
707
len =
6
;
708
}
...
763
return
android_reboot_with_callback(cmd,
0
, reboot_target,
764
callback_on_ro_remount);
765
}
|
函数和cmd值在/system/core/include/cutils/android_reboot.h 中有声明
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
|
int
android_reboot_with_callback(
213
int
cmd,
int
flags __unused,
const
char
*arg,
214
void
(*cb_on_remount)(
const
struct mntent*))
215
{
216
int
ret;
217
remount_ro(cb_on_remount);
218
switch
(cmd) {
219
case
ANDROID_RB_RESTART:
220
ret = reboot(RB_AUTOBOOT);
221
break
;
222
223
case
ANDROID_RB_POWEROFF:
224
ret = reboot(RB_POWER_OFF);
225
break
;
226
227
case
ANDROID_RB_RESTART2:
228
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
229
LINUX_REBOOT_CMD_RESTART2, arg);
230
break
;
231
232
default
:
233
ret = -
1
;
234
}
235
236
return
ret;
237
}
|
所以最后又调用到了reboot()函数,传入的参数是RB_POWER_OFF。
reboot()定义在/bionic/libc/bionic/reboot.cpp
1
2
3
4
5
6
7
8
|
#include
30
#include 31
32
extern
"C"
int
__reboot(
int
,
int
,
int
,
void
*);
33
34
int
reboot(
int
mode) {
35
return
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
36
}
|
又调用到了__reboot(),在__reboot.s中用汇编实现,不同的cpu架构在不同目录下,比如32为arm平台。
bionic/libc/arch-arm/syscalls/__reboot.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
3
#include <
private
bionic_asm.h=
""
>
4
5
ENTRY(__reboot)
6
mov ip, r7
7
.cfi_register r7, ip
8
ldr r7, =__NR_reboot
9
swi #
0
10
mov r7, ip
11
.cfi_restore r7
12
cmn r0, #(MAX_ERRNO +
1
)
13
bxls lr
14
neg r0, r0
15
b __set_errno_internal
16
END(__reboot)
|
这里又将__reboot的实现映射到了__NR_reboot。
bionic/libc/kernel/uapi/asm-generic/unistd.h
1
2
3
4
5
|
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
219
#define __NR_rt_sigreturn
139
220
#define __NR_setpriority
140
221
#define __NR_getpriority
141
222
#define __NR_reboot
142
|
__NR_reboot对应的系统调用声明在Unistd.h中
linux-4.10/include/uapi/asm-generic/unistd.h
1
2
3
4
5
6
7
8
9
10
|
#define __NR_rt_sigreturn
139
419
__SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn)
420
421
/* kernel/sys.c */
422
#define __NR_setpriority
140
423
__SYSCALL(__NR_setpriority, sys_setpriority)
424
#define __NR_getpriority
141
425
__SYSCALL(__NR_getpriority, sys_getpriority)
426
#define __NR_reboot
142
427
__SYSCALL(__NR_reboot, sys_reboot)
|
对应的函数是sys_reboot(),声明在linux-4.10\include\linux\syscalls.h
1
2
3
4
5
6
7
|
struct timespec __user *interval);
311
asmlinkage
long
sys_setpriority(
int
which,
int
who,
int
niceval);
312
asmlinkage
long
sys_getpriority(
int
which,
int
who);
313
314
asmlinkage
long
sys_shutdown(
int
,
int
);
315
asmlinkage
long
sys_reboot(
int
magic1,
int
magic2, unsigned
int
cmd,
316
void
__user *arg);
|
sys_reboot()的定义在linux-4.10.3\kernel\reboot.c。
/*
add by puo补充一点,这里sys_reboot的实现其实就是 SYSCALL_DEFINE4(reboot,
int
, magic1,
int
, magic2, unsigned
int
, cmd,
void
__user *, arg); 自己看SYSCALL_DEFINE4的宏定义实现就知道了。
*/
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
|
280
SYSCALL_DEFINE4(reboot,
int
, magic1,
int
, magic2, unsigned
int
, cmd,
281
void
__user *, arg)
282
{
283
...
314
mutex_lock(&reboot_mutex);
315
switch
(cmd) {
316
case
LINUX_REBOOT_CMD_RESTART:
317
kernel_restart(NULL);
318
break
;
319
320
case
LINUX_REBOOT_CMD_CAD_ON:
321
C_A_D =
1
;
322
break
;
323
324
case
LINUX_REBOOT_CMD_CAD_OFF:
325
C_A_D =
0
;
326
break
;
327
328
case
LINUX_REBOOT_CMD_HALT:
329
kernel_halt();
330
do_exit(
0
);
331
panic(
"cannot halt"
);
332
333
case
LINUX_REBOOT_CMD_POWER_OFF:
334
kernel_power_off();
335
do_exit(
0
);
336
break
;
...
361
default
:
362
ret = -EINVAL;
363
break
;
364
}
365
mutex_unlock(&reboot_mutex);
366
return
ret;
367
}
|
1
2
3
4
5
6
7
|
/* use glibc names as well */
37
38
#define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART
39
#define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT
40
#define RB_ENABLE_CAD LINUX_REBOOT_CMD_CAD_ON
41
#define RB_DISABLE_CAD LINUX_REBOOT_CMD_CAD_OFF
42
#define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
253 * kernel_power_off - power_off the system
254 *
255 * Shutdown everything and perform a clean system power_off.
256 */
257
void
kernel_power_off(
void
)
258
{
259
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
260
if
(pm_power_off_prepare)
261
pm_power_off_prepare();
262
migrate_to_reboot_cpu();
263
syscore_shutdown();
264
pr_emerg(
"Power down\n"
);
265
kmsg_dump(KMSG_DUMP_POWEROFF);
266
machine_power_off();
267
}
268
EXPORT_SYMBOL_GPL(kernel_power_off);
|
kernel/kernel/exit.c
do_exit
exit_notify(tsk, group_dead);
forget_original_parent(tsk, &dead);
reaper = find_child_reaper(father);
panic("Attempted to kill init! exitcode=0x%08x\n",father->signal->group_exit_code ?: father->exit_code);