[Android5.1]关机工作流程

Posted 迷途小书童Eric

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Android5.1]关机工作流程相关的知识,希望对你有一定的参考价值。

这篇文章主要分析一下android5.1系统的关机流程。

当我们长按电源键时,按键消息会传递给PhoneWindowManager中的interceptKeyBeforeQueueing()函数处理。该函数代码如下:

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) 
    
    ......
    
    switch (keyCode) 

    ......

    case KeyEvent.KEYCODE_POWER: 
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) 
                    interceptPowerKeyDown(event, interactive);
                 else 
                    interceptPowerKeyUp(event, interactive, canceled);
                
                break;
            

    ......

    

    ......

这个函数会对一些特殊KeyEvent进行处理,比如KeyEvent.KEYCODE_ENDCALL、KeyEvent.KEYCODE_CAMERA等等,当然也包括电源键按下。

代码中,‘down’用来标识动作是按下还是抬起,定义如下:

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

由于是按下电源键,因此down=1,会进一步调用interceptPowerKeyDown()处理。

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) 
	// Hold a wake lock until the power key is released.
	if (!mPowerKeyWakeLock.isHeld()) 
	    mPowerKeyWakeLock.acquire();
	
	
	// Cancel multi-press detection timeout.
	if (mPowerKeyPressCounter != 0) 
	    mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
	
	
	// Detect user pressing the power button in panic when an application has
	// taken over the whole screen.
	boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
	        event.getDownTime(), isImmersiveMode(mLastSystemUiFlags));
	if (panic) 
	    mHandler.post(mRequestTransientNav);
	
	
	// Latch power key state to detect screenshot chord.
	if (interactive && !mScreenshotChordPowerKeyTriggered
	        && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) 
	    mScreenshotChordPowerKeyTriggered = true;
	    mScreenshotChordPowerKeyTime = event.getDownTime();
	    interceptScreenshotChord();
	
	
	// Stop ringing or end call if configured to do so when power is pressed.
	TelecomManager telecomManager = 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" is enabled,
	        // the Power button will hang up any current active call.
	        hungUp = telecomManager.endCall();
	    
	
	
	// If the power key has still not yet been handled, then detect short
	// press, long press, or multi press and decide what to do.
	mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
	        || mScreenshotChordVolumeUpKeyTriggered;
	if (!mPowerKeyHandled) 
	    if (interactive) 
	        // When interactive, we're already awake.
	        // Wait for a long press or for the button to 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());
	        final int maxCount = getMaxMultiPressPowerCount();
	
	        if (maxCount <= 1) 
	            mPowerKeyHandled = true;
	         else 
	            mBeganFromNonInteractive = true;
	        
	    
	

可以看到,由于有很多情况下电源键都会被按下,比如,来电时按电源键静音或者挂电话、屏幕截图等。该函数会依次执行这些操作。最后,如果还没有执行,则会根据短按或长按来执行不同的操作。

接下来会判断"interactive",该参数表示屏幕是否已经唤醒,如果没有唤醒,则为false,函数会调用wakeUpFromPowerKey唤醒屏幕;如果已唤醒,则为true,程序会使用Handler机制发送一个“MSG_POWER_LONG_PRESS”的消息,消息处理代码如下:

private class PolicyHandler extends Handler 
        @Override
        public void handleMessage(Message msg) 
            switch (msg.what) 
                case MSG_ENABLE_POINTER_LOCATION:
                    enablePointerLocation();
                    break;
                case MSG_DISABLE_POINTER_LOCATION:
                    disablePointerLocation();
                    break;
                
                ......
                
                case MSG_POWER_LONG_PRESS:
                    powerLongPress();
                    break;
            
        
可见,会进一步执行powerLongPress,代码如下:

private void powerLongPress() 
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) 
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) 
                performAuditoryFeedbackForAccessibilityIfNeed();
            
            showGlobalActionsInternal();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:
        case LONG_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;
        
    
该函数会根据behavior值,做出不同的操作。该值是通过调用getResolvedLongPressOnPowerBehavior获取,代码如下:

private int getResolvedLongPressOnPowerBehavior() 
        if (FactoryTest.isLongPressOnPowerOffEnabled()) 
            return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
        
        return mLongPressOnPowerBehavior;
    
由于不是出厂测试,所以会返回mLongPressOnPowerBehavior。mLongPressOnPowerBehavior的值是PhoneWindowManager::init()中赋值的。代码如下:

