您的位置:首页 > 移动开发 > Android开发

Android之Input子系统事件分发流程

2014-11-17 13:56 288 查看
Android创建窗口机制,请看如下转载:
http://blog.csdn.net/sfdev/article/details/9130527

一、Android4.2系统服务侧——与View关系
1.服务端channel注册过程
frameworks/base/core/java/android/view/ViewRootImpl.java
[cpp] view plaincopy




public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

mInputChannel = new InputChannel(); //创建InputChannel

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

getHostVisibility(), mDisplay.getDisplayId(),

mAttachInfo.mContentInsets, mInputChannel); //创建与上述InputChannel对应的通道至服务端

/*

mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public static IWindowSession getWindowSession(Looper mainLooper) {

IWindowManager windowManager = getWindowManagerService();

sWindowSession = windowManager.openSession(

imm.getClient(), imm.getInputContext());

return sWindowSession;

}

frameworks/base/services/java/com/android/server/wm/WindowManagerService.java

public IWindowSession openSession(IInputMethodClient client,

IInputContext inputContext) {

if (client == null) throw new IllegalArgumentException("null client");

if (inputContext == null) throw new IllegalArgumentException("null inputContext");

Session session = new Session(this, client, inputContext);

return session;

}

*/

mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,

Looper.myLooper()); //将本通道注册进InputEventReceiver

}

frameworks/base/services/java/com/android/server/wm/Session.java
[cpp] view plaincopy




public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,

int viewVisibility, int displayId, Rect outContentInsets,

InputChannel outInputChannel) {

return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,

outContentInsets, outInputChannel);

}

frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
[cpp] view plaincopy




public int addWindow(Session session, IWindow client, int seq,

WindowManager.LayoutParams attrs, int viewVisibility, int displayId,

Rect outContentInsets, InputChannel outInputChannel) {

//以下包括了管道的创建(用于WMS与应用程序View通信)等

String name = win.makeInputChannelName();

InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

win.setInputChannel(inputChannels[0]);

inputChannels[1].transferTo(outInputChannel);

//以下便是注册至server端过程

//final InputManagerService mInputManager;

mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);

}

frameworks/base/service/java/com/android/server/input/InputManagerService.java
[cpp] view plaincopy




public void registerInputChannel(InputChannel inputChannel,

InputWindowHandle inputWindowHandle) {

nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);

}

private static native void nativeRegisterInputChannel(int ptr, InputChannel inputChannel,

InputWindowHandle inputWindowHandle, boolean monitor);

frameworks/base/service/jni/com_android_server_input_InputManagerService.cpp
[cpp] view plaincopy




static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,

jint ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {

NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

status_t status = im->registerInputChannel(

env, inputChannel, inputWindowHandle, monitor);

}

status_t NativeInputManager::registerInputChannel(JNIEnv* env,

const sp<InputChannel>& inputChannel,

const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

return mInputManager->getDispatcher()->registerInputChannel(

inputChannel, inputWindowHandle, monitor);

//mInputManager = new InputManager(eventHub, this, this);

/*

frameworks/base/services/input/InputManager.cpp

sp<InputDispatcherInterface> InputManager::getDispatcher() {

return mDispatcher;

}

mDispatcher = new InputDispatcher(dispatcherPolicy);

*/

}

frameworks/base/services/input/InputDispatcher.cpp
[cpp] view plaincopy




status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,

const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

int fd = inputChannel->getFd();

mConnectionsByFd.add(fd, connection);

//该fd监听对应的处理函数为handleReceiveCallback

mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

}

2.服务端上报过程

2.1.InputReaderThread线程从驱动读取数据并处理,如实现鼠标右键上报back键即在此处完成、以下代码将会看到



frameworks/base/services/input/InputReader.cpp
[cpp] view plaincopy




bool InputReaderThread::threadLoop() {

mReader->loopOnce();

return true;

}

void InputReader::loopOnce() {

size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

/*

frameworks/base/services/input/EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

int32_t readSize = read(device->fd, readBuffer,

sizeof(struct input_event) * capacity);//从驱动读取事件

}

*/

processEventsLocked(mEventBuffer, count);

}

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {

processEventsForDeviceLocked(deviceId, rawEvent, batchSize);

}

void InputReader::processEventsForDeviceLocked(int32_t deviceId,

const RawEvent* rawEvents, size_t count) {

device->process(rawEvents, count);

}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {

//该设备的所有mapper进行处理;注意:这里使用了多态

for (size_t i = 0; i < numMappers; i++) {

InputMapper* mapper = mMappers[i];

mapper->process(rawEvent);

}

}

//以下就是各个mapper

//CursorInput鼠标设备

void CursorInputMapper::process(const RawEvent* rawEvent) {

mCursorButtonAccumulator.process(rawEvent);

mCursorMotionAccumulator.process(rawEvent);

mCursorScrollAccumulator.process(rawEvent);

if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {

sync(rawEvent->when);

}

}

