android4.4 车载灭屏 按任意键及触摸屏幕恢复亮屏

车载上的android4.4系统,基本上常亮。但最近需要一个新功能可以在launcher新增一个按钮,点击的时候。屏幕亮度为0,但实际上不等于按power键,不会睡眠。

然后可以按任意键恢复亮度,包括触屏事件。


一、PowerManagerService原先屏幕亮度流程

PowerManagerService是通过updateDisplayPowerStateLocked函数,把亮度更新到DisplayPowerController那块,然后再去调用lightsService获取背光的light,再去设置背景光的亮度。

但是这有问题,在PowerManagerService的updateDisplayPowerStateLocked函数,通常有个最低的亮度值(一般为10),如果低于这个亮度还是为这个值。

所以调用PowerManager的setBacklightBrightness,哪怕设的为0,最终亮度也为10.

    private void updateDisplayPowerStateLocked(int dirty) {
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
            int newScreenState = getDesiredScreenPowerStateLocked();
            if (newScreenState != mDisplayPowerRequest.screenState) {
                if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
                        && mDisplayPowerRequest.screenState
                                != DisplayPowerRequest.SCREEN_STATE_OFF) {
                    mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
                }

                mDisplayPowerRequest.screenState = newScreenState;
                nativeSetPowerState(
                        newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
                        newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
            }

            int screenBrightness = mScreenBrightnessSettingDefault;
            float screenAutoBrightnessAdjustment = 0.0f;
            boolean autoBrightness = (mScreenBrightnessModeSetting ==
                    Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
            if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                screenBrightness = mScreenBrightnessOverrideFromWindowManager;
                autoBrightness = false;
            } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
                screenBrightness = mTemporaryScreenBrightnessSettingOverride;
            } else if (isValidBrightness(mScreenBrightnessSetting)) {
                screenBrightness = mScreenBrightnessSetting;
            }
            if (autoBrightness) {
                screenBrightness = mScreenBrightnessSettingDefault;
                if (isValidAutoBrightnessAdjustment(
                        mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
                    screenAutoBrightnessAdjustment =
                            mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
                } else if (isValidAutoBrightnessAdjustment(
                        mScreenAutoBrightnessAdjustmentSetting)) {
                    screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
                }
            }
            screenBrightness = Math.max(Math.min(screenBrightness,//这个就是最小亮度的调整。
                    mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
            screenAutoBrightnessAdjustment = Math.max(Math.min(
                    screenAutoBrightnessAdjustment, 1.0f), -1.0f);
            mDisplayPowerRequest.screenBrightness = screenBrightness;
            mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                    screenAutoBrightnessAdjustment;
            mDisplayPowerRequest.useAutoBrightness = autoBrightness;

            mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();

            mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();

            mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);//最终都是设置到DisplayPowerController
            mRequestWaitForNegativeProximity = false;


二、PowerManagerService设置背光文件节点

因此我们的选择还是直接设置背光的文件节点,况且我们还要事先获取背光值,用来下次恢复亮度的时候,设置之前的亮度值。

代码如下我们再PowerManager中做了两个接口turnoffBacklight和turnOnBacklight来控制背光,最终通过binder调用到PowerManagerService中。

在PowerManagerService中直接通过了写节点/读节点的方式,来设置/获取背光。

    @Override // Binder call
    public void turnOffBacklight() {
        int lightBrightness = getBackLightBrightness();
        if (lightBrightness != 0) {
            mBackLightBrightness = getBackLightBrightness();
            setBackLightBrightness(0);
        }
    }

    @Override // Binder call
    public boolean turnOnBacklight() {
        int lightBrightness = getBackLightBrightness();
        if (lightBrightness == 0) {
            Slog.d(TAG, "turnOnBacklight setBackLightBrightness :" + mBackLightBrightness);
            setBackLightBrightness(mBackLightBrightness);
            return true;
        }
        return false;
    }

    private int getBackLightBrightness() {
        int level = -1;
        File localFile = new File("/sys/class/leds/lcd-backlight/brightness");
        if (!localFile.canRead()) {
            Slog.w(TAG, "/sys/class/leds/lcd-backlight can not read!");
            return level;
        }
        try {
            FileInputStream in = new FileInputStream(localFile);
            byte[] b = new byte[4];
            in.read(b);
            int count = 0;
            for (int i = 0; i < 4; i++) {
                if (b[i] >= '0' && b[i] <= '9') {//非数字去除
                    count++;
                } else {
                    break;
                }
            }
            String str = new String(b, 0, count);
            str = str.replaceAll("\\s+", "");//去除空格,换行符等
            level = Integer.parseInt(str);

            in.close();

        } catch (Exception e) {
        }
        return level;
    }

    private void setBackLightBrightness(int level) {
        File localFile = new File("/sys/class/leds/lcd-backlight/brightness");
        if (!localFile.canWrite()) {
            Slog.w(TAG, "/sys/class/leds/lcd-backlight can not write!");
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(localFile);
            fos.write(String.valueOf(level).getBytes());
            fos.close();
        } catch (Exception e) {
        }
    }

我们再Launcher的按钮点击后调用PowerManager中的turnoffBacklight函数,灭屏。


三、恢复屏幕亮度

3.1 按键

最后我们在按键和触屏的时候恢复屏幕亮度,普通按键的流程会先到PhoneWindowManager的interceptKeyBeforeQueueing函数先处理,我们可以在这个函数中先进行背光亮度的判读, 部分代码如下:

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            return 0;
        }

        if (mPowerManager.isScreenOn()) {
            if (mPowerManager.turnOnBacklight()) {
                return 0;
            }
        }.......

逻辑无非两种情况,调用mPowerManager.isScreenOn函数

1.如果isScreenOn是返回true的。因为PowerManagerService的流程没有改变,系统还认为屏幕是亮着的。

所以我们turnOnBacklight,如果这个时候屏幕的亮度为0,代表我们之前调用过turnoffBacklight函数把背景光亮度设置为0了,我们把它恢复的亮度,而函数turnOnBacklight返回true,在interceptKeyBeforeQueueing函数中直接return了,代表这次的按键就点亮屏幕了,不会走interceptKeyBeforeQueueing的后续流程了,返回0也不会最后传给用户了。

如果这个时候有亮度,代表之前没有调用turnoffBacklight,函数turnOnBacklight返回false,继续interceptKeyBeforeQueueing的原有流程。代表如果屏幕亮着,按键处理走自己的流程。

2.如果isScreenOn返回false,代表现在灭屏了。那么这是PowerManagerService的流程了,和我们的背景光无关。


3.2 触屏

触屏的话流程会有一个不一样,因为在NativeInputManager的interceptMotionBeforeQueueing函数中,只有当isScreenOn是false的时候才会调用上层的interceptMotionBeforeQueueingWhenScreenOff函数。也就是灭屏的时候才会到PhoneWindowManager的interceptMotionBeforeQueueingWhenScreenOff函数,而我们恰恰是要在PowerManagerService亮屏的时候,进行我们的逻辑。

void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
    // Policy:
    // - Ignore untrusted events and pass them along.
    // - No special filtering for injected events required at this time.
    // - Filter normal events based on screen state.
    // - For normal events brighten (but do not wake) the screen if currently dim.
    if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
        if (isScreenOn()) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;

            if (!isScreenBright()) {
                policyFlags |= POLICY_FLAG_BRIGHT_HERE;
            }
        } else {
            JNIEnv* env = jniEnv();
            jint wmActions = env->CallIntMethod(mServiceObj,
                        gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
                        policyFlags);
            if (checkAndClearExceptionFromCallback(env,
                    "interceptMotionBeforeQueueingWhenScreenOff")) {
                wmActions = 0;
            }

            policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE;
            handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
        }
    } else {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    }
}
所以在PhoneWindowManager的interceptMotionBeforeQueueingWhenScreenOff函数处理不行,最后只能到应用进程处理,看我之前几篇分析按键的博客知道,触屏事件最终会调用到ViewRootImpl的各个InputStage中,而触屏和按键会走到ViewPostImeInputStage中去

    final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {//触屏事件
                    if (mPowerManager.isScreenOn()) {
                        if (mPowerManager.turnOnBacklight()) {
                            return FORWARD;
                        }
                    }
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }
这里的话流程和按键那边的处理逻辑是一样的,就不分析了。


四、注意

有一点我们必须注意,手机如果要杀应用的话,输入法绝对不能杀,杀了输入法后,会导致输入法没有重启的这段时间,底层按键不能分发到上层应用。具体原因,后续分析。



你可能感兴趣的:(android电源,android按键)