Android知识点4--Android 事件产生、分发机制?

914 阅读20分钟
digraph G {
    硬件事件->输入子系统
    输入子系统->IMS
    IMS->WMS
    WMS->View
}

输入事件传递流程:输入系统部分(InputManagerService)、WMS处理部分和View处理部分。

4.1. 输入系统部分:

参考:sharrychoo.github.io/blog/androi… Android提供了getevent和sendevent两个工具从设备节点读取输入事件和写入输入事件。 InputManagerService就是监听/dev/input下所有的设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的窗口,将输入事件派发给它。

IMS 的启动可以分为 构建过程和启动过程

4.1.1. IMS的构造:(同Handler消息队列思想一致)

public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
            
    public InputManagerService(Context context) {
        this.mContext = context;
        // 1. 构建 DisplayThread 线程 Looper 的 handler
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
        ......
        // 2. 初始化 Native 层
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
        ......
    }
    
    private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);
            
}

IMS 构造函数中主要完成:

  • 构建DisplayThread线程Looper的handler
  • 调用nativeInit,进行native层的初始化操作

在nativeInit中,获取当前线程的MessageQueue,然后构造一个与Java层对应的NativeInputManagerduixiang

// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);
    ......
    // 创建了一个 EventHub
    sp<EventHub> eventHub = new EventHub();
    // 创建了 InputManager 对象
    mInputManager = new InputManager(eventHub, this, this);
}

4.1.1.1. EventHub的创建

// frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    ......
    // 1. 创建了一个 Epoll, 用于监听 mINotifyFd 和 mWakeReadPipeFd 中的数据
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    ......
    // 2. Epoll 监听 input 驱动设备文件的监听者
    // 2.1 创建输入驱动设备文件路径的监听者 mINotifyFd
    mINotifyFd = inotify_init()
    // DEVICE_PATH 为 "/dev/input" 表示监听输入驱动设备文件
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    ......
    // 2.3 调用 epoll 文件的 ctl 系统调用, 监听 mINotifyFd 文件描述符
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    ......
    // 3. 创建管道
    int wakeFds[2];
    result = pipe(wakeFds);
    // 记录读端
    mWakeReadPipeFd = wakeFds[0];
    // 记录写端
    mWakeWritePipeFd = wakeFds[1];
    // 将管道的读写两端设置为非阻塞的模式
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    ......
    // 4. Epoll 监听管道的读端 mWakeReadPipeFd 的数据变化
    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ......
}

EventHub主要做了如下操作:

  • 创建用于IO多路复用对象Epoll
  • Epoll监听mINotifyFd
    • 创建输入驱动设备文件的监听者mINotifyFd
    • Epoll监听mINotifyFd
  • 创建非阻塞读写的管道
  • Epoll监听管道的读端文件描述符

4.1.1.2. InputManager的创建

// frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    // 创建 InputDispatcher,输入流的分发者
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    // 创建 InputReader,输入流的读取者
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    // 3. 执行初始化操作
    initialize();
}

