上一篇文章Android 开关机动画显示源码分析详细介绍了开关机动画的显示过程,Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootanim进程,实现开机动画显示过程;当系统关机时,又是如何启动关机动画的呢?Android系统的整个关机流程又是怎样的呢?本文就针对这两个问题透过源码来给出具体的分析。我们知道,当长按电源键,系统会弹出关机提示对话框

当点击选择关机时,系统就会完成整个关机流程。接下来就通过源码来介绍Android关机流程的完整实现过程。当长按电源键时,按键消息被分发到PhoneWindowManager的interceptKeyBeforeQueueing函数中处理:

  1. public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
  2. ...
  3. switch (keyCode) {
  4. ...
  5. case KeyEvent.KEYCODE_POWER: {
  6. result &= ~ACTION_PASS_TO_USER;
  7. if (down) {
  8. if (isScreenOn && !mPowerKeyTriggered
  9. && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
  10. mPowerKeyTriggered = true;
  11. mPowerKeyTime = event.getDownTime();
  12. interceptScreenshotChord();//抓屏
  13. }
  14. ITelephony telephonyService = getTelephonyService();
  15. boolean hungUp = false;
  16. if (telephonyService != null) {
  17. try {
  18. if (telephonyService.isRinging()) {
  19. //当来电时按下电源键,启动静音
  20. telephonyService.silenceRinger();
  21. } else if ((mIncallPowerBehavior
  22. & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
  23. && telephonyService.isOffhook()) {
  24. // Otherwise, if "Power button ends call" is enabled,
  25. // the Power button will hang up any current active call.
  26. hungUp = telephonyService.endCall();
  27. }
  28. } catch (RemoteException ex) {
  29. Log.w(TAG, "ITelephony threw RemoteException", ex);
  30. }
  31. }
  32. interceptPowerKeyDown(!isScreenOn || hungUp
  33. || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
  34. } else {
  35. ...
  36. }
  37. break;
  38. }
  39. ...
  40. }
  41. return result;
  42. }

电源键和音量键的组合可以实现特定功能,比如按下电源键和音量向下键,可实现抓屏,interceptKeyBeforeQueueing函数首先根据条件处理电源键按下的特定任务,然后调用interceptPowerKeyDown做进一步处理

  1. private void interceptPowerKeyDown(boolean handled) {
  2. mPowerKeyHandled = handled;
  3. if (!handled) {
  4. //隔500ms处理电源按键事件
  5. mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
  6. }
  7. }

这里的mHandler是在初始化PhoneWindowManager对象时创建的

  1. public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs,
  2. LocalPowerManager powerManager) {
  3. ...
  4. mHandler = new PolicyHandler();
  5. ...
  6. }

将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理

  1. private final Runnable mPowerLongPress = new Runnable() {
  2. public void run() {
  3. // The context isn't read
  4. if (mLongPressOnPowerBehavior < 0) {
  5. mLongPressOnPowerBehavior = mContext.getResources().getInteger(
  6. com.android.internal.R.integer.config_longPressOnPowerBehavior);
  7. }
  8. switch (mLongPressOnPowerBehavior) {
  9. case LONG_PRESS_POWER_NOTHING:
  10. break;
  11. case LONG_PRESS_POWER_GLOBAL_ACTIONS:
  12. mPowerKeyHandled = true;
  13. performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
  14. sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  15. showGlobalActionsDialog();
  16. break;
  17. case LONG_PRESS_POWER_SHUT_OFF:
  18. mPowerKeyHandled = true;
  19. performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
  20. sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  21. mWindowManagerFuncs.shutdown();
  22. break;
  23. }
  24. }
  25. };

在处理电源长按事件时,根据mLongPressOnPowerBehavior完成不同的处理过程,mLongPressOnPowerBehavior的值是通过配置文件来设置的,在frameworks/base/core/res/values/config.xml中有以下一段配置:


通过读取配置文件取得config_longPressOnPowerBehavior配置的值为1,因此将显示关机对话框

  1. case LONG_PRESS_POWER_GLOBAL_ACTIONS:
  2. mPowerKeyHandled = true;
  3. performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
  4. //向ActivityManagerService请求关闭所有窗口
  5. sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
  6. //显示关机对话框
  7. showGlobalActionsDialog();
  8. break;

关机对话框显示:

  1. void showGlobalActionsDialog() {
  2. //创建GlobalActions对象
  3. if (mGlobalActions == null) {
  4. mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
  5. }
  6. final boolean keyguardShowing = keyguardIsShowingTq();
  7. //显示对话框
  8. mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
  9. if (keyguardShowing) {
  10. // since it took two seconds of long press to bring this up,
  11. // poke the wake lock so they have some time to see the dialog.
  12. mKeyguardMediator.pokeWakelock();
  13. }
  14. }

通过GlobalActions的showDialog函数来显示对话框

  1. public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
  2. mKeyguardShowing = keyguardShowing;
  3. mDeviceProvisioned = isDeviceProvisioned;
  4. if (mDialog != null) {
  5. mDialog.dismiss();
  6. mDialog = null;
  7. // Show delayed, so that the dismiss of the previous dialog completes
  8. mHandler.sendEmptyMessage(MESSAGE_SHOW);
  9. } else {
  10. handleShow();//关机对话框显示
  11. }
  12. }
  1. private void handleShow() {
  2. //创建对话框
  3. mDialog = createDialog();
  4. prepareDialog();//设置对话框属性
  5. mDialog.show();//显示对话框
  6. mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
  7. }

