Android 2.2 滑盖(lid)的影响

前段时间项目之中遇到了按键灯不亮的问题,稍微看了一下framework的代码,发现是因为滑盖的开关状态对其有影响。

在PhoneWindowManager中有定义:

    boolean mLidOpen;

这里没有初始化,所以为false,而且硬件确实没有装滑盖,所以PhoneWindowManager对就不会对mLidOpen进行更新。

    void readLidState() {
        try {
            int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID);  //return -1
            if (sw >= 0) {
                mLidOpen = sw == 0;
            }
        } catch (RemoteException e) {
            // Ignore
        }
    }

这里,mLidOpen也不会更新,输入事件不会主动上报。

    /** {@inheritDoc} */
    public boolean preprocessInputEventTq(RawInputEvent event) {
        switch (event.type) {
            case RawInputEvent.EV_SW:
                if (event.keycode == RawInputEvent.SW_LID) {
                    // lid changed state
                    mLidOpen = event.value == 0;
                    boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen);
                    updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
                    // ...省略部分代码
                }
        }
        return false;
    }

因此,PhoneWindowManager会调用PowerManagerService的setKeyboardVisibility

    /** {@inheritDoc} */
    public void adjustConfigurationLw(Configuration config) {
        readLidState();
        final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen;
        mPowerManager.setKeyboardVisibility(lidOpen);
        // ...省略部分代码
    }

在PowerManagerService中,会根据滑盖状态,决定键盘的可见状态。

    public void setKeyboardVisibility(boolean visible) {
        synchronized (mLocks) {
            if (mKeyboardVisible != visible) {
                mKeyboardVisible = visible;
                // ...省略部分代码
            }
        }
    }

也就是说,如果滑盖没有打开,键盘是不可见的(用过滑盖手机的都知道)。

这里要命的就是,PowerManagerService会根据键盘的可见状态,来决定亮不亮键盘灯,因此,我们的键盘灯一直是不亮的。

    private int applyKeyboardState(int state) {
        int brightness = -1;
        if (!mKeyboardVisible) {
            brightness = 0;
        }
        // ...省略部分代码
    }
现在反思一下,我们要亮的是按键灯,不是键盘灯,现在两个灯等同为一个灯,键盘跟按键还是有区别的吧,android没有考虑到按键灯。所以我们只能把键盘灯当成按键灯使用了。

解决方法看起来很简单,就是回到问题的源头,在PhoneWindowManager中,就把mLidOpen的值初始化为true,这样它就一直会保持在true的状态,相当于滑盖一直是开的,因此键盘就一只是可见的,键盘灯就会亮了。


但是事情远远没有这么简单,自从改了这个以后,问题接踵而至,各种传感器失效,由于传感起也是刚开始调,因此不会不知道是就是因为改了这个值引起的。

1、重力传感器检测到方向有改变时,系统会调用PhoneWindowManager的rotationForOrientationLw方法,继而根据角度更新界面。

    public int rotationForOrientationLw(int orientation, int lastRotation,
            boolean displayEnabled) {

        synchronized (mLock) {
            switch (orientation) {
                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                    //always return landscape if orientation set to landscape
                    return mLandscapeRotation;
                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                    //always return portrait if orientation set to portrait
                    return mPortraitRotation;
            }
            // case for nosensor meaning ignore sensor and consider only lid
            // or orientation sensor disabled
            //or case.unspecified

            if (mLidOpen) {
                return mLidOpenRotation;
            } 
            // ...省略部分代码
        } 
    } 
这里,因为 mLidOpen一直保持在true状态,因此屏幕旋转角度一直保持在mLidOpenRotation。任凭你怎么转,屏幕就是不旋转。

mLidOpenRotation在frameworks/base/core/res/res/values/config.xml中读取。

因此可以感叹android的逻辑是多么严谨,如果你滑盖一直打开,说明就是有按键操作,这时候在转屏幕,是不合理的。


