Android 关机流程 从kernel到framework

Android6.0关机流程

Android系统关机有如下方式:1.定时关机、2.命令行输入reboot重启、3.长按电源键出现关机对画框等,本文以长按电源键为例来分析基于Android 6.0的高通源码。

长按电源键会启动Android系统的按键消息处理机制。每个activity具有一个phonewindow对象,每个phonewindow对象具有一个DecorView对象,每个DecorVier又被设置到一个ViewRoot对象中(如图一).每个activity创建的时候会通过ViewRoot注册一个inputmanager,然后创建一组对应的事件获取和分发的线程:InputReader 和InputDispatch。当没有事件发生的时候Reader处于轮训状态,Dispatch处于休眠状态。当Reader获取到event的时候,Reader将会唤醒Dispatch,让其分发这个event到当前focus
的activity,然在activity的dispatchkeyEvent中进行处理。我们将关机流程分为PowerKey事件获取、Java层的关机流程分析、Native层的关机流程分析和kernel层的关机流程分析四部分来分析。

Kernel层事件处理

kernel层的事件上传,是指PowerKey按下的事件传递到Framework之前的流程,然后Framework层进行关机的相关操作。

1.pmic8xxx-pwrkey.c-input_report_key

static irqreturn_t pwrkey_press_irq(int irq, void*_pwrkey)

{

struct pmic8xxx_pwrkey*pwrkey = _pwrkey;

if (pwrkey->press== true) {

pwrkey->press = false;

return IRQ_HANDLED;

} else {

pwrkey->press = true;

}

input_report_key(pwrkey->pwr,KEY_POWER, 1);

//PowerKey按下事件,进入input.c中的inputEvent函数

input_sync(pwrkey->pwr);

return IRQ_HANDLED;

}

检测powerkey的按下,开始处理

2.input.c-inputEvent

void input_event(struct input_dev *dev,

unsigned int type, unsigned int code, intvalue)

{

unsigned long flags;

if (is_event_supported(type,dev->evbit, EV_MAX)) {

spin_lock_irqsave(&dev->event_lock,flags);

input_handle_event(dev, type, code, value);

//进入input_handle_event,上下文进行中断锁的操作

spin_unlock_irqrestore(&dev->event_lock,flags);

}

}

进入input_handle_event,然后注册该事件到device,交给Android的消息处理服务进行处理,其中我们开始说的reader读取到该事件。

static void input_handle_event(struct input_dev *dev,

unsigned int type, unsigned int code,int value)

{

int disposition;

disposition = input_get_disposition(dev,type, code, value);

if ((disposition &INPUT_PASS_TO_DEVICE) && dev->event)

dev->event(dev, type, code,value);

//event 处理

if (!dev->vals)

return;

if (disposition &INPUT_PASS_TO_HANDLERS) {

struct input_value *v;

if (disposition &INPUT_SLOT) {

v =&dev->vals[dev->num_vals++];

v->type = EV_ABS;

v->code = ABS_MT_SLOT;

v->value =dev->mt->slot;

}

v = &dev->vals[dev->num_vals++];

v->type = type;

v->code = code;

v->value = value;

}

if (disposition &INPUT_FLUSH) {

if (dev->num_vals >= 2)

input_pass_values(dev,dev->vals, dev->num_vals);

dev->num_vals = 0;

} else if (dev->num_vals >=dev->max_vals - 2) {

dev->vals[dev->num_vals++]= input_value_sync;

input_pass_values(dev,dev->vals, dev->num_vals);

dev->num_vals = 0;

}

}

inputread在读取到有keyboard事件上报后,会调用到keydispatch的notifykey,紧接着调用interceptKeyBeforeQueueing,最终返回到phonewindowmanager. interceptKeyBeforeQueueing(kernel相关的内容待更新、消息服务内容待更新)

kernel层工作 总结:

1.powerkey事件存储

2.消息服务读取powerkey事件

3.消息服务将keyevent进行传递

Java层关机

3.Phonewindowmanager.java-interceptKeyBeforeQueueing

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {

final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;

final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;

final boolean canceled = event.isCanceled();

final int keyCode = event.getKeyCode();

// Handle special keys.

switch (keyCode) {

case KeyEvent.KEYCODE_POWER: {

result &=~ACTION_PASS_TO_USER;

isWakeKey = false; // wake-upwill be handled separately

if (down) {

interceptPowerKeyDown(event, interactive);

//进入interceptPowerKeyDown,上文处理各种消息来源,以及特殊按键功能

} else {

interceptPowerKeyUp(event,interactive, canceled);

}

break;

}

if (useHapticFeedback) {

performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);

}

if (isWakeKey) {

wakeUp(event.getEventTime(),mAllowTheaterModeWakeFromKey,android.policy:KEY");

}

return result;

}

该方法主要完成各种按键消息的处理,包括按下电源键与音量下键进行抓屏、来电时按电源键启动静音模式等。在该方法根据条件处理按下电源键后的特殊任务,然后调用interceptPowerKeyDown进行进一步的处理。

4. interceptPowerKeyDown方法

private voidinterceptPowerKeyDown(KeyEvent event, boolean interactive) {

    // 持有锁

if(!mPowerKeyWakeLock.isHeld()) {

mPowerKeyWakeLock.acquire();

}

// 多次按下,只处理一次

if(mPowerKeyPressCounter != 0) {

mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);

}

// Detect userpressing the power button in panic when an application has

// taken overthe whole screen.

boolean panic =mImmersiveModeConfirmation.onPowerKeyDown(interactive,

SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));