createDialog()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。

  1. private AlertDialog createDialog() {
  2. //=========================创建对话框显示的列表项视图=====================================
  3. //每一个列表项被被抽象为Action对象,
  4. // Simple toggle style if there's no vibrator, otherwise use a tri-state
  5. if (!mHasVibrator) {
  6. mSilentModeAction = new SilentModeToggleAction();
  7. } else {
  8. mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
  9. }
  10. //创建飞行模式切换ToggleAction对象
  11. mAirplaneModeOn = new ToggleAction(
  12. R.drawable.ic_lock_airplane_mode,
  13. R.drawable.ic_lock_airplane_mode_off,
  14. R.string.global_actions_toggle_airplane_mode,
  15. R.string.global_actions_airplane_mode_on_status,
  16. R.string.global_actions_airplane_mode_off_status) {
  17. void onToggle(boolean on) {
  18. if (mHasTelephony && Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
  19. mIsWaitingForEcmExit = true;
  20. // Launch ECM exit dialog
  21. Intent ecmDialogIntent =new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
  22. ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  23. mContext.startActivity(ecmDialogIntent);
  24. } else {
  25. changeAirplaneModeSystemSetting(on);
  26. mHandler.removeMessages(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT);
  27. mHandler.sendEmptyMessageDelayed(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT,DELAY_AIRPLANE_SET_TIME);
  28. }
  29. }
  30. @Override
  31. protected void changeStateFromPress(boolean buttonOn) {
  32. if (!mHasTelephony) return;
  33. // In ECM mode airplane state cannot be changed
  34. if (!(Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
  35. mState = buttonOn ? State.TurningOn : State.TurningOff;
  36. mAirplaneState = mState;
  37. }
  38. }
  39. public boolean showDuringKeyguard() {
  40. return true;
  41. }
  42. public boolean showBeforeProvisioning() {
  43. return false;
  44. }
  45. };
  46. //更新当前飞行模式状态
  47. onAirplaneModeChanged();
  48. onRadioBusyStateChanged();
  49. mItems = new ArrayList<Action>();
  50. //向mItems列表中依次添加关机选择,飞行模式切换,静音模式选择
  51. // first: power off
  52. mItems.add(
  53. //创建关机SinglePressAction
  54. new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off,
  55. R.string.global_action_power_off) {
  56. public void onPress() {
  57. // shutdown by making sure radio and power are handled accordingly.
  58. mWindowManagerFuncs.shutdown();
  59. }
  60. public boolean onLongPress() {
  61. mWindowManagerFuncs.rebootSafeMode();
  62. return true;
  63. }
  64. public boolean showDuringKeyguard() {
  65. return true;
  66. }
  67. public boolean showBeforeProvisioning() {
  68. return true;
  69. }
  70. });
  71. // next: airplane mode
  72. mItems.add(mAirplaneModeOn);
  73. // last: silent mode
  74. if (SHOW_SILENT_TOGGLE) {
  75. mItems.add(mSilentModeAction);
  76. }
  77. //获取系统中所有用户信息
  78. List<UserInfo> users = mContext.getPackageManager().getUsers();
  79. if (users.size() > 1) {//对于多用户Android系统,在显示的对话框下面添加用户切换选项
  80. UserInfo currentUser;
  81. try {
  82. currentUser = ActivityManagerNative.getDefault().getCurrentUser();
  83. } catch (RemoteException re) {
  84. currentUser = null;
  85. }
  86. for (final UserInfo user : users) {
  87. boolean isCurrentUser = currentUser == null
  88. ? user.id == 0 : (currentUser.id == user.id);
  89. SinglePressAction switchToUser = new SinglePressAction(
  90. com.android.internal.R.drawable.ic_menu_cc,
  91. (user.name != null ? user.name : "Primary")
  92. + (isCurrentUser ? " \u2714" : "")) {
  93. public void onPress() {
  94. try {
  95. ActivityManagerNative.getDefault().switchUser(user.id);
  96. getWindowManager().lockNow();
  97. } catch (RemoteException re) {
  98. Log.e(TAG, "Couldn't switch user " + re);
  99. }
  100. }
  101.  
  102. public boolean showDuringKeyguard() {
  103. return true;
  104. }
  105.  
  106. public boolean showBeforeProvisioning() {
  107. return false;
  108. }
  109. };
  110. mItems.add(switchToUser);
  111. }
  112. }
  113. mAdapter = new MyAdapter();//创建适配器,保存了所有数据,这里用MyAdapter保存列表项视图
  114. //=========================创建对话框=========================================
  115. final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
  116. ab.setAdapter(mAdapter, this).setInverseBackgroundForced(true);
  117. final AlertDialog dialog = ab.create();
  118. dialog.getListView().setItemsCanFocus(true);
  119. dialog.getListView().setLongClickable(true);
  120. dialog.getListView().setOnItemLongClickListener(
  121. new AdapterView.OnItemLongClickListener() {
  122. @Override
  123. public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
  124. long id) {
  125. return mAdapter.getItem(position).onLongPress();//视图和数据相关联
  126. }
  127. });
  128. dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
  129. dialog.setOnDismissListener(this);
  130. return dialog;
  131. }

这里需要介绍对话框列表创建的方法,代码实现比较灵魂,值得学习。首先将显示对话框列表中的每一项用应该Action来描述,Action是一个接口,定义了每一个列表项的动作方法

各个列表项定义不同类型的Action,比如关机选项使用SinglePressAction来描述,目前Android系统定义了几种类型的Action,对应关机对话框中的不同选项。

将创建的列表选项添加到动态数组mItems中,并且为该对话框定义一个适配器MyAdapter,在单击对话框列表项时,调用适配器中对应的项来响应单击事件。Android适配器的使用实现了MVC编程模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。

  1. public boolean onItemLongClick(AdapterView<?> parent, View view, int position,long id) {
  2. return mAdapter.getItem(position).onLongPress();
  3. }
  4.  
  5. public void onClick(DialogInterface dialog, int which) {
  6. if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
  7. dialog.dismiss();
  8. }
  9. mAdapter.getItem(which).onPress();
  10. }