2、打电话时,如果脸贴近屏幕,接近传感器会将屏幕背光灯熄灭(省电,且防止误操作),这本身也是个很人性化的操作,没想到也会影响。在InCallScreen中

    /* package */ void updateProximitySensorMode(Phone.State state) {
        if (proximitySensorModeEnabled()) {
            synchronized (mProximityWakeLock) {
                // turn proximity sensor off and turn screen on immediately if
                // we are using a headset, the keyboard is open, or the device
                // is being held in a horizontal position.
                boolean screenOnImmediately = (isHeadsetPlugged()
                            || PhoneUtils.isSpeakerOn(this)
                            || ((mBtHandsfree != null) && mBtHandsfree.isAudioOn())
                            || mIsHardKeyboardOpen
                            || mOrientation != AccelerometerListener.ORIENTATION_VERTICAL);
                if (((state == Phone.State.OFFHOOK) || mBeginningCall) && !screenOnImmediately) {
                    // Phone is in use!  Arrange for the screen to turn off
                    // automatically when the sensor detects a close object.
                    if (!mProximityWakeLock.isHeld()) {
                        if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");
                        mProximityWakeLock.acquire();
                    } else {
                        if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held.");
                    }
                }            
            }
        }
    }

这里的mIsHardKeyboardOpen会一直为true,因此就不会去acquire这个wake lock,因此靠近屏幕就不会熄背光灯。值得注意的是还依赖mOrientation,它是竖直方向才aquire,因为只有举起电话之时,才代表接起了电话。

mIsHardKeyboardOpen在onConfigurationChanged得到赋值

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
            mIsHardKeyboardOpen = true;
        } else {
            mIsHardKeyboardOpen = false;
        }

        // Update the Proximity sensor based on keyboard state
        updateProximitySensorMode();
        super.onConfigurationChanged(newConfig);
    }

因此,应用层根据Configuration中的hardKeyboardHidden来判断键盘是否可见。

这个值是在PhoneWindowManager中决定的,

    /** {@inheritDoc} */
    public void adjustConfigurationLw(Configuration config) {
        readLidState();
        final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen;
        mPowerManager.setKeyboardVisibility(lidOpen);
        config.hardKeyboardHidden = determineHiddenState(lidOpen,
                mLidKeyboardAccessibility, Configuration.HARDKEYBOARDHIDDEN_YES,
                Configuration.HARDKEYBOARDHIDDEN_NO);
        config.navigationHidden = determineHiddenState(lidOpen,
                mLidNavigationAccessibility, Configuration.NAVIGATIONHIDDEN_YES,
                Configuration.NAVIGATIONHIDDEN_NO);
        config.keyboardHidden = (config.hardKeyboardHidden
                        == Configuration.HARDKEYBOARDHIDDEN_NO || mHasSoftInput)
                ? Configuration.KEYBOARDHIDDEN_NO
                : Configuration.KEYBOARDHIDDEN_YES;
    }
因此,PowerManagerService可能跟Configuration针对键盘状态可能不同,取决于determineHiddenState的返回值,determineHiddenState综合了根据滑盖状态,和mLidKeyboardAccessibility,来决定hardKeyboardHidden的状态,mLidKeyboardAccessibility从config.xml中读取

    <!-- Indicate whether the lid state impacts the accessibility of
         the physical keyboard.  0 means it doesn't, 1 means it is accessible
         when the lid is open, 2 means it is accessible when the lid is
         closed.  The default is 1. -->
    <integer name="config_lidKeyboardAccessibility">1</integer>

再看determineHiddenState,意思滑盖关闭的时候,如果mLidKeyboardAccessibility为1,代表键盘同时不可见。

    private int determineHiddenState(boolean lidOpen,
            int mode, int hiddenValue, int visibleValue) {
        switch (mode) {
            case 1:
                return lidOpen ? visibleValue : hiddenValue;
            case 2:
                return lidOpen ? hiddenValue : visibleValue;
        }
        return visibleValue;
    }


这些写出来很容易,其实定位问题花了我好长时间,因此吸取一个教训,framework里面的千万不能随意改,不然会出大乱子。



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值