二,关机流程

从前一篇博文我们知道,当用户长按Power键时会弹出(关机、重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框。那么从选项对话框到关机确认对话框又是一个什么流程呢。下面我们在简单分析一下:

showGlobalActionsDialog()-->showDialog()-->handleShow()-->createDialog()-->onPress()-->shutdown()

PhoneWindowManager.java

    void showGlobalActionsDialog() {

        ......

        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());

        ......

    }



GlobalActions.java



    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {

        ......

        handleShow();

        ......

    }





    private void handleShow() {

        ......

        mDialog = createDialog();

        ......

    }



    private GlobalActionsDialog createDialog(){

        ......

        mItems = new ArrayList<Action>();





        // first: power off

        mItems.add(

            new SinglePressAction(

                    com.android.internal.R.drawable.uirom_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;

                }

            });

        ......

    }

上述代码中的mWindowManagerFuncs实际上是WindowManagerService的对象,该对象有PhoneWindowManager的init的方法传入GlobalActions的构造函数中,并在上述代码中进行调用。下面这一行代码是调用的关键代码。

mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);

下面是弹出“关机确认对话框”的堆栈:

01-16 18:08:21.497 D/bill    (  720): java.lang.Throwable

01-16 18:08:21.497 D/bill    (  720): at com.android.server.power.ShutdownThread.shutdown(ShutdownThread.java:175)

01-16 18:08:21.497 D/bill    (  720): at com.android.server.wm.WindowManagerService.shutdown(WindowManagerService.java:5783)01-16 18:08:21.497 D/bill    (  720):at com.android.internal.policy.impl.GlobalActions$2.onPress(GlobalActions.java:352)//WindowManagerService实现了接口WindowsManagerFuncs

01-16 18:08:21.497 D/bill    (  720): at com.android.internal.policy.impl.GlobalActions.onClick(GlobalActions.java:581)

01-16 18:08:21.497 D/bill    (  720): at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:952)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AdapterView.performItemClick(AdapterView.java:299)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView.performItemClick(AbsListView.java:1152)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView$PerformClick.run(AbsListView.java:3014)

01-16 18:08:21.497 D/bill    (  720): at android.widget.AbsListView$3.run(AbsListView.java:3865)

01-16 18:08:21.497 D/bill    (  720): at android.os.Handler.handleCallback(Handler.java:808)

01-16 18:08:21.497 D/bill    (  720): at android.os.Handler.dispatchMessage(Handler.java:103)

01-16 18:08:21.497 D/bill    (  720): at android.os.Looper.loop(Looper.java:193)

01-16 18:08:21.497 D/bill    (  720): at android.os.HandlerThread.run(HandlerThread.java:61)

从这里(shutdown())我们正式进入关机流程的关键。

shutdown()<ShutdownThread.java> --->shutdownInner() --->beginShutdownSequence()--->run()--->rebootOrShutdown()--->lowLevelShutdown()<PowerManagerService.java>--->



源码来自:https://github.com/android/platform_frameworks_base/blob/master/services/java/com/android/server/power/ShutdownThread.java

 public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}

注!

参数2:confir;关机操作前是否需要用户进行确认

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

        //longPressBehavior的值标示当前长按Power操作意向(关机、重启。。。)

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

        }

    }

注:上述代码中,如果需要用户确认关机操作,则会弹出对话框,在对话框的确认按钮被触发时,调用beginShutdownSequence()方法继续关机流程。如果无需用户确认,则直接调用beginShutdownSequence()进入下一个关机流程节点。

beginShutdownSequence()有些手机厂商常常会在这里添加一些定制功能,例如在对话框中添加“下次快速开机”,定制关机动画等等。随后会根据不同平台进行讲解。下面这张图是Android原生系统的关机画面(对应下面加粗显示的代码):

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

    }

注解!

①上述红色代码中的作用主要是为了防止手机进入休眠状态,从代码中我们看到,此时通过PowerManager的newWakeLock方法生成了PowerManager.WakeLock对象。newWakeLock()是PowerManager中最为常用的方法,该对象是一种锁机制,通过该对象可以控制设备的电源状态。在生成WakeLock实例时通过第一个参数的传入只开控制获取不同的WakeLock,主要是不同的lock对CPU,屏幕,键盘灯有不同的影响。如下:

  1. PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
  2. SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
  3. SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
  4. FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度

②Wake Lock 是一种锁的机制,只要有人拿着这个锁,系统九五案发进入休眠,可以被用户动态程序和内核获得,这个锁可以使有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了,或者超时了,内核就会启动休眠的那套机制来进入休眠。PowerManager.WakeLock有加锁和解锁的两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非是显示的放开,否则是不会解锁的,所以这种锁用起来要非常小心,第二种锁是超时锁,这种锁会在锁住一段时间后自动解锁。

sInstance.mCpuWakeLock.setReferenceCounted(false);是设置锁的方式为永久的锁住。

sInstance.mCpuWakeLock.acquire(); 加锁

④上述蓝色代码的作用是为了保证用户体验,保持屏幕、键盘的亮度

⑤接着启动关机线程,进入关机流程的下一个节点。