//CursorButtonAccumulator::process(const RawEvent* rawEvent)

//CursorMotionAccumulator::process(const RawEvent* rawEvent)

//CursorScrollAccumulator::process(const RawEvent* rawEvent)

void CursorInputMapper::sync(nsecs_t when) {

int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();

/*

uint32_t CursorButtonAccumulator::getButtonState() const {

if (mBtnRight) {

//Changed by tank for mouse left button to back

result |= AMOTION_EVENT_BUTTON_BACK;

// result |= AMOTION_EVENT_BUTTON_SECONDARY;

}

if (mBtnMiddle) {

//change by tank@tcl.com for mouse middle button to menu

result |= AMOTION_EVENT_BUTTON_MENU;

//result |= AMOTION_EVENT_BUTTON_TERTIARY;

}

}

*/

getListener()->notifyMotion(&args);

synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,

policyFlags, lastButtonState, currentButtonState);

/*

static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,

nsecs_t when, int32_t deviceId, uint32_t source,

uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {

synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,

lastButtonState, currentButtonState,

AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);

synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,

lastButtonState, currentButtonState,

AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);

//add by tank mouse key event middle->menu.

synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,

lastButtonState, currentButtonState,

AMOTION_EVENT_BUTTON_MENU, AKEYCODE_MENU);

//end tank

}

static void synthesizeButtonKey(InputReaderContext* context, int32_t action,

nsecs_t when, int32_t deviceId, uint32_t source,

uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,

int32_t buttonState, int32_t keyCode) {

if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState)

&& (currentButtonState & buttonState))

|| (action == AKEY_EVENT_ACTION_UP

&& (lastButtonState & buttonState)

&& !(currentButtonState & buttonState))) {

context->getListener()->notifyKey(&args);

}

}

*/

}

//TouchInput触摸板设备

void SingleTouchInputMapper::process(const RawEvent* rawEvent)

TouchInputMapper::process(rawEvent);

mSingleTouchMotionAccumulator.process(rawEvent);

}

//SingleTouchMotionAccumulator::process(const RawEvent* rawEvent)

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {

TouchInputMapper::process(rawEvent);

mMultiTouchMotionAccumulator.process(rawEvent);

}

//MultiTouchMotionAccumulator::process(const RawEvent* rawEvent)

void TouchInputMapper::process(const RawEvent* rawEvent) {

mCursorButtonAccumulator.process(rawEvent);

mCursorScrollAccumulator.process(rawEvent);

mTouchButtonAccumulator.process(rawEvent);

if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {

sync(rawEvent->when);

}

}

//TouchButtonAccumulator::process(const RawEvent* rawEvent)

void TouchInputMapper::sync(nsecs_t when) {

dispatchTouches(when, policyFlags);

}

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {

dispatchMotion(when, policyFlags, mSource,

AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,

AMOTION_EVENT_EDGE_FLAG_NONE,

mCurrentCookedPointerData.pointerProperties,

mCurrentCookedPointerData.pointerCoords,

mCurrentCookedPointerData.idToIndex,

currentIdBits, -1,

mOrientedXPrecision, mOrientedYPrecision, mDownTime);

}

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,

int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,

const PointerProperties* properties, const PointerCoords* coords,

const uint32_t* idToIndex, BitSet32 idBits,

int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {

getListener()->notifyMotion(&args);

}

//SwitchInput设备

void SwitchInputMapper::process(const RawEvent* rawEvent) {

sync(rawEvent->when);

}

void SwitchInputMapper::sync(nsecs_t when) {

getListener()->notifySwitch(&args);

}

//JoystickInput游戏手柄设备

void JoystickInputMapper::process(const RawEvent* rawEvent) {

sync(rawEvent->when, false /*force*/);

}

void JoystickInputMapper::sync(nsecs_t when, bool force) {

getListener()->notifyMotion(&args);

}

//KeyboardInput按键设备

void KeyboardInputMapper::process(const RawEvent* rawEvent) {

processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);

}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,

int32_t scanCode, uint32_t policyFlags) {

getListener()->notifyKey(&args);

}

2.2.InputReaderThread线程对系统层按键做处理(比较重要的是POWER键,最终在PhoneWindowManager中的
interceptKeyBeforeQueueing和interceptMotionBeforeQueueingWhenScreenOff)后分
发给InputDispatcherThread线程,以下分析将看到之前一个鼠标操作过程中无法待机的问题解决
以下几种情况都会唤醒InputDispatcherThread线程,即调用mLooper->wake()唤醒正在awoken()中的InputReaderThread线程:
frameworks/base/services/input/InputDispatcher.cpp
[cpp] view plaincopy




//有新输入设备注册等

void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {

ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);

needWake = enqueueInboundEventLocked(newEntry);