对于关机选项,其单击和长按事件处理过程如下:

  1. public void onPress() {
  2. // shutdown by making sure radio and power are handled accordingly.
  3. mWindowManagerFuncs.shutdown();
  4. }
  5.  
  6. public boolean onLongPress() {
  7. mWindowManagerFuncs.rebootSafeMode();
  8. return true;
  9. }

对关机的处理都是调用mWindowManagerFuncs来完成的,mWindowManagerFuncs的类型为WindowManagerFuncs,在调用PhoneWindowManager的init函数时通过参数传进来。mWindowManagerFuncs对象在那里构造呢?WindowManagerService定义了一个WindowManagerPolicy类型变量:

  1. final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();

通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象

  1. public static WindowManagerPolicy makeNewWindowManager() {
  2. return sPolicy.makeNewWindowManager();
  3. }

该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程

Policy.java

  1. public WindowManagerPolicy makeNewWindowManager() {
  2. return new PhoneWindowManager();
  3. }

在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy

  1. private WindowManagerService(Context context, PowerManagerService pm,
  2. boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
  3. ...
  4. PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
  5. thr.start();
  6. synchronized (thr) {
  7. while (!thr.mRunning) {
  8. try {
  9. thr.wait();
  10. } catch (InterruptedException e) {
  11. }
  12. }
  13. }
  14. ...
  15. }

在PolicyThread线程中执行初始化函数

  1. static class PolicyThread extends Thread {
  2. @Override
  3. public void run() {
  4. Looper.prepare();
  5. ...
  6. mPolicy.init(mContext, mService, mService, mPM);
  7. synchronized (this) {
  8. mRunning = true;
  9. notifyAll();
  10. }
  11. ...
  12. Looper.loop();
  13. }
  14. }

mPolicy的类型定义为WindowManagerPolicy类型,而WindowManagerPolicy是一个接口类型,PhoneWindowManager实现了该接口,为mPolicy创建的真正对象是PhoneWindowManager对象,因此PolicyThread线程将调用PhoneWindowManager的init函数

  1. public void init(Context context, IWindowManager windowManager,
  2. WindowManagerFuncs windowManagerFuncs,LocalPowerManager powerManager) {
  3. ...
  4. mWindowManagerFuncs = windowManagerFuncs;
  5. ...
  6. }

传进来的参数windowManager和windowManagerFuncs都是WindowManagerService对象,因为WindowManagerService继承于IWindowManager.Stub类同时实现了WindowManagerPolicy.WindowManagerFuncs接口,这下就很清晰地知道GlobalActions类中的mWindowManagerFuncs变量其实是WindowManagerService对象,因此关机的单击及长按事件由WindowManagerService实现:

  1. public void shutdown() {
  2. ShutdownThread.shutdown(mContext, true);
  3. }
  4.  
  5. public void rebootSafeMode() {
  6. ShutdownThread.rebootSafeMode(mContext, true);
  7. }

WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:

  1. public static void shutdown(final Context context, boolean confirm) {
  2. mReboot = false;
  3. mRebootSafeMode = false;
  4. shutdownInner(context, confirm);
  5. }

