[Android] Input事件分发流程之IMS初始化(1)

IMS初始化

一、简介

当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件

二、对象介绍

  1. frameworks/native/services/inputflinger/reader/InputReader.cpp
  2. frameworks/native/services/inputflinger/reader/EventHub.cpp
  3. frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
  4. frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  5. frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
  6. frameworks/native/services/inputflinger/InputManager.cpp

三**、IMS初始化分析**

InputManagerService位于SystemServer.java#startOtherServices方法中被启动,源码如下

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ...
        InputManagerService inputManager = null;
				...
				t.traceBegin("StartInputManagerService");
        inputManager = new InputManagerService(context);
        t.traceEnd();
				...
				// WMS与:InputManagerService/ActivityTaskManager/PhoneWindowManager相互绑定
				// WMS创建时会基于DisplayThread,也就是WMS是运行在android.display线程中
        wm = WindowManagerService.main(context, **inputManager**, !mFirstBoot, mOnlyCore,
				new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
				// 将IMS加入ServiceManager中统一管理
				ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
			  ...
				inputManager.start();
				

所以IMS的执行顺序为:new InputManagerService(context);inputManager.start();并且属于systemServer线程,继续看构造方法

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public InputManagerService(Context context) {
        this.mContext = context;
        // 运行在android.display线程,这个handler很重要
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

        ...
        //初始化native对象
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
				...
        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }

this.mHandler是非常重要的,运行在android.display线程上,跟WMS所处的线程一样,主要用于以下几个方面:

  1. 接收和处理输入事件:mHandler会接收来自底层硬件(如触摸屏、按键等)的原始输入事件,并将其封装成消息发送给mHandler处理。这样可以将输入事件的处理从主线程转移到后台线程(android.display)
  2. **分发输入事件:**mHandler会将接收到的输入事件分发给相应的窗口或应用程序进行处理。通过消息的处理和分发机制,确保输入事件能够顺利传递给正确的目标,实现用户输入的精确响应。
  3. **处理输入事件队列:**mHandler会维护一个输入事件队列,将接收到的输入事件按照一定的顺序存储在队列中,并逐个处理。这样可以保证输入事件的有序性,避免输入事件的丢失或混乱。

总而言之,mHandler在InputManagerService中的作用是接收、处理和分发输入事件,实现用户输入的响应和传递。通过将输入事件的处理从主线程转移到android.display线程,systemServer线程会等待display线程处理完成后才会继续执行

继续看看nativeInit方法,位于:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

类名叫:NativeInputManager

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj/*IMS*/, jobject contextObj/*context*/, jobject messageQueueObj/*MessageQueue*/) {
    // messageQueueObj就是上层调用的:mHandler.getLooper().getQueue()
    // 该handler属于android.display线程
    // 这段代码定义了一个名为sp的变量,它的类型是一个智能指针,指向一个MessageQueue对象。智能指针是一种封装了指针的对象,
    // 它具有自动管理内存的能力,即在不需要使用该指针时会自动释放它所指向的内存空间,
    // 避免了内存泄漏的问题。这里使用的是Android系统提供的sp智能指针,它是一个引用计数智能指针,可以在多个线程间安全地传递和共享
		// 这里获取了Message队列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    //NativeInputManager中创建了InputManager
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

先介绍一下方法里的形参:

  • JNIEnv* env:JNI对象,native方法必须也有的,该参数无需开发者去关心(只有JNI开发时会用到里面的参数和方法,此处不做关心)
  • serviceObj:InputManagerService.java的实例对象
  • contextObj:IMS中的context上下文
  • messageQueueObj:mHandler中Looper里管理的队列

以上代码只关注:获取了messageQueue和创建了NativeInputManager对象

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper/*messageQueue的looper*/) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
    // mServiceObj就是InputManagerService
    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;
    // 初始化InputManager,创建InputManager对象
    InputManager* im = new InputManager(this, this);
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}

然后继续创建了InputManager对象,并将im加入服务队列中,继续看看IM对象的构造方法

frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
     // 创建InputDispatcher
     // 创建InputReader
     // InputReader读取/dev/input节点,然后将数据传递给InputDispatcher
     // 由InputDispatcher来分发事件到window
     // readerPolicy和dispatcherPolicy都是NativeInputManager对象,NativeInputManager实现了这两个接口
     // 就饿可以通过Policy来回调数据给NativeInputManager了
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    // 包含了EventHub和InputListenerInterface及readerPolicy
    mReader = createInputReader(readerPolicy, mClassifier);
}

// 创建InputReader,并创建了EventHub
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                           const sp<InputListenerInterface>& listener) {
    return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

// 创建InputDispatcher
sp<InputDispatcherInterface> createInputDispatcher(
        const sp<InputDispatcherPolicyInterface>& policy) {
    return new android::inputdispatcher::InputDispatcher(policy);
}

1.记住两个重要的接口InputReaderPolicyInterfaceInputDispatcherPolicyInterface,他们的具体实现在NativeInputManager中,也就是com_android_server_input_InputManagerService.cpp中。

2.然后创建了InputDispatcherInputReader,并在InputReader创建过程中同步创建了EventHub

3.InputReader:读取/dev/input节点,然后将数据传递给InputDispatcher

4.InputDispatcher:接收来自InputReader的事件,然后由InputDispatcher来分发事件到window

继续看看EventHub的构造方法,看看有什么作用

frameworks/native/services/inputflinger/reader/EventHub.cpp

EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {
    ensureProcessCanBlockSuspend();

    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));

    // inotify是Linux内核提供的一种文件系统监控机制,它可以监测文件或目录的变化,并通过特定的事件通知机制通知进程
    mINotifyFd = inotify_init();
    // 此处DEVICE_PATH为"/dev/input",监听该设备节点创建与删除操作
    // 屏幕的所有事件都会经过处理存放进/dev/input,内核态
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
                        strerror(errno));
    if (isV4lScanningEnabled()) {
        mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
        LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
                            VIDEO_DEVICE_PATH, strerror(errno));
    } else {
        mVideoWd = -1;
        ALOGI("Video device scanning disabled");
    }

    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = mINotifyFd;
    // 将mINotifyFd添加到fd池中
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);

    int wakeFds[2];
    // 调用了 pipe() 函数来创建一个无名管道,并将管道的读取端口和写入端口的文件描述符分别存储在 wakeFds[0] 和 wakeFds[1] 中。
    // 这个管道可以用于进程间通信,其中一个进程将数据写入管道,而另一个进程则可以从管道中读取这些数据。
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    // 将pipe的读设置为非阻塞方式
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
                        errno);
    // 将pipe的写设置为非阻塞方式
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
                        errno);

    eventItem.data.fd = mWakeReadPipeFd;
    // 添加管道的读端到epoll实例
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                        errno);
}

作用总结:创建文件系统监控机制,它可以监测/dev/input的变化(因为从屏幕触碰的事件都会存入这个驱动节点中),创建管道用于进程间通信(文件描述符存放到wakeFds[0] 和 wakeFds[1]中),其中一个进程将数据写入管道,而另一个进程则可以从管道中读取这些数据, 再将pipe的读/写设置为非阻塞方式。

创建监控驱动节点变化的文件描述符,通过监听驱动节点的内容,将内容放入管道,进行两端通信

frameworks/native/services/inputflinger/reader/InputReader.cpp

InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub/*eventHub*/,
                         const sp<InputReaderPolicyInterface>& policy/*NativeInputManager*/,
                         const sp<InputListenerInterface>& listener)
      : mContext(this),
        mEventHub(eventHub),
        mPolicy(policy)...{
    // 创建了QueuedInputListener--->创建输入监听对象
    // 此处mQueuedListener的成员变量mInnerListener便是InputDispatcher对象。
    mQueuedListener = new QueuedInputListener(listener);
	...
}

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
        mInnerListener(innerListener) {
}

创建了QueuedInputListener,里面有个成员变量mInnerListener(InputListenerInterface),指向的回调就是InputDispatcher对象,InputListenerInterface接口很重要,用于将输入事件封装成NotifyKeyArgs对象回调给InputDispatcher