if (needWake) {

mLooper->wake();

}

}

//分发按键事件

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {

//说明:PhoneWindowManager.java中policyFlags位决定系统按键(如HOME等是否需要由系统处理)

mPolicy->interceptKeyBeforeQueueing(&event, policyFlags);

//以下分析将看到,该调用实际是在PhoneWindowManager.java中实现

/*

frameworks/base/services/input/InputManager.cpp

InputManager::InputManager(

const sp<EventHubInterface>& eventHub,

const sp<InputReaderPolicyInterface>& readerPolicy,

const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {

mDispatcher = new InputDispatcher(dispatcherPolicy);

mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

}

frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

NativeInputManager::NativeInputManager(jobject contextObj,

jobject serviceObj, const sp<Looper>& looper) :

mLooper(looper) {

mInputManager = new InputManager(eventHub, this, this);

}

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,

uint32_t& policyFlags) {

wmActions = env->CallIntMethod(mServiceObj,

gServiceClassInfo.interceptKeyBeforeQueueing,

keyEventObj, policyFlags, isScreenOn);

//如下函数中将有待机和开机的处理

handleInterceptActions(wmActions, when, policyFlags);

}

frameworks/base/service/java/com/android/server/input/InputManagerService.java

private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {

return mWindowManagerCallbacks.interceptKeyBeforeQueueing(

event, policyFlags, isScreenOn);

}

frameworks/base/service/java/com/android/server/SystemServer.java

inputManager = new InputManagerService(context, wmHandler);

wm = WindowManagerService.main(context, power, display, inputManager,

uiHandler, wmHandler,

factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,

!firstBoot, onlyCore);

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

frameworks/base/service/java/com/android/server/wm/WindowManagerService.java

public InputMonitor getInputMonitor() {

return mInputMonitor;

}

frameworks/base/service/java/com/android/server/wm/InputMonitor.java

public int interceptKeyBeforeQueueing(

KeyEvent event, int policyFlags, boolean isScreenOn) {

return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);

}

public InputMonitor(WindowManagerService service) {

mService = service;

}

frameworks/base/service/java/com/android/server/wm/WindowManagerService.java

final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();

frameworks/base/core/java/com/android/internal/policy/PolicyManager.java

public static WindowManagerPolicy makeNewWindowManager() {

return sPolicy.makeNewWindowManager();

}

private static final String POLICY_IMPL_CLASS_NAME =

"com.android.internal.policy.impl.Policy";

Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);

sPolicy = (IPolicy)policyClass.newInstance();

frameworks/base/core/java/com/android/internal/policy/Policy.java

package com.android.internal.policy.impl;

public class Policy implements IPolicy {

public WindowManagerPolicy makeNewWindowManager() {

return new PhoneWindowManager();

}

}

frameworks/base/core/java/com/android/internal/policy/PhoneWindowManager.java

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {

case KeyEvent.KEYCODE_POWER: {

result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;

}

}

*/

KeyEntry* newEntry = new KeyEntry(args->eventTime,

args->deviceId, args->source, policyFlags,

args->action, flags, args->keyCode, args->scanCode,

metaState, repeatCount, args->downTime);

needWake = enqueueInboundEventLocked(newEntry);

if (needWake) {

mLooper->wake();

}

}

//分发Motion事件

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {

mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);

/*

如上分析,不再累赘;该接口是:

frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {

jint wmActions = env->CallIntMethod(mServiceObj,

gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff,

policyFlags);

handleInterceptActions(wmActions, when, policyFlags);

}

如上interceptMotionBeforeQueueingWhenScreenOff在PhoneWindowManager中实现;分析同上,不再累赘:

frameworks/base/core/java/com/android/internal/policy/PhoneWindowManager.java

public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {

//result |= ACTION_WAKE_UP;

//add by tank

result = result & (~ACTION_WAKE_UP);

//end tank

return result;

}

看看handleInterceptActions函数:

void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,

uint32_t& policyFlags) {

//接上边PhoneWindowManager中interceptKeyBeforeQueueing对于power键的返回值可知,系统将待机

if (wmActions & WM_ACTION_GO_TO_SLEEP) {

#if DEBUG_INPUT_DISPATCHER_POLICY

ALOGD("handleInterceptActions: Going to sleep.");

#endif

android_server_PowerManagerService_goToSleep(when);

}

//以下说明PhoneWindowManager中interceptMotionBeforeQueueingWhenScreenOff返回值WM_ACTION_WAKE_UP将会导致唤醒

//当然,是可是收到motion事件的前提下

if (wmActions & WM_ACTION_WAKE_UP) {

#if DEBUG_INPUT_DISPATCHER_POLICY

ALOGD("handleInterceptActions: Waking up.");

#endif

android_server_PowerManagerService_wakeUp(when);

}

//以下是可以上报给系统的

if (wmActions & WM_ACTION_PASS_TO_USER) {

policyFlags |= POLICY_FLAG_PASS_TO_USER;

}

}

*/

