对学习anr很好的一篇文章https://blog.csdn.net/qiujiwuhen00/article/details/47043477
先看下anr日志
07-16 15:31:47.551 E/ActivityManager( 1775): Reason: Input dispatching timed out (Waiting because the focused window's input channel is not registered with the input dispatcher. The window may be in the process of being removed.)
07-16 15:31:47.551 E/ActivityManager( 1775): 17% TOTAL: 12% user + 4.7% kernel + 0% iowait + 0% softirq
07-16 15:31:47.551 E/ActivityManager( 1775): 16% TOTAL: 14% user + 2.2% kernel
07-16 15:31:47.555 W/ActivityManager( 1775): Force finishing activity 1 包名/activity
cpu占用率不高可以,io流也没堵塞,提示是分配键超时(等待因为当前聚焦window的通信渠道没有注册在Inputdispatcher中。这个window有可能正被移除中)
再看下trace文件
DALVIK THREADS (40):
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 obj=0x71775000 self=0xf5027800
| sysTid=2115 nice=0 cgrp=default sched=0/0 handle=0xf7707bec
| state=S schedstat=( 0 0 0 ) utm=2600 stm=85 core=2 HZ=100
| stack=0xff3cc000-0xff3ce000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0x74/0x8c
kernel: SyS_epoll_wait+0x3b4/0x4ac
kernel: compat_SyS_epoll_pwait+0x148/0x150
kernel: el0_svc_naked+0x24/0x28
native: #00 pc 0003ada8 /system/lib/libc.so (__epoll_pwait+20)
native: #01 pc 00015c25 /system/lib/libc.so (epoll_pwait+26)
native: #02 pc 00015c33 /system/lib/libc.so (epoll_wait+6)
native: #03 pc 0001203b /system/lib/libutils.so (android::Looper::pollInner(int)+98)
native: #04 pc 0001226d /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+100)
native: #05 pc 00080435 /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+22)
native: #06 pc 00002c2b /data/dalvik-cache/arm/system@[email protected] (Java_android_os_MessageQueue_nativePollOnce__JI+102)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:143)
at android.os.Looper.loop(Looper.java:122)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:938)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:733)
at android.os.MessageQueue.next(MessageQueue.java:143) 代表从消息队列中获取下一个要处理的消息
native: #06 pc 00002c2b /data/dalvik-cache/arm/system@[email protected] (Java_android_os_MessageQueue_nativePollOnce__JI+102) 代表进入了本地方法
native: #02 pc 00015c33 /system/lib/libc.so (epoll_wait+6)
native: #03 pc 0001203b /system/lib/libutils.so (android::Looper::pollInner(int)+98)
说明线程进入了等待,直到epoll_wait返回信息
从以上现象实在是看不出什么问题,只好走上最笨办法,跟踪日志和框架源码。。。。
贴出关键日志
07-16 15:31:36.232 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.232 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:36.234 E/Looper ( 2115): Error adding epoll events for fd 41, errno=9
07-16 15:31:36.234 W/InputEventSender( 2115): Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9
07-16 15:31:36.235 W/InputMethodManager( 2115): Unable to send input event to IME: com.zxic.inputmethod.remote/.RemoteIME dropping: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15693903, downTime=15693903, deviceId=1, source=0x101 }
07-16 15:31:36.236 I/当前activity( 2115): onKeyDown. keyCode:165
07-16 15:31:36.551 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.552 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:36.577 E/InputTransport( 1775): channel ‘2d826e61 你的apk和包名 (server)’ publisher ~ Received unexpected message of type 1 from consumer
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Failed to receive finished signal. status=-2147483648
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Channel is unrecoverably broken and will be disposed!
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): KeyEvent: ACTION_UP but key was not down.
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): in android.view.ViewRootImpl@1cfd7704
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): 0: sent at 15694223000000, KeyEvent { action=ACTION_UP, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15694223, downTime=15693903, deviceId=1, source=0x101 }
07-16 15:31:36.866 D/mali_winsys( 2115): new_window_surface returns 0x3000
07-16 15:31:36.904 D/RemoteIME( 2026): onFinishInput.
07-16 15:31:36.904 D/RemoteIME( 2026): onStartInput ccontentType: 0 Restarting:false
07-16 15:31:36.904 I/RemoteIME( 2026): Default language!
07-16 15:31:38.855 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:38.856 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:39.175 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:39.176 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:43.861 I/InputDispatcher( 1775): Application is not responding: AppWindowToken{1d23d307 token=Token{20605546 ActivityRecord{24568d21 u0 你的apk和包名 t1}}} - Window{2d826e61 u0 你的apk和包名}. It has been 5005.9ms since event, 5004.3ms since wait started. Reason: Waiting because the focused window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
07-16 15:31:43.864 I/WindowManager( 1775): Input event dispatching timed out sending to 你的apk和包名. Reason: Waiting because the focused window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
贴出来最后的日志才是anr真实发生的时间,根据5秒规则,尽量找出5秒前的日志。
接下来就根据日志分析
一。
07-16 15:31:36.232 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.232 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
type==2代表是键盘类型
InputDispatcher.cpp
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
ALOGD("====add keyevent to queue======= type=%d", entry->type);
if (mInboundQueue.count() > 0 && (entry->type == EventEntry::TYPE_KEY)) {
KeyEntry* keyBeforeEntry = static_cast(entry);
if (keyBeforeEntry->deviceId == -1) {
mInboundQueue.dequeueAtHead();
}
}
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
case EventEntry::TYPE_KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
case EventEntry::TYPE_MOTION: {
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
MotionEntry* motionEntry = static_cast(entry);
if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
&& mInputTargetWaitApplicationHandle != NULL) {
int32_t displayId = motionEntry->displayId;
int32_t x = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_Y));
sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
if (touchedWindowHandle != NULL
&& touchedWindowHandle->inputApplicationHandle
!= mInputTargetWaitApplicationHandle) {
// User touched a different application than the one we are waiting on.
// Flag the event, and start pruning the input queue.
mNextUnblockedEvent = motionEntry;
needWake = true;
}
}
break;
}
}
return needWake;
}
needWake = true;唤醒dispatcher的线程
接下来
07-16 15:31:36.234 E/Looper ( 2115): Error adding epoll events for fd 41, errno=9
Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback.get(), data);
#endif
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
{ // acquire lock
AutoMutex _l(mLock);
Request request;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data;
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.replaceValueAt(requestIndex, request);
}
} // release lock
return 1;
}
再分配键值的过程中,有新的监听设备。
究竟是谁加入fd失败啊??? errno = 9,代表EBADF 对比下面
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
EBADF epfd or fd is not a valid file descriptor,只能看出来这个fd是无效的
目前还看不出什么来
接下来的日志
07-16 15:31:36.234 W/InputEventSender( 2115): Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9
07-16 15:31:36.235 W/InputMethodManager( 2115): Unable to send input event to IME: com.zxic.inputmethod.remote/.RemoteIME dropping: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15693903, downTime=15693903, deviceId=1, source=0x101 }
之前怀疑不知道是哪里发给哪里 现在确认了 肯定发送给输入法失败了,也和前面type==2对的上了。
因为新的页面必定会触发更新焦点的逻辑,也会通知输入法程序重新生成inputchanel与当前的页面建立socket通信。
新的页面和 inputdispatcher建立连接,新的页面也会重新绑定输入法,当inputdispatcher发送键值时,重新注册fd,其中输入法是客户端。上面出现Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9几乎可以确定是因为新的页面和输入法建立socket产生的。
根据以上逻辑来看下源码,参考https://blog.csdn.net/jieqiong1/article/details/71262987
首先是新页面重新绑定输入法。
创建新的页面经过ViewrootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);}
mWindowSession.addToDisplay最终跳到WindowManagerService.java
updateFocusedWindowLocked 更新聚焦窗口
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
}
computeFocusedWindowLocked
findFocusedWindowLocked
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_FOCUS_CHANGE: {
if (newFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Gaining focus: " + newFocus);
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
notifyFocusChanged();
}
if (lastFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing focus: " + lastFocus);
lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
}
}
//注意mClient是在ViewrootImpl.java中创建的代理并且通过setView中ipc进程间通信发送到Windowmanagerservice
//关于binder作为参数的进程间通信可以参考https://www.cnblogs.com/hrhguanli/p/4593041.html
//WindowState.java
public void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
try {
mClient.windowFocusChanged(focused, inTouchMode);
} catch (RemoteException e) {
}
if (mFocusCallbacks != null) {
final int N = mFocusCallbacks.beginBroadcast();
for (int i=0; itry {
if (focused) {
obs.focusGained(mWindowId.asBinder());
} else {
obs.focusLost(mWindowId.asBinder());
}
} catch (RemoteException e) {
}
}
mFocusCallbacks.finishBroadcast();
}
}
//ViewRootImpl.java
@Override
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
msg.arg1 = hasFocus ? 1 : 0;
msg.arg2 = inTouchMode ? 1 : 0;
mHandler.sendMessage(msg);
}
@Override
public void handleMessage(Message msg) {
case MSG_WINDOW_FOCUS_CHANGED: {
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
mWindowAttributes.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
((WindowManager.LayoutParams)mView.getLayoutParams())
.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
}
}
}
//InputMethodManager.java
public void onWindowFocus(View rootView, View focusedView, int softInputMode,
boolean first, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
+ " softInputMode=" + softInputMode
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
if (mHasBeenInactive) {
if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
mHasBeenInactive = false;
forceNewFocus = true;
}
focusInLocked(focusedView != null ? focusedView : rootView);
}
int controlFlags = 0;
if (focusedView != null) {
controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
if (focusedView.onCheckIsTextEditor()) {
controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
}
}
if (first) {
controlFlags |= CONTROL_WINDOW_FIRST;
}
if (checkFocusNoStartInput(forceNewFocus, true)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
// smooth.
if (startInputInner(rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags)) {
return;
}
}
// For some reason we didn't do a startInput + windowFocusGain, so
// we'll just do a window focus gain and call it a day.
synchronized (mH) {
try {
if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
mService.windowGainedFocus(mClient, rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags, null, null);
} catch (RemoteException e) {
}
}
}
focusInLocked
static void scheduleCheckFocusLocked(View view) {
ViewRootImpl viewRootImpl = view.getViewRootImpl();
if (viewRootImpl != null) {
viewRootImpl.dispatchCheckFocus();
}
}
//viewRootImpl.java
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
}
}
case MSG_CHECK_FOCUS: {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
imm.checkFocus();
}
} break;
//InputMethodManager.java
/**
* @hide
*/
public void checkFocus() {
if (checkFocusNoStartInput(false, true)) {
startInputInner(null, 0, 0, 0);
}
}
boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " controlFlags=#"
+ Integer.toHexString(controlFlags));
InputBindResult res;
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
} else {
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
}
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurId = res.id;
mNextUserActionNotificationSequenceNumber =
res.userActionNotificationSequenceNumber;
} else {
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
if (mCurMethod == null) {
// This means there is no input method available.
if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
return true;
}
}
}
if (mCurMethod != null && mCompletions != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
//startInputInner() 来绑定输入法, startInputInner 中的setInputChannelLocked(res.channel);出现了channel,有了channel 就有socket的fd
//其中res 是由
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
}
//或者
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
//看下res的生成
//InputMethodManagerService.java
@Override
public InputBindResult startInput(IInputMethodClient client,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
if (!calledFromValidUser()) {
return null;
}
synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity();
try {
return startInputLocked(client, inputContext, attribute, controlFlags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
startInputUncheckedLocked
InputBindResult startInputUncheckedLocked(ClientState cs,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
if (mHaveConnection) {
if (mCurMethod != null) {
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
return new InputBindResult(null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
// don't yet have its interface. If it hasn't been too
// long since we did the connection, we'll return to
// the client and wait to get the service interface so
// we can report back. If it has been too long, we want
// to fall through so we can try a disconnect/reconnect
// to see if we can get back in touch with the service.
return new InputBindResult(null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
}
}
return startInputInnerLocked();
}
//requestClientSessionLocked 最终这里生成inputchannel 打开了socket
//看怎么处理channnel的 为什么两个channel都传?其中服务端的channel塞进MethodCallback
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, mCurMethod, channels[1],
new MethodCallback(this, mCurMethod, channels[0])));
}
}
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
try {
method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
// Dispose the channel if the input method is not local to this process
// because the remote proxy will get its own copy when unparceled.
if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
args.recycle();
return true;
}
//上面是IMMS端,下面就看IMS输入法端的处理
// IInputMethodWrapper.java
@Override
public void createSession(InputChannel channel, IInputSessionCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
channel, callback));
}
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
// AbstractInputMethodService.java
//有个内部类 实现了InputMethod
public abstract class AbstractInputMethodImpl implements InputMethod {
/**
* Instantiate a new client session for the input method, by calling
* back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
* AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
*/
public void createSession(SessionCallback callback) {
callback.sessionCreated(onCreateInputMethodSessionInterface());
}
//InputMethodService.java:
@Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
}
// InputMethodManagerService.java
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
//mChannel服务端
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
InputBindResult res = attachNewInputLocked(true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_METHOD, mCurClient.client, res));
}
return;
}
}
}
// Session abandoned. Close its associated input channel.
channel.dispose();
}
// 输入法和view绑定 完全不懂 为什么之前创建了渠道了还重新new一个channel 且fd和之前一样
InputBindResult attachNewInputLocked(boolean initial) {
if (!mBoundToMethod) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
final SessionState session = mCurClient.curSession;
if (initial) {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
} else {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
}
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(session.session,
(session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
/* Returns a new object that has a duplicate of this channel's fd. */返回一个新的对象,该对象具有该通道的FD的副本。
sp dup() const;
//回到 startinputinner setInputChannelLocked(res.channel); 这里获取channel 且设置为mCurChannel
//目前为止都只是生成fd还没加入looper中,那fd是时候加入???前面提到是新的页面的key事件传给输入法的时候。
//接下来看key事件传递
viewrootimpl.java
//键值回调对象,在setview的时候生成
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
/**
* Delivers an event to be processed.
*/
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
// The IME could not handle it, so skip along to the next InputStage
return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
}
}
return FORWARD;
}
//InputMethodManager.java
/**
* Dispatches an input event to the IME.
*
* Returns {@link #DISPATCH_HANDLED} if the event was handled.
* Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
* Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
* callback will be invoked later.
*
* @hide
*/
public int dispatchInputEvent(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
if (mCurMethod != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked();
return DISPATCH_HANDLED;
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
if (mMainLooper.isCurrentThread()) {
// Already running on the IMM thread so we can send the event immediately.
return sendInputEventOnMainLooperLocked(p);
}
// Post the event to the IMM thread.
Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessage(msg);
return DISPATCH_IN_PROGRESS;
}
}
return DISPATCH_NOT_HANDLED;
}
// Must be called on the main looper
int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) {
if (mCurSender == null) {
mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
}
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mCurSender.sendInputEvent(seq, event)) {
mPendingEvents.put(seq, p);
Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
mPendingEvents.size());
Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
// mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
//ImeInputEventSender 继承与InputEventSender
//当new一个InputEventSender时会调用到native方法 nativeInit
mSenderPtr = nativeInit(new WeakReference(this),
inputChannel, mMessageQueue);
//android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp sender = new NativeInputEventSender(env,
senderWeak, inputChannel, messageQueue);
status_t status = sender->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event sender. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
return reinterpret_cast(sender.get());
}
status_t NativeInputEventSender::initialize() {
int receiveFd = mInputPublisher.getChannel()->getFd();
mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
return OK;
}
//终于可以看到mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
//说明第一次有键值时才会加入fd
//和日志所看到的现象符合的,因为新页面出现,重新绑定输入法,又因为有键过来所以addFd且出现异常。
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
接下来看日志这条的出处
07-16 15:31:36.234 W/InputEventSender( 2115): Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9
tatus_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
#endif
uint32_t publishedSeq = mNextPublishedSeq++;
status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
event->getKeyCode(), event->getScanCode(), event->getMetaState(),
event->getRepeatCount(), event->getDownTime(), event->getEventTime());
if (status) {
ALOGW("Failed to send key event on channel '%s'. status=%d",
getInputChannelName(), status);
return status;
}
mPublishedSeqMap.add(publishedSeq, seq);
return OK;
}
因为发送失败result == InputMethodManager.DISPATCH_NOT_HANDLED
所以return FORWARD;交给下一个inputstage,如果没有下一个就结束当前的event 且调用finishinput
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
// The IME could not handle it, so skip along to the next InputStage
return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
}
}
return FORWARD;
}
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
跟着日志看 key没有传递给输入法,倒是传递给了activity。
07-16 15:31:36.236 I/当前activity( 2115): onKeyDown. keyCode:165
最后通过finishInputEvent通知inputdispatcher.cpp
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
recycleQueuedInputEvent(q);
}
接着日志
07-16 15:31:36.551 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.552 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:36.577 E/InputTransport( 1775): channel ‘2d826e61 你的apk和包名 (server)’ publisher ~ Received unexpected message of type 1 from consumer
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Failed to receive finished signal. status=-2147483648
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Channel is unrecoverably broken and will be disposed!
虽然之前addfd出错了 但是接下来发送key给输入法是成功,最后通过finishInputEvent通知inputdispatcher.cpp,因为没有之前报错日志,但却是冒出新的错误,是Inputdispatcher.cpp处理其它进程消费了键值后出现的。
//Inputdispatcher.cpp
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
InputDispatcher* d = static_cast<InputDispatcher*>(data);
{ // acquire lock
AutoMutex _l(d->mLock);
ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
if (connectionIndex < 0) {
ALOGE("Received spurious receive callback for unknown input channel. "
"fd=%d, events=0x%x", fd, events);
return 0; // remove the callback
}
bool notify;
sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
if (!(events & ALOOPER_EVENT_INPUT)) {
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x", connection->getInputChannelName(), events);
return 1;
}
nsecs_t currentTime = now();
bool gotOne = false;
status_t status;
for (;;) {
uint32_t seq;
bool handled;
status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
if (status) {
break;
}
d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
gotOne = true;
}
if (gotOne) {
d->runCommandsLockedInterruptible();
if (status == WOULD_BLOCK) {
return 1;
}
}
notify = status != DEAD_OBJECT || !connection->monitor;
if (notify) {
ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
connection->getInputChannelName(), status);
}
} else {
// Monitor channels are never explicitly unregistered.
// We do it automatically when the remote endpoint is closed so don't warn
// about them.
notify = !connection->monitor;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
"events=0x%x", connection->getInputChannelName(), events);
}
}
// Unregister the channel.
d->unregisterInputChannelLocked(connection->inputChannel, notify);
return 0; // remove the callback
} // release lock
}
//07-16 15:31:36.577 E/InputTransport( 1775): channel '2d826e61 你的apk和包名 (server)' publisher ~ Received unexpected message of type 1 from consumer
//这条日志出现在 status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
#endif
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
*outSeq = 0;
*outHandled = false;
return result;
}
if (msg.header.type != InputMessage::TYPE_FINISHED) {
ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
mChannel->getName().string(), msg.header.type);
return UNKNOWN_ERROR;
}
*outSeq = msg.body.finished.seq;
*outHandled = msg.body.finished.handled;
return OK;
}
//可以看出来当msg.header.type != InputMessage::TYPE_FINISHED时就会报UNKNOWN_ERROR,且根据type = 1,我们知道是键盘类型。
//这样问题就很奇怪了,因为所有调用finishInputEvent的最后会设置msg.header.type = InputMessage::TYPE_FINISHED
跟踪finishInputEvent的代码,最后是进入sendUnchainedFinishedSignal,根本不可能type的类型是键盘,而且搜索有可能为msg.header.type 赋值为键盘类型的地方只有publishKeyEvent,而publishKeyEvent是服务端发送键值给客户端的,因为日志消息较小且这种anr难复现,在这里只能猜测是这个时段只有InputDispatcher 向 当前页面发送键值 和 当前页面向输入法发送键值 这两只情况是用到了publishKeyEvent,而第一种情况是不可能的,所以推论是第二次传递的key的socket fd用的是当前页面通知inputdispatcher的fd。
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
msg.header.type = InputMessage::TYPE_FINISHED;
msg.body.finished.seq = seq;
msg.body.finished.handled = handled;
return mChannel->sendMessage(&msg);
}
//只有发布键值时才会赋值为InputMessage::TYPE_KEY
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) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
其实到这里已经发现问题和linux内核bug有很大关系,因为fd都是系统分配的,为何错乱这里应该交给驱动的同事去排查下socket fd的问题,但是关于出现anr的直接原因还没分析完,出于不整明白不舒服的心情,还是硬着头皮继续分析下去。
接着看日志
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Channel is unrecoverably broken and will be disposed!
//handleReceiveCallback
//Unregister the channel.
//如果接受出错就要销毁注册
d->unregisterInputChannelLocked(connection->inputChannel, notify);
//InputDispatcher.cpp
status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
bool notify) {
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex < 0) {
ALOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
//标记下 这里是问题关键 移除connection
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
mConnectionsByFd.removeItemsAt(connectionIndex);
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
//标记下 这里是问题关键 移除fd
mLooper->removeFd(inputChannel->getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
connection->status = Connection::STATUS_ZOMBIE;
return OK;
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool notify) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
connection->getInputChannelName(), toString(notify));
#endif
// Clear the dispatch queues.
drainDispatchQueueLocked(&connection->outboundQueue);
traceOutboundQueueLengthLocked(connection);
drainDispatchQueueLocked(&connection->waitQueue);
traceWaitQueueLengthLocked(connection);
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
if (connection->status == Connection::STATUS_NORMAL) {
connection->status = Connection::STATUS_BROKEN;
if (notify) {
// Notify other system components.
onDispatchCycleBrokenLocked(currentTime, connection);
}
}
}
void InputDispatcher::onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection) {
ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
connection->getInputChannelName());
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
commandEntry->connection = connection;
}
void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
if (connection->status != Connection::STATUS_ZOMBIE) {
mLock.unlock();
//com_android_server_input_InputManagerService.cpp中传进来的mpolicy
mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);
mLock.lock();
}
}
//com_android_server_input_InputManagerService.cpp
void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyInputChannelBroken");
#endif
JNIEnv* env = jniEnv();
jobject inputWindowHandleObj =
getInputWindowHandleObjLocalRef(env, inputWindowHandle);
if (inputWindowHandleObj) {
//jni 调用 java
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputChannelBroken,
inputWindowHandleObj);
checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
env->DeleteLocalRef(inputWindowHandleObj);
}
}
//InputManagerService.java
// Native callback.
private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
}
//mWindowManagerCallbacks 是由systemserver.java设置
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
//WindowManagerService.java
public InputMonitor getInputMonitor() {
return mInputMonitor;
}
//最终调用的是
//InputMonitor.java
/* Notifies the window manager about a broken input channel.
*
* Called by the InputManager.
*/
@Override
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
if (inputWindowHandle == null) {
return;
}
synchronized (mService.mWindowMap) {
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
mService.removeWindowLocked(windowState.mSession, windowState);
}
}
}
//WindowManagerService.java
public void removeWindowLocked(Session session, WindowState win) {
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
}
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
TAG, "Remove " + win + " client="
+ Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
+ ", surface=" + win.mWinAnimator.mSurfaceControl + " Callers="
+ Debug.getCallers(4));
final long origId = Binder.clearCallingIdentity();
win.disposeInputChannel();//销毁inputchanel,且销毁注册,这里还是会调用到Inputdispatcher,之前inputdispatcher已经移除过该inputchanel,再次移除应当报错才对,但很奇怪没有报错
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurfaceControl
+ " mExiting=" + win.mExiting
+ " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
// First, see if we need to run an animation. If we do, we have
// to hold off on removing the window until the animation is done.
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
if (wasVisible) {
int transit = WindowManagerPolicy.TRANSIT_EXIT;
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
// Try starting an animation.
if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
win.mExiting = true;
}
//TODO (multidisplay): Magnification is supported only for the default display.
if (mAccessibilityController != null
&& win.getDisplayId() == Display.DEFAULT_DISPLAY) {
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
}
if (win.mExiting || win.mWinAnimator.isAnimating()) {
// The exit animation is running... wait for it!
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent != null) {
displayContent.layoutNeeded = true;
}
//也是更新窗口不包括更新inputdispatcher
final boolean focusChanged = updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
if (focusChanged) {
//更新inputdispatcher的窗口
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
//dump();
Binder.restoreCallingIdentity(origId);
return;
}
}
removeWindowInnerLocked(session, win);
// Removing a visible window will effect the computed orientation
// So just update orientation if needed.
if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
void removeWindowInnerLocked(Session session, WindowState win) {
if (win.mRemoved) {
// Nothing to do.
return;
}
for (int i=win.mChildWindows.size()-1; i>=0; i--) {
WindowState cwin = win.mChildWindows.get(i);
Slog.w(TAG, "Force-removing child win " + cwin + " from container "
+ win);
removeWindowInnerLocked(cwin.mSession, cwin);
}
win.mRemoved = true;
if (mInputMethodTarget == win) {
moveInputMethodWindowsIfNeededLocked(false);
}
if (false) {
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Slog.w(TAG, "Removing window " + win, e);
}
mPolicy.removeWindowLw(win);
win.removeLocked();
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
if (win.mAppOp != AppOpsManager.OP_NONE) {
mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
}
mPendingRemove.remove(win);
mResizingWindows.remove(win);
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
} else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.remove(win);
}
final WindowToken token = win.mToken;
final AppWindowToken atoken = win.mAppToken;
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + win + " from " + token);
token.windows.remove(win);
if (atoken != null) {
atoken.allAppWindows.remove(win);
}
if (localLOGV) Slog.v(
TAG, "**** Removing window " + win + ": count="
+ token.windows.size());
if (token.windows.size() == 0) {
if (!token.explicit) {
mTokenMap.remove(token.token);
} else if (atoken != null) {
atoken.firstWindowDrawn = false;
}
}
if (atoken != null) {
if (atoken.startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Notify removed startingWindow " + win);
scheduleRemoveStartingWindowLocked(atoken);
} else
if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
// If this is the last window and we had requested a starting
// transition window, well there is no point now.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling last startingWindow");
atoken.startingData = null;
} else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
// If this is the last window except for a starting transition
// window, we need to get rid of the starting transition.
scheduleRemoveStartingWindowLocked(atoken);
}
}
if (win.mAttrs.type == TYPE_WALLPAPER) {
mLastWallpaperTimeoutTime = 0;
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
final WindowList windows = win.getWindowList();
if (windows != null) {
windows.remove(win);
if (!mInLayout) {
assignLayersLocked(windows);
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent != null) {
displayContent.layoutNeeded = true;
}
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
}
}
//更新inputdispatcher的窗口
mInputMonitor.updateInputWindowsLw(true /*force*/);
}
小结下,就是说当inputdispatcher接收的message msg.header.type != InputMessage::TYPE_FINISHED时会触发销毁inputchannel的注册流程,且更新dispatcher的当前聚焦窗口。
然后对比anr错误日志Reason: Waiting because the focused window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
机翻下:
等待,因为聚焦窗口的输入通道没有注册在inputdispatcher。窗口可能在被移除的过程中。
这就奇怪了,说明是有聚焦的窗口但是没有注册,问题很可能就是销毁inputchannel的注册流程中,我们先看下这段日志的流程
//InputDispatcher.cpp
// Check whether the window is ready for more input.
reason = checkWindowReadyForMoreInputLocked(currentTime,
mFocusedWindowHandle, entry, "focused");
//mFocusedWindowHandle 是当前聚焦窗口
String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp& windowHandle, const EventEntry* eventEntry,
const char* targetType) {
// If the window's connection is not registered then keep waiting.
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
if (connectionIndex < 0) {
return String8::format("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
"of being removed.", targetType);
}
}
ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputChannel) {
ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
if (connectionIndex >= 0) {
sp connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->inputChannel.get() == inputChannel.get()) {
return connectionIndex;
}
}
return -1;
}
//先看看mConnectionsByFd是什么
// All registered connections mapped by channel file descriptor.
//已channel fd为key 的所有注册connections
KeyedVector<int, sp > mConnectionsByFd;
status_t InputDispatcher::registerInputChannel(const sp& inputChannel,
const sp& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
toString(monitor));
#endif
{ // acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp connection = new Connection(inputChannel, inputWindowHandle, monitor);
//每次服务端在inputdispatcher注册时,mConnectionsByFd的都会新一个connection
//可以知道inputdispatcher已经保存了所有的channel,只需上层应用调用
//mInputMonitor.updateInputWindowsLw(true /*force*/);
//通知inputdispatcher更新当前聚焦的窗口以便传递键值
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
anr原因:等待,因为聚焦窗口的输入通道没有注册在inputdispatcher。窗口可能在被移除的过程中。
这样我们可以分析anr的真实原因
因为apk的所有新的窗口都必须调用InputDispatcher::registerInputChannel与inputdispatcher建立连接,当inputdispatcher因为异常调用unregisterInputChannelLocked后会销毁链接和移除fd,后续不会有重新注册且通知更新inputdispatcher的聚焦窗口,这样也出问题只能说明当前页面没有销毁但却取消了注册在inputdispatcher的inputchanel。