void InputManager::initialize() {
    // 创建读取输入流的线程
    mReaderThread = new InputReaderThread(mReader);
    // 创建分发输入流的线程
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager构造函数中,创建了四个对象:

  • InputDispatcher : 输入流的分发者
  • InputReader : 输入流的读取者
    • 在InputReader中持有一个事件队列QueuedInputListener,用于存储接收的事件,并且通知InputDispatcher进行后续的分发操作
  • InputReaderThread : 输入流的读取线程
  • InputDispatcherThread : 输入流的分发线程

InputManager整个事件的读取和分发使用的是 生产者-消费者 模型。

4.1.2. IMS的构造总结

  • 在Java层创建一个DisplayThread消息队列的Handler
  • 将DisplayThread的MessageQueue传入Native层,执行Native层IMS的初始化操作
  • Native层创建了一个与Java层InputManagerService对应的NativeInputManager对象
    • 创建EventHub,监听输入设备驱动的事件流变化
      • 创建用于IO多路复用对象Epoll
      • Epoll监听mINotifyFd文件描述符,进而监听输入设备集合(Android手机支持连接多个输入设备)
        • 创建输入驱动设备文件的监听者mINotifyFd
        • Epoll监听mINotifyFd
      • 创建非阻塞读写管道
      • Epoll监听管道的读端文件描述符
    • 创建InputManager来读取和分发事件序列
      • 创建线程InputReaderThread
        • 使用InputRead读取输入设备产生事件到QueuedInputListener
      • 创建线程InputDispatcherThread
        • 使用InputDispatcher分发数据

4.1.3. IMS的启动

4.1.3.1. 事件生产线程工作机制

image.png

InputReadThread启动之后会不断的调用InputReader的loopOnce来读取输入事件:

  • 调用EventHub的getEvents来获取输入事件流,写入mEventBuffer数组
    • 使用epoll阻塞式的获取epoll_event事件
    • 使用epoll_event获取具体的输入设备
    • 从输入设备中读取事件序列input_event存到RawEvent数据中并返回给上层
  • 调用ProcessEventsLocked处理事件
    • 调用InputDevice的process处理该设备产生的事件
    • 调用InputMapper,将抽象的RawEvent事件映射程具体的事件类型
      • 映射完毕后,使用NotifyArgs描述
    • 将NotifyArgs投入QueuedInputListener队列
  • 调用QueuedInputListener的flush函数
    • 通知InputDispatcher.notify消费事件
    • 将NotifyArgs构建成KeyEvent添加到InputDispatcher的mInboundQueue队列
    • 唤醒InputDispatcherThread执行事件分发

整个流程:

    epoll_event -> input_event -> RawEvent -> NotifyArgs -> KeyEntry

4.1.3.2. 事件分发线程工作机制

image.png

InputDispatchThread启动之后不断的调用InputDispatcher的dispatchOnce来执行事件分发:

  • 从mInboundQueue中获取队列首事件KeyEntry保存到MPendingEvent进行分发处理
  • 找寻事件的相应窗体保存到inputTargets集合
    • 找寻焦点窗体mFocusedWindowHandle,将它的数据注入inputTarget
  • 遍历inputTargets集合进行事件分发
    • 获取响应者连接Connection
    • 将KeyEvent构建成了DispatchEntry对象,添加到Connection的outboundQueue队列中
    • Connection内部的inputPublisher获取队列中的元素进行发布
    • 将KeyEvent构建成InputMessage,通过inputChannel.sendMessage发布

整个流程:

    KeyEntry -> DispatchEntry -> InputMessage

4.1.3.3. IMS的启动流程

image.png

4.2. IMS的事件分发

ViewRootImpl会通过WindowManagerSession向WMS发起请求创建一个窗体,对应的实现如下:

public class WindowManagerService extends IWindowManager.Stub {

    final WindowManagerPolicy mPolicy;// 在 WMS 的构造函数中赋值, 其实例为 PhoneWindowManager
    final WindowHashMap mWindowMap = new WindowHashMap();

    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
            
        .......
        synchronized(mWindowMap) {
            ......
            // 1. 创建一个窗体的描述
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            ......
            // 2. 给这个窗体描述打开一个输入通道, 用于接收屏幕的点击事件(事件分发)
            final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
            ......
            // 3. 将新创建添加的 WindowState 设置为 IMS 的焦点 Window, 即 native 层的 mFocusedWindowHandle
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                ......
            }
            .......
        }
        ......
        return res;
    }
}

WMS 添加一个Window的过程:

  • 创建窗体描述WindowState
  • 打开窗体的输入通道InputChannel
  • 更新IMS的焦点窗体
    • native层的mFocusedWindowHandle

那么IMS是如何将事件分发到窗体上openInputChannel输入通道的呢?

4.2.1. 创建InputChannel通道组

class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
   
    // Input channel and input window handle used by the input dispatcher.
    // 与IMS的inputDispatcher关联上了
    final InputWindowHandle mInputWindowHandle;
    InputChannel mInputChannel;
    private InputChannel mClientChannel;
    
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
            PowerManagerWrapper powerManagerWrapper) {
        ......
        // 描述当前 WindowState 的句柄值
        mInputWindowHandle = new InputWindowHandle(
                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
                    getDisplayId());
    }
    
    void openInputChannel(InputChannel outInputChannel) {
        ......
        // 1. 创建一个 InputChannel 组
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        // 2. 一个通道保存在当前 WindowState 中
        mInputChannel = inputChannels[0];
        // 2.1 保存到 WindowState 句柄值中
        mInputWindowHandle.inputChannel = inputChannels[0];
        // 3. 一个通道用于给对应的应用进程使用
        mClientChannel = inputChannels[1];
        if (outInputChannel != null) {
            // 3.1 将客户端的通道拷贝到 outInputChannel 对象中, 以便通过 Binder 驱动返回给客户端
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            ......
        }
        // 4. 向 InputManager 注册这个窗体的输入通道
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }
    
}