MotionEntry* newEntry = new MotionEntry(args->eventTime,

args->deviceId, args->source, policyFlags,

args->action, args->flags, args->metaState, args->buttonState,

args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,

args->displayId,

args->pointerCount, args->pointerProperties, args->pointerCoords);

needWake = enqueueInboundEventLocked(newEntry);

if (needWake) {

mLooper->wake();

}

}

//设备重置

void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {

DeviceResetEntry* newEntry = new DeviceResetEntry(args->eventTime, args->deviceId);

needWake = enqueueInboundEventLocked(newEntry);

if (needWake) {

mLooper->wake();

}

}

//C层的按键注入接口

int32_t InputDispatcher::injectInputEvent(const InputEvent* event,

int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,

uint32_t policyFlags) {

needWake |= enqueueInboundEventLocked(entry);

if (needWake) {

mLooper->wake();

}

}

//setInputWindows

//setFocusedApplication

//setInputDispatchMode

//setInputFilterEnabled

//transferTouchFocus

//registerInputChannel

//unregisterInputChannel

//monitor

2.3.InputDispatcherThread线程处理,根据PhoneWindowManager中的interceptKeyBeforeDispatching决定是否丢弃按键



InputDispatcherThread线程被唤醒
[cpp] view plaincopy




bool InputDispatcherThread::threadLoop() {

mDispatcher->dispatchOnce();

return true;

}

void InputDispatcher::dispatchOnce() {

dispatchOnceInnerLocked(&nextWakeupTime);

mLooper->pollOnce(timeoutMillis);

}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {

if (!mPolicy->isKeyRepeatEnabled()) {

resetKeyRepeatLocked();

}

switch (mPendingEvent->type) {

case EventEntry::TYPE_CONFIGURATION_CHANGED: {

done = dispatchConfigurationChangedLocked(currentTime, typedEntry);

}

case EventEntry::TYPE_DEVICE_RESET: {

done = dispatchDeviceResetLocked(currentTime, typedEntry);

}

case EventEntry::TYPE_KEY: {

done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);

}

case EventEntry::TYPE_MOTION: {

done = dispatchMotionLocked(currentTime, typedEntry,

&dropReason, nextWakeupTime);

}

}

dropInboundEventLocked(mPendingEvent, dropReason); //丢弃的事件!!!!

}

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,

DropReason* dropReason, nsecs_t* nextWakeupTime) {

CommandEntry* commandEntry = postCommandLocked(

& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);

/*

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(

CommandEntry* commandEntry) {

//说明:PhoneWindowManager.java中可以截断事件而不上报,即返回-1、将被丢弃

nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,

&event, entry->policyFlags);

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;

}

}

*/

else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {

if (*dropReason == DROP_REASON_NOT_DROPPED) {

*dropReason = DROP_REASON_POLICY; //dropReason是因为策略丢弃

}

}

if (*dropReason != DROP_REASON_NOT_DROPPED) {

setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY

? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);

return true;

}

dispatchEventLocked(currentTime, entry, inputTargets);

}

bool InputDispatcher::dispatchMotionLocked(

nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {

dispatchEventLocked(currentTime, entry, inputTargets);

}

2.4.InputDispatcherThread线程分发给应用程序进程
在这里解决了up事件上报两次的问题!!!!!!
frameworks/base/services/input/InputDispatcher.cpp
[cpp] view plaincopy




void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,

EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {

pokeUserActivityLocked(eventEntry); //和Activity相关,后边三中有设备删除的分析;基本同下

ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);

sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);

prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);

}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,

const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {

enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);

}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,

const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {

enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,

InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); //将按键注入队列

/*

void InputDispatcher::enqueueDispatchEntryLocked(

const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,

int32_t dispatchMode) {

DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref

inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,

inputTarget->scaleFactor);

if (!connection->inputState.trackKey(keyEntry,

dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags) || (dispatchEntry->resolvedFlags == 0x28)){

//add by tankai 0x28

delete dispatchEntry;

return;

}

}

*/

//dropInboundEventLocked

//synthesizeCancelationEventsForAllConnectionsLocked->

//synthesizeCancelationEventsForConnectionLocked->

/*

void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(

const sp<Connection>& connection, const CancelationOptions& options) {

Vector<EventEntry*> cancelationEvents;

connection->inputState.synthesizeCancelationEvents(currentTime,

cancelationEvents, options);

//关键在这里,mKeyMementos;在enqueueDispatchEntryLocked时调用trackKey由addKeyMemento注入!!!!!!

if (!cancelationEvents.isEmpty()) {

enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref

&target, InputTarget::FLAG_DISPATCH_AS_IS);

}

}

*/