再来看看rameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
      : mPolicy(policy),
        mPendingEvent(nullptr),
        mLastDropReason(DropReason::NOT_DROPPED),
        mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
        mAppSwitchSawKeyDown(false),
        mAppSwitchDueTime(LONG_LONG_MAX),
        mNextUnblockedEvent(nullptr),
        mDispatchEnabled(false),
        mDispatchFrozen(false),
        mInputFilterEnabled(false),
        // mInTouchMode will be initialized by the WindowManager to the default device config.
        // To avoid leaking stack in case that call never comes, and for tests,
        // initialize it here anyways.
        mInTouchMode(true),
        mMaximumObscuringOpacityForTouch(1.0f),
        mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
        mWindowTokenWithPointerCapture(nullptr),
        mLatencyAggregator(),
        mLatencyTracker(&mLatencyAggregator),
        mCompatService(getCompatService()) {
    // 创建一个looper
    mLooper = new Looper(false);
    mReporter = createInputReporter();

    mKeyRepeatState.lastKeyEntry = nullptr;
    // 获取分发超时参数
    policy->getDispatcherConfiguration(&mConfig);
}
  1. 创建属于自己线程的Looper对象;
  2. 配置超时机制,超时参数来自于IMS,参数默认值keyRepeatTimeout= 500,keyRepeatDelay = 50。
  3. keyRepeatTimeout的值为500 * 1000000LL,即500毫秒。这个值表示当用户按住一个键不放时,系统会在这个键按下后的500毫秒之后开始重复这个键的事件
  4. keyRepeatDelay的值为50 * 1000000LL,即50毫秒。这个值表示当用户按住一个键不放时,系统会在这个键按下后的50毫秒之后开始发送第一个重复事件

初始化就算完成了,再来总结一下:首先通过上层的IMS进入native层,创建NativeInputManager—>InputManager,然后创建了EventHub,InputReader,InputDispatcher,EventHub用于创建文件描述符来监听/dev/input驱动节点的内容,再通过管道形式进行数据传递,InputReader创建了事件输入的监听器(QueuedInputListener),InputDispatcher创建了属于自己的looper和配置了超时机制。以上的输入事件均运行在android.display线程,因为looper是由IMS传递下来的在这里插入代码片

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android IMS(IP Multimedia Subsystem)通话流程涉及多个组件和协议之间的交互。下面是一个简要的Android IMS通话流程: 1. 注册:设备启动时,IMS客户端将向IMS网络注册。这包括与P-CSCF(Proxy Call Session Control Function)进行SIP注册,然后与S-CSCF(Serving Call Session Control Function)进行认证和注册。 2. 呼叫建立:当用户拨打电话时,IMS客户端将向S-CSCF发起呼叫请求。S-CSCF将执行鉴权和授权,并将呼叫请求转发给相应的应用服务器(AS)。 3. 呼叫路由:AS根据呼叫请求中的信息,确定呼叫目标的位置,并将呼叫路由到目标用户所在的S-CSCF。 4. 呼叫传输:S-CSCF将呼叫请求转发给目标用户所在的P-CSCF。P-CSCF将负责与目标用户设备建立会话并传输呼叫请求。 5. 呼叫控制:目标用户设备接收到呼叫请求后,IMS客户端将对呼叫进行控制,包括呼叫接听、挂断等操作。 6. 媒体传输:一旦呼叫建立成功,媒体流将通过IMS网络进行传输。这可能涉及到RTP(Real-time Transport Protocol)和RTCP(Real-time Transport Control Protocol)。 7. 呼叫结束:当通话结束时,IMS客户端将向S-CSCF发送呼叫释放请求,S-CSCF将释放与呼叫相关的资源,并通知AS。 这只是Android IMS通话流程的简要描述,实际流程可能会因网络配置和运营商要求而有所不同。IMS通话流程涉及的协议包括SIP(Session Initiation Protocol)、SDP(Session Description Protocol)、RTP等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值