Android之Input子系統事件分發流程
Android建立視窗機制,請看如下轉載:
一、Android4.2系統服務側——與View關係
1.服務端channel註冊過程
frameworks/base/core/java/android/view/ViewRootImpl.java
frameworks/base/services/java/com/android/server/wm/Session.javapublic 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/WindowManagerService.javapublic 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/service/java/com/android/server/input/InputManagerService.javapublic 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); }
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.cppstatic 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
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
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 [email protected] 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
//有新輸入設備註冊等
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執行緒被喚醒
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
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.cppstatus_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
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
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
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
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
int Looper::pollInner(int timeoutMillis) {
awoken(); //阻塞,等待
int callbackResult = response.request.callback->handleEvent(fd, events, data);
}
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
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.javaprivate void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
frameworks/base/core/java/android/view/ViewRootImpl.javafinal 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機制請看下文:
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();
}
}
///////////////////////////////////////////////////////
這其中ViewRootImpl.java的deliverKeyEventPostIme介面中在呼叫mView.dispatchKeyEvent(event)返回為false時,會再次呼叫mFallbackEventHandler.dispatchKeyEvent(event)讓系統做預設處理。
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
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
public boolean dispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
應用程式Activity在分發給與之關聯的某個View,如果這個View沒有處理、最終交給該Activity自己處理。
應用程式有關View的設定:
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
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
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的關鍵部分!!!public boolean dispatchKeyEvent(KeyEvent event) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
super.dispatchKeyEvent(event)
}
frameworks/base/core/java/android/view/View.java
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
三、Input裝置與Activity關係
1.InputReaderThread執行緒檢測到裝置插入刪除
frameworks/base/service/input/InputReader.cpp
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不需要重新整理;可以在上處做遮蔽:
// 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
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
needWake = enqueueInboundEventLocked(newEntry);
if (needWake) {
mLooper->wake();
}
}
3.InputReaderThread執行緒收到訊息並處理frameworks/base/service/input/InputDispatcher.cpp
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
void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConfigurationChanged, when);
}
frameworks/base/services/java/com/android/server/input/InputManagerService.cppprivate void notifyConfigurationChanged(long whenNanos) {
mWindowManagerCallbacks.notifyConfigurationChanged();
}
如上,不再做分析:frameworks/base/service/java/com/android/server/wm/InputMonitor.java
public void notifyConfigurationChanged() {
mService.sendNewConfiguration();
}
frameworks/base/service/java/com/android/server/wm/WindowManagerService.javavoid 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
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.javafinal 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.javapublic 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方法
}
四、專案問題
1.resumeTopActivity時的Activity重啟。
操作邏輯:開啟Launcher介面下的一個應用(比如播放器),完後接入USB鍵盤;之後退出該應用,也就是resumeTopActivity到Launcher時也引發了config配置更新導致的Activity重啟。
原理以及解決部分:
frameworks/base/services/java/com/android/server/am/ActivityStack.java
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
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
}
}
2.面板裝置與虛擬驅動導致的up上報兩次:
drop類按鍵
down或up:
dispatchOnceInnerLocked>
dropInboundEventLocked>synthesizeCancelationEventsForAllConnectionsLocked-synthesizeCancelationEventsForConnectionLocked>inputState.synthesizeCancelationEvents->mKeyMementos.itemAt(i),最後上報系統(synthesizeCancelationEventsForConnectionLocked呼叫enqueueDispatchEntryLocked)
非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
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 [email protected] 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
*/
}
3.焦點request錯誤導致不能響應按鍵
正確呼叫:setFocusable(true)和requestFocus()重新獲取焦點
錯誤呼叫:setFocusable(false)和requestFocus()
系統側為該應用tv.huan.deezer強制修改:
frameworks/base/core/java/android/view/View.java
public final boolean requestFocus() {
Log.d("TKTK","TK---->>>View.java>>>>requestFocus()");//add by tank
if(SystemProperties.get("sys.user.camera",null).equals("tv.huan.deezer"))
{
setFocusable(true);
}
//end tank
return requestFocus(View.FOCUS_DOWN);
}