//enqueueDispatchEntriesLocked,注入了0x28标志的按键

startDispatchCycleLocked(currentTime, connection);

}

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,

const sp<Connection>& connection) {

switch (eventEntry->type) {

case EventEntry::TYPE_KEY: {

status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,

keyEntry->deviceId, keyEntry->source,

dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,

keyEntry->keyCode, keyEntry->scanCode,

keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,

keyEntry->eventTime);

}

case EventEntry::TYPE_MOTION: {

status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,

motionEntry->deviceId, motionEntry->source,

dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,

motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,

xOffset, yOffset,

motionEntry->xPrecision, motionEntry->yPrecision,

motionEntry->downTime, motionEntry->eventTime,

motionEntry->pointerCount, motionEntry->pointerProperties,

usingCoords);

}

}

}

frameworks/base/libs/androidfw/InputTransport.cpp

[cpp] view plaincopy




status_t InputPublisher::publishKeyEvent(

uint32_t seq,

int32_t deviceId,

int32_t source,

int32_t action,

int32_t flags,

int32_t keyCode,

int32_t scanCode,

int32_t metaState,

int32_t repeatCount,

nsecs_t downTime,

nsecs_t eventTime) {

return mChannel->sendMessage(&msg);

}

status_t InputChannel::sendMessage(const InputMessage* msg) {

do {

nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);

} while (nWrite == -1 && errno == EINTR);

}

二、Android4.2系统应用程序侧——与View关系



InputManagerService也就是InputDispatcher与应用程序通信是靠looper。
说明:
InputReader从设备文件中读取的是RawEvent,在交给InputDispatcher进行分发之前,它需要先把RawEvent进行转化分类,拆分成KeyEvent、MotionEvent、TrackEvent各种类型等。
InputDispatcher获得按键事件后,根据当前设备的状况来优先消化事件(该过程交由PhoneWindowManager.java来处理);最后,剩余事件分发给ViewRoot;ViewRoot再分发给IME输入法或View、Activity。
1.应用程序View中channel注册过程
frameworks/base/core/java/android/view/ViewRootImpl.java
[cpp] view plaincopy




public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

mInputChannel = new InputChannel(); //创建InputChannel

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

getHostVisibility(), mDisplay.getDisplayId(),

mAttachInfo.mContentInsets, mInputChannel); //创建与上述InputChannel对应的通道至服务端

mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,

Looper.myLooper()); //将本通道注册进InputEventReceiver

}

final class WindowInputEventReceiver extends InputEventReceiver {

public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {

super(inputChannel, looper);

}

@Override

public void onInputEvent(InputEvent event) {

enqueueInputEvent(event, this, 0, true);

}

}

frameworks/base/core/java/android/view/InputEventReceiver.java
[cpp] view plaincopy




public InputEventReceiver(InputChannel inputChannel, Looper looper) {

mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);

}

private static native int nativeInit(InputEventReceiver receiver,

InputChannel inputChannel, MessageQueue messageQueue);

frameworks/base/core/jni/android_view_InputEventReceiver.cpp
[cpp] view plaincopy




static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,

jobject inputChannelObj, jobject messageQueueObj) {

sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,

receiverObj, inputChannel, messageQueue);

status_t status = receiver->initialize();

}

status_t NativeInputEventReceiver::initialize() {

int receiveFd = mInputConsumer.getChannel()->getFd();

mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);

return OK;

}

frameworks/native/libs/utils/Looper.cpp
[cpp] view plaincopy




int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {

request.callback = callback;

}

2.应用程序View响应过程
frameworks/native/libs/utils/Looper.cpp
[cpp] view plaincopy




int Looper::pollInner(int timeoutMillis) {

awoken(); //阻塞,等待

int callbackResult = response.request.callback->handleEvent(fd, events, data);

}

frameworks/base/core/jni/android_view_InputEventReceiver.cpp
[cpp] view plaincopy




int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {

status_t status = consumeEvents(env, false /*consumeBatches*/, -1);

}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,

bool consumeBatches, nsecs_t frameTime) {

env->CallVoidMethod(mReceiverObjGlobal,

gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

}

frameworks/base/core/java/android/view/InputEventReceiver.java
[cpp] view plaincopy




private void dispatchInputEvent(int seq, InputEvent event) {

mSeqMap.put(event.getSequenceNumber(), seq);

onInputEvent(event);

}

frameworks/base/core/java/android/view/ViewRootImpl.java
[cpp] view plaincopy




final class WindowInputEventReceiver extends InputEventReceiver {

public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {

super(inputChannel, looper);

}

@Override

public void onInputEvent(InputEvent event) {

enqueueInputEvent(event, this, 0, true);

}

}

void enqueueInputEvent(InputEvent event,

InputEventReceiver receiver, int flags, boolean processImmediately) {

scheduleProcessInputEvents();

}

/////////////////////////////////////////////////////////////
有关handler机制请看下文:
http://blog.csdn.net/itachi85/article/details/8035333

[cpp] view plaincopy




final ViewRootHandler mHandler = new ViewRootHandler();

private void scheduleProcessInputEvents() {

Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);

mHandler.sendMessage(msg);

}

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_PROCESS_INPUT_EVENTS:

doProcessInputEvents();

}

}

///////////////////////////////////////////////////////
[cpp] view plaincopy




void doProcessInputEvents() {

deliverInputEvent(q);

}

private void deliverInputEvent(QueuedInputEvent q) {

deliverKeyEvent(q);

deliverPointerEvent(q);

deliverTrackballEvent(q);

deliverGenericMotionEvent(q);

}

private void deliverKeyEvent(QueuedInputEvent q) {

imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback); //分发给输入法

deliverKeyEventPostIme(q);//分发给View

/*

private void deliverKeyEventPostIme(QueuedInputEvent q) {

mView.dispatchKeyEvent(event)

}

*/

}

private void deliverPointerEvent(QueuedInputEvent q) {

boolean handled = mView.dispatchPointerEvent(event); //分发给View

}

private void deliverTrackballEvent(QueuedInputEvent q) {

imm.dispatchTrackballEvent(mView.getContext(), seq, event,

mInputMethodCallback); //分发给输入法

deliverTrackballEventPostIme(q); //分发给View

/*

private void deliverTrackballEventPostIme(QueuedInputEvent q) {

mView.dispatchTrackballEvent(event)

}

*/

}

private void deliverGenericMotionEvent(QueuedInputEvent q) {

imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,

mInputMethodCallback); //分发给输入法

deliverGenericMotionEventPostIme(q); //分发给View

/*

private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {

updateJoystickDirection(event, false); //游戏手柄的摇杆就是在这处理

mView.dispatchGenericMotionEvent(event)

}

*/

}

分发给应用程序Activity:
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
[java] view plaincopy




private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

public boolean dispatchKeyEvent(KeyEvent event) {

final Callback cb = getCallback();

//cb为应用程序MainActivity

final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) : super.dispatchKeyEvent(event);

//给应用程序Activity的dispatchKeyEvent处理或交给View的dispatchKeyEvent

}

}

而上述应用程序中的dispatchKeyEvent一般会调用其父类的该方法,例如:
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
[java] view plaincopy




public boolean dispatchKeyEvent(KeyEvent event) {

return super.dispatchKeyEvent(event);

}

应用程序Activity在分发给与之关联的某个View,如果这个View没有处理、最终交给该Activity自己处理。
应用程序有关View的设置:
[java] view plaincopy




private Dialog mMenuWin;

mMenuWin = new Dialog(aActivity, R.style.CameraDialog);

mMenuWin.setContentView(mMenuLayout);

mMenuWin.setOnClickListener(); //鼠标单击

mMenuWin.setOnLongClickListener(); //

mMenuWin.setOnTouchListener(); //触摸板

mMenuWin.setOnKeyListener(new OnKeyListener() {

public boolean onKey(); //按键

public void onClick(View v); //鼠标单击

}

frameworks/base/core/java/android/app/Activity.java
[java] view plaincopy




public boolean dispatchKeyEvent(KeyEvent event) {

onUserInteraction();

Window win = getWindow();

if (win.superDispatchKeyEvent(event)) { //首先由Window消化,即如果View消化了、则Activity将不在回调onKeyDown

return true;

}

View decor = mDecor; //如果没被消化,会调用Activity的onKeyDown

if (decor == null) decor = win.getDecorView();

return event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this);

}

}

我们重点分析win.superDispatchKeyEvent,也就是View的处理流程:

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
[java] view plaincopy




public class PhoneWindow extends Window implements MenuBuilder.Callback {

public boolean superDispatchKeyEvent(KeyEvent event) {

return mDecor.superDispatchKeyEvent(event);

}

}

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

public boolean superDispatchKeyEvent(KeyEvent event) {

super.dispatchKeyEvent(event)

}

}

frameworks/base/core/java/android/view/ViewGroup.java //分发给View的关键部分!!!
[java] view plaincopy




public boolean dispatchKeyEvent(KeyEvent event) {

mInputEventConsistencyVerifier.onKeyEvent(event, 1);

super.dispatchKeyEvent(event)

}

frameworks/base/core/java/android/view/View.java
[java] view plaincopy




public boolean dispatchKeyEvent(KeyEvent event) {

li.mOnKeyListener.onKey(this, event.getKeyCode(), event); //回调应用程序View相应方法

event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this)

/*

frameworks/base/core/java/android/view/KeyEvent.java

public final boolean dispatch(Callback receiver, DispatcherState state,

Object target) {

//按键响应

boolean res = receiver.onKeyDown(mKeyCode, this); //应用程序回调函数

}

*/

}

