Android键值上报流程

一、介绍

     在常用手机中,常用的键值有power,volume_up,volume_down,home,back,menu。其中power先跳过不管,它与唤醒睡眠流程相关,余下键值中volume_up和volume_down是在键值驱动中的实体键值,home,back,menu则是tp来模拟的虚拟键值。本次就用除去power之外的其他几个键值来探索下Android键值的上报流程。

二、驱动层

     驱动层中,对应的驱动为volume_up和volume_down在keypad/kpd.c中,通过属性为KEY的方式注册到input子系统中,当按下这两个键值产生中断之后,就通过input来上报键值。home,back和menu这是通过tp来模拟的虚拟键值,在tp驱动,触发之后上报特定的tp坐标来表示。

三、应用层

    1、InputManagerService

     在开机时候会注册InputManagerService服务:

 inputManager = new InputManagerService(context, wmHandler);
 inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
 inputManager.start();
     在inputManager中开启了两个线程:InputReader  和 InputDispatcher
            InputReader:不停的通过EventHub来监测读取input上报的键值数据,将键值数据初步整理,封装之后,发送给InputDispatcher
            InputDispatcher:不停的循环等待来自InputReader传过来的键值数据,在接收到键值数据之后,对键值进行整理分发,按键背光也会在该线程中点亮。

      2、InputReader

      在InputReader中,主要是一个 mReader->loopOnce();在不断循环,我们抽取它按键相关函数做讲解:

void InputReader::loopOnce() {
       .......
       .......
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
       .......
       .......
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
       ........
       ........
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
    //ALOGD("loopOnce:: release lock 2" );


    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
       mPolicy->notifyInputDevicesChanged(inputDevices);
    }


    mQueuedListener->flush();
}
       在该函数中,通过mEventHub->getEvents来打开input接口,监听input,如果没有键值数据上报,线程就在这里睡眠等待,到接受到数据之后,就通过函数mEventHub->getEvents对键值进行简单处理,封装成notify格式,通过notifyInputDevicesChanged将封装后的键值加入队列,最后通过mQueuedListener->flush()发送出去。
  其中在getEvents中,首先会检查是不是第一次读取,如果是第一次的话,会首先浏览系统,打开所有需要监听的input设备,将打开的设备文件节点加入到epoll中监听,当有没有数据时候就睡眠在epoll_wait中,有数据时候就接收数据。
  当接收到数据之后,就会随后调用函数processEventsLocked进行处理:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
               }
               batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
        ..........
        ..........
    }
}
       因为是处理的上报键值,于是进入函数processEventsForDeviceLocked中,该函数将调用不同键值处理的process中:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    device->process(rawEvents, count);
}
     通过device->process来轮询,选择对应的封装函数:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
     ...........
     ...........
      mapper->process(rawEvent);
     ...........
     ...........
}
      该mapper->process在之前通过createDeviceLocked将各类的键值处理函数加入到了Mapper中:

InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            identifier, classes);
      ..........
      ..........
    // Keyboard-like devices.
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }
    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    .........
    .........
    return device;
}
        键值上报的处理函数对应就是如上的KeyboardInputMapper,所以mapper->process就会对应调用到KeyboardInputMapper->process对上报的键值进行处理。
  KeyboardInputMapper::process  ---> processKey来将键值封装成notify格式。

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {
    ...........
    ...........
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
           down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
           AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);
}
          之后就是通过getInputDevicesLocked, mPolicy->notifyInputDevicesChanged,进行加入队列之类操作,最后mQueuedListener->flush()来发送出去。
      3、InputDispatcher

      InputDispatcher也是类似的通过mDispatcher->dispatchOnce();来循环分发InputReader传过来的键值,对应函数如下:  

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}
         这个函数会用dispatchOnceInnerLocked对InputReader的函数传的键值进行处理,之后runCommandsLockedInterruptible调用CommandEntry中加入的操作函数。
   dispatchOnceInnerLocked会点亮按键背光,并根据键值做对应处理:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime){
     ...........
     ...........
     pokeUserActivityLocked(mPendingEvent);    //点亮按键背光
     ...........
     ...........
     case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
           } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
    ............
    ............
}
      pokeUserActivityLocked会进入PowerManagerService中,最终通过mButtonLight.turnOff();和mButtonLight.turnOn();来关闭或者点亮按键背光。之后调用dispatchKeyLocked来做进一步的处理:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...............
    ...............
    // Handle case where the policy asked us to try again later last time.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            if (mFocusedWindowHandle != NULL) {
                commandEntry->inputWindowHandle = mFocusedWindowHandle;
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        if (*dropReason == DROP_REASON_NOT_DROPPED) {
            *dropReason = DROP_REASON_POLICY;
        }
    }

    // Clean up if dropping the event.
    if (*dropReason != DROP_REASON_NOT_DROPPED) {
        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
        return true;
    }

    // Identify targets.
    Vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    addMonitoringTargetsLocked(inputTargets);

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
       这个函数在KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN的时候会将doInterceptKeyBeforeDispatchingLockedInterruptible函数通过postCommandLocked加入到commandEntry中,然后直接false返回。当处理按键键值(如volume_up)时候,第一次就会在这里直接返回。之后就会进入到