if (panic) {

mHandler.post(mHiddenNavPanic);

}

// Latch power key stateto detect screenshot chord.

if (interactive&& !mScreenshotChordPowerKeyTriggered

&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {

mScreenshotChordPowerKeyTriggered = true;

mScreenshotChordPowerKeyTime = event.getDownTime();

interceptScreenshotChord();

}

//来电时powerKey,停止响铃

TelecomManagertelecomManager = getTelecommService();

boolean hungUp = false;

if (telecomManager !=null) {

if (telecomManager.isRinging()) {

// Pressing Power while there‘s a ringing incoming

// call should silence the ringer.

telecomManager.silenceRinger();

} else if ((mIncallPowerBehavior

&Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0

&& telecomManager.isInCall() &&interactive) {

// Otherwise, if "Power button ends call" isenabled,

// the Power button will hang up any current active call.

hungUp = telecomManager.endCall();

}

}

mPowerKeyHandled =hungUp || mScreenshotChordVolumeDownKeyTriggered

||mScreenshotChordVolumeUpKeyTriggered;

if (!mPowerKeyHandled) {

if (interactive) {

// When interactive, we‘re already awake.

// Wait for a long press or for the buttonto be released to decide what to do.

if(hasLongPressOnPowerBehavior()) {

Message msg =mHandler.obtainMessage(MSG_POWER_LONG_PRESS);

msg.setAsynchronous(true);

mHandler.sendMessageDelayed(msg,

ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

}

} else{

wakeUpFromPowerKey(event.getDownTime());

if(mSupportLongPressPowerWhenNonInteractive &&hasLongPressOnPowerBehavior()) {

Message msg =mHandler.obtainMessage(MSG_POWER_LONG_PRESS);

//PowerKey 的事件传递到handler进行处理

msg.setAsynchronous(true);

mHandler.sendMessageDelayed(msg,

ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());

mBeganFromNonInteractive = true;

}else {

final int maxCount = getMaxMultiPressPowerCount();

if (maxCount <= 1) {

mPowerKeyHandled = true;

} else {

mBeganFromNonInteractive = true;

}

}

}

}

}

该方法中主要处理电源键按下后的一下情况,比如电源键松开之前一直亮屏、取消多次按键时的超时消息、当设置后按下电源键后结束响铃,挂断电话等。如果电源键按下的处理情况还没有处理时,解会分长按、短按和多次按来进行处理,此处我们只关心长按电源键,因此会进入mHandler.sendMessageDelayed的方法,而mHandler的类型是PolicyHandler,在初始化PhoneWindowManager时对其进行了赋值。调用sendMessageDelayed方法发送消息,最终会在PolicyHandler的handleMessage进行处理该消息。

5.PolicyHandler 函数

private class PolicyHandler extends Handler {

@Override

public voidhandleMessage(Message msg) {

switch(msg.what) {

case MSG_POWER_LONG_PRESS:

powerLongPress();

//处理函数进入powerLongPress,上下文中处理不同按键的逻辑

break;

}

}

}

该函数很简单,就是处理各种消息,这里大部分都是键盘按键消息,我们关心的长按电源键的消息处理。由源码可知,当PolicyHandler接收到MSG_POWER_LONG_PRESS消息之后,将会进入powerLongPress方法做进一步的处理。

6.powerLongPress 函数

private void powerLongPress() {

final intbehavior = getResolvedLongPressOnPowerBehavior();

switch(behavior) {

case LONG_PRESS_POWER_GLOBAL_ACTIONS:

mPowerKeyHandled = true;

if(!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {

performAuditoryFeedbackForAccessibilityIfNeed();

}

showGlobalActionsInternal();

//进入showGlobalActionsInternal,上下文针对不同的powerkey事件进行处理

break;

caseLONG_PRESS_POWER_SHUT_OFF:

caseLONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:

mPowerKeyHandled = true;

performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS,false);

sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);

mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);

break;

}

}

在getResolvedLongPressOnPowerBehavior方法中,如果是工厂模式测试时,返回LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM,否则返回mLongPressOnPowerBehavior成员变量,而该成员变量是在PhoneWindowManager初始化时进行设置

mLongPressOnPowerBehavior =mContext.getResources().getInteger(

com.android.internal.R.integer.config_longPressOnPowerBehavior);

由mLongPressOnPowerBehavior成员变量初始化可知,getResolvedLongPressOnPowerBehavior方法中最终返回的是config_longPressOnPowerBehavior的值,而该值是在/framework/base/core/res/values/config.xml中进行配置的,在该配置文件中将config_longPressOnPowerBehavior属性值配为1代表是全局的动作;0代表不做任何动作;2表示确认后关机;3表示不确认,直接关机,这就是LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM的值,在工厂模式测试时,不需要确认直接关机。此处我们将会进入全局动作中,即会进入showGlobalActionsInternal方法,进行关机对话框的显示。