窗体通道的注册如下:

  • 调用InputChannel.openInputChannelPair获取一个输入通道组
  • inputChannel[0]保存在WindowState的句柄值中 mInputWindowHandle
  • inputChannel[1]通过Binder驱动返回给客户端
  • 向IMS的InputManager注册这个窗体的输入通道

4.2.2. InputChannel是如何创建的?

public final class InputChannel implements Parcelable {
    
    public static InputChannel[] openInputChannelPair(String name) {
        ......
        // 调用Native层
        return nativeOpenInputChannelPair(name);
    }
    
    private static native InputChannel[] nativeOpenInputChannelPair(String name);

}
// frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    // 1. 获取 name
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    std::string name = nameChars;
    env->ReleaseStringUTFChars(nameObj, nameChars);
    
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    // 2. 调用了 InputChannel 的 openInputChannelPair 创建两个 InputChannel
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    ......
    // 3. 通过 JNI 创建一个 InputChannel 数组, 将 serverChannelObj 保存
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    ......
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}
// frameworks/native/libs/input/InputTransport.cpp
static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;

status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    // 1. 创建一个 Socket
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ......
        return result;
    }
    // 2. 配置 Socket 端口数据
    // Socket 传输数据大小为 32 KB
    int bufferSize = SOCKET_BUFFER_SIZE;
    // 2.1 配置服务端端口
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    // 2.2 配置客户端端口
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    // 3. 创建 InputChannel
    // 3.1 创建服务端的 InputChannel
    std::string serverChannelName = name;
    serverChannelName += " (server)";
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    // 3.2 创建客户端的 InputChannel
    std::string clientChannelName = name;
    clientChannelName += " (client)";
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

可以看出来,服务端的IMS与应用进程的交互是通过Socket进行跨进程通信的。 最后,看一下InputChannel的构造过程:

// frameworks/native/libs/input/InputTransport.cpp
InputChannel::InputChannel(const std::string& name, int fd) :
        mName(name), 
        // 保存 socket 端口的文件描述符
        mFd(fd) {
    ......
    // O_NONBLOCK 意为设置为非阻塞式 Socket 通信
    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
    ......
}

至此,InputChannel就创建好了,下面看一下IMS是如何注册InputChannel的?

4.2.3. IMS 注册InputChannel

public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
    
    public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        // 调用了Native层的 nativeRegisterInputChannel
        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }
    
}
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    
    // NativeInputManager 属于IMS
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 获取 InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    ......
    // 获取 WindowStateHandle
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
    // 调用了 InputManager 的 registerInputChannel 注册这个 WindowStateHandle 和 InputChannel
    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) {
    // 将注册操作转发到 InputManager.InputDispatcher 中, 注册新创建的WindowStateHandle 和 InputChannel
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}
// frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

    { // acquire lock
        AutoMutex _l(mLock);
        // 1. 创建一个 Connection 描述 IMS 和窗体的连接
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
        // 2. 将 InputChannel 的 socket 文件描述符, 添加到 mConnectionsByFd 中缓存
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        // 3. 让 Looper 中的 epool 监听这个服务端的 socket 端口
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
    // 4. 一个新 Window 添加进来了, 唤醒阻塞在 Looper.pollOnce 上的 InputDispatcherThread 线程
    mLooper->wake();
    return OK;
}

在注册InputChannel方法中,创建了一个描述 IMS 和 WindowState 连接的Connection 对象,让后让InputDispatcher的Looper去监听InputChannel中的Socket端口。

为什么要监听服务端的端口? Socket与匿名管道不同,它支持双端读写,当应用进程读取了事件,消费之后是需要通知IMS所在系统服务进程,事件已经被消费了,这里的监听操作就是为了接受客户端的回执消息。