@Override
public void init(Context context, IWindowManager windowManager,
        WindowManagerFuncs windowManagerFuncs) 
        
        ......
        
        mLongPressOnPowerBehavior = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_longPressOnPowerBehavior);
        
        ......
        
进一步,mLongPressOnPowerBehavior的值是通过配置文件设置的,配置文件路径为:frameworks/base/core/res/res/values/config.xml。代码为:

<!-- Control the behavior when the user long presses the power button.
            0 - Nothing
            1 - Global actions menu
            2 - Power off (with confirmation)
            3 - Power off (without confirmation)
-->
<integer name="config_longPressOnPowerBehavior">1</integer>
可以看到,mLongPressOnPowerBehavior=1。

一步一步往上回到powerLongPress函数,由于各case项的定义如下:

static final int LONG_PRESS_POWER_NOTHING = 0;
static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
因此会执行如下语句:

case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) 
                performAuditoryFeedbackForAccessibilityIfNeed();
            
            showGlobalActionsInternal();
            break;
即进一步执行showGlobalActionsInternal:

void showGlobalActionsInternal() 
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        if (mGlobalActions == null) 
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) 
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
        
    
该函数首先向ActivityManagerService请求关闭所有的窗口,然后调用GlobalActions::showDialog()。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 previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
         else 
            handleShow(); 
        
    
该函数最后调用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);
        
    
首先分析一下creatDialog()函数,由于程序代码比较长,下面分块介绍,代码如下:

private GlobalActionsDialog createDialog() 
    // Simple toggle style if there's no vibrator, otherwise use a tri-state
    
    ......

    mItems = new ArrayList<Action>();
    String[] defaultActions = mContext.getResources().getStringArray(
            com.android.internal.R.array.config_globalActionsList);

    .......
可见,关机对话框中的每个列表项都抽象为一个Action,Action中定义了动作方法。并存放在一个ArrayList<Action>对象mItems中。关机对话框中的列表项有很多种,定义如下:

/* Valid settings for global actions keys.
     * see config.xml config_globalActionList */
    private static final String GLOBAL_ACTION_KEY_POWER = "power";  //关机
    private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";  //飞行模式
    private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";  //bug报告
    private static final String GLOBAL_ACTION_KEY_SILENT = "silent";  //静默模式
    private static final String GLOBAL_ACTION_KEY_USERS = "users";    //列出所有用户,针对一个系统多个用户
    private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";  //打开设置窗口
    private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";  //锁定手机

当然,关机对话框中不可能这些列表项都包括,开发者可以根据产品需求,选择合适的列表项。那如何设置呢?接着往下看代码:

        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());
             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(getBugReportAction());
                
             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 
                Log.e(TAG, "Invalid global action key " + actionKey);
            
            // Add here so we don't add more than one.
            addedKeys.add(actionKey);
        
字符串数组defaultActions[]中保存了要显示的列表项,然后逐个判断,如果存在,则新建一个对应的Action,并加入到mItems中。

现在,接着讲creatDialog()函数剩下的代码:

mAdapter = new MyAdapter();  //创建适配器,用于存放关机对话框要显示的数据

AlertParams params = new AlertParams(mContext);   //
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
/* 创建关机对话框 */
GlobalActionsDialog dialog = new GlobalActionsDialog(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);

dialog.setOnDismissListener(this);

return dialog;

可以看到,定义了一个Adapter,当单击列表项目的某一项时,会调用Adapter中的对应项来响应该事件。

这样关机对话框就创建完成了,再回到上面的handleShow()函数,最终调用mDialog.show(),关机对话框就呈现在屏幕上了。

由于关机对话框中的每个列表项对应一个Action,从creatDialog()函数中可知,关机对应的Action为PowerAction()。代码如下:

private final class PowerAction extends SinglePressAction implements LongPressAction 
        private PowerAction() 
            super(com.android.internal.R.drawable.ic_lock_power_off,  //关机图标
                R.string.global_action_power_off);  //关机字样
        

        @Override
        public boolean onLongPress()   //长按执行的动作
            mWindowManagerFuncs.rebootSafeMode(true);
            return true;
        

        ......
        
        @Override
        public void onPress()   //单击执行的动作  
            // shutdown by making sure radio and power are handled accordingly.  
            mWindowManagerFuncs.shutdown(false);           
        
    
可见,当单击对话框中的"关机"时,会执行WindowManagerFuncs对象mWindowManagerFuncs的成员函数shutdown(),正式进入关机流程。