7. showGlobalActionsInternal函数

void showGlobalActionsInternal() {

sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);

if(mGlobalActions == null) {

mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);

}

final booleankeyguardShowing = isKeyguardShowingAndNotOccluded();

mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());

//进入showdialog函数,显示关机对话框

if(keyguardShowing) {

// since it took two seconds of long press tobring this up,

        // poke the wake lock so they have sometime to see the dialog.

        mPowerManager.userActivity(SystemClock.uptimeMillis(),false);

}

}

该函数中首先调用sendCloseSystemWindows函数,发送由于全局关机动作的原因,最终会调用ActivityManagerService类的closeSystemDialogs函数关闭其他的系统对话框。利用单例模式创建GlobalActions对象,并保存到其成员变量mGlobalActions中,最终会调用GlobalActions的showDialog方法进行显示关机对话框。

8.GlobalActions.java-showdialog 函数

public void showDialog(

boolean keyguardShowing, boolean isDeviceProvisioned) {

mKeyguardShowing =keyguardShowing;

mDeviceProvisioned =isDeviceProvisioned;

if (mDialog != null) {

mDialog.dismiss();

mDialog = null;

// Show delayed, so that the dismiss of the previousdialog completes

mHandler.sendEmptyMessage(MESSAGE_SHOW);

} else {

handleShow();

//进入handleshow,上文处理keyguard是否显示,显示则推迟处理。

}

}

该方法中主要是判断是否会显示keyguard,如果之前已经有全局对话框显示,则发生延迟消息,以便其显示完后最终关闭,如果是第一次启动全局对话框,则会进入handleShow方法中进行处理。

9.handleshow 函数

private void handleShow() {

awakenIfNecessary();

mDialog = createDialog();//创建对话框,并相应点击事件

prepareDialog();//更新各个模式如静音、飞行

// If we only have 1 item and it‘s a simple press action,just do this action.

if (mAdapter.getCount() == 1

&& mAdapter.getItem(0)instanceof SinglePressAction

&&!(mAdapter.getItem(0) instanceof LongPressAction)) {

((SinglePressAction)mAdapter.getItem(0)).onPress();

} else {

WindowManager.LayoutParams attrs =mDialog.getWindow().getAttributes();

attrs.setTitle("GlobalActions");

mDialog.getWindow().setAttributes(attrs);

mDialog.show();

mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);

}

}

在该方法中,首先调用awakenIfNecessary方法进行了屏幕唤醒,然后调用createDialog()创建全局关机对话框,当对话框创建完成后,调用prepareDialog方法进行keyguard窗口风格样式的设置,最后会进行我们全局关机对匡样式进行判断,如果是只有一个item,则会通过onPress方法进行处理,否则会进行系统UI显示的设置。此处我们进入creatDialog方法进行创建全局关机对话框。

10.PowerAction 点击函数

private final class PowerAction extends SinglePressAction

implementsLongPressAction {

public void onPress() {

// shutdownby making sure radio and power are handled accordingly.

mWindowManagerFuncs.shutdown(false /* confirm */);

//mWindowManagerFuncs实际上是windowmanagerservice的对象,进入shutdown

}

}

如果长按会进入过PowerAction的onLongPress函数,最后会进入到mWindowManagerFuncs.rebootSafeMode函数中;如果是短按关机Aciton会进入到PowerAction的onPress函数,最后进入到mWindowManagerFuncs.shutDown方法进行处理。在PhoneWindowManager的初始化过程可知,mWindowManagerFuncs被赋值为WindowManagerService,因此会调用WindowManagerService的shutdown方法。

11.windowmanagerservice.java-shutdown 函数

public void shutdown(boolean confirm) {

ShutdownThread.shutdown(mContext,confirm);

//调用shutdownThread的shutdown方法

}

12.shutdownThread.java-shutdown 方法

public static void shutdown(final Context context,boolean confirm) {

mReboot =false;

mRebootSafeMode = false;

Log.d(TAG,"!!! Request to shutdown !!!");

if (mSpew){

StackTraceElement[] stack = new Throwable().getStackTrace();

for(StackTraceElement element : stack)

{

Log.d(TAG, "    |----" +element.toString());

}

}

if(SystemProperties.getBoolean("ro.monkey", false)) {

Log.d(TAG, "Cannot request to shutdown when Monkey is running,returning.");

return;

}

shutdownInner(context, confirm);

//进一步调用shutdowninner,上文判断,若在monkey,不进行关机操作

}

在该方法中只是初始化了一些参数,最后由shutdownInner方法进行处理,参数confirm来设置是否要弹出关机对话框。

13.shutdowninner 方法

static void shutdownInner(final Context context, booleanconfirm) {

if(confirm) {

} else {

beginShutdownSequence(context);

//开始进行关机准备,进入beginShutdownSequence

}

}

在该方法中,会通过传进来的confirm参数来判断是否要显示关机对话框,如果显示会设置关键对话框,如果不显示直接关机,无论显示与否,最后会进入到beginShutdownSequence方法,做进一步的关机处理。

