• Android Framework层Power键关机流程(二,关机流程)


    二,关机流程

        从前一篇博文我们知道,当用户长按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




  • 相关阅读:
    关于table表格的一些问题
    leetcode 845. 数组中的最长山脉 做题笔记
    leetcode 845. 数组中的最长山脉 做题小结
    leetcode 925. 长按键入小结
    leetcode 925. 长按键入小结
    java单链表反转 详细讲述
    java单链表反转 详细讲述
    Leetcode 3. 无重复字符的最长子串 做题小结
    Leetcode 3. 无重复字符的最长子串 做题小结
    复变函数的幂函数
  • 原文地址:https://www.cnblogs.com/bill-technology/p/4130806.html
Copyright © 2020-2023  润新知