深入解析Android关机

转自http://www.importnew.com/6356.html

Android 关机顺序

  • 当我们长按电源按钮时,手机里究竟发了什么?
  • 什么又是关机顺序?
  • Android的关机顺序与Linux桌面系统有何不同?
  • 如何更改关机菜单?

当我们说起Android的关机顺序时,许多诸如此类的问题便会涌进我们的脑袋。 不过,在继续阅读之前,建议您首先能对开机顺序有一个了解开机顺序文章

Android是基于Linux内核的开源操作系统。尽管x86(x86 是一系列计算机微处理器指令集及其架构的统称,这种架构基于Intel 8086 CPU)是大多数Linux系统所采用的处理器架构,然而绝大多数Android系统却运行于ARM架构之上(ARM,又称Advanced RISC Machine,其前身为Acorn RISC Machine),除了来自Intel的Xolo设备。这种移动设备使用了Atom 1. 6Ghz x86处理器。 但不论哪种架构,Android的关机顺序都区别于Linux的桌面系统,如Ubuntu、Fedora等。 本文主要介绍Android的关机顺序, 如果想更多地了解Linux桌面系统的关机顺序,请参考Linux的启动与关闭流程

下图详细阐释了Android的关机顺序。



第一步: 按住电源按钮半秒钟(500ms)。

第二步: 之后,PhoneWindowManager.java 将捕获长按电源按钮这一事件并调用“interceptKeyBeforeQueueing”方法。

下面是处理长按电源键事件的代码片段


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/** {@inheritDoc} */

@Override

public
int
interceptKeyBeforeQueueing(KeyEvent event,
int policyFlags,
boolean isScreenOn) {

....

....

....

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()) {

                            // 如果处在通话中且电源键挂断选项已启用,则按下电源键会结束当前通话

                            hungUp = telephonyService.endCall();

                       }

                   }
catch (RemoteException ex) {

                        Log.w(TAG,
"ITelephony threw RemoteException", ex);

                   }

               }

               interceptPowerKeyDown(!isScreenOn || hungUp

                       || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);

           }
else {

               mPowerKeyTriggered =
false;

               cancelPendingScreenshotChordAction();

               if
(interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {

                   result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;

               }

               mPendingPowerKeyUpCanceled =
false;

           }

          break;

      }

....

....

....

}

上面的代码包含了对多种情形下对长按电源键时间的处理,例如静默来电响铃、屏幕截图以及关闭电源等。 系统将根据电源键被按住的时间长短以及相关按键的使用情况来决定如何恰当地处理当前的用户操作。 当电源键被按下且没有截屏操作触发时interceptPowerKeyDown 将被调用,这时其他的按键响应(其他按键响应指 interceptKeyBeforeQueueing 中其他cases)将不会被触发。

下面的代码展示了 interceptPowerKeyDown 函数内容, 函数将注册一个回调函数,在500毫秒超时事件(ViewConfiguration#getGlobalActionKeyTimeout())触发时启动 mPowerLongPress 线程。


1

2

3

4

5

6

private
void
interceptPowerKeyDown(boolean
handled) {

  mPowerKeyHandled = handled;

  if
(!handled) {

       mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());

  }

}

mPowerLongPress 线程的实现如下:

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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;

            }

        }

    };

 

第三步: 由上面代码的Switch分支可知,当程序进去Long_Press_Power_Global_Options时控制将移交给 GlobalActions 类, 该模块则负责显示关机选项的对话框,这些选项在各Android发行版(各OEM厂商定制的Android系统, 不同的手机型号和不同版本的Android系统)中不尽相同,通常包括关闭电源、飞行模式和屏幕截图。也可能包括其他一些选项按键。GlobalActions 类实现了一个showdialog方法,该方法将根据当前系统支持的菜单内容来创建这个对话框。


1

2

3

4

5

6

7

8

9

10

11