14.beginshutdownsequence 函数

private static void beginShutdownSequence(Contextcontext) {

// Throw up a system dialog to indicate thedevice is rebooting / shutting down.

ProgressDialog pd = new ProgressDialog(context);

if(PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {

mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();

if(mRebootUpdate) {

pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));

pd.setMessage(context.getText(

com.android.internal.R.string.reboot_to_update_prepare));

pd.setMax(100);

pd.setProgressNumberFormat(null);

pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

pd.setProgress(0);

pd.setIndeterminate(false);

} else{

//Factory reset path. Set the dialog message accordingly.

pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));

pd.setMessage(context.getText(

com.android.internal.R.string.reboot_to_reset_message));

pd.setIndeterminate(true);

}

} else {

pd.setTitle(context.getText(com.android.internal.R.string.power_off));

pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));

pd.setIndeterminate(true);

}

pd.setCancelable(false);

pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

// startthe thread that initiates shutdown

sInstance.mHandler = new Handler() {

};

beginAnimationTime = 0;

booleanmShutOffAnimation = configShutdownAnimation(context);

intscreenTurnOffTime = getScreenTurnOffTime(context);

synchronized (mEnableAnimatingSync) {

if(mEnableAnimating) {

if(mShutOffAnimation) {

Log.d(TAG, "mIBootAnim.isCustBootAnim() is true");

bootanimCust();//播放动画

}else {

pd.show();

sInstance.mProgressDialog = pd;

}

sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime);

}

}

if(sInstance.getState() != Thread.State.NEW || sInstance.isAlive()) {

} else {

sInstance.start();//进入线程run方法

}

}

该方法主要初始化一些关机操作,比如获取audio,停止启动应用程序播放music等,最后调用ShutdownThread的start函数来启动关机线程,进入到ShutdownThread线程的run方法中。

15.run 函数

public void run() {

BroadcastReceiver br = new BroadcastReceiver() {

@Overridepublic void onReceive(Context context, Intent intent) {

// We don‘tallow apps to cancel this, so ignore the result.

actionDone();

}

};

/*

* Write asystem property in case the system_server reboots before we

* get tothe actual hardware restart. If that happens, we‘ll retry at

* thebeginning of the SystemServer startup.

*/

{

Stringreason = (mReboot ? "1" : "0") + (mRebootReason != null ?mRebootReason : "");

SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);

//记录关机原因

}

/*

* If weare rebooting into safe mode, write a system property

*indicating so.

*/

if(mRebootSafeMode) {

SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");

}

Log.i(TAG,"Sending shutdown broadcast...");

// Firstsend the high-level shut down broadcast.

mActionDone= false;

Intentintent = new Intent(Intent.ACTION_SHUTDOWN);

intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

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 timedout");

break;

}

try{

mActionDoneSync.wait(delay);

}catch (InterruptedException e) {

}

}

}

Log.i(TAG,"Shutting down activity manager...");

 //关闭activitymanager

finalIActivityManager am =

ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));

if (am !=null) {

try {

am.shutdown(MAX_BROADCAST_TIME);

} catch(RemoteException e) {

}

}

Log.i(TAG,"Shutting down package manager...");

//关闭packagemanager

finalPackageManagerService pm = (PackageManagerService)

ServiceManager.getService("package");

if (pm !=null) {

pm.shutdown();

}

String shutDownFile = null;

//showShutdownAnimation() is called from here to sync

//music andanimation properly

if(checkAnimationFileExist()) {

lockDevice();

showShutdownAnimation();

if(!isSilentMode()

&& (shutDownFile = getShutdownMusicFilePath()) != null) {

isShutdownMusicPlaying= true;

shutdownMusicHandler.obtainMessage(0, shutDownFile).sendToTarget();

}

}

Log.i(TAG,"wait for shutdown music");

final longendTimeForMusic = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;

synchronized (mActionDoneSync) {

while(isShutdownMusicPlaying) {

long delay = endTimeForMusic - SystemClock.elapsedRealtime();

if(delay <= 0) {

Log.w(TAG, "play shutdown music timeout!");

break;

}

try{

mActionDoneSync.wait(delay);

}catch (InterruptedException e) {

}

}

if(!isShutdownMusicPlaying) {

Log.i(TAG, "play shutdown music complete.");

}

}

//关闭通信相关内容

// Shutdownradios.

shutdownRadios(MAX_RADIO_WAIT_TIME);

// ShutdownMountService to ensure media is in a safe state

IMountShutdownObserver observer = newIMountShutdownObserver.Stub() {

public void onShutDownComplete(int statusCode) throwsRemoteException {

Log.w(TAG, "Result code " + statusCode + " fromMountService.shutdown");

actionDone();

}

};

Log.i(TAG,"Shutting down MountService");

//关闭挂载服务

// Setinitial variables and time out time.

mActionDone= false;

final longendShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;

