Android关机流程源码分析
上一篇文章Android 开关机动画显示源码分析详细介绍了开关机动画的显示过程,Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootanim进程,实现开机动画显示过程;当系统关机时,又是如何启动关机动画的呢?Android系统的整个关机流程又是怎样的呢?本文就针对这两个问题透过源码来给出具体的分析。我们知道,当长按电源键,系统会弹出关机提示对话框
当点击选择关机时,系统就会完成整个关机流程。接下来就通过源码来介绍Android关机流程的完整实现过程。当长按电源键时,按键消息被分发到PhoneWindowManager的interceptKeyBeforeQueueing函数中处理:
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { ... switch (keyCode) { ... case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { if (isScreenOn && !mPowerKeyTriggered && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { mPowerKeyTriggered = true; mPowerKeyTime = event.getDownTime(); interceptScreenshotChord();//抓屏 } ITelephony telephonyService = getTelephonyService(); boolean hungUp = false; if (telephonyService != null) { try { if (telephonyService.isRinging()) { //当来电时按下电源键,启动静音 telephonyService.silenceRinger(); } else if ((mIncallPowerBehavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 && telephonyService.isOffhook()) { // Otherwise, if "Power button ends call" is enabled, // the Power button will hang up any current active call. hungUp = telephonyService.endCall(); } } catch (RemoteException ex) { Log.w(TAG, "ITelephony threw RemoteException", ex); } } interceptPowerKeyDown(!isScreenOn || hungUp || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered); } else { ... } break; } ... } return result; }
电源键和音量键的组合可以实现特定功能,比如按下电源键和音量向下键,可实现抓屏,interceptKeyBeforeQueueing函数首先根据条件处理电源键按下的特定任务,然后调用interceptPowerKeyDown做进一步处理
private void interceptPowerKeyDown(boolean handled) { mPowerKeyHandled = handled; if (!handled) { //隔500ms处理电源按键事件 mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); } }
这里的mHandler是在初始化PhoneWindowManager对象时创建的
public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs, LocalPowerManager powerManager) { ... mHandler = new PolicyHandler(); ... }
将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理
private final Runnable mPowerLongPress = new Runnable() { public void run() { // The context isn't read if (mLongPressOnPowerBehavior < 0) { mLongPressOnPowerBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); } switch (mLongPressOnPowerBehavior) { case LONG_PRESS_POWER_NOTHING: break; case LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); showGlobalActionsDialog(); break; case LONG_PRESS_POWER_SHUT_OFF: mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); mWindowManagerFuncs.shutdown(); break; } } };
在处理电源长按事件时,根据mLongPressOnPowerBehavior完成不同的处理过程,mLongPressOnPowerBehavior的值是通过配置文件来设置的,在frameworks/base/core/res/values/config.xml中有以下一段配置:
通过读取配置文件取得config_longPressOnPowerBehavior配置的值为1,因此将显示关机对话框
case LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); //向ActivityManagerService请求关闭所有窗口 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); //显示关机对话框 showGlobalActionsDialog(); break;
关机对话框显示:
void showGlobalActionsDialog() { //创建GlobalActions对象 if (mGlobalActions == null) { mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); } final boolean keyguardShowing = keyguardIsShowingTq(); //显示对话框 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. mKeyguardMediator.pokeWakelock(); } }
通过GlobalActions的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();//关机对话框显示 } }
private void handleShow() { //创建对话框 mDialog = createDialog(); prepareDialog();//设置对话框属性 mDialog.show();//显示对话框 mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND); }
createDialog()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。
private AlertDialog createDialog() { //=========================创建对话框显示的列表项视图===================================== //每一个列表项被被抽象为Action对象, // Simple toggle style if there's no vibrator, otherwise use a tri-state if (!mHasVibrator) { mSilentModeAction = new SilentModeToggleAction(); } else { mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler); } //创建飞行模式切换ToggleAction对象 mAirplaneModeOn = new ToggleAction( R.drawable.ic_lock_airplane_mode, R.drawable.ic_lock_airplane_mode_off, R.string.global_actions_toggle_airplane_mode, R.string.global_actions_airplane_mode_on_status, R.string.global_actions_airplane_mode_off_status) { void onToggle(boolean on) { if (mHasTelephony && Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { mIsWaitingForEcmExit = true; // Launch ECM exit dialog Intent ecmDialogIntent =new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null); ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(ecmDialogIntent); } else { changeAirplaneModeSystemSetting(on); mHandler.removeMessages(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT); mHandler.sendEmptyMessageDelayed(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT,DELAY_AIRPLANE_SET_TIME); } } @Override protected void changeStateFromPress(boolean buttonOn) { if (!mHasTelephony) return; // In ECM mode airplane state cannot be changed if (!(Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) { mState = buttonOn ? State.TurningOn : State.TurningOff; mAirplaneState = mState; } } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return false; } }; //更新当前飞行模式状态 onAirplaneModeChanged(); onRadioBusyStateChanged(); mItems = new ArrayList<Action>(); //向mItems列表中依次添加关机选择,飞行模式切换,静音模式选择 // first: power off mItems.add( //创建关机SinglePressAction new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off, R.string.global_action_power_off) { public void onPress() { // shutdown by making sure radio and power are handled accordingly. mWindowManagerFuncs.shutdown(); } public boolean onLongPress() { mWindowManagerFuncs.rebootSafeMode(); return true; } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return true; } }); // next: airplane mode mItems.add(mAirplaneModeOn); // last: silent mode if (SHOW_SILENT_TOGGLE) { mItems.add(mSilentModeAction); } //获取系统中所有用户信息 List<UserInfo> users = mContext.getPackageManager().getUsers(); if (users.size() > 1) {//对于多用户Android系统,在显示的对话框下面添加用户切换选项 UserInfo currentUser; try { currentUser = ActivityManagerNative.getDefault().getCurrentUser(); } catch (RemoteException re) { currentUser = null; } for (final UserInfo user : users) { boolean isCurrentUser = currentUser == null ? user.id == 0 : (currentUser.id == user.id); SinglePressAction switchToUser = new SinglePressAction( com.android.internal.R.drawable.ic_menu_cc, (user.name != null ? user.name : "Primary") + (isCurrentUser ? " \u2714" : "")) { public void onPress() { try { ActivityManagerNative.getDefault().switchUser(user.id); getWindowManager().lockNow(); } catch (RemoteException re) { Log.e(TAG, "Couldn't switch user " + re); } } public boolean showDuringKeyguard() { return true; } public boolean showBeforeProvisioning() { return false; } }; mItems.add(switchToUser); } } mAdapter = new MyAdapter();//创建适配器,保存了所有数据,这里用MyAdapter保存列表项视图 //=========================创建对话框========================================= final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); ab.setAdapter(mAdapter, this).setInverseBackgroundForced(true); final AlertDialog dialog = ab.create(); 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) { return mAdapter.getItem(position).onLongPress();//视图和数据相关联 } }); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); dialog.setOnDismissListener(this); return dialog; }
这里需要介绍对话框列表创建的方法,代码实现比较灵魂,值得学习。首先将显示对话框列表中的每一项用应该Action来描述,Action是一个接口,定义了每一个列表项的动作方法
各个列表项定义不同类型的Action,比如关机选项使用SinglePressAction来描述,目前Android系统定义了几种类型的Action,对应关机对话框中的不同选项。
将创建的列表选项添加到动态数组mItems中,并且为该对话框定义一个适配器MyAdapter,在单击对话框列表项时,调用适配器中对应的项来响应单击事件。Android适配器的使用实现了MVC编程模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。
public boolean onItemLongClick(AdapterView<?> parent, View view, int position,long id) { return mAdapter.getItem(position).onLongPress(); } public void onClick(DialogInterface dialog, int which) { if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) { dialog.dismiss(); } mAdapter.getItem(which).onPress(); }
对于关机选项,其单击和长按事件处理过程如下:
public void onPress() { // shutdown by making sure radio and power are handled accordingly. mWindowManagerFuncs.shutdown(); } public boolean onLongPress() { mWindowManagerFuncs.rebootSafeMode(); return true; }
对关机的处理都是调用mWindowManagerFuncs来完成的,mWindowManagerFuncs的类型为WindowManagerFuncs,在调用PhoneWindowManager的init函数时通过参数传进来。mWindowManagerFuncs对象在那里构造呢?WindowManagerService定义了一个WindowManagerPolicy类型变量:
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象
public static WindowManagerPolicy makeNewWindowManager() { return sPolicy.makeNewWindowManager(); }
该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程
Policy.java
public WindowManagerPolicy makeNewWindowManager() { return new PhoneWindowManager(); }
在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy
private WindowManagerService(Context context, PowerManagerService pm, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { ... PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); thr.start(); synchronized (thr) { while (!thr.mRunning) { try { thr.wait(); } catch (InterruptedException e) { } } } ... }
在PolicyThread线程中执行初始化函数
static class PolicyThread extends Thread { @Override public void run() { Looper.prepare(); ... mPolicy.init(mContext, mService, mService, mPM); synchronized (this) { mRunning = true; notifyAll(); } ... Looper.loop(); } }
mPolicy的类型定义为WindowManagerPolicy类型,而WindowManagerPolicy是一个接口类型,PhoneWindowManager实现了该接口,为mPolicy创建的真正对象是PhoneWindowManager对象,因此PolicyThread线程将调用PhoneWindowManager的init函数
public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs,LocalPowerManager powerManager) { ... mWindowManagerFuncs = windowManagerFuncs; ... }
传进来的参数windowManager和windowManagerFuncs都是WindowManagerService对象,因为WindowManagerService继承于IWindowManager.Stub类同时实现了WindowManagerPolicy.WindowManagerFuncs接口,这下就很清晰地知道GlobalActions类中的mWindowManagerFuncs变量其实是WindowManagerService对象,因此关机的单击及长按事件由WindowManagerService实现:
public void shutdown() { ShutdownThread.shutdown(mContext, true); } public void rebootSafeMode() { ShutdownThread.rebootSafeMode(mContext, true); }
WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:
public static void shutdown(final Context context, boolean confirm) { mReboot = false; mRebootSafeMode = false; shutdownInner(context, confirm); }
该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。
static void shutdownInner(final Context context, boolean confirm) { // ensure that only one thread is trying to power down. // any additional calls are just returned synchronized (sIsStartedGuard) { if (sIsStarted) { Log.d(TAG, "Request to shutdown already running, returning."); return; } } final int longPressBehavior = context.getResources().getInteger( com.android.internal.R.integer.config_longPressOnPowerBehavior); final int resourceId = mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_confirm : (longPressBehavior == 2 ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); //显示关机确认对话框 if (confirm) { final CloseDialogReceiver closer = new CloseDialogReceiver(context); final AlertDialog dialog = 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 = dialog; dialog.setOnDismissListener(closer); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); dialog.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(); }
这个函数执行两个任务,1)通过虚拟机Runtime启动关机动画进程bootanimation,用于显示关机动画,开关机动画在 Android 开关机动画显示源码分析进行了详细的分析介绍。2)启动关机线程ShutdownThread来完成关机操作
public void run() { BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 用于接收关机广播 actionDone(); } }; //写属性"sys.shutdown.requested"保存关机原因 { String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : ""); SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); } //如果是安全模式关机,写属性"persist.sys.safemode" if (mRebootSafeMode) { SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); } Log.i(TAG, "Sending shutdown broadcast..."); // First send the high-level shut down broadcast. mActionDone = false; //发送关机广播 mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null, br, mHandler, 0, null, null); //等待10S,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待 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) { } } } //10S时间内关闭ActivityManager服务 final IActivityManager am =ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } //12s内关闭radios. shutdownRadios(MAX_RADIO_WAIT_TIME); //10s内关闭ICCS shutdownIccs(MAX_ICC_WAIT_TIME); // Shutdown MountService to ensure media is in a safe state 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"); //20s内关闭MountService服务 mActionDone = false; final long endShutTime = 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) { } } } //关机时间定义为5s,这里计算关机超过的时间 long shutdownDelay = shutdownTime - SystemClock.elapsedRealtime(); if (shutdownDelay > 0) { Log.i(TAG, "Shutdown delay:"+shutdownDelay); SystemClock.sleep(shutdownDelay); } //继续关机 rebootOrShutdown(mReboot, mRebootReason); }
该函数内主要完成以下一些工作:
(1)发送关机广播ACTION_SHUTDOWN
(2)关闭ActivityManager 服务
(3)关闭无线相关的服务
(4)关闭Iccs
(5)关闭MountService服务
public static void rebootOrShutdown(boolean reboot, String reason) { if (reboot) {//是否重启 Log.i(TAG, "Rebooting, reason: " + reason); try { PowerManagerService.lowLevelReboot(reason); } catch (Exception e) { Log.e(TAG, "Reboot failed, will attempt shutdown instead", e); } } else if (SHUTDOWN_VIBRATE_MS > 0) {//震动时间为500ms // vibrate before shutting down Vibrator vibrator = new SystemVibrator(); try { //关机震动500ms vibrator.vibrate(SHUTDOWN_VIBRATE_MS); } 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) { } } //关闭电源 Log.i(TAG, "Performing low-level shutdown..."); PowerManagerService.lowLevelShutdown(); }
这里是启动关机震动并关闭电源
public static void lowLevelShutdown() { nativeShutdown(); }
这里通过JNI调用C++的关机函数
static void nativeShutdown(JNIEnv *env, jobject clazz) { delFlag(); android_reboot(ANDROID_RB_POWEROFF, 0, 0); }
android_reboot函数最终通过Linux系统调用关闭系统。到此关机操作就基本完成了。