该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。

  1. static void shutdownInner(final Context context, boolean confirm) {
  2. // ensure that only one thread is trying to power down.
  3. // any additional calls are just returned
  4. synchronized (sIsStartedGuard) {
  5. if (sIsStarted) {
  6. Log.d(TAG, "Request to shutdown already running, returning.");
  7. return;
  8. }
  9. }
  10. final int longPressBehavior = context.getResources().getInteger(
  11. com.android.internal.R.integer.config_longPressOnPowerBehavior);
  12. final int resourceId = mRebootSafeMode
  13. ? com.android.internal.R.string.reboot_safemode_confirm
  14. : (longPressBehavior == 2
  15. ? com.android.internal.R.string.shutdown_confirm_question
  16. : com.android.internal.R.string.shutdown_confirm);
  17. Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
  18. //显示关机确认对话框
  19. if (confirm) {
  20. final CloseDialogReceiver closer = new CloseDialogReceiver(context);
  21. final AlertDialog dialog = new AlertDialog.Builder(context)
  22. .setTitle(mRebootSafeMode
  23. ? com.android.internal.R.string.reboot_safemode_title
  24. : com.android.internal.R.string.power_off)
  25. .setMessage(resourceId)
  26. .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
  27. public void onClick(DialogInterface dialog, int which) {
  28. beginShutdownSequence(context);
  29. }
  30. })
  31. .setNegativeButton(com.android.internal.R.string.no, null)
  32. .create();
  33. closer.dialog = dialog;
  34. dialog.setOnDismissListener(closer);
  35. dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
  36. dialog.show();
  37. } else {//不显示关机确认对话框,直接进入关机流程
  38. beginShutdownSequence(context);
  39. }
  40. }

调用beginShutdownSequence函数进入关机流程。

  1. private static void beginShutdownSequence(Context context) {
  2. synchronized (sIsStartedGuard) {
  3. if (sIsStarted) {
  4. Log.d(TAG, "Shutdown sequence already running, returning.");
  5. return;
  6. }
  7. sIsStarted = true;
  8. }
  9. // throw up an indeterminate system dialog to indicate radio is
  10. // shutting down.
  11. ProgressDialog pd = new ProgressDialog(context);
  12. pd.setTitle(context.getText(com.android.internal.R.string.power_off));
  13. pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
  14. pd.setIndeterminate(true);
  15. pd.setCancelable(false);
  16. pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
  17. //pd.show();
  18. shutdownTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_TIME;
  19. //执行关机动画
  20. String[] bootcmd = {"bootanimation", "shutdown"} ;
  21. try {
  22. Log.i(TAG, "exec the bootanimation ");
  23. SystemProperties.set("service.bootanim.exit", "0");
  24. Runtime.getRuntime().exec(bootcmd);
  25. } catch (Exception e){
  26. Log.e(TAG,"bootanimation command exe err!");
  27. }
  28. //初始化关机线程ShutdownThread
  29. sInstance.mContext = context;
  30. sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
  31. // make sure we never fall asleep again
  32. sInstance.mCpuWakeLock = null;
  33. try {
  34. sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
  35. sInstance.mCpuWakeLock.setReferenceCounted(false);
  36. sInstance.mCpuWakeLock.acquire();
  37. } catch (SecurityException e) {
  38. Log.w(TAG, "No permission to acquire wake lock", e);
  39. sInstance.mCpuWakeLock = null;
  40. }
  41. // also make sure the screen stays on for better user experience
  42. sInstance.mScreenWakeLock = null;
  43. if (sInstance.mPowerManager.isScreenOn()) {
  44. try {
  45. sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
  46. PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
  47. sInstance.mScreenWakeLock.setReferenceCounted(false);
  48. sInstance.mScreenWakeLock.acquire();
  49. } catch (SecurityException e) {
  50. Log.w(TAG, "No permission to acquire wake lock", e);
  51. sInstance.mScreenWakeLock = null;
  52. }
  53. }
  54. // start the thread that initiates shutdown
  55. sInstance.mHandler = new Handler() {
  56. };
  57. //启动关机线程ShutdownThread
  58. sInstance.start();
  59. }