synchronized (mActionDoneSync) {

try {

final IMountService mount = IMountService.Stub.asInterface(

ServiceManager.checkService("mount"));

if(mount != null) {

mount.shutdown(observer);

}else {

Log.w(TAG, "MountService unavailable for shutdown");

}

} catch(Exception e) {

Log.e(TAG, "Exception during MountService shutdown", e);

}

while(!mActionDone) {

long delay = endShutTime - SystemClock.elapsedRealtime();

if(delay <= 0) {

Log.w(TAG, "Shutdown wait timed out");

break;

}

try{

mActionDoneSync.wait(delay);

}catch (InterruptedException e) {

}

}

}

//进入rebootorshutdown函数

rebootOrShutdown(mReboot, mRebootReason);

}

该函数中主要完成创建接收关机的广播,设置关机原因,关闭ActivityManagerService、PackagerManagerService、MountServie等,最后进入rebootOrshutDown进行关机操作。

16.rebootorshutdown 函数

public staticvoid rebootOrShutdown(boolean reboot, String reason) {

deviceRebootOrShutdown(reboot, reason);

//检查厂商的关机处理

if (reboot){

Log.i(TAG, "Rebooting, reason: " + reason);

PowerManagerService.lowLevelReboot(reason);

Log.e(TAG, "Reboot failed, will attempt shutdown instead");

} else if(SHUTDOWN_VIBRATE_MS > 0) {

//vibrate before shutting down

//关机震动

Vibrator vibrator = new SystemVibrator();

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) {

}

}

// Shutdownpower

Log.i(TAG,"Performing low-level shutdown...");

PowerManagerService.lowLevelShutdown();

//进入lowlevelshutdown函数

}

在该方法中,首先调用deviceRebootOrShutdown方法来查找OEM关机相关类,然后判断是否是重启,如果重启PowerManagerService.lowLevelReboot方法进行重启;否则如果是关机前有震动,则会创建Vibrator对象,并调用其vibrate方法执行震动操作,最后进入到PowerManagerService.lowLevelShutdown方法中执行关机操作。

17.powermanagerservice.java- lowLevelShutdown 函数

public static void lowLevelShutdown() {

SystemProperties.set("sys.powerctl", "shutdown");

//设置关机属性值,进入systemproperties.set函数

}

在该方法中调用,SystemProperties.set方法,修改sys.powerctl的属性值为shutdown

18. SystemProperties.java-set 函数

public static void set(String key, String val) {

//判断传入值的合法性

if(key.length() > PROP_NAME_MAX) {

throw newIllegalArgumentException("key.length > " + PROP_NAME_MAX);

}

if (val !=null && val.length() > PROP_VALUE_MAX) {

thrownew IllegalArgumentException("val.length > " +

PROP_VALUE_MAX);

}

native_set(key, val);

//调用底层native_set函数

}

该方法比较简单,只是判断了一下key和value的长度后,就进入本地方法native_set中。

Java层工作总结:

1.PhoneWindowManger负责接收由InputManagerService的发放的按键信息,本例中将会获取长

按Power键信息。

2.PolicyHandler发送长按Power键信息,并在其handleMessage方法中做相应的处理。

3.PhoneWindowManger的powerLongPress处理长按Power键的不同情况,1代表是全局的动作;

0代表不做任动作;2表示确认后关机;3表示不确认,直接关机。

4.GlobalActionsDialog调用createDialog创建关键对话框,将关机选择、重启选择、飞行模

式选择以封装的Action对象添加在适配器列表中。封装完成Action后就是创建关机对话框,采用MyAdapter适配器保存这些匹配。

5.WindowManagerService的shutDown方法,最终会进入到shutDownThread线程的rebootOrs

hutDown方法成关机操作。

6.最后进入到PowerManagerServie的lowLevelShutdown方法,进入到SystemProperties.se

t方法,将“sys.powerctl”属性设置为“shutdown”最后进入native层完成关机操作。

Native层关机

19.android_os_properties_set.cpp-native_set 函数

static void SystemProperties_set(JNIEnv *env, jobjectclazz,

jstringkeyJ, jstring valJ)

{

int err;

const char*key;

const char*val;

if (keyJ ==NULL) {

jniThrowNullPointerException(env, "key must not be null.");

return ;

}

key = env->GetStringUTFChars(keyJ, NULL);

if (valJ ==NULL) {

val ="";       /* NULL pointer notallowed here */

} else {

val =env->GetStringUTFChars(valJ, NULL);

}

err = property_set(key, val);

//调用perperty_set方法,进入native关机

env->ReleaseStringUTFChars(keyJ, key);

if (valJ !=NULL) {

env->ReleaseStringUTFChars(valJ, val);

}

if (err < 0){

jniThrowException(env, "java/lang/RuntimeException",

"failed to set system property");

}

}

20.properties_set.cpp-property_set 函数

int property_set(const char *key, const char *value)

{

return__system_property_set(key, value);

//进一步调用 __system_property_set函数

}

21.system_properties.cpp-__system_property_set 函数

int __system_property_set(constchar *key, const char *value)