InputChannel 是在WMS新建窗体时创建的(WindowState的openInputChannel中),InputChannel的sendMessage是最终会发送给应用进程的socket端口

4.2.4. InputChannel的创建以及和WindowState向IMS注册过程

  • 创建InputChannel输入通道组
    • 创建最大缓存为32kb的Socket
    • 服务端的 InputChannel 保留Socket 服务端的文件描述符
    • 客户端的 InputChannel 保留Socket 客户端的文件描述符
  • 服务端新建的WindowState和InputChannel注册到IMS中
    • InputDispatcher将WindowState 和 InputChannel 封装到Connection中用于后续的事件分发

4.3. 应用进程是如何监听InputChannel的?

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
            
    InputChannel mInputChannel;
    
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                // 这里是我们的请求布局
                requestLayout(); 
                // 1. 创建了一个 InputChannel 对象, 此时它是没有意义的
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
    
                try {
                    // 2. 调用 addToDisplay, 进而调用 WMS.addWindow 进行窗体添加操作
                    // mInputChannel 作为传出参数,此时就获取系统服务进程创建的inputChannel组的Native层的值
                    res = mWindowSession.addToDisplay(......, /*将 mInputChannel 传入*/mInputChannel);
                } catch (RemoteException e) {
                   ......
                } finally {
                   ......
                }
                ......
                // 3. WMS 的添加窗体完成后, mInputChannel 便通过 Binder 驱动拷贝 WMS 中传递过来的值
                if (mInputChannel != null) {
                    ......
                    // 3.1 创建了输入事件接收器
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
                ......
            }
        }
    }          
}

**InputChannel中持有的是Socket一端的文件描述符,而在linux系统中fd不是只在当前进程有效吗?在从系统服务进程传输到应用进程时不就无效了吗?

  • 因为Binder驱动对fd的传输做了特殊处理,它不仅仅只是传递一个句柄int值,而是会在另一个进程找到一个空闲的句柄,然后让它指向对应的文件,因此客户端mInputChannel中的mFd与在系统服务进程中创建的InputChannel的mFd不一定相同,但是他们指向的确实同一个Socket端口文件。**

接下来,看一下输入事件接收器,WindowInputEventReceiver

4.3.1. 事件接收器初始化

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
    final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
    }
    
}

public abstract class InputEventReceiver {

    private long mReceiverPtr;
    private InputChannel mInputChannel;
    private MessageQueue mMessageQueue;

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        .......
        mInputChannel = inputChannel;
        // 这个 mMessageQueue 是主线程的消息队列
        mMessageQueue = looper.getQueue();
        // 执行 Native 初始化
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
    }
    
    private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
            InputChannel inputChannel, MessageQueue messageQueue);
}
// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    // 1. 获取 Native 的 InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    // 2. 获取 MessageQueue 对应的 native 的对象
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    // 3. 创建了一个 native 的 NativeInputEventReceiver 对象, 用于监听 InputChannel 中的事件
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);
    // 3.1 初始化这个 receiver 对象
    receiver->initialize();
    ......
    // 将句柄返回给 Java 层
    return reinterpret_cast<jlong>(receiver.get());
}

Native层的处理如下:

  • 先获取InputChannel和MessageQueue对应的native对象
  • 创建一个NativeInputEventReceiver,
  • 调用NativeInputReceiver.initialize对其初始化
// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<InputChannel>& inputChannel,
        const sp<MessageQueue>& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        // 保存服务端传来的 InputChannel
        mInputConsumer(inputChannel),
        // 保存主线程的 MessageQueue
        mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), 
        mFdEvents(0) {
    
}

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        // 获取 Socket 的端口文件描述符
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            // 让主线程的 Looper 监听这个 Socket 端口的文件描述符
            // this 指的是 fd 中有数据流入时的回调处理函数, 它由 NativeInputEventReceiver 的 handleEvent 实现
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        }
        ......
    }
}

NativeInputEventReceiver 初始化中,使用主线程Looper(Native)对InputChannel中保存的Socket端口文件描述符进行了监听操作,当Socket端口的fd中有数据流入时,会回调NativeInputEventReceiver 的 handleEvent 事件