这个函数执行两个任务,1)通过虚拟机Runtime启动关机动画进程bootanimation,用于显示关机动画,开关机动画在 Android 开关机动画显示源码分析进行了详细的分析介绍。2)启动关机线程ShutdownThread来完成关机操作

  1. public void run() {
  2. BroadcastReceiver br = new BroadcastReceiver() {
  3. @Override public void onReceive(Context context, Intent intent) {
  4. // 用于接收关机广播
  5. actionDone();
  6. }
  7. };
  8. //写属性"sys.shutdown.requested"保存关机原因
  9. {
  10. String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
  11. SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
  12. }
  13. //如果是安全模式关机,写属性"persist.sys.safemode"
  14. if (mRebootSafeMode) {
  15. SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
  16. }
  17. Log.i(TAG, "Sending shutdown broadcast...");
  18. // First send the high-level shut down broadcast.
  19. mActionDone = false;
  20. //发送关机广播
  21. mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
  22. br, mHandler, 0, null, null);
  23. //等待10S,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待
  24. final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
  25. synchronized (mActionDoneSync) {
  26. while (!mActionDone) {
  27. long delay = endTime - SystemClock.elapsedRealtime();
  28. if (delay <= 0) {
  29. Log.w(TAG, "Shutdown broadcast timed out");
  30. break;
  31. }
  32. try {
  33. mActionDoneSync.wait(delay);
  34. } catch (InterruptedException e) {
  35. }
  36. }
  37. }
  38. //10S时间内关闭ActivityManager服务
  39. final IActivityManager am =ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
  40. if (am != null) {
  41. try {
  42. am.shutdown(MAX_BROADCAST_TIME);
  43. } catch (RemoteException e) {
  44. }
  45. }
  46. //12s内关闭radios.
  47. shutdownRadios(MAX_RADIO_WAIT_TIME);
  48. //10s内关闭ICCS
  49. shutdownIccs(MAX_ICC_WAIT_TIME);
  50. // Shutdown MountService to ensure media is in a safe state
  51. IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
  52. public void onShutDownComplete(int statusCode) throws RemoteException {
  53. Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
  54. actionDone();
  55. }
  56. };
  57. Log.i(TAG, "Shutting down MountService");
  58. //20s内关闭MountService服务
  59. mActionDone = false;
  60. final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
  61. synchronized (mActionDoneSync) {
  62. try {
  63. final IMountService mount = IMountService.Stub.asInterface(ServiceManager.checkService("mount"));
  64. if (mount != null) {
  65. mount.shutdown(observer);
  66. } else {
  67. Log.w(TAG, "MountService unavailable for shutdown");
  68. }
  69. } catch (Exception e) {
  70. Log.e(TAG, "Exception during MountService shutdown", e);
  71. }
  72. while (!mActionDone) {
  73. long delay = endShutTime - SystemClock.elapsedRealtime();
  74. if (delay <= 0) {
  75. Log.w(TAG, "Shutdown wait timed out");
  76. break;
  77. }
  78. try {
  79. mActionDoneSync.wait(delay);
  80. } catch (InterruptedException e) {
  81. }
  82. }
  83. }
  84. //关机时间定义为5s,这里计算关机超过的时间
  85. long shutdownDelay = shutdownTime - SystemClock.elapsedRealtime();
  86. if (shutdownDelay > 0) {
  87. Log.i(TAG, "Shutdown delay:"+shutdownDelay);
  88. SystemClock.sleep(shutdownDelay);
  89. }
  90. //继续关机
  91. rebootOrShutdown(mReboot, mRebootReason);
  92. }

该函数内主要完成以下一些工作:

(1)发送关机广播ACTION_SHUTDOWN

(2)关闭ActivityManager 服务

(3)关闭无线相关的服务

(4)关闭Iccs

(5)关闭MountService服务

  1. public static void rebootOrShutdown(boolean reboot, String reason) {
  2. if (reboot) {//是否重启
  3. Log.i(TAG, "Rebooting, reason: " + reason);
  4. try {
  5. PowerManagerService.lowLevelReboot(reason);
  6. } catch (Exception e) {
  7. Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
  8. }
  9. } else if (SHUTDOWN_VIBRATE_MS > 0) {//震动时间为500ms
  10. // vibrate before shutting down
  11. Vibrator vibrator = new SystemVibrator();
  12. try {
  13. //关机震动500ms
  14. vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
  15. } catch (Exception e) {
  16. // Failure to vibrate shouldn't interrupt shutdown. Just log it.
  17. Log.w(TAG, "Failed to vibrate during shutdown.", e);
  18. }
  19. // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
  20. try {
  21. Thread.sleep(SHUTDOWN_VIBRATE_MS);
  22. } catch (InterruptedException unused) {
  23. }
  24. }
  25. //关闭电源
  26. Log.i(TAG, "Performing low-level shutdown...");
  27. PowerManagerService.lowLevelShutdown();
  28. }