void
showGlobalActionsDialog() {

    if
(mGlobalActions == null) {

        mGlobalActions =
new GlobalActions(mContext, mWindowManagerFuncs);

    }

    final
boolean keyguardShowing = keyguardIsShowingTq();

    mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());

    if
(keyguardShowing) {

         // 由于激活关机对话框需要长按电源键两秒以上,所以当对话框显示之后,屏幕的唤醒状态将被锁定,以方便用户浏览对话框中内容

        mKeyguardMediator.userActivity();

    }

}



 

第四步: 若用户选择“关闭电源“,则对系统的控制将交回给 PhoneWindowManager, 然后由PhoneWindowManager 启动关闭流程。

第五步: 整个关机过程起始于ShutdownThread模块中的shutdowninner方法。该方法首先创建一个确认对话框给用户, 用户可以选择确认关机或是取消关机操作。 如果用户选择确认,则系统将真正进入关机流程。



第六步: 如上所述,当用户点击确认按钮后beginShutdownSequence方法将被调用以启动关机顺序。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

private
static
void beginShutdownSequence(Context context) {

        synchronized
(sIsStartedGuard) {

            if
(sIsStarted) {

                Log.d(TAG,
"Shutdown sequence already running, returning.");

                return;

            }

            sIsStarted =
true;

        }

 

        // 显示正在关闭电源的对话框

        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);

 

        // 阻止CPU进入休眠状态

        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;

        }

 

        // 电源关闭前一直保持屏幕唤醒状态,以便提升用户体验

        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;

            }

        }

 

        // 启动负责关机顺序的线程

        sInstance.mHandler =
new Handler() {

        };

        sInstance.start();

    }

运行函数,启动实际的关机流程

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

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();

            }

        };

 

        /*

         *  写入一个系统参数,以防Android系统中的System Server

         * (一个运行于Dalvik虚拟机与真实系统内核间的server,负责虚拟机与内核的通信)在真实硬件重启前完成重启。

         * 当上述情况发生时, 则在System Server完成启动后重试之前的重启操作。

         */

        {

            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");

            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);

        }

 

        /*

         * 写入一个系统参数以便重启后进入安全模式

         */

        if
(mRebootSafeMode) {

            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY,
"1");

        }

 

        Log.i(TAG,
"Sending shutdown broadcast...");

 

        // 关闭移动通信

        mActionDone =
false;

        Intent intent =
new Intent(Intent.ACTION_SHUTDOWN);

        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

        mContext.sendOrderedBroadcastAsUser(intent,

                UserHandle.ALL,
null, br, mHandler,
0, null,
null);

 

        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) {

            }

        }

 

        // 关闭移动通信

        shutdownRadios(MAX_RADIO_WAIT_TIME);

 

        // 安全移除外部存储卡

        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");

 

        // 初始化变量,并设置关机超时时限

        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);

    }

 

第七步: 当rebootOrShutdown方法被调用时,系统控制权首先转至底层函数 nativeShutdown(在com_android_server_power_PowerManagerService。cpp中定义) 并最终调用android_reboot函数(定义于android_reboot.c中)来完成整个关机顺序


1

2

3