4.3.2. handleEvent Native层事件流分发

// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ......
    if (events & ALOOPER_EVENT_INPUT) {
        ......
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        ......
    }
    .....
    return 1;
}
int register_android_view_InputEventReceiver(JNIEnv* env) {
    ......
    gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
            gInputEventReceiverClassInfo.clazz,
            "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V");
    ......
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ......
    for (;;) {
        ......
        if (!skipCallbacks) {
            jobject inputEventObj;
            ...... // 构建 InputEvent 实例
            if (inputEventObj) {
                ......
                // 回调了 Java 的 InputEventReceiver.dispatchInputEvent 方法
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
                        displayId);
                ......
            } else {
               ......
            }
        }
        ......
    }
}

至此,有事件发生时,就会通过JNI回调 InputEventReceiver.dispatchInputEvent 回调Java层去进行分发事件,Native 层的处理就结束了。

image.png

4.3.3. Java 层的事件流分发

public abstract class InputEventReceiver {
    
    // Called from native code.
    // 从Native层NativeInputEventReceiver::consumeEvents方法中回调
    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
        onInputEvent(event, displayId);
    }
    
}

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
    final class WindowInputEventReceiver extends InputEventReceiver {
        @Override
        public void onInputEvent(InputEvent event, int displayId) {
            enqueueInputEvent(event, this, 0, true);
        }
    }
    
}

InputEventReceiver.dispatchInputEvent 回调onInputEvent方法,真正实现是在ViewRootImpl 的enqueueInputEvent方法中

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    // ViewRootImpl 中维护了一个 InputEvent 链表, 用来描述待处理的输入事件
    QueuedInputEvent mPendingInputEventHead;        
    QueuedInputEvent mPendingInputEventTail;
    
    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        // 1. 将 InputEvent 包装成一个 QueuedInputEvent, 添加到 ViewRootImpl 中维护的事件队列尾部
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        
        if (processImmediately) {
            // 2.1 是立即处理
            doProcessInputEvents();
        } else {
            // 2.2 添加到 MessageQueue 处理
            scheduleProcessInputEvents();
        }
    }
    
}

ViewRootImpl中维护了一个输入事件队列,enqueueInputEvent 先将它添加到队列尾部, 然后判断是否立即处理,立即处理或者添加到MessageQueue中最后都会调用doProcessInputEvents方法。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
            
    void doProcessInputEvents() {
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            ......
            // 分发这个待处理的事件
            deliverInputEvent(q);
        }
        ......
    }
    
    InputStage mFirstInputStage;
    InputStage mFirstPostImeInputStage;

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                ......
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);
                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
            }
        }
    }

    private void deliverInputEvent(QueuedInputEvent q) {
        ......
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        if (stage != null) {
            ......
            // 调用 stage.deliver 分发事件, 它的对象在 setView 中构建
            stage.deliver(q);
        } else {
            // 描述事件分发完毕
            finishInputEvent(q);
        }
    }
               
}

doProcessInputEvents 会调用deliverInputEvent 来分发这个事件

  • 调用InputStage.deliver 来启动事件分发
  • 调用finishInputEvent 来结束事件分发

4.3.3.1. 事件分发的启动

ViewRootImpl 的InputState 对象是在setView时初始化的,看一下ViewPostImeInputStage实现

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    abstract class InputStage {
        
        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                // 1. 调用子类重写的 onProcess 方法
                apply(q, onProcess(q));
            }
        }
        
    }
    
    final class ViewPostImeInputStage extends InputStage {
        
        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                ......
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    // 调用了 processPointerEvent 
                    return processPointerEvent(q);
                } 
                ......
            }
        }
        
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            // 调用了 DecorView 的 dispatchPointerEvent
            boolean handled = mView.dispatchPointerEvent(event);
            ......
            return handled ? FINISH_HANDLED : FORWARD;
        }
        
    }
                
}

InputState.deliver 中回调了子类重写的onProcess, ViewPostImeInputStage 中调用processPointerEvent方法,最终会回调DecorView 的dispatchPointerEvent方法

4.3.3.1.1. DecorView的事件分发

