天天看点

Android定制化------关机事件及增加功能

Android关机顺序

  • step1:按住电源按钮半秒钟(500ms)。
  • step2:之后此按键事件将会被

    PhoneWindowManager.java

    捕获,并且调用

    interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)

    由于这个方法定义了很多的按键事件捕获,下面只贴出电源按键这个case的部分。
/** {@inheritDoc} */
     @Override
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
     ...省略部分代码...
     case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                //此处就是具体实现电源按键事件
                    interceptPowerKeyDown(event, interactive);
                } else {
                //继续向下传递
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }
            ...省略部分代码...
     }
           

我们进一步跟踪

interceptPowerKeyDown(event, interactive);

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) 
{
    ...省略部分代码...
    // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
                || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
        if (!mPowerKeyHandled) {
            if (interactive) {
                // When interactive, we're already awake.
                // Wait for a long press or for the button to be released to decide what to do.
                //此处就是500毫秒超时事件(ViewConfiguration#getDeviceGlobalActionKeyTimeout())触发时启动 mPowerLongPress 线程。
                if (hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                }
            } else {
                wakeUpFromPowerKey(event.getDownTime());

                if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                    mBeganFromNonInteractive = true;
                } else {
                    final int maxCount = getMaxMultiPressPowerCount();

                    if (maxCount <= ) {
                        mPowerKeyHandled = true;
                    } else {
                        mBeganFromNonInteractive = true;
                    }
                }
            }
        }
}
           

上面的代码包含了对多种情形下对长按电源键时间的处理,系统将根据电源键被按住的时间长短以及相关按键的使用情况来决定如何恰当地处理当前的用户操作。

我们进一步跟踪此消息事件:

MSG_POWER_LONG_PRESS

实际上就是调用了此方法

powerLongPress();

private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                performAuditoryFeedbackForAccessibilityIfNeed();
            }
            showGlobalActionsInternal();
            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(behavior == LONG_PRESS_POWER_SHUT_OFF);
            break;
        }
    }
           

在这里我们终于能够识得庐山真面目了。

  • step3:由上面代码的Switch分支可知,当程序进去根据case,将移交给 GlobalActions类,该模块则负责显示关机选项的对话框,通常包括关闭电源、飞行模式和屏幕截图。也可能包括其他一些选项按键。GlobalActions 类实现了一个showdialog方法,该方法将根据当前系统支持的菜单内容来创建这个对话框。这里我们关注此方法

    showGlobalActionsInternal();

void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) {
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
        }
    }
           

我们可以进一步跟踪源码,进入GlobalActions.java,最终在此方法handleShow()创建了那个关机对话框的选项。

private void handleShow() {
        awakenIfNecessary();
        //这里我们重点关注createDialog()与prepareDialog()
        mDialog = createDialog();
        prepareDialog();
        ...省略部分代码...
    }
           

发现在此处

mWindowManagerFuncs.shutdown(false );

实现了关机功能,我们进一步跟踪

shutdown

发现其实定义在接口中,然而最终的实现确实在

WindowManagerService.java

之中。

@Override
    public void shutdown(boolean confirm) {
        ShutdownThread.shutdown(mContext, true);
    }

      /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void shutdown(final Context context, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        //此处才是最终显现了关机
        shutdownInner(context, confirm);
    }
           
  • step4:整个关机过程起始于ShutdownThread模块中的shutdowninner方法。该方法首先创建一个确认对话框给用户,用户可以选择确认关机或是取消关机操作。如果用户选择确认,则系统将真正进入关机流程。
  • step5:如上所述,当用户点击确认按钮后beginShutdownSequence方法将被调用以启动关机顺序。
private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }
        Log.d(TAG, "mmk_debug: checkAnimationFileExist. ");
        if (!checkAnimationFileExist()){
            // Throw up a system dialog to indicate the device is rebooting / shutting down. 显示正在关闭电源的对话框
            ProgressDialog pd = new ProgressDialog(context);

            // Path : Reboot to recovery and install the update
            //   Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
            //   (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
            //   UI: progress bar
            //
            // Path : Reboot to recovery for factory reset
            //   Condition: mRebootReason == REBOOT_RECOVERY
            //   UI: spinning circle only (no progress bar)
            //
            // Path : Regular reboot / shutdown
            //   Condition: Otherwise
            //   UI: spinning circle only (no progress bar)
            Log.d(TAG, "mmk_debug: check REBOOT_RECOVERY = " + mRebootReason);
            if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
                mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
                Log.e(TAG, "mmk_debug: not found /cache/recovery/uncrypt_file ?! mRebootUpdate = " + mRebootUpdate);
                if (mRebootUpdate) {
                    pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
                    pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_prepare));
                    pd.setMax();
                    pd.setProgressNumberFormat(null);
                    pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                    pd.setProgress();
                    pd.setIndeterminate(false);
                } else {
                    // Factory reset path. Set the dialog message accordingly.
                    pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                    pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_reset_message));
                    pd.setIndeterminate(true);
                }
            } else {
                pd.setTitle(context.getText((mReboot ? com.android.internal.R.string.reboot : com.android.internal.R.string.power_off)));
                pd.setMessage(context.getText((mReboot ? com.android.internal.R.string.reboot_progress : com.android.internal.R.string.shutdown_progress)));
                pd.setIndeterminate(true);
            }
            pd.setCancelable(false);
            pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

            pd.show();

            sInstance.mProgressDialog = pd;
        }else {
            if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {   
                mNeedUncrypt = new File(UNCRYPT_PACKAGE_FILE).exists();
                Log.d(TAG, "mmk_debug: find uncrypt_file and set mNeedUncrypt = " + mNeedUncrypt);
            }
        }
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        // make sure we never fall asleep again 阻止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;
        }

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

实际关机流程就是调用该类中的

run()

方法。

/**
     * 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) {
                // We don't allow apps to cancel this, so ignore the result.
                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);
        if (checkAnimationFileExist()) {
           intent.putExtra("PLAY_SHUTDOWN_ANIMATION",);
        }
        mContext.sendOrderedBroadcastAsUser(intent,
                UserHandle.ALL, null, br, mHandler, , null, null);

        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= ) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_BROADCAST_TIME - delay) *  *
                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
        }

        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) {
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }

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

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }

        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootUpdate) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }

        start_shutdownanim();

        // 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);
                } 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 <= ) {
                    Log.w(TAG, "Shutdown wait timed out");
                    break;
                } else if (mRebootUpdate) {
                    int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) *  *
                            (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
                            MAX_SHUTDOWN_WAIT_TIME);
                    status += RADIO_STOP_PERCENT;
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootUpdate) {
            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

            // If it's to reboot to install update, invoke uncrypt via init service.
            uncrypt();
        }else if(mNeedUncrypt) {
            uncrypt();
        }
        wait_shutdownanim_end();
        //这里就会实际调用底层方法来工作啦。
        rebootOrShutdown(mContext, mReboot, mRebootReason);
    }
           

那么到此为止呢,关机的流程已经理清楚了。在这里,其实增加一些功能也是很容易的事了。不过,由于我这套代码,默认就是增加了重启功能的。所以,在此也不再赘述这个问题了。

这里有一篇文章就是专门讲增加重启功能的。大家有兴趣的呢可以去看看,写的很详细。

点我就看如何增加重启

文章之中如有写的不对的地方,还望各位看官能够指出来。谢谢