{

if (key == 0) return -1;

if (value == 0) value = "";

if (strlen(key) >= PROP_NAME_MAX) return-1;

if (strlen(value) >= PROP_VALUE_MAX)return -1;

prop_msg msg;

//将关机属性的name,value,size 放入到msg中

memset(&msg, 0, sizeof msg);

msg.cmd = PROP_MSG_SETPROP;

strlcpy(msg.name, key, sizeof msg.name);

strlcpy(msg.value, value, sizeofmsg.value);

const int err =send_prop_msg(&msg);

//将msg发送到服务端,property的服务进程在init.c中运行

//触发 关机属性值的设定 调用dopwrctrl

if (err < 0) {

return err;

}

return 0;

}

22.init.rc

on property:sys.powerctl=*

//表示当sys.powerctl被设置成任意值时触发下面的动作

powerctl${sys.powerctl}

init进程是Android系统的第一个进程,是由Linux内核启动。init进程主要作用是两个:一个是解析init.rc以及init{hardware}.rc等rc文件;

onproperty:sys.powerctl=*

powerctl${sys.powerctl}

在init.rc文件主要由以on开头和service开头,分别代表动作和服务;init进程会调用init_parser.c的parse_config函数来解析这些rc文件,最终会生成动作列表和服务列表,动作列表的定义在keywords.h中

int do_powerctl(int nargs, char**args);

#endif

……

KEYWORD(powerctl,    COMMAND, 1,do_powerctl)

……

#ifdef __MAKE_KEYWORD_ENUM__

KEYWORD_COUNT,

};

Init进程通过drain_action_queue函数来解析动作列表和服务列表;通过设置init.rc中动作参数从而执行动作列表中对应的函数。另一个是初始化系统属性,init进程可以通过property_init函数初始化系统属性,并通过property_set函数来设置系统属性。并将设置的参数作为参数传入动作列表对应函数的参数中。因此在关机过程Step17中,通过property_set函数,将sys.powerctl属性值设置为“shutdown”,并交个动作列表中对应的函数do_powerctl来处理。

23.bulltins.c –do_powerctl 函数

int do_powerctl(int nargs, char **args)

{

charcommand[PROP_VALUE_MAX];

int res;

int len = 0;

int cmd = 0;

char*reboot_target;

res =expand_props(command, args[1], sizeof(command));

if (res) {

ERROR("powerctl: cannot expand ‘%s‘\n", args[1]);

return-EINVAL;

}

if(strncmp(command, "shutdown", 8) == 0) {

cmd =ANDROID_RB_POWEROFF;

len = 8;

} else if(strncmp(command, "reboot", 6) == 0) {

cmd =ANDROID_RB_RESTART2;

len = 6;

} else {

ERROR("powerctl: unrecognized command ‘%s‘\n", command);

return-EINVAL;

}

if(command[len] == ‘,‘) {

reboot_target = &command[len + 1];

} else if(command[len] == ‘\0‘) {

reboot_target = "";

} else {

ERROR("powerctl: unrecognized reboot target ‘%s‘\n",&command[len]);

return-EINVAL;

}

returnandroid_reboot(cmd, 0, reboot_target);

//进入android_reboot 函数

}

该函数中首先读取Java层设置的该属性的值,我们设置为“shutdown”,所以会将cmd设置为ANDROID_RB_POWEROFF,长度为8;如果为重启,则cmd设置为ANDROID_RB_RESTART2。否则就是显示命令错误。因此我们知道sys.powerctl属性只有两个值,一个是shutdown(关机);一个是reboot(重启)。最后将cmd命令作为参数传入android_reboot函数,做最后的关机操作。

24.Android_reboot.c-android_reboot 函数

int android_reboot(int cmd, int flags UNUSED, char *arg)

{

int ret;

sync();

remount_ro();

switch (cmd) {

caseANDROID_RB_RESTART:

ret =reboot(RB_AUTOBOOT);

break;

caseANDROID_RB_POWEROFF:

ret =reboot(RB_POWER_OFF);

//进入reboot 函数

break;

case ANDROID_RB_RESTART2:

ret =syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

LINUX_REBOOT_CMD_RESTART2, arg);

break;

default:

ret =-1;

}

return ret;

}

该函数首先是做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。

cmd参数中ANDROID_RB_RESTART为普通关机,reason为RB_AUTOBOOT;ANDROID_RB_POWEROFF,无需reason,直接调用reboot进行关机;带参数的特殊重启ANDROID_RB_RESTART2,reason 将为默认值-1。本例中是reboot “shutdown”,则在Step18中传入的cmd参数为ANDROID_RB_POWEROFF,则会进入reboot(RB_POWER_OFF)函数进行处理。这些cmd值是在/bionic/libc/include/sys/Reboot.h中

#define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART

#define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT

#define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON

#define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF

#define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF

而LINUX_REBOOT_CMD_XXXX是在/binoic/libc/kernel/uapi/linux/Reboot.h中:我们直接进入到reboot(RB_POWER_OFF)函数中做关机操作。

#define LINUX_REBOOT_CMD_RESTART0x01234567

