1. 程式人生 > >安卓框架,分析解決專案中出現的anr

安卓框架,分析解決專案中出現的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/[email protected]@boot.oat (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/[email protected]@boot.oat (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 [email protected]
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<KeyEntry*>(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<KeyEntry*>(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<MotionEntry*>(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<InputWindowHandle> 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<LooperCallback>& 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產生的。

建立新的頁面經過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; i<N; i++) {
                IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
                try {
                    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<InputChannel> 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<InputEventSender>(this),
                inputChannel, mMessageQueue);

//android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
            senderWeak, inputChannel, messageQueue);
    status_t status =