先看一下View类的事件分发

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
            
    public final boolean dispatchPointerEvent(MotionEvent event) {
        // 若为触摸事件, 则调用 dispatchTouchEvent 进行分发
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            // 处理通用事件
            return dispatchGenericMotionEvent(event);
        }
    }
                
}

DecorView 重写了dispatchTouchEvent

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 1. 获取 Window 的 Callback
        final Window.Callback cb = mWindow.getCallback();
        // 2. 若 Callback 有效, 则调用 Callback.dispatchTouchEvent 进行事件分发否则执行 View 的事件分发
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }
    
}

这个Window.Callback是谁创建的?,看一下Activity的window创建过程

public class Activity extends ContextThemeWrapper {

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        ......
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ......
        // 把Activity与Window管理上
        mWindow.setCallback(this);
        ......
    }
}
4.3.3.1.2. Activity事件分发
public class Activity extends ContextThemeWrapper {
    
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        // 还是会优先调用 Window 的 superDispatchTouchEvent
        // 对应的实现是phoneWindow
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        // 若是没有被消费, 则自行处理
        return onTouchEvent(ev);
    }
    
}

Activity中事件分发的处理:

  • 先调用Window的superDispatchTouchEvent进行分发处理
  • 若事件没有被消费,则调用onTouchEvent处理(下面的人都不处理,只能boss来了)
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        // 调用了 DecorView 的 superDispatchTouchEvent 方法
        return mDecor.superDispatchTouchEvent(event);
    }
    
}

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
     public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
    
}

可以发现,最终还是回调了View 的dispatchTouchEvent 方法,从此便进入了View的事件分发流程。

4.3.3.2. 事件分发的结束

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
    private void finishInputEvent(QueuedInputEvent q) {
        ......
        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            // 调用了 InputEventReceiver 的 finishInputEvent 方法
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        } else {
            ......
        }
    }
    
}

public abstract class InputEventReceiver {
    
    public final void finishInputEvent(InputEvent event, boolean handled) {
        ......
        if (mReceiverPtr == 0) {
            ......
        } else {
            ......
            if (index < 0) {
                ......
            } else {
                ......
                // 到了 Native 层结束事件分发
                nativeFinishInputEvent(mReceiverPtr, seq, handled);
            }
        }
        ......
    }
    
}

finishInputEvent 最终会调用InputEventReceiver的nativeFinishInputEvent ,主要是负责向IMS发送回执消息。

客户端发送回执消息

// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
        jint seq, jboolean handled) {
    // 调用了 NativeInputEventReceiver 的 finishInputEvent
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(seq, handled);
    ......
}

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    ......
    // 调用了 InputChannel 的 sendFinishedSignal 向服务端发送了一个执行结束的指令
    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    ......
    return status;
}

最终调用了InputChannel的sendFinishedSignal通知服务端应用进程的事件分发结束了

还记得上面IMS注册InputChannel过程吗?它使用InputDispatcher内部的mLooper监听了服务端的socket端口,有数据会回调handleReceiveCallback,那么服务端收到了回执消息后的处理是怎样的?

服务端处理回执消息

// frameworks/native/services/inputflinger/InputDispatcher.cpp
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        AutoMutex _l(d->mLock);
        // 1. 找到分发的窗体连接
        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
        ......
        bool notify;
        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
            ......
            nsecs_t currentTime = now();
            bool gotOne = false;
            status_t status;
            for (;;) {
                uint32_t seq;
                bool handled;
                // 2. 接收 Socket 发送过来的回执信息
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                // 3. 处理回执数据
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
            if (gotOne) {
                // 4. 执行 Commands
                d->runCommandsLockedInterruptible();
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }
            ......
        } else {
            ......
            // InputChannel 被关闭或者发生错误
        }
        ......
    } // release lock
}

IMS 处理回执消息流程:

  • 找到分发事件的窗体连接Connection
  • 通过Socket获取回执数据
  • 调用finishDispatchCycledLocked添加分发结束指令
  • 调用runCommandsLockedInterruptible对回执消息的处理

添加结束指令

// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, uint32_t seq, bool handled) {
    connection->inputPublisherBlocked = false;
    if (connection->status == Connection::STATUS_BROKEN
            || connection->status == Connection::STATUS_ZOMBIE) {
        return;
    }
    // 1. 通知其他组件分发下一个事件序列
    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}