/**

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

                actionDone();//这里用于接受关机广播,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;

        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);//关闭ActivityManagerService

            } catch (RemoteException e) {

            }

        }





        Log.i(TAG, "Shutting down package manager...");





        final PackageManagerService pm = (PackageManagerService)

            ServiceManager.getService("package");

        if (pm != null) {

            pm.shutdown();//关闭PackageManagerService服务

        }





        // 关闭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);//关闭MountService

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

    public static void rebootOrShutdown(boolean reboot, String reason) {

        if (reboot) {

            Log.i(TAG, "Rebooting, reason: " + reason);

            PowerManagerService.lowLevelReboot(reason);

            Log.e(TAG, "Reboot failed, will attempt shutdown instead");

        } 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();//关闭电源

    }

}





     /**

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

        SystemProperties.set("sys.powerctl", "shutdown");//这里通过修改Android属性进行关机

    }

注:上述代码中,红色加粗部分为关机关键代码,我也可以通过adb 命令进行修改Android系统的属性进行关机,具体命令如下

adb shell setprop sys.powerctl shutdown

Android Framework层Power键关机流程(二,关机流程)的更多相关文章

  1. Android Framework层Power键关机流程(一,Power长按键操作处理)

    一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...

  2. 怎样从C++代码直接訪问android framework层的WifiService

    说究竟,Java层的service就是就C++层的binder的封装.所以从原理上来讲通过C++代码直接訪问android framework层的service是全然可能的,这篇文章以訪问WifiSe ...

  3. Android 手动按power键上锁,没有锁屏提示音,无法恢复【单机必现】

    測试步骤 [測试版本号]T0606 [模块版本号] NAVI锁屏:5.0.0.ck [測试步骤] 1.手动按power键上锁, [測试结果] 没有锁屏提示音,无法恢复[单机必现] [预期结果] 有提示 ...

  4. Android framework层实现实现wifi无缝切换AP

    http://www.linuxidc.com/Linux/2013-12/93476.htm Android市场上有一款叫Wifijumper的软件,实现相同ssid的多个AP之间根据wifi信号的 ...

  5. Android 7.0 Power 按键处理流程

    Android 7.0  Power 按键处理流程 Power按键的处理逻辑由PhoneWindowManager来完成,本文只关注PhoneWindowManager中与Power键相关的内容,其他 ...

  6. Android下添加新的自定义键值和按键处理流程【转】

    本文转载自: Android下添加新的自定义键值和按键处理流程     说出来不怕大家笑话,我写这篇博客的原因在于前几天去一个小公司面试Android系统工程师,然后在面试的时候对方的技术总监问了我一 ...

  7. 关于一条定制长按Power键弹出Dialog的需求

    如题,需要定制长按Power键弹出的Dialog,UI上的大致效果是:全屏,中间下拉按钮“Swipe Down To Power Off”下拉关机,底部左右两侧“Reboot”,“Cancel”按钮, ...

  8. 如何调试Android Framework?

    Linus有一句名言广为人知:Read the fucking source code. 但其实,要深入理解某个软件.框架或者系统的工作原理,仅仅「看」代码是远远不够的.就拿Android Frame ...

  9. Android按键事件传递流程(二)

    5    应用层如何从Framework层接收按键事件 由3.2和4.5.4节可知,当InputDispatcher通过服务端管道向socket文件描述符发送消息后,epoll机制监听到了I/O事件, ...

随机推荐

  1. jQuery右键菜单contextMenu使用实例

    在最近项目中需要频繁的右键菜单操作.我采用了contextMenu这款jQuery插件. 参考网址:http://www.jb51.net/article/58709.htm 官网demo http: ...

  2. Java堆和栈详解

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  3. linux 如何开机自动运行sh脚本

    vi /etc/rc.d/rc.local #自动启动oracleecho 502 >/proc/sys/vm/hugetlb_shm_group su - oracle -c 'sh /dat ...

  4. Hadoop 2.4.x集群安装配置问题总结

    配置文件:/etc/profile export JAVA_HOME=/usr/java/latest export HADOOP_PREFIX=/opt/hadoop-2.4.1 export HA ...

  5. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用异步及存储过程

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第九篇:为ASP.NET MVC应用程序 ...

  6. (转)内联(inline)函数与虚函数(virtual)的讨论

    本文转自: http://topic.csdn.net/t/20051220/09/4469273.html 函数的inline属性是在编译时确定的, 然而,virtual的性质是在运行时确定的,这两 ...

  7. Export Data from mysql Workbench 6.0

    原文地址:export-data-from-mysql-workbench-6-0 问题描述 I'm trying to export my database, using MySQL Workben ...

  8. Scrum Meeting 1-20151201

    任务安排 姓名 今日任务 明日任务 困难 董元财 学习下拉刷新的实现 完成下拉刷新的实现 手机的点击动作长按和下拉有类似的地方,比较难解决 胡亚坤 学习圆形头像代码设计 完成圆形头像代码设计 无 刘猛 ...

  9. 小计-git

    今天在发布项目的时候遇到了一个问题,就是项目一直提示与最新版本有差异,导致发布不不成功.仔细考虑了一下,自己这次的开发与原来的是不一样的,父子工程,不是单模块开发,发现原来是别人发布过他们的模块到ma ...

  10. MFC编程入门之十一(对话框:模态对话框及其弹出过程)

    加法计算器对话框程序大家照着做一遍后,相信对基于对话框的程序有了些解了,有个好的开始对于以后的学习大有裨益.趁热打铁,这一节讲讲什么是对话框和非模态对话框,以及模态对话框怎样弹出. 一.模态对话框和非 ...