static
void
nativeShutdown(JNIEnv *env, jclass clazz) {

    android_reboot(ANDROID_RB_POWEROFF,
0, 0);

时间: 2024-12-03 04:51:44

深入解析Android关机的相关文章

xml-http返回一个String类型的数据(但是满足XML格式)要怎么解析Android

问题描述 http返回一个String类型的数据(但是满足XML格式)要怎么解析Android http返回一个String类型的数据(但是满足XML格式)要怎么解析Android 解决方案 http://blog.csdn.net/liuhe688/article/details/6415593 建议返回json格式 解决方案二: 那就用XML解析啊 DMOE解析 解决方案三: 现在基本上都是json格式了吧 可以使用fastjson 或者google的gson 可以看看别人的bloghttp

《深入解析Android 5.0系统》——第6章,第6.2节Android native层的同步方法

6.2 Android native层的同步方法 深入解析Android 5.0系统 Android在Linux提供的线程同步函数的基础上进行了二次封装,让实现线程同步更加简单方便.这些同步类和函数在native层的代码中出现的非常频繁. 6.2.1 互斥体Mutex和自动锁Autolock Mutex和Autolock是Android native层最常见的一种临界区保护手段,Autolock只是提供了一种更简便的使用Mutex的方法. Mutex是一个C++的类,它的接口如下所示: clas

《Android 网络开发与应用实战详解》——2.2节解析Android SDK实例

2.2 解析Android SDK实例 Android 网络开发与应用实战详解 在Android安装后的目录中有一个名为"samples"的子目录,在里面保存了SDK中的几个演示实例.这些实例从不同的方面展示了SDK的特性.例如"android-3"目录中的实例文件结构如图2-8所示. 在本节的内容中,将引领读者浏览图2-8中各个实例的效果. 1.HelloActivity 这和编程语言中的Hello Word程序类似,是一个Android平台上的最简单程序,运行后

《深入解析Android 5.0系统》——第1章,第1.4节下载源码

1.4 下载源码 深入解析Android 5.0系统 对于国内的开发者而言,下载Android的源码从来不是一件简单的事.因为一些原因,目前国内已经不能访问Android的源码网站了,最近好像连Android的官方网站也访问不了.对公司而言这不是难题,因为很多公司都有国外的VPN账号或者海外服务器.笔者下载Android的源码就是通过亚马逊的云服务器完成的.只要有国内大型银行的信用卡帐号,就可以在亚马逊平台上免费开通一个EC2服务器(免费使用期一年).亚马逊提供的带宽差不多有一个G,不到半个小时

Android关机充电的第一张图片位置偏了怎么修改?

问题描述 Android关机充电的第一张图片位置偏了怎么修改?

《深入解析Android 5.0系统》——第1章,第1.1节安装操作系统

第1章 建立Android系统开发环境 深入解析Android 5.0系统 在开始研究Android系统之前,需要预先准备好系统开发需要的各种资源:包括操作系统.各种开发工具以及Android源码等.本章将介绍这些资源的获取途径和安装方法. 在阅读本书前,读者需要掌握一些必要的技能.Android应用使用Java语言开发,底层使用C/C++开发,因此,掌握Java 语言和 C/C++语言是进行Android 系统开发的必要条件.Android运行在Linux内核(从标准的Linux内核修改移植而

xml-如何解析 android 中的 XML?

问题描述 如何解析 android 中的 XML? 我需要把下面的 xml 解析 为输出: String id="3" String name="str1" String path="/mnt/sdcard/path2" String type="2" String desc="des3)" 在 android中如何实现呢? <xmldump> <mfs id="3"

解析android framework下利用app_process来调用java写的命令及示例

解析android framework下利用app_process来调用java写的命令及示例          在android SDK的framework/base/cmds目录下了,有不少目录,这些目的最终都是build出一个bin文件,再存放到/system/bin目录下,对于C/CPP写的命令,我们还是比较好理解的,都有一个main函数作为入口,但是在cmds目录下还有一些原生代码是java的,比如input.settings,那么这种类型的命令是怎么实现的呢?       笔者研习了

《深入解析Android 虚拟机》——第2章,第2.1节虚拟机的作用

第2章 Java虚拟机基础 深入解析Android 虚拟机 Java虚拟机和Android虚拟机十分相似,所以在本书中将以Java虚拟机开始,逐步引领广大读者步入Android虚拟机的世界.在本章的内容中,将简要讲解Java虚拟机技术的基本知识,为读者步入本书后面知识的学习打下基础. 2.1 虚拟机的作用 虚拟机(Virtual Machine)这一概念最初由波佩克与戈德堡定义,是指通过软件模拟的具有完整硬件系统功能的.运行在一个完全隔离环境中的完整计算机系统.由此可见,虚拟机是跟特定硬件无关的