void InputDispatcher::onDispatchCycleFinishedLocked(
        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
    // 2. 创建了一个指令信息实体类 CommandEntry
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
    // 3. 填充数据
    commandEntry->connection = connection;
    commandEntry->eventTime = currentTime;
    commandEntry->seq = seq;
    commandEntry->handled = handled;
}

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    // 2.1 创建指令信息 CommandEntry 对象, 指令为 doDispatchCycleFinishedLockedInterruptible
    CommandEntry* commandEntry = new CommandEntry(command);
    // 2.2 添加到指令队列尾部
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}

finishedDispatchCycleLocked处理回执数据的过程:

  • 创建一个doDispatchCycleFinishedLockedInterruptible执行指令对象CommandEntry
  • 将它添加到了指令队列的尾部,等待后续的执行

执行结束指令

// frameworks/native/services/inputflinger/InputDispatcher.cpp
bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }
    do {
        // 取出指令
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
        // 执行指令
        Command command = commandEntry->command;
        (this->*command)(commandEntry);
        // 删除指令
        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());
    return true;
}

InputDispatcher 执行指令的过程即遍历指令队列,执行具体的指令,我们看一下分发结束的执行函数:doDispatchCycleFinishedLockedInterruptible

// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
        CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;
    nsecs_t finishTime = commandEntry->eventTime;
    uint32_t seq = commandEntry->seq;
    bool handled = commandEntry->handled;
    // 1. 获取分发的事件
    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
    if (dispatchEntry) {
        ......
        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
            // 2. 将事件从等待分发完毕的队列中移除
            connection->waitQueue.dequeue(dispatchEntry);
            ......
        }
        // 3. 让当前 connection 去处理其 outboundQueue 中的后续事件
        startDispatchCycleLocked(now(), connection);
    }
}

至此,事件分发结束了,过程如下

  • 获取本次分发的事件描述 dispatchEntry
  • 从Connection待回执的事件队列waitQueue中移除
  • 让Connection继续处理其待分发队列outboundQueue的后续时间

4.3.4. Connection整个流程

  • 服务端Connection分发事件
    • 从outboundQueue取出一个事件,交给InputChannel的Socket端口发给客户端
    • 将这个事件添加到等待队列waitQueue
  • 客户端处理完毕后通过InputChannel的Socket端口发给服务端
  • 服务Connection的InputChannel接收到回执消息
    • 将这个事件从waitQueue中移除
    • 从outboundQueue获取下一个事件重复上述操作进行分发

4.4. IMS 总结

image.png

整个IMS事件分发大致的流程分析完毕,回顾一下整个事件分发流程

  • 在Window创建过程时(WMS.addWindow),创建了InputChannel输入通道组
    • 创建最大缓冲32kb的Socket
    • 服务端的InputChannel保留Socket服务端的文件描述符
    • 客户端的InputChannel保留Socket客户端的文件描述符
  • 将服务端新建的 WindowState 和 InputChannel 注册到IMS 中
    • InputDispatcher 把WindowState 和InputChannel 封装到Connection 中用于后续事件的分发
  • 客户端监听InputChannel
    • 应用进程拿到WMS传递过来的InputChannel后便会创建一个InputChannelReceived
    • InputChannelReceived在Native层监听了InputChannel的Socket端口
    • 有数据写入时便会通过JNI调用dispatchInputEvent分发到Java层
  • 执行事件分发
    • 事件分发的启动
      • ViewRootImpl中维护了一个输入事件队列,这个事件最终会交给InputStage消费
      • InputStage中会将事件传递给DecorView
      • DecorView 首先会尝试将事件传给给当前的Window的 Callback 对象处理
        • 在Activity中创建Window时,执行了window.setCallback(activity),Callback就是Activity本身
      • Activity执行事件时会先将事件再次传给DecorView,最终调用View的dispatchTouchEvent 进入ViewGroup的事件分发流程
    • 事件分发的结束
      • 客户端发送回执消息
      • 服务端接收回执消息并处理
        • 创建结束指令到mCommandQueue指令队列
        • mCommandQueue执行的结束指令
          • 从Connection待回执的事件队列waitQueue中移除
          • 让Connection继续处理其待分发队列outboundQueue中的后续事件