Android 输入系统【1】通过 IMS 的创建理解 Android 的输入流程

Android 操作系统能够接收外部输入事件并进行处理来实现设备与用户的交互。完成这一工作需要系统多方配合,以触摸屏幕为例,整体的流程大概是这样的:

  1. 用户触摸屏幕;
  2. 输入系统接收到触摸事件;
  3. 输入系统将事件传递给窗口管理器;
  4. 窗口管理器将事件传递给目标窗口;
  5. 目标窗口将事件进行分发,直到某个 View 处理触摸事件。

第二步中Android 操作系统基于 Linux 内核,其设备管理机制本质上还是 Linux 那一套逻辑,在 Java 层面,通过 InputManagerService 来与 Native 层进行通信(JNI)。

第三、四步,IMS 将事件交给 WMS ,WMS 筛选出要处理事件的窗口。最后将事件在选定的窗口中进行分发,即第五步,原理就是 Android 中的 View 的事件分发机制。

InputManagerService

简称 IMS,是管理输入设备的 IInputManager 的系统实现,它封装了 C++ 中的 InputManager 并提供了回调。InputManagerService 本身是 Android 的系统服务之一,其启动逻辑在 SystemServer 中的 startOtherServices 方法中进行。

启动流程

整体流程

在 startOtherServices 方法中具有完整的创建、绑定 WMS 和启动的逻辑:

    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {       
        WindowManagerService wm = null;
        InputManagerService inputManager = null;
        try {
            // ...
            t.traceBegin("创建 IMS");
            inputManager = new InputManagerService(context);
            t.traceEnd();

            t.traceBegin("创建 WMS");
            // WMS needs sensor service ready
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
           // 通过 IMS 创建 WMS
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            // 将 WMS、IMS 添加到系统服务管理器中
           ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            t.traceEnd();
      
           // ... 这里省略一些 WMS 关联其他服务的逻辑
            t.traceBegin("WMS 初始化完成");
            wm.onInitReady();
            t.traceEnd();

            t.traceBegin("IMS 添加 callback ,并启动");
            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
            inputManager.start();
            t.traceEnd();
        } catch (Throwable e) {
            Slog.e("System""************ Failure starting core service");
            throw e;
        }
      
        // ... 
       
       final InputManagerService inputManagerF = inputManager;
        final WindowManagerService windowManagerF = wm;

        // 我们现在告诉 ActivityManagerService 可以运行第三方代码。 
        // 一旦它到达第三方代码可以真正运行的状态(但在它实际开始启动初始应用程序之前),它将回调我们,以便我们完成初始化。
        mActivityManagerService.systemReady(() -> {
            t.traceBegin("MakeInputManagerServiceReady");
            try {
                // TODO(BT) 将参数传递给输入管理器
                if (inputManagerF != null) {
                    inputManagerF.systemRunning(); // 【5】
                }
            } catch (Throwable e) {
                reportWtf("Notifying InputManagerService running", e);
            }
            t.traceEnd();
        }, t);
  }
  1. startOtherServices 方法中,IMS 首先直接实例化:

    inputManager = new InputManagerService(context);
  2. 然后 WMS 紧跟着初始化,与 IMS 不同,WMS 是通过 main 方法创建的:

    mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                        new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
  3. WMS 对象创建完成后,IMS 和 WMS 一起加入到 SystemManager 中:

    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                        DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
  4. 在 WMS 初始化完成后,紧跟着 IMS 添加了 WMS 的回调,并调用 start 方法:

    t.traceBegin("StartInputManager");
    inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
    inputManager.start();
    t.traceEnd();
  5. 最后,会在 AMS 准备完成后,调用 IMS 更新一些系统设备状态来完成系统的初始化。

            mActivityManagerService.systemReady(() -> {
                t.traceBegin("MakeInputManagerServiceReady");
                try {
                    // TODO(BT) 将参数传递给输入管理器
                    if (inputManagerF != null) {
                        inputManagerF.systemRunning(); // 【5】
                    }
                } catch (Throwable e) {
                    reportWtf("Notifying InputManagerService running", e);
                }
                t.traceEnd();
            }, t);

从整体流程上可以看出 IMS 优先创建,并用于 WMS 的创建,IMS 在 WMS 创建完成后添加了来自 WMS 的回调,两者关系密不可分。

本文介绍 IMS 的创建流程,只讲解 IMS 实例化中的逻辑。

InputManagerService 的构造方法