/* WARNING: DO NOT EDIT,AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

#define LINUX_REBOOT_CMD_HALT0xCDEF0123

#define LINUX_REBOOT_CMD_CAD_ON0x89ABCDEF

#define LINUX_REBOOT_CMD_CAD_OFF0x00000000

#define LINUX_REBOOT_CMD_POWER_OFF0x4321FEDC

/* WARNING: DO NOT EDIT,AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

#define LINUX_REBOOT_CMD_RESTART20xA1B2C3D4

#defineLINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2

#define LINUX_REBOOT_CMD_KEXEC0x45584543

#endif

25.reboot.cpp – reboot 函数

include <unistd.h>

#include <sys/reboot.h>

extern "C" int __reboot(int, int, int, void*);

int reboot(int mode) {

return__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);

//表明这是外部定义实现的一个C函数 进入kernel层关机

}

该函数中,调用Kernel层的_reboot函数做进一步分析。该函数在/bionic/libc/bionic/Reboot.cpp文件中。另外,在该文件中有extern"C" int __reboot,该表示在外部文件定义的__reboot函数,并且是以C语言方式进行编译连接,因此__reboot函数是c语言函数。

Native层工作总结 :

1.采用property_set函数来设置系统属性的值,通过init进程解析init.rc文件的生成的动作

的列表,最终根据传进去的属性值(“shutdown”),最终调用do_powerctl函数做关机的操作。

2.Android_reboot通cmd命令值做各种操作,cmd参数是在这些cmd值是在/bionic/libc/incl

ude/sys/Reboot.h中

3. 通过reboot中C++函数,最终调用到kernel层_-_reboot函数做kernel层的关机操作。

Kernel层关机

26.__reboot.s-reboot 函数

#include <private/bionic_asm.h>

ENTRY(__reboot)

mov     ip, r7

ldr     r7, =__NR_reboot

swi     #0

mov     r7, ip

cmn     r0, #(MAX_ERRNO + 1)

bxls    lr

neg     r0, r0

b       __set_errno_internal

END(__reboot)

发现__reboot函数最终映射到__NR_reboot,而__NR_reboot在文件/development/ndk/platforms/android-19/arch-mips/include/sys/Linux-syscalls.h中定义。

#define__NR_reboot     (__NR_SYSCALL_BASE + 88)

其被指定了一个固定的偏移量,其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,/Include/asm-generic/unistd.h中可以找到内核对__NR_reboot的syscall函数映射。

/* kernel/sys.c */

#define __NR_setpriority 140

__SYSCALL(__NR_setpriority,sys_setpriority)

#define __NR_getpriority 141

__SYSCALL(__NR_getpriority,sys_getpriority)

#define __NR_reboot 142

__SYSCALL(__NR_reboot, sys_reboot)

由定义可知,__NR_reboot被映射到sys_reboot函数,该函数的定义在/kernel/inlude/linux/syscalls.h中

asmlinkagelong sys_reboot(int magic1, int magic2, unsigned int cmd,void __user *arg);

我们在文件sys.c中没有找到sys_reboot函数,但是发现有这样一个函数SYSCALL_DEFINE4(reboot,int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)参数和sys_reboot函数基本相同,我们/kernel/inlude/linux/syscalls.h文件中有这样的定义:

#define SYSCALL_DEFINE4(name, ...)SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, sname,...)

#define __SYSCALL_DEFINEx(x, name,...) asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));

整合后的结果如下:

#defineSYSCALL_DEFINE4(name, ...)  asmlinkagelong sys##_name(__SC_DECL##4(__VA_ARGS__))

可知就是sys_reboot函数,因此我们直接进入到SYSCALL_DEFINE4函数

27. sys.c-SYSCALL_DEFINE4 函数

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2,unsigned int, cmd,

void __user*, arg)