InputReader::loopOnce() ---> runCommandsLockedInterruptible()中调用commandEntry中的函数,也就是刚刚加入的 doInterceptKeyBeforeDispatchingLockedInterruptible:

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;

    KeyEvent event;
    initializeKeyEvent(&event, entry);

    mLock.unlock();

    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);

    mLock.lock();

    if (delay < 0) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry->interceptKeyWakeupTime = now() + delay;
    }
    entry->release();
}
       这个函数中,首先将entry中的键值属性赋值到event中,然后调用函数interceptKeyBeforeDispatching,对应函数为:
PhoneWindowManager---->interceptKeyBeforeDispatchingd 处理上报的按键键值,在这个函数中如果键值是Home则,对应对应处理该键值,并返回-1,如果是其他键值(如volume)之类的,这不做任何处理,直接返回0.之后delay被赋值为interceptKeyBeforeDispatching的返回值,到返回值是-1的时候,设置interceptKeyResult为KeyEntry::INTERCEPT_KEY_RESULT_SKIP,如果返回值是0,则设置为INTERCEPT_KEY_RESULT_CONTINUE。之后进入InputDispatcher::dispatchOnce()的第二次循环。从新进入到InputDispatcher::dispatchKeyLocked中:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...............
    ...............
    // Handle case where the policy asked us to try again later last time.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            if (mFocusedWindowHandle != NULL) {
                commandEntry->inputWindowHandle = mFocusedWindowHandle;
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        if (*dropReason == DROP_REASON_NOT_DROPPED) {
            *dropReason = DROP_REASON_POLICY;
        }
    }

    // Clean up if dropping the event.
    if (*dropReason != DROP_REASON_NOT_DROPPED) {
        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
                ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
        return true;
    }

    // Identify targets.
    Vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    addMonitoringTargetsLocked(inputTargets);

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}
       这一次进入dispatchKeyLocked函数之后,如果键值是home,则entry->interceptKeyResult的属性为:KeyEntry::INTERCEPT_KEY_RESULT_SKIP,之后就会在setInjectionResultLocked中做表示键值处理完成的操作,拦截键值,不往上层发送;如果属性为KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE,则接着通过findFocusedWindowTargetsLocked找到目前焦点所在的activity,然后dispatchEventLocked来上报键值:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif

    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true

    pokeUserActivityLocked(eventEntry);

    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
#if DEBUG_FOCUS   
            ALOGD("Dropping event delivery to target with channel '%s' because it "
                    "is no longer registered with the input dispatcher.",
                    inputTarget.inputChannel->getName().string());
#endif
        }
    }
}
       在这个函数中,又一次的调用了pokeUserActivityLocked(eventEntry);来点亮按键背光,所以如果想要关闭按键时候的按键背光,就需要去掉这两处的pokeUserActivityLocked(eventEntry)函数,之后就通过connection往上层发送键值了。

四、 按键背光 

        在上面的流程分析中,已经知道了在Android上报键值的时候,点亮按键背光是通过函数pokeUserActivityLocked(eventEntry);主要流程如下:
pokeUserActivityLocked --->  doPokeUserActivityLockedInterruptible --->android_server_PowerManagerService_userActivity ---> userActivityInternal(PowerManagerService) ---> userActivityInternal ---> updatePowerStateLocked() ---> updateDisplayPowerStateLocked -->mButtonLight.turnOff() /  mButtonLight.setBrightness(screenBrightness); 
  最后通过mButtonLight进入到LightsService中,通过setLight_native进入jni层,做对应的点亮或者关闭按键背光处理,进入LightsService之后,按键背光的处理就和呼吸灯的处理差不多了,只是把选择熟悉换成了button。对了表示点亮按键背光之后,按键背光亮多长时间定义在:SCREEN_BUTTON_LIGHT_DURATION = 8 * 1000;
  所以当我们需要关闭到按键背光时候,一般来说有两种办法,第一种就是Android上报键值流程中去掉对函数pokeUserActivityLocked(eventEntry);,注意一共需要去掉两处,如果只去掉第一处的话,会出现home不会点亮按键背光,其他键值可以点亮;只去掉第二处的话,情况相反。

五、屏蔽按键方法

        1.在某些情况下我们可能需要一些特定的按键失效,在键值流程中直接拦截掉键值,使键值失效的方法很多,这里介绍三种办法:
        2.修改按键的kl映射文件
          找到Android中对应的键值映射对应的kl文件,然后直接修改它的映射值。比如我们要屏蔽volnme_up的按键,就做如下修改:

.........
.........
key 105   DPAD_LEFT
key 106   DPAD_RIGHT
key 115   VOLUME_UP         WAKE_DROPPED
key 114   VOLUME_DOWN       WAKE_DROPPED
key 113   MUTE              WAKE_DROPPED
.......
.......
              正常情况下,VOLUME_UP和linux对应的键值映射为115,我们可以直接修改它的映射为一个无用的值,比如:

key 500   VOLUME_UP         WAKE_DROPPED
             这样VOLUME_UP键值就失效了,不过这样还是会上传键值,会点亮按键背光。
  2.在kpd.c,按键驱动里面,直接去掉将对应按键加入input的步骤。
  3.首先确定需要屏蔽的键值是多少,然后在InputReader或者是InputDispatcher中直接再加上一个拦截规则,丢弃对应的键值就好。










  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值