InputManagerService 的创建逻辑在 InputManagerService 的构造方法中:

    public InputManagerService(Context context) {
        this(new Injector(context, DisplayThread.get().getLooper()));
    }

同名不同参的构造方法,看起来是重构了方便测试使用:

  @VisibleForTesting
    InputManagerService(Injector injector) {
        // java和native代码都可以访问静态关联映射,所以在初始化native service之前必须先进行初始化。
        mStaticAssociations = loadStaticInputPortAssociations();
        mContext = injector.getContext();
        mHandler = new InputManagerHandler(injector.getLooper());
        mNative = injector.getNativeService(this); // NativeInputManagerService
        // ... 
        injector.registerLocalService(new LocalService());
    }

在构造方法中有一个 Injector ,它是 IMS 的内部类,单独抽象是方便测试时使用:

    /** 测试依赖项的注入点 */
    @VisibleForTesting
    static class Injector {
        private final Context mContext;
        private final Looper mLooper;

        Injector(Context context, Looper looper) {
            mContext = context;
            mLooper = looper;
        }

        Context getContext() return mContext; }

        Looper getLooper() return mLooper; }

        NativeInputManagerService getNativeService(InputManagerService service) {
            return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());
        }

        void registerLocalService(InputManagerInternal localService) {
            LocalServices.addService(InputManagerInternal.classlocalService);
        }
    }

Injector 对象的初始化在 InputManagerService 的构造方法中,context 是来自 SystemServer 中的上下文,Looper 是 DisplayThread 线程中的 Looper 对象。

    public InputManagerService(Context context) {
        this(new Injector(context, DisplayThread.get().getLooper()));
    }

InputManagerService 的构造方法中执行逻辑:

  1. 创建 java 和 native 代码都可以访问静态关联映射,所以在初始化 native service 之前必须先进行初始化;
  2. 从 injector 中取出 context 对象;
  3. 通过 injector 的 Looper 创建 InputManagerHandler;
  4. 通过 injector 获取 NativeInputManagerService 对象;
  5. 创建多指触控的配置文件;
  6. injector 调用 registerLocalService 方法,LocalServices 添加服务 LocalService 。

InputManagerHandler

在第 3 步中,通过 DisplayThread 的 Looper 创建了一个 Handler ,说明 Handler 消息最终发往 DisplayThread 线程去执行。

    private final class InputManagerHandler extends Handler {
        public InputManagerHandler(Looper looper) {
            super(looper, nulltrue /*async*/);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DELIVER_INPUT_DEVICES_CHANGED:
                    deliverInputDevicesChanged((InputDevice[])msg.obj);
                    break;
                case MSG_SWITCH_KEYBOARD_LAYOUT:
                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
                    break;
                case MSG_RELOAD_KEYBOARD_LAYOUTS:
                    reloadKeyboardLayouts();
                    break;
                case MSG_UPDATE_KEYBOARD_LAYOUTS:
                    updateKeyboardLayouts();
                    break;
                case MSG_RELOAD_DEVICE_ALIASES:
                    reloadDeviceAliases();
                    break;
                case MSG_DELIVER_TABLET_MODE_CHANGED:
                    SomeArgs args = (SomeArgs) msg.obj;
                    long whenNanos = (args.argi1 & 0xFFFFFFFFL) | ((long) args.argi2 << 32);
                    boolean inTabletMode = (boolean) args.arg1;
                    deliverTabletModeChanged(whenNanos, inTabletMode);
                    break;
                case MSG_POINTER_DISPLAY_ID_CHANGED:
                    handlePointerDisplayIdChanged((PointerDisplayIdChangedArgs) msg.obj);
                    break;
            }
        }
    }

从消息名和方法名可以看出,InputManagerHandler 中是一些处理输入设备变化的逻辑。

NativeInputManagerService

第 4 步创建了一个 NativeInputManagerService 对象,NativeInputManagerService 是一个接口,实现类是 NativeInputManagerService.NativeImpl  。后者的方法都是 native 方法,说明实现逻辑是在 C++ 层,这些方法对应的逻辑在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp 中。

NativeInputManagerService.NativeImpl 的构造方法:

NativeImpl(InputManagerService service, Context context, MessageQueue messageQueue) {
    mPtr = init(service, context, messageQueue);
}

NativeInputManagerService.NativeImpl 的构造实际上是调用了一个 native 方法 init,来获取了一个指针的内存地址:

private native long init(InputManagerService service, Context context, MessageQueue messageQueue);

