android O 打开设置>声音>“点按时震动问题”

主要原因是和导航栏和屏幕最下方3个按键的属性配置有关,因为在PhoneWindowManager中调用方法performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);会去判断policyFlags & WindowManagerPolicy.FLAG_VIRTUAL,policyFlags这一属性。

下面介绍一下事件的处理流程:

1.对象的创建

InputManagerService的初始化

InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象

2.事件的传递

RawEvent是待发出去的事件,InputReader经由QueueInputListener就可以关联到InputDispatch,最后由InputDispatch将事件处理或分发出去。

  1. InputManager::InputManager(  
  2.         const sp<EventHubInterface>& eventHub,  
  3.         const sp<InputReaderPolicyInterface>& readerPolicy,  
  4.         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {  
  5.     mDispatcher = new InputDispatcher(dispatcherPolicy);  
  6.     mReader = new InputReader(eventHub, readerPolicy, mDispatcher);  
  7.     initialize();  
  8. }  
  1. InputReader::InputReader(const sp<EventHubInterface>& eventHub,  
  2.         const sp<InputReaderPolicyInterface>& policy,  
  3.         const sp<InputListenerInterface>& listener) :  
  4.         mContext(this), mEventHub(eventHub), mPolicy(policy),  
  5.         mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),  
  6.         mConfigurationChangesToRefresh(0) {  
  7.     mQueuedListener = new QueuedInputListener(listener);  
  8.   
  9.     ...  
  10. }  

InputManager里创建了InputDispatch和InputReader,就是在此时将这两者关联了起来。以mDispatcher为参数创建了InputReader,mDispatcher就是InputReader的实际监听者,那么InputReader一收到事件就要主动通知监听者mDispatcher把这个事件处理掉。

2.1. InputReader

InputReader会创建InputReaderThread线程,threadloop()返回true表示该线程会一直执行loopOnce(). 

  1. bool InputReaderThread::threadLoop() {  
  2.     mReader->loopOnce();  
  3.     return true;  
  4. }  
  1. void InputReader::loopOnce() {  
  2.   
  3.     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);  
  4.   
  5.     { // acquire lock  
  6.         AutoMutex _l(mLock);  
  7.   
  8.         if (count) {  
  9.             processEventsLocked(mEventBuffer, count);  
  10.         }  
  11.   
  12.     } // release lock  
  13.   
  14.     // Flush queued events out to the listener.  
  15.     // This must happen outside of the lock because the listener could potentially call  
  16.     // back into the InputReader's methods, such as getScanCodeState, or become blocked  
  17.     // on another thread similarly waiting to acquire the InputReader lock thereby  
  18.     // resulting in a deadlock.  This situation is actually quite plausible because the  
  19.     // listener is actually the input dispatcher, which calls into the window manager,  
  20.     // which occasionally calls into the input reader.  
  21.     mQueuedListener->flush();  
  22. }  

loopOnce()先用getEvents()从设备文件中读出事件数据,然后进行一些处理。这种处理包括根据事件的类型(EV_KEY, EV_ABS, EV_SW)使用不同的InputMapper来处理事件,最后将事件放到列队中。最后的flush()会调用监听者(实际上是InputDispatch)来处理掉列队中所有的事件。

3. 事件的策略标志policyFlags

Android里事件有许多标志,如flags, policyFlags, metaState等,其中policyFlags用于决定一个事件的策略行为。比如一些特殊按键POWER,VOLUME,HOME等的处理行为在上层策略上不同,就需要对policyFlags设置不同的标志位。

frameworks/base/include/ui/Input.h 文件中定义了policyFlags所有的标志位。

有两个地方会设置policyFlags:

1. EventHub 对每个设备都有一个struct Device结构,每个Device又有自己的KeyMap。EventHub::openDeviceLocked()打开一个设备时会使用loadKeyMapLocked()加载并解析keylayout文件。关于keylayout可以看官方的说明 http://source.android.com/tech/input/key-layout-files.html

比如 frameworks/base/data/keyboards/Generic.kl 这个文件中有:
  1. key 113   VOLUME_MUTE  
  2. key 114   VOLUME_DOWN  
  3. key 115   VOLUME_UP  
  4. key 116   POWER             WAKE  
 
frameworks/base/libs/ui/KeyLayoutMap.cpp
frameworks/base/include/ui/KeycodeLabels.h 
  1. // NOTE: If you edit these flags, also edit policy flags in Input.h.  
  2. static const KeycodeLabel FLAGS[] = {  
  3.     { "WAKE", 0x00000001 },  
  4.     { "WAKE_DROPPED", 0x00000002 },  
  5.     { "SHIFT", 0x00000004 },  
  6.     { "CAPS_LOCK", 0x00000008 },  
  7.     { "ALT", 0x00000010 },  
  8.     { "ALT_GR", 0x00000020 },  
  9.     { "MENU", 0x00000040 },  
  10.     { "LAUNCHER", 0x00000080 },  
  11.     { "VIRTUAL", 0x00000100 },  
  12.     { "FUNCTION", 0x00000200 },  
  13.     { NULL, 0 }  
  14. };  
需要注意的是,在 frameworks/base/include/ui/Input.h 中定义了所有策略相关的标志,且需要和FLAGS[]里的值保持一致,不然就乱套了。

EventHub::getEvents()读到一个事件之后,会从按照scancode从keylayout中得到相应的策略标志,此时记录于event->flags中。按下POWER键时,getEvents()收到的scanCode是116,再从keyMap中用扫一遍,POWER键有"WAKE"属性,则设置0x00000001标志位。
  1. if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {  
  2.     status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,  
  3.                 &event->keyCode, &event->flags);  
  4.     LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",  
  5.             iev.code, event->keyCode, event->flags, err);  
  6. }  
 
2. InputDispatcher 中也会再做一些判断来设置policyFlags标志。
原文地址:https://www.cnblogs.com/wangym/p/9483791.html