转载地址:http://blog.csdn.net/xzhang76/article/details/11033245
最近遇到fastboot关机电流偏高的问题,虽然最后确认是硬件的问题,但还是顺便分析了一下android开关机的流程。总结一下,加深印象,也方便日后查阅。
Android智能手机和平板一般都有Power key,长按Power key弹出关机对话框,选择power off就会让系统关闭。关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入framework层,再从 framework层到kernel层执行关机任务。本文分析过程将分成两篇,(1)Framework层 (2)JNI和Kernel层,代码基于自己的android4.3源码。
一. 长按power key对应的handler代码:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
- private final Runnable mPowerLongPress = new Runnable() {
- @Override
- public void run() {
- // The context isn't read
- if (mLongPressOnPowerBehavior < 0) {
- mLongPressOnPowerBehavior = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_longPressOnPowerBehavior);
- }
- int resolvedBehavior = mLongPressOnPowerBehavior;
- if (FactoryTest.isLongPressOnPowerOffEnabled()) {
- resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
- }
- switch (resolvedBehavior) {
- case LONG_PRESS_POWER_NOTHING:
- break;
- case LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
- performAuditoryFeedbackForAccessibilityIfNeed();
- }
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
- showGlobalActionsDialog();
- 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(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
- break;
- }
- }
- };
run()函数中通过从mLongPressOnPowerBehavior向resolvedBehavior传入值,从而基于resolvedBehavior决定关机动作。下面是对代码的分析:
1. mLongPressOnPowerBehavior初始值为-1,所以会通过getInteger()从config_longPressOnPowerBehavior配置选项中得到mLongPressOnPowerBehavior。
- int mLongPressOnPowerBehavior = -1;
2. 检查是否是FactoryTest模式enable了长按powerkey进行关机的选项,是则LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM。
3. switch(resolvedBehavior)来决定关机动作:
- LONG_PRESS_POWER_NOTHING = 0,Do nothing;
- LONG_PRESS_POWER_GLOBAL_ACTIONS = 1,这是正常关机所走的流程。首先mPowerKeyHandled = ture,然后sendCloseSystemWindows()发送显示关闭系统的对话框的请求,reason是SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS,
- void sendCloseSystemWindows(String reason) {
- sendCloseSystemWindows(mContext, reason);
- }
- static void sendCloseSystemWindows(Context context, String reason) {
- if (ActivityManagerNative.isSystemReady()) {
- try {
- ActivityManagerNative.getDefault().closeSystemDialogs(reason);
- } catch (RemoteException e) {
- }
- }
- }
- LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3,直接调用mWindowManagerFuncs.shutdown()进行关机,shutdown()原型:
- public void shutdown(boolean confirm);
二、显示power off对话框:showGlobalActionsDialog()
showGlobalActionsDialog()的定义:
- void showGlobalActionsDialog() {
- 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.userActivity();
- }
- }
下面是对代码的分析:
1. 这里首先new GlobalActions,注意一下GlobalActions()的后一个参数是mWindowManagerFuncs;
2. 然后会调用mGlobalActions.showDialog()来显示对话框;
3. 最后根据keyguardShowing的值,也就是是否在锁屏状态,决定是否需要userActivity。一般不会有这种情况。
显示对话框的具体调用是mGlobalActions.showDialog(),它在frameworks/base/policy/src/com/android/internal/policy/impl/GlobalActions.java中。
- /**
- * Show the global actions dialog (creating if necessary)
- * @param keyguardShowing True if keyguard is showing
- */
- 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() {
- awakenIfNecessary();
- mDialog = createDialog();
- prepareDialog();
- WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
- attrs.setTitle("GlobalActions");
- mDialog.getWindow().setAttributes(attrs);
- mDialog.show();
- mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
- }
- /**
- * Create the global actions dialog.
- * @return A new dialog.
- */
- private GlobalActionsDialog createDialog() {
- // 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);
- }
- 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);
- }
- }
- @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();
- mItems = new ArrayList<Action>();
- // first: power off
- mItems.add(
- 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(true);
- }
- public boolean onLongPress() {
- mWindowManagerFuncs.rebootSafeMode(true);
- return true;
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return true;
- }
- });
- // next: airplane mode
- mItems.add(mAirplaneModeOn);
- // next: bug report, if enabled
- if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0) {
- mItems.add(
- new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
- R.string.global_action_bug_report) {
- public void onPress() {
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setTitle(com.android.internal.R.string.bugreport_title);
- builder.setMessage(com.android.internal.R.string.bugreport_message);
- builder.setNegativeButton(com.android.internal.R.string.cancel, null);
- builder.setPositiveButton(com.android.internal.R.string.report,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // Add a little delay before executing, to give the
- // dialog a chance to go away before it takes a
- // screenshot.
- mHandler.postDelayed(new Runnable() {
- @Override public void run() {
- try {
- ActivityManagerNative.getDefault()
- .requestBugReport();
- } catch (RemoteException e) {
- }
- }
- }, 500);
- }
- });
- AlertDialog dialog = builder.create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- dialog.show();
- }
- public boolean onLongPress() {
- return false;
- }
- public boolean showDuringKeyguard() {
- return true;
- }
- public boolean showBeforeProvisioning() {
- return false;
- }
- });
- }
- // last: silent mode
- if (mShowSilentToggle) {
- mItems.add(mSilentModeAction);
- }
- // one more thing: optionally add a list of users to switch to
- if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
- addUsersToMenu(mItems);
- }
- 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) {
- return mAdapter.getItem(position).onLongPress();
- }
- });
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- dialog.setOnDismissListener(this);
- return dialog;
- }
- 前面函数中为稍后的各类选项做了准备工作;
- first: power off,这里主要定义了两种动作,短按(onPress)和长按(onLongPress),对应的两种结果就是shutdown()和rebootSafeMode();
- next: airplane mode
- next: bug report, if enabled
- last: silent mode
因为本文主要分析关机流程,所以对后面几个选项就不分析了。回到handleShow()中,然后调用mDialog类中的函数做显示窗口的动作。
三、执行ShutdownThread.shutdown(),启动关机线程执行关机动作。
前面在GlobalActions.createDialog()中,当进入power off,然后onPress()的时候,mWindowManagerFuncs.shutdown(ture)将被调用。
- public void onPress() {
- // shutdown by making sure radio and power are handled accordingly.
- mWindowManagerFuncs.shutdown(true);
- }
- private final WindowManagerFuncs mWindowManagerFuncs;
在WindowManagerFuncs类中,shutdown(boolean confirm)只给出了原型,并没有具体实现。搜索一下很容易发现真正被调用执行的是在/frameworks/base/services/java/com/android/server/wm/WindowManagerService.java中定义的shutdown(),即WindowManagerService.shutdown()。
- // Called by window manager policy. Not exposed externally.
- @Override
- public void shutdown(boolean confirm) {
- ShutdownThread.shutdown(mContext, confirm);
- }
- // Called by window manager policy. Not exposed externally.
- @Override
- public void rebootSafeMode(boolean confirm) {
- ShutdownThread.rebootSafeMode(mContext, confirm);
- }
下面来看一下具体的关机流程:
ShutdownThread类在frameworks/base/services/java/com/android/server/power/ShutdownThread.java中定义,下面是shutdown()的代码。
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void shutdown(final Context context, boolean confirm) {
- mReboot = false;
- mRebootSafeMode = false;
- shutdownInner(context, 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);
- 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);
- }
- }
一般情况下confirm为true,即表示确认shutdown,然后系统会显示确认shutdown的对话框,同时执行beginShutdownSequence(),启动shutdown线程。如果不需要confirm就可以shutdown,则直接执行beginShutdownSequence(),启动shutdown线程。
3. 下面看一下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();
- 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() {
- };
- sInstance.start();
- }
- 参数context非常重要,它继续用来显示关机进程的对话框;还要注意一点,sInstance是ShutdownThread静态类实例,也是this thread的实例,接下来还会用到很多它的函数。
- // static instance of this thread
- private static final ShutdownThread sInstance = new ShutdownThread();
- ProgressDialog pd用来启动一个不明确的系统对话框来表明radio正在关闭,在Google原生系统中可以看到这样的对话框。pd的成员函数setTitle()、setMessage()、setIndeterminate()、setCancelable()会根据context内容对对话框属性进行设置,show()会显示对话框。
- 确保系统不会再进入睡眠。通过sInstance.mCpuWakeLock来获得wakelock,使系统不会睡眠。
- 保证screen不会马上黑下去,这样可以保证更好的用户体验。通过sInstance.mScreenWakeLock获得screen wakelock使屏幕长亮。
- 最后启动shutdown线程sInstance.start()。
四、framework层最后的一步:ShutdownThread.run()
当进程执行到sInstance.start()后,就进入了framework层最后的一步:ShutdownThread.run()。
- /**
- * Makes sure we handle the shutdown gracefully.
- * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
- */
- public void run() {
- 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();
- }
- };
- /*
- * Write a system property in case the system_server reboots before we
- * get to the actual hardware restart. If that happens, we'll retry at
- * the beginning of the SystemServer startup.
- */
- {
- String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
- SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
- }
- /*
- * If we are rebooting into safe mode, write a system property
- * indicating so.
- */
- 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.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
- 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 timed out");
- break;
- }
- try {
- mActionDoneSync.wait(delay);
- } catch (InterruptedException e) {
- }
- }
- }
- Log.i(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) {
- }
- }
- // Shutdown radios.
- shutdownRadios(MAX_RADIO_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");
- // 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"));
- 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(mReboot, mRebootReason);
- }
1. 如果分配的时间已经过去,不管radio和bluetooth状态如何都关闭。
2. 如果是rebootSafeMode,会设置一些系统属性。
3. 接下来是shutdown的流程,首先向APP层发送shutdown的广播。注意synchronized。
4. shutdown radios,调用shutdownRadios(),它也是ShutdownThread的成员函数。
5. shutdown mountservice,这样可以保证media的安全。
6. 设置初始化参数和时间。
7. rebootOrShutdown(),最后执行的shutdown流程的函数。
- /**
- * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
- * or {@link #shutdown(Context, boolean)} instead.
- *
- * @param reboot true to reboot or false to shutdown
- * @param reason reason for reboot
- */
- 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) {
- // vibrate before shutting down
- Vibrator vibrator = new SystemVibrator();
- try {
- 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) {
- }
- }
- // Shutdown power
- Log.i(TAG, "Performing low-level shutdown...");
- PowerManagerService.lowLevelShutdown();
- }
- }
2. 如果reboot是true,则会执行PowerManagerService.lowLevelReboot(reason)的过程;否则继续shutdown的过程。文章中分析shutdown的流程,所以reboot暂时不看。
3. 接下来系统会确保shutdown之前的机器震动,从注释很容易看明白。
4. Shutdown power开始进入底层的shutdown,即进入JNI层和kernel层。
PowerManagerService.lowLevelShutdown()是在frameworks/base/services/java/com/android/server/power/PowerManagerService.java中定义的。它是一个通往下层JNI的函数。
- /**
- * Low-level function turn the device off immediately, without trying
- * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
- */
- public static void lowLevelShutdown() {
- nativeShutdown();
- }
上层framework层的分析就到此为止了,后续的文章将继续分析JNI层以及kernel的关机流程。