那mWindowManagerFuncs从哪来的呢?是创建GlobalActino对象时候通过构造函数的参数传进来的。其实mWindowManagerFuncs就是一个WindowManagerService对象,然后在创建PhoneWindowManager时候传进来,进一步传递给GlobalAction。那shutdown()的实现自然是WindowManagerService::shutdown(),代码如下:

// Called by window manager policy.  Not exposed externally.
    @Override
    public void shutdown(boolean confirm) 
        ShutdownThread.shutdown(mContext, confirm);
    
可见,WindowManagerService进一步调用了ShutdownThread中的shutdown函数:

public static void shutdown(final Context context, boolean confirm) 
    mReboot = false;
    mRebootSafeMode = false;
    
    shutdownInner(context, confirm);

可见,进一步调用了shutdownInner(),代码如下:

static void shutdownInner(final Context context, boolean confirm) 
    //该函数只能被一个线程调用,如果已经被调用了,则其他线程再调用的话,会直接返回
    synchronized (sIsStartedGuard) 
        if (sIsStarted) 
            Log.d(TAG, "Request to shutdown already running, returning.");
            return;
        
    

    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);
 
    //confirm用来标识是否需要显示确认对话框,若为true,则显示
    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);  //不显示确认对话框,则直接进入关机流程
    
经历千三万水,最终调用了beginShutdownSequence()来正式开始关机流程:

private static void beginShutdownSequence(Context context)  
     //该函数只能被一个线程调用,如果已经被调用了,则其他线程再调用的话,会直接返回
    synchronized (sIsStartedGuard)   
        if (sIsStarted)   
            Log.d(TAG, "Shutdown sequence already running, returning.");  
            return;  
          
        sIsStarted = true;  
      
    // throw up an indeterminate system dialog to indicate radio is  
    // shutting down.  
    ProgressDialog pd = new ProgressDialog(context);  
    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);  
    //pd.show();  
    shutdownTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_TIME;  
    
    //显示关机动画  
    String[] bootcmd = "bootanimation", "shutdown" ;  
    try   
        Log.i(TAG, "exec the bootanimation ");  
        SystemProperties.set("service.bootanim.exit", "0");  
        Runtime.getRuntime().exec(bootcmd);  
     catch (Exception e)   
       Log.e(TAG,"bootanimation command exe err!");  
       
    
    //初始化关机线程ShutdownThread  
    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;  
          
      
    // start the thread that initiates shutdown  
    sInstance.mHandler = new Handler()   
    ;  
    
    //启动关机线程ShutdownThread  
    sInstance.start();  
该函数主要做了两件事:第一,执行命令:“bootanimation shutdown",显示关机动画。这个具体后面再讲。第二,初始化关机线程,然后启动关机线程。线程执行函数代码如下:

public void run() 
		//创建一个广播接收器,用于接收关机广播,如果收到则设置mActionDone为true
		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();
		    
		;
		
	  //设置属性"sys.shutdown.requested",用于记录关机原因
		
		    String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
		    SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
		
		
		
		//如果是重启进入安全模式,则设置属性"persist.sys.safemode"值为1
		if (mRebootSafeMode) 
		    SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
		
		
		Log.i(TAG, "Sending shutdown broadcast...");
		Slog.d(TAG, "*****Sending shutdown broadcast...");
				
		//发送关机广播
		mActionDone = false;
		Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
		intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
		 
		mContext.sendOrderedBroadcastAsUser(intent,
		        UserHandle.ALL, null, br, mHandler, 0, null, null);
		
		//等待10s,直到前面定义的广播接收器收到关机广播,或者超时
		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;
		        
		        try 
		            mActionDoneSync.wait(delay);
		         catch (InterruptedException e) 
		        
		    
		
		
		//关闭ActivityManager,超时时间为10s
		Log.i(TAG, "Shutting down activity manager...");
		Slog.d(TAG, "*****Shutting down activity manager...");		
		final IActivityManager am =
		    ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
		if (am != null) 
		    try 
		        am.shutdown(MAX_BROADCAST_TIME);
		     catch (RemoteException e) 
		    
		
		
		//关闭PackageManagerService
		Log.i(TAG, "Shutting down package manager...");		
		Slog.d(TAG, "*****Shutting down package manager...");		
		final PackageManagerService pm = (PackageManagerService)
		    ServiceManager.getService("package");
		if (pm != null) 
		    pm.shutdown();
		
		
		//关闭无线电话,超时时间为12s
		mContext.sendBroadcast(new Intent(TelephonyIntents.ACTION_SHUTDOWN_SPRD));
		shutdownRadios(MAX_RADIO_WAIT_TIME);
		Slog.d(TAG, "*****after shutdownRadios");
		if(!SystemProperties.getBoolean("persist.sys.volte.enable", false))
		    Slog.d(TAG, "*****before shutdownIccs");
		    shutdownIccs(MAX_ICC_WAIT_TIME);
		    Slog.d(TAG, "*****end shutdownIccs");
		
		
		//关闭挂载服务,超时时间为20s
		Slog.d(TAG, "*****before IMountShutdownObserver.Stub");
		IMountShutdownObserver observer = new IMountShutdownObserver.Stub() 
		    public void onShutDownComplete(int statusCode) throws RemoteException 
		        Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
		        actionDone();
		    
		;
		
		Log.i(TAG, "Shutting down MountService");
		Slog.d(TAG, "*****Shutting down MountService");
		
		// Set initial variables and time out time.
		mActionDone = false;
		final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
		synchronized (mActionDoneSync) 
		    try 
		        final IMountService mount = IMountService.Stub.asInterface(
		                ServiceManager.checkService("mount"));
		        Slog.d(TAG, "*****IMountService.Stub.asInterface");
		        if (mount != null) 
		            Slog.d(TAG, "*****before mount.shutdown");
		            mount.shutdown(observer);
		            Slog.d(TAG, "*****end mount shutdown");
		         else 
		            Log.w(TAG, "MountService unavailable for shutdown");
		        
		     catch (Exception e) 
		        Log.e(TAG, "Exception during MountService shutdown", e);
		        Slog.d(TAG, "*****Exception during MountService shutdown");
		    
		    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) 
		        
		    
		
		
		//继续执行其他的关机工作
		Slog.d(TAG, "***** before rebootOrShutdown");
		rebootOrShutdown(mReboot, mRebootReason);
		Slog.d(TAG, "***** end rebootOrShutdown");
继续调用rebootOrShutdown()完成剩下的关机工作:
public static void rebootOrShutdown(boolean reboot, String reason) 
		if (reboot)   //如果是重启,则调用PowerManagerService::lowLevelReboot重启系统  
		    Log.i(TAG, "Rebooting, reason: " + reason);
		    PowerManagerService.lowLevelReboot(reason);
		    Log.e(TAG, "Reboot failed, will attempt shutdown instead");
		 else if (SHUTDOWN_VIBRATE_MS > 0)   //如果关机,先震动500ms
		    // 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);
		    
		
		    //由于震动是个异步过程,因此在这里需要等500ms,指导震动完成.
		    try 
		        Thread.sleep(SHUTDOWN_VIBRATE_MS);
		     catch (InterruptedException unused) 
		    
		
		
		//关闭电源
		Log.i(TAG, "Performing low-level shutdown...");
		PowerManagerService.lowLevelShutdown();

public static void lowLevelShutdown() 
    SystemProperties.set("sys.powerctl", "shutdown");
public static void set(String key, String val) 
    if (key.length() > PROP_NAME_MAX) 
        throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
    
    if (val != null && val.length() > PROP_VALUE_MAX) 
        throw new IllegalArgumentException("val.length > " +
            PROP_VALUE_MAX);
    
    native_set(key, val);
SystemProperties::native_set()是native方法,JNI方法位置:\\frameworks\\base\\core\\jni\\android_os_SystemProperties.cpp

static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)

    ......

    err = property_set(key, val);

    ......
可见,最终调用property_set将属性"sys.powerctl"的值设置为”shutdown“。这样就到了init进程。sys.powerctl在init.rc中的定义如下:

on property:sys.powerctl=*
    powerctl $sys.powerctl
init进程得到这个事件后,会执行do_powerctl函数,路径为:system/core/init/builtins.c。
int do_powerctl(int nargs, char **args)

    ......

    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;
    

    .......

    return android_reboot(cmd, 0, reboot_target);
最终,调用android_reboot完成最后的关机工作,并关闭手机电源。

以上是关于[Android5.1]关机工作流程的主要内容,如果未能解决你的问题,请参考以下文章

[Android5.1]开机动画显示工作流程分析

[Android5.1]开机动画desc.txt描述文件的分析

电脑每次关机都会显示xx内存错误,不能存为xx,请问是怎么回事?

Android5.1修改以太网MAC地址(SElinux)

在同步调用上强制 NSUrlConnection 超时

[Android5.1][RK3288] LCD Mipi 调试方法及问题汇总