[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完成最后的关机工作,并关闭手机电源。

### 回答1: Android 5.1是一个广泛使用的操作系统版本,支持HDMI输出功能。要设置HDMI分辨率,可以按照以下步骤进行: 1. 首先,将Android设备与HDMI显示器或电视连接。确保HDMI线缆正常连接。 2. 在Android设备上打开设置菜单。可以通过下拉通知栏或点击主屏幕上的设置图标来访问设置菜单。 3. 在设置菜单中,向下滚动并找到“显示”选项。点击进入显示设置页面。 4. 在显示设置页面中,可能会看到“屏幕投射”或“显示模式”的选项。点击进入相关设置。 5. 在屏幕投射或显示模式设置页面中,可以看到HDMI选项。点击进入HDMI设置。 6. 在HDMI设置页面中,通常会有分辨率选项。点击进入分辨率设置。 7. 在分辨率设置页面中,可以看到可用的分辨率选项列表。根据显示器或电视的支持能力和个人偏好,选择所需的分辨率。 8. 选择完分辨率后,点击确认或应用。系统将应用所选的分辨率设置。 9. 返回到上一个菜单或主屏幕,查看是否已成功设置HDMI分辨率。显示器或电视上的画面应该会根据所选的分辨率进行调整。 需要注意的是,不同的Android设备或系统版本可能会有略微不同的设置流程。因此,根据具体的设备和系统版本,上述步骤可能会有所变化。但总体而言,通过进入设置菜单,找到显示设置,再进入HDMI设置,选择适当的分辨率,就可以完成Android 5.1设置HDMI分辨率的流程。 ### 回答2: Android 5.1版本的设备在设置HDMI分辨率时,可以按照以下步骤进行操作: 1. 连接HDMI线缆:首先,将一端的HDMI线缆插入Android设备的HDMI输出接口,另一端插入显示设备(如电视或投影仪)的HDMI输入接口。 2. 打开设置界面:在Android设备上,找到并点击打开“设置”应用程序,通常可以在应用程序列表中找到该选项。 3. 进入显示设置:在“设置”主界面中,向下滚动并找到“显示”选项,点击进入显示设置界面。 4. 选择HDMI设置:在显示设置界面中,找到并点击“HDMI”选项,这将打开HDMI设置界面。 5. 选择分辨率:在HDMI设置界面中,通常会显示可用的HDMI分辨率选项。根据你的显示设备和个人需求,选择适当的分辨率选项。 6. 保存设置:选择完分辨率后,点击界面上的“保存”或“应用”按钮,以保存并应用新的HDMI分辨率设置。 7. 测试分辨率:你可以通过在显示设备上观察图像是否清晰和完整来测试新的HDMI分辨率设置。如果满意,设置流程就结束了。 请注意,以上步骤仅适用于Android 5.1版本的系统设备,不同的Android版本或设备类型可能略有不同。确保你的设备支持HDMI输出功能并运行在Android 5.1版本或更高版本。 ### 回答3: 要设置Android 5.1的HDMI分辨率,可以按照以下流程操作: 1. 首先,确保你的Android设备已连接到HDMI显示器上。 2. 在设备上滑动屏幕,进入主菜单,找到并点击“设置”图标。 3. 在设置菜单中,向下滚动找到“显示”或类似的选项,并点击进入。 4. 在显示设置菜单中,找到“屏幕分辨率”或类似的选项,并点击进入。 5. 系统会列出可用的屏幕分辨率选项。根据你的需求和HDMI显示器的能力,选择一个适合的分辨率。 6. 点击选中分辨率后,系统会提示你是否确认应用此分辨率。确认后,系统会应用新的分辨率设置。 7. 返回到主菜单或桌面,你会注意到HDMI显示器的分辨率已经改变为你所设置的分辨率。 需要注意的是,不同的Android设备可能会在设置菜单的布局和选项名称上有所区别,具体操作可能会有所不同。但一般来说,通过“设置”-“显示”-“屏幕分辨率”可以找到相关的选项。 如果你在Android设备上找不到上述选项,可能是因为该设备的固件版本或制造商定制的界面导致了菜单布局的差异。在这种情况下,你可能需要查阅设备的用户手册或进行在线搜索来了解具体的设置方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值