这里是启动关机震动并关闭电源

  1. public static void lowLevelShutdown() {
  2. nativeShutdown();
  3. }

这里通过JNI调用C++的关机函数

  1. static void nativeShutdown(JNIEnv *env, jobject clazz) {
  2. delFlag();
  3. android_reboot(ANDROID_RB_POWEROFF, 0, 0);
  4. }

android_reboot函数最终通过Linux系统调用关闭系统。到此关机操作就基本完成了。

Android关机流程源码分析的更多相关文章

  1. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  2. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  3. Android笔记--View绘制流程源码分析(二)

    Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...

  4. Android笔记--View绘制流程源码分析(一)

    Android笔记--View绘制流程源码分析 View绘制之前框架流程分析 View绘制的分析始终是离不开Activity及其内部的Window的.在Activity的源码启动流程中,一并包含 着A ...

  5. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  6. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  7. Spark(四十九):Spark On YARN启动流程源码分析(一)

    引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...

  8. spring boot 加载web容器tomcat流程源码分析

    spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...

  9. springboot 事务创建流程源码分析

    springboot 事务创建流程源码分析 目录 springboot 事务创建流程源码分析 1. 自动加载配置 2. InfrastructureAdvisorAutoProxyCreator类 3 ...

随机推荐

  1. 本地网址连不上远程mysql问题

    问题:host 'XXX.XXX.XXX.XXX'is not allowed to connect to this MySQL server 解决办法: 进入远程mysql #mysql -u ro ...

  2. Web页面在手机上显示过大问题

    网上抄来了,自己也备忘下:增加<meta name="viewport" content="width=device-width, initial-scale=1. ...

  3. 判断是否联网_检测网络的类型为3G、2G、wap、wifi

    判断是否联网_检测网络的类型为3G.2G.wap.wifi  判断是否联网: /*** * judge Internet is available * * @author wei-spring * @ ...

  4. 关于http状态码204理解

    HTTP的状态码有很多种,主要有1xx(临时响应).2xx(成功).3xx(已重定向).4xx(请求错误)以及5xx(服务器错误)五个大类,每个大类还对应一些具体的分类.平时我们接触比较多的是200. ...

  5. Flink Program Guide (5) -- 预定义的Timestamp Extractor / Watermark Emitter (DataStream API编程指导 -- For Java)

    本文翻译自Pre-defined Timestamp Extractors / Watermark Emitter ------------------------------------------ ...

  6. python---连接MySQL第五页

    Connector/Python Connection Arguments A connection with the MySQL server can be established using ei ...

  7. Codeforces 293B

    Codeforces 293B 原题 题目描述:给出一个\(n \times m\)的网格, 给定一个整数\(k\),网格上的每个数都不超过\(k\),其中有的格子是\(0\),要求把这些格子变成\( ...

  8. How to convert string to wstring?

    How to convert string to wstring? - Codejie's C++ Space - C++博客     How to convert string to wstring ...

  9. 安装vmware tools失败解决方法

    失败提示: reating a new initrd boot image for the kernel. update-initramfs: Generating /boot/initrd.img- ...

  10. Spring、使用注解方式装配对象(@Resource、@Autowired)

    使用手工注解方式有两种方式@Resource.@Autowired 首先,引入注解所使用的Jar包  :common-annotations.jar 然后在beans.xml中加入命名空间空间 xml ...