public final boolean dispatchPointerEvent(MotionEvent event) {

if (event.isTouchEvent()) {

return dispatchTouchEvent(event);

} else {

return dispatchGenericMotionEvent(event);

}

}

public boolean dispatchTouchEvent(MotionEvent event) {

//触摸板响应

li.mOnTouchListener.onTouch(this, event) //应用程序继承OnTouchListener,实现的回调接口

//鼠标左键响应

onTouchEvent(event)

/*

public boolean onTouchEvent(MotionEvent event) {

performClick();

//该接口调用li.mOnClickListener.onClick(this);为应用程序继承OnClickListener的回调函数

}

*/

}

以下不再做分析
dispatchGenericMotionEvent

dispatchTrackballEvent
dispatchConfigurationChanged //添加或删除键盘设备Activity重启,见http://blog.csdn.net/tankai19880619/article/details/16805401

三、Input设备与Activity关系
1.InputReaderThread线程检测到设备插入删除
frameworks/base/service/input/InputReader.cpp
[cpp] view plaincopy




void InputReader::loopOnce() {

size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

/*

frameworks/base/services/input/EventHub.cpp

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

int32_t readSize = read(device->fd, readBuffer,

sizeof(struct input_event) * capacity);//从驱动读取事件

}

*/

processEventsLocked(mEventBuffer, count);

}

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {

case EventHubInterface::FINISHED_DEVICE_SCAN:

handleConfigurationChangedLocked(rawEvent->when);

}

void InputReader::handleConfigurationChangedLocked(nsecs_t when) {

updateGlobalMetaStateLocked();

// Enqueue configuration changed.

NotifyConfigurationChangedArgs args(when);

mQueuedListener->notifyConfigurationChanged(&args);

}

说明:有的平台需要在接入硬件键盘时Activity不需要刷新;可以在上处做屏蔽:
[cpp] view plaincopy




// add by tank

// do not send configuration change

//NotifyConfigurationChangedArgs args(when);

//mQueuedListener->notifyConfigurationChanged(&args);

// end tank

2.InputReaderThread线程分发给InputDispatcherThread线程
frameworks/base/service/input/InputDispatcher.cpp
[cpp] view plaincopy




void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {

needWake = enqueueInboundEventLocked(newEntry);

if (needWake) {

mLooper->wake();

}

}

3.InputReaderThread线程收到消息并处理

frameworks/base/service/input/InputDispatcher.cpp
[cpp] view plaincopy




bool InputDispatcherThread::threadLoop() {

mDispatcher->dispatchOnce();

return true;

}

void InputDispatcher::dispatchOnce() {

dispatchOnceInnerLocked(&nextWakeupTime);

}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {

case EventEntry::TYPE_CONFIGURATION_CHANGED: {

ConfigurationChangedEntry* typedEntry =

static_cast<ConfigurationChangedEntry*>(mPendingEvent);

done = dispatchConfigurationChangedLocked(currentTime, typedEntry);

}

}

bool InputDispatcher::dispatchConfigurationChangedLocked(

nsecs_t currentTime, ConfigurationChangedEntry* entry) {

CommandEntry* commandEntry = postCommandLocked(

& InputDispatcher::doNotifyConfigurationChangedInterruptible);

}

void InputDispatcher::doNotifyConfigurationChangedInterruptible(

CommandEntry* commandEntry) {

mPolicy->notifyConfigurationChanged(commandEntry->eventTime);

}

如上,不再做分析:

frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
[cpp] view plaincopy




void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {

env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConfigurationChanged, when);

}

frameworks/base/services/java/com/android/server/input/InputManagerService.cpp
[cpp] view plaincopy




private void notifyConfigurationChanged(long whenNanos) {

mWindowManagerCallbacks.notifyConfigurationChanged();

}

如上,不再做分析:

frameworks/base/service/java/com/android/server/wm/InputMonitor.java
[cpp] view plaincopy




public void notifyConfigurationChanged() {

mService.sendNewConfiguration();

}

frameworks/base/service/java/com/android/server/wm/WindowManagerService.java
[cpp] view plaincopy




void sendNewConfiguration() {

mActivityManager.updateConfiguration(null);

/*

mActivityManager = ActivityManagerNative.getDefault();

frameworks/base/core/java/android/app/ActivityManagerNative.java

static public IActivityManager getDefault() {

return gDefault.get();

}

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {

IBinder b = ServiceManager.getService("activity");

IActivityManager am = asInterface(b);

return am;

}

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

public static void setSystemProcess() {

ActivityManagerService m = mSelf;

ServiceManager.addService("activity", m, true);

}

*/

}

4.交由ActivityManagerService进程处理

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
[cpp] view plaincopy




public void updateConfiguration(Configuration values) {

updateConfigurationLocked(values, null, false, false);

}