这里的 init 方法最终调用的是com_android_server_input_InputManagerService.cpp 中的 nativeInit 方法:Android 输入系统【1】通过 IMS 的创建理解 Android 的输入流程

static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

nativeInit 方法中首先创建了一个消息队列;然后创建 NativeInputManager 对象指针。reinterpret_cast 方法将 NativeInputManager 指针强制转换并返回为 jlong 类型(个人理解应该是返回了内存地址)。

NativeInputManager

NativeInputManager 的构造方法:

NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerAcceleration = android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;

    InputManager* im = new InputManager(thisthis); // 【1】
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}

在这个方法中,最核心的一步就是创建了 InputManager 。

InputManager

InputManager 是 Native 层真实处理事件的类,事件流通过 InputListener 接口传递,InputListener 的传递流程如下:

InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher

InputManager 的构造方法:

private:
    std::unique_ptr<InputReaderInterface> mReader;

    std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;

    std::unique_ptr<InputClassifierInterface> mClassifier;

    std::unique_ptr<InputDispatcherInterface> mDispatcher;
};

InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
    mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);
    mReader = createInputReader(readerPolicy, *mBlocker);
}

这里创建了一些输入流程中的核心组件:

  • InputReaderInterface :InputReader shared library 的接口,管理一个或多个处理原始输入事件的线程,并将经过处理的事件数据发送到 input listener 。实现必须保证该接口的线程安全。然而,由于 input listener  不是线程安全的,所有对 listener 的调用必须发生在同一个线程。
  • UnwantedInteractionBlockerInterface :InputListener 层的基本接口。阻止意外的输入事件。 不是线程安全的。 必须从同一个线程调用。 所有工作都在调用线程上执行。
  • InputClassifierInterface:InputListener 层的基本接口,将事件进行分类。
  • InputDispatcherInterface:将input reader 生成的输入事件通知给系统,调度程序主要是异步执行的。

对应的四个对象分别是:

  • 输入读取器 mReader
  • 输入拦截器 mBlocker
  • 输入分类器 mClassifier
  • 输入分发器 mDispatcher

回顾最开始提到的输入流程:

InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher

本质上也是输入事件的处理流程:

输入事件读取 -> 输入事件拦截 -> 输入事件分类 -> 输入事件分发

最后再提一下 InputManager 的构造方法接收两个参数 readerPolicy 和 dispatcherPolicy ,它们都是 this ,也就是 NativeInputManager 自身,这是因为 NativeInputManager 实现了两个参数类型的接口:

class NativeInputManager : public virtual RefBase,
    public virtual InputReaderPolicyInterface,
    public virtual InputDispatcherPolicyInterface,
    public virtual PointerControllerPolicyInterface 

InputDispatcher

InputManager 中最先创建的是 InputDispatcher :

namespace android {

std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
        const sp<InputDispatcherPolicyInterface>& policy)
 
{
    return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
}

// namespace android

InputManager 会将事件分派到输入目标。输入调度程序的一些功能,如识别输入目标,是由一个单独的策略对象控制的。

重要提示因为策略可能会阻塞或导致重新进入输入调度程序,所以在持有策略的内部锁时,输入调度程序永远不会调用策略。 该实现还经过精心设计,以从诸如在识别输入目标或处理超时时输入通道未注册等情况中恢复。

标记为“Locked”的方法必须在获得锁时调用。

标记为“LockedInterruptible”的方法必须在获得锁时调用,但在执行过程中可能会释放锁,调用策略,然后重新获得锁。调用者负责优雅地恢复。

一个’LockedInterruptible’方法可以称为’Locked’方法,反之亦然。

InputDispatcher 的内部持有 WMS 管理的窗口信息,在这里将事件派发给合适的 Window 。

InputClassifier

第二个创建的对象是 InputClassifier ,它是 InputClassifierInterface 的实现。所有的输入事件都要经过这一层处理。 InputClassifier 充当除 Motion 事件以外的所有输入事件的传递器,而 Motion 类型的事件会被发送到 MotionClassifier。

UnwantedInteractionBlocker

UnwantedInteractionBlocker 是 UnwantedInteractionBlockerInterface 的实现,表示输入过程中处理过滤的一层,所有的输入事件都要经过这一层,同样除了 Motion 事件;Motion 类型的事件会被发送到 PalmRejectors ,后者会检测不需要的触摸(误触),并对其进行过滤。

InputReader

最后是 InputReader :

namespace android {

std::unique_ptr<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener)
 
{
    return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
}