{

structpid_namespace *pid_ns = task_active_pid_ns(current);

charbuffer[256];

int ret = 0;

/* We only trustthe 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 thecurrent task is in a child

* pid_namespace, the command is handled byreboot_pid_ns() which will

* call do_exit().

*/

ret =reboot_pid_ns(pid_ns, cmd);

if (ret)

return ret;

/* Instead oftrying to make the power_off code look like

* halt when pm_power_off is not set do it theeasy way.

*/

if ((cmd ==LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)

cmd =LINUX_REBOOT_CMD_HALT;

mutex_lock(&reboot_mutex);

switch (cmd) {

caseLINUX_REBOOT_CMD_RESTART:

kernel_restart(NULL);

break;

caseLINUX_REBOOT_CMD_CAD_ON:

C_A_D = 1;

break;

caseLINUX_REBOOT_CMD_CAD_OFF:

C_A_D = 0;

break;

caseLINUX_REBOOT_CMD_HALT:

kernel_halt();

do_exit(0);

panic("cannothalt");

caseLINUX_REBOOT_CMD_POWER_OFF:

kernel_power_off();

// kernel 关闭电源

do_exit(0);

break;

caseLINUX_REBOOT_CMD_RESTART2:

if(strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {

ret =-EFAULT;

break;

}

buffer[sizeof(buffer)- 1] = ‘\0‘;

kernel_restart(buffer);

break;

#ifdef CONFIG_KEXEC

caseLINUX_REBOOT_CMD_KEXEC:

ret =kernel_kexec();

break;

#endif

#ifdef CONFIG_HIBERNATION

caseLINUX_REBOOT_CMD_SW_SUSPEND:

ret =hibernate();

break;

#endif

default:

ret =-EINVAL;

break;

}

mutex_unlock(&reboot_mutex);

return ret;

}

在该函数中,首先检测权限问题,只有超级用户才可以执行重启操作,否则返回权限错误,对应的权限列表在/kernel/include/uapi/linux/Capability.h文件中。CAP_SYS_BOOT的值为22,随后对magicnumber进行了校验。接下来做了一个判断就是如果用户要求关机,而pm_power_off为空,则就把用户的关机命令转化为挂起。pm_power_off的定义位置在/kernel/arch/arm/kernel/Process.c

void (*pm_power_off)(void);

EXPORT_SYMBOL(pm_power_off);

可知pm_power_off为函数指针,而且做了全局操作,整个kernel都可以调用它。最后会调用kernel_power_off函数完成关机操作。

28.kernel_power_off 函数

/*  kernel_power_off- power_off the system

*

*  Shutdown everything and perform a clean systempower_off.

*/

void kernel_power_off(void)

{

kernel_shutdown_prepare(SYSTEM_POWER_OFF);//准备shutdown

if(pm_power_off_prepare)

pm_power_off_prepare();

migrate_to_reboot_cpu();

syscore_shutdown();

printk(KERN_EMERG"Power down.\n");

kmsg_dump(KMSG_DUMP_POWEROFF);

machine_power_off();//machine关闭电源

}

Kernel层工作总结:

1. 调用syscalldefine 关机

2. 存储相关信息并关闭电源

时间: 2024-11-05 14:40:26

Android 关机流程 从kernel到framework的相关文章

android关机流程

关机过程的主要实现在ShutdownThread.java中在关机过程中,主要做了三件事:1.发送关机广播 有的模块可能需要监听手机关机事件,所以在关机时发送关机广播,通知相关模块处理.2.关闭一些主要服务进程 而在关机过程中为了不损坏手机性能,记录当前一些状态,需要将一些模块服务进程先关闭,然后才进行关机3.通过PowerManagerService调用底层进行关机 原文地址:https://www.cnblogs.com/lixiangfu/p/10400278.html

深入解析Android关机

下图详细阐释了Android的关机顺序. 第一步: 按住电源按钮半秒钟(500ms). 第二步: 之后,PhoneWindowManager.java 将捕获长按电源按钮这一事件并调用"interceptKeyBeforeQueueing"方法. 下面是处理长按电源键事件的代码片段 1 /** {@inheritDoc} */ 2 @Override 3 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFl

Android Framework层Power键关机流程(二,关机流程)

二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重新启动,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.以下我们在简单分析一下: showGlobalActionsDialog()-->showDialog()-->handleShow()-->createDialog()-->onPress()-->shutdown() PhoneWindowManager.java void s

android 电池(二):android关机充电流程、充电画面显示【转】

本文转载自:http://blog.csdn.net/xubin341719/article/details/8498580 上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下android关机充电是怎么.充电画面显示是怎么实现的,这个在工作中也比较有用,我们开始做这一块的时候也走了不少的弯路.我记得我们做adnroid2.3的时候,关机状态和充电logo显示是在uboot中做的.应该是有两种做法,回头我再看下uboot中做画面显示那一块是怎么做的,这一节我们重点说系统中的

Android 启动流程简介

Android 启动流程: 1. linux OS Bootloader -> Linux kernel 2. Android/Init Runtime Init Process -> Services -> Zygote/VM -> System Server 3. Framework HomeScreen Server Manager -> Home Luncher 如下图: Android 启动流程: 1. 系统引导 Bootloader 1). 源码 bootable

【Android 系统开发】 Android 系统启动流程简介

Android 系统启动总结 : Android 系统启动分底层 Linux 内核启动 和 应用系统启动; -- 底层系统启动 : 系统上电, bootloader 启动, linux kernel 启动, init 进程启动; -- 应用系统启动 : init 进程启动关键的进程如 Zygote 进程 和 System Server 等系统服务, 之后进入 Home 界面; 一. Android 底层系统启动流程(Bootloader Kernel init) 1. 系统上电 执行 ROM 引

Android启动流程分析(一)

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# Android的启动流程绝大部分人都知道,但是大多数人都是这样描述的: Android启动,首先是启动Bootloader,然后挂载kernel,挂载完kernel之后,会启动android的init进程,init进程会去孵化Zygote,Zygote是android的重要支柱之

进入Recovery之前的关机流程

进入Recovery之前的关机流程 作者:李志强       2016-02-22 欢迎转载,请注明出处 http://www.cnblogs.com/kaios/p/5208523.html Recovery模式和Android的正常模式是独立的,通常在Recovery模式中完成的工作包括,Fota升级,SD卡升级,以及恢复出厂设置这三个功能.这三个功能中Fota升级的升级包通常上由Fota的上层应用来下载的,而存放的位置一般上/cache分区或者/data分区.如果是存放在cache分区,那

Android启动流程分析(十) action的执行和service的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 在前面的文章分析完init.rc的解析以后,我们知道现在action按照init.c的main函数中的秩序,维护了一条qlist的链表,listnode为action_qlist service也维护了一条链表,listnode为service_list. 那么,在android