Android 2.2 滑盖(lid)的影响

来源:互联网 发布:nginx重启服务 编辑:程序博客网 时间:2024/04/23 22:39

前段时间项目之中遇到了按键灯不亮的问题,稍微看了一下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里面的千万不能随意改,不然会出大乱子。