boolean updateConfigurationLocked(Configuration values,

ActivityRecord starting, boolean persistent, boolean initLocale) {

kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);

public void setWindowManager(WindowManagerService wm) {

mWindowManager = wm;

}

}

frameworks/base/services/java/com/android/server/am/ActivityStack.java
[cpp] view plaincopy




final boolean ensureActivityConfigurationLocked(ActivityRecord r,

int globalChanges) {

//一般会重启Activity

if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {

relaunchActivityLocked(r, r.configChangeFlags, false);

return false;

}

//应用程序AndroidMenifest中写标记将不会重启

r.app.thread.scheduleActivityConfigurationChanged(r.appToken);

}

frameworks/base/core/java/android/app/ActivityThread.java
[cpp] view plaincopy




public void scheduleActivityConfigurationChanged(IBinder token) {

queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);

}

//消息循环同上,不再分析

public void handleMessage(Message msg) {

case ACTIVITY_CONFIGURATION_CHANGED:

handleActivityConfigurationChanged((IBinder)msg.obj);

}

final void handleActivityConfigurationChanged(IBinder token) {

performConfigurationChanged(r.activity, mCompatConfiguration);

}

private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {

cb.onConfigurationChanged(config); //回调Activity类的onConfigurationChanged方法

}

四、项目问题
resumeTopActivity时的Activity重启。http://blog.csdn.net/jivin_shen/article/details/6839175
操作逻辑:打开Launcher界面下的一个应用(比如播放器),完后接入USB键盘;之后退出该应用,也就是resumeTopActivity到Launcher时也引发了config配置更新导致的Activity重启。
原理以及解决部分:
frameworks/base/services/java/com/android/server/am/ActivityStack.java

[cpp] view plaincopy




final boolean resumeTopActivityLocked(ActivityRecord prev) {

return resumeTopActivityLocked(prev, null);

}

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {

Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(

mService.mConfiguration,

next.mayFreezeScreenLocked(next.app) ? next.appToken : null);

}

frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
[cpp] view plaincopy




public Configuration updateOrientationFromAppTokens(

Configuration currentConfig, IBinder freezeThisOneIfNeeded) {

config = updateOrientationFromAppTokensLocked(currentConfig,

freezeThisOneIfNeeded);

}

private Configuration updateOrientationFromAppTokensLocked(

Configuration currentConfig, IBinder freezeThisOneIfNeeded) {

computeScreenConfigurationLocked(mTempConfiguration)

}

boolean computeScreenConfigurationLocked(Configuration config) {

if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {

//change by tank

config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;

//config.touchscreen = Configuration.TOUCHSCREEN_FINGER;

//end tank

}

else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD

&& config.navigation == Configuration.NAVIGATION_NONAV) {

//change by tank

//config.navigation = Configuration.NAVIGATION_DPAD;

//navigationPresence |= presenceFlag;

//end tank

}

if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {

//change by tank

//config.keyboard = Configuration.KEYBOARD_QWERTY;

//keyboardPresence |= presenceFlag;

//end tank

}

}

面板设备与虚拟驱动导致的up上报两次:
1.drop类按键
down或up:
dispatchOnceInnerLocked>
dropInboundEventLocked>synthesizeCancelationEventsForAllConnectionsLocked-
synthesizeCancelationEventsForConnectionLocked>inputState.synthesizeCancelationEvents->mKeyMementos.itemAt(i),
最后上报系统(synthesizeCancelationEventsForConnectionLocked调用
enqueueDispatchEntryLocked)

2.非drop类按键
down:
dispatchOnceInnerLocked->
dispatchKeyLocked->dispatchEventLocked->prepareDispatchCycleLocked->enqueueDispatchEntriesLocked->enqueueDispatchEntryLocked->InputState::trackKey->addKeyMemento
//只在down时保存对up的处理

问题:
面板down->drop
虚拟down->非drop,保存up
面板down->drop,将虚拟保存的up送上去
虚拟up->非drop,直接上报
结果——两个虚拟的up
修改方法:
frameworks/base/service/input/InputDispatcher.cpp
[cpp] view plaincopy




void InputDispatcher::enqueueDispatchEntryLocked(

const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,

int32_t dispatchMode)

{

if (!connection->inputState.trackKey(keyEntry,

dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)/*add by tank@tcl.com end */ || (dispatchEntry->resolvedFlags == 0x28))

{

#if DEBUG_DISPATCH_CYCLE

ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",

connection->getInputChannelName());

#endif

delete dispatchEntry;

return; // skip the inconsistent event

}

/*

//add by tankai

if(dispatchEntry->resolvedFlags == 0x28 && keyEntry->deviceId == 3){

ALOGD("TK--------->>>delete sim KeyMementos up\n");

delete dispatchEntry;

return; // skip the inconsistent event

}

//end tankai

*/

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息