Android关机流程源码分析

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


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

[java] view plaincopy
  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做进一步处理

[java] view plaincopy
  1. private void interceptPowerKeyDown(boolean handled) {  
  2.     mPowerKeyHandled = handled;  
  3.     if (!handled) {  
  4.         //隔500ms处理电源按键事件  
  5.         mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());  
  6.     }  
  7. }  
这里的mHandler是在初始化PhoneWindowManager对象时创建的

[java] view plaincopy
  1. public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs,  
  2.         LocalPowerManager powerManager) {  
  3.     ...  
  4.     mHandler = new PolicyHandler();  
  5.     ...  
  6. }  
将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理

[java] view plaincopy
  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,因此将显示关机对话框

[java] view plaincopy
  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;  
关机对话框显示:

[java] view plaincopy
  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函数来显示对话框

[java] view plaincopy
  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. }  


[java] view plaincopy
  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()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。

[java] view plaincopy
  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编程模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。

[java] view plaincopy
  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. }  
对于关机选项,其单击和长按事件处理过程如下:

[java] view plaincopy
  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类型变量:

[java] view plaincopy
  1. final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();  
通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象

[java] view plaincopy
  1. public static WindowManagerPolicy makeNewWindowManager() {  
  2.     return sPolicy.makeNewWindowManager();  
  3. }  
该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程

Policy.java

[java] view plaincopy
  1. public WindowManagerPolicy makeNewWindowManager() {  
  2.     return new PhoneWindowManager();  
  3. }  
在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy

[java] view plaincopy
  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线程中执行初始化函数

[java] view plaincopy
  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函数
[java] view plaincopy
  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实现:

[java] view plaincopy
  1. public void shutdown() {  
  2.     ShutdownThread.shutdown(mContext, true);  
  3. }  
  4.   
  5. public void rebootSafeMode() {  
  6.     ShutdownThread.rebootSafeMode(mContext, true);  
  7. }  
WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:

[java] view plaincopy
  1. public static void shutdown(final Context context, boolean confirm) {  
  2.     mReboot = false;  
  3.     mRebootSafeMode = false;  
  4.     shutdownInner(context, confirm);  
  5. }  
该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。

[java] view plaincopy
  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函数进入关机流程。

[java] view plaincopy
  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来完成关机操作

[java] view plaincopy
  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, 0nullnull);  
  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服务

[java] view plaincopy
  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. }  
这里是启动关机震动并关闭电源

[java] view plaincopy
  1. public static void lowLevelShutdown() {  
  2.     nativeShutdown();  
  3. }  
这里通过JNI调用C++的关机函数

[java] view plaincopy
  1. static void nativeShutdown(JNIEnv *env, jobject clazz) {  
  2.     delFlag();  
  3.     android_reboot(ANDROID_RB_POWEROFF, 00);  
  4. }  
android_reboot函数最终通过Linux系统调用关闭系统。到此关机操作就基本完成了。

posted on 2015-01-18 11:40  王亮1  阅读(170)  评论(0编辑  收藏  举报

导航