// namespace android

注意:早些版本 EventHub 是在 InputManager 的构造方法中创建对象的,最新版本的 Android 代码已经将其移动到 createInputReader 方法时创建。

InputReader 从事件中心(event hub)读取原始事件数据并将其处理为输入事件,然后发送到输入侦听器(input listener)。InputReader 的某些功能,例如低功耗状态下的早期事件过滤,由单独的策略对象控制。

InputReader 拥有一组 InputMappers。 InputReader 启动它自己的线程,大部分工作都发生在这里,但 InputReader 可以接收来自运行在任意线程上的其他系统组件的查询。 为了使事情易于管理,InputReader 使用单个 Mutex 来保护其状态。 Mutex 可能在调用 EventHub 或 InputReaderPolicy 时被保持,但在调用 InputListener 时它永远不会被保持。 所有对 InputListener 的调用都必须发生在 InputReader 的线程中。

EventHub

在 InputReader 的创建方法中需要一个 EventHub 参数,它也在这个方法中进行了初始化,EventHub 通过 Linux 内核的 INotify 与 Epol l机制监听设备节点,通过 EventHub 的 getEvent 函数读取设备节点的增删事件和原始输入事件。

Java 层系统服务添加注册

在 IMS 的构造方法的最后调用了 registerLocalService 方法,首先我们看看这个方法的定义:

void registerLocalService(InputManagerInternal localService) {
  LocalServices.addService(InputManagerInternal.classlocalService);
}

InputManagerInternal 是一个抽象类,实现是 IMS 中的内部类 LocalService ,InputManagerInternal 的注释是 输入管理器本地系统服务接口,仅用于系统服务 。

LocalService :

private final class LocalService extends InputManagerInternal {
    @Override
    public void setDisplayViewports(List<DisplayViewport> viewports) {
        setDisplayViewportsInternal(viewports);
    }
  
   // ...

    @Override
    public void setInteractive(boolean interactive) {
        mNative.setInteractive(interactive);
    }

    // ...

    @Override
    public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
            @NonNull IBinder toChannelToken)
 
{
        return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
    }

    // ...
}

LocalService 实现的方法基本上分为三类:

  1. 因为 LocalService 是 IMS 内部类,直接调用 IMS 中的方法;
  2. 调用 mNative (NativeInputManagerService) 的方法;
  3. 调用 IMS 实现父类或接口的方法 (通过 InputManagerService.this 调用)。

而 LocalService 添加到了 LocalServices 中,LocalServices 是一个服务管理类,使用方式和 ServiceManager 类似,只不过这里注册的服务不是 Binder 对象,只能在同一个进程中使用。一旦所有服务都转换为 SystemService 接口,这个类就可以被吸收到 SystemServiceManager 中。

public final class LocalServices {
    private LocalServices() {}

    private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
            new ArrayMap<Class<?>, Object>();

    @SuppressWarnings("unchecked")
    public static <T> getService(Class<T> type) {
        synchronized (sLocalServiceObjects) {
            return (T) sLocalServiceObjects.get(type);
        }
    }

    public static <T> void addService(Class<T> type, T service) {
        synchronized (sLocalServiceObjects) {
            if (sLocalServiceObjects.containsKey(type)) {
                throw new IllegalStateException("Overriding service registration");
            }
            sLocalServiceObjects.put(type, service);
        }
    }

    @VisibleForTesting
    public static <T> void removeServiceForTest(Class<T> type) {
        synchronized (sLocalServiceObjects) {
            sLocalServiceObjects.remove(type);
        }
    }
}

为什么 IMS 构造的最后需要 registerLocalService ,猜测是通过 LocalServices ,在系统进程的其他服务中快速获取 IMS 服务,例如:

Android 输入系统【1】通过 IMS 的创建理解 Android 的输入流程
image-20221114172522823.png

总结

从 IMS 的创建流程就可窥探出 Android 输入系统的宏观角度的逻辑,事件经由输入设备发送信号给 Linux 内核,Linux 内核的事件通过 EventHub 传递给 Native 层,Native 层的 InputReader 读取事件,传递给 UnwantedInteractionBlocker 进行过滤错误的输入,然后经过 InputClassifier 进行归类,最后交给 InputDispatcher 传递给目标窗口,目标窗口通过 View 的事件分发机制将事件传递给消耗事件的 View。


原文始发于微信公众号(八千里路山与海):Android 输入系统【1】通过 IMS 的创建理解 Android 的输入流程

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/85006.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!