• input子系统:InputManagerService M


    由于客户新提了一个屏蔽设备的功能需求(屏蔽鼠标、键盘、触摸等输入子系统事件),刚开始一头雾水,不知道从哪改起。

    看了一些网上的文章,这就要涉及到InputManagerService这个庞大的东西,就需要大概了解这个服务的基本流程。

    先贴几张框架图:

    一.输入事件传递流程的组成部分:

    输入系统是外界与Android设备交互的基础,仅凭输入系统是无法完成输入事件传递的,因此需要输入系统和Android系统的其他成员来共同完成事件传递。输入系统事件传递需要经过以下几个部分。

    输入事件传递流程可以大致的分为三个部分,分别是输入系统部分、WMS处理部分和View处理部分。

    输入系统部分

    输入系统部分主要又分为输入子系统和InputManagerService组成(以下简称IMS),在Android中还有一个IMS(IP Multimedia Subsystem)意为为IP多媒体子系统,不要搞混了。
    Android的输入设备有很多种,比如屏幕、键盘、鼠标、游戏手柄、操纵杆等等,其中应用开发接触最多的屏幕。当输入设备可用时,Linux内核会在/dev/input中创建对应的设备节点。
    用户操作这些输入设备时会产生各种事件比如按键事件、触摸事件、鼠标事件等。
    输入事件所产生的原始信息会被Linux内核中的输入子系统采集,原始信息由Kernel space的驱动层一直传递到User space的设备节点。

    Android提供了getevent和sendevent两个工具帮助开发者从设备节点读取输入事件和写入输入事件。

     

    IMS所做的工作就是监听/dev/input下的所有的设备节点,当设备节点有数据时会将数据进行加工处理并找到合适的Window,将输入事件派发给它。

    WMS处理部分

     WMS的职责之一就是输入系统的中转站,WMS作为Window的管理者,会配合IMS将输入事件交由合适的Window来处理。

    View处理部分

    View处理部分应该是大家最熟悉的了,一般情况下,输入事件最终会交由View来处理,应用开发者就可以通过一些回调方法轻松得到这个事件的封装类并对其进行处理,比如onTouchEvent(MotionEvent ev)方法。

    二、IMS服务部分

    与AMS、WMS、PMS一样,IMS的在SyetemServer进程中被创建的,SyetemServer进程用来创建系统服务。

    启动内容:
    0.startOtherServices启动InputManagerService

    1.inputManagerService启动初始化

    2.eventhub初始化

    3.InputDispatcherThread初始化和启动

    4.inputReaderThread启动

    frameworks/base/services/java/com/android/server/SystemServer.java

    main方法中只调用了SystemServer的run方法,如下所示。
    frameworks/base/services/java/com/android/server/SystemServer.java startOtherServices函数中则启动了CameraService、AlarmManagerService、VrManagerService等服务,这些服务的父类为SystemService

     在startOtherServices这个函数里面实现ims的创建和启动。

    2.1 InputManagerService的构建:
    public InputManagerService(Context context) {
            this.mContext = context;
            this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    
            mUseDevInputEventForAudioJack =
                    context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
            Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                    + mUseDevInputEventForAudioJack);
            mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());////调用jni
    
            String doubleTouchGestureEnablePath = context.getResources().getString(
                    R.string.config_doubleTouchGestureEnableFile);
            mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
                new File(doubleTouchGestureEnablePath);
    
            LocalServices.addService(InputManagerInternal.class, new LocalService());
        }
    这里面nativeInit的方法很明显通过JNI调用Navive方法:
    frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
    NativeInputManager如下:
    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);
    
        {
            AutoMutex _l(mLock);
            mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
            mLocked.pointerSpeed = 0;
            mLocked.pointerGesturesEnabled = true;
            mLocked.showTouches = false;
            mLocked.hardwareRotation = 0;
        char property[PROPERTY_VALUE_MAX];
            if (property_get("ro.sf.hwrotation", property, "0") > 0) {
                mLocked.hardwareRotation = atoi(property) / 90;
            }
    
        }
        mInteractive = true;
    
        sp<EventHub> eventHub = new EventHub();    ////创建EventHub
    mInputManager = new InputManager(eventHub, this, this); }   ///创建InputManager
    
    
    native主要涉及代码如下:frameworks/native/services/inputflinger:
    ---EventHub.cpp
    ---InputManager.cpp
    ---InputReader.cpp
    ---InputListener.cpp
    ---InputDispatcher.cpp
     
    2.2inputManager.start()
     只需屏蔽inputManager.start(),就可以屏蔽整个input事件了,就可以实现我们需要的功能,但如果需要做到动态设置,这个地方只有开机才会执行这段代码,需要开机重启才能实现。需要做到里面设置属性,立马生效。再往下看看start里面做了什么东西:
    frameworks\base\services\core\java\com\android\server\input\InputManagerService.java:
    public void start() {
            Slog.i(TAG, "Starting input manager");
            nativeStart(mPtr);
    
            // Add ourself to the Watchdog monitors.
            Watchdog.getInstance().addMonitor(this);
    
            registerPointerSpeedSettingObserver();
            registerShowTouchesSettingObserver();
            registerAccessibilityLargePointerSettingObserver();
    
            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    updatePointerSpeedFromSettings();
                    updateShowTouchesFromSettings();
                    updateAccessibilityLargePointerFromSettings();
                }
            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
    
            updatePointerSpeedFromSettings();
            updateShowTouchesFromSettings();
            updateAccessibilityLargePointerFromSettings();
    
            //wmc enable filterInputEvent
            nativeSetInputFilterEnabled(mPtr,true);   //这个函数enable,才能启动filterInputEvent这个拦截的函数
    }
    nativeStart同样是调用的com_android_server_input_InputManagerService.cpp中的:
     
     filterInputEvent实现input事件拦截功能,return false才会拦截事件,return true正常分发事件
     // Native callback.
        final boolean filterInputEvent(InputEvent event, int policyFlags) {
            //wmc add lock inputevent
            boolean disableInputManager = "open".equals(SystemProperties.get("persist.sys.lock_device"));
            if(disableInputManager){
                return false;    //根据读到的属性,直接返回false,
            }
    
            synchronized (mInputFilterLock) {
                if (mInputFilter != null) {
                    try {
                        mInputFilter.filterInputEvent(event, policyFlags);
                    } catch (RemoteException e) {
                        /* ignore */
                    }
                    return false;
                }
            }
            event.recycle();
            return true;
        }

    可以设置广播来实现设置属性:

    if(intent.getAction().equals(BOARDCAST_LOCK_DEVICE)) {
                    boolean lockdevice= intent.getBooleanExtra("lockdevice",false);
                    if(lockdevice){
                        SystemProperties.set("persist.sys.lock_device", "open");
                    }else{
                        SystemProperties.set("persist.sys.lock_device", "close");
                    }
                    
                } 

    这样就可以实现这个功能了。

    整个框架流程图如下:

    此篇文章用了很多网上文章框架图,在此声明一下!

    参考的主要文章如下:

    https://www.jianshu.com/p/42d95cdca70b

    https://blog.csdn.net/vv0_0vv/article/details/105217737

    https://blog.csdn.net/wd229047557/article/details/100764889

  • 相关阅读:
    闭包
    值类型和引用类型的区别?
    计算机基础知识和基本操作
    Git常用命令
    任务八
    CSS任务七
    CSS任务六
    MySql+EF6,不能选实体框架6.x或者闪退
    以一定的格式生成最新的数据库流水号
    layui父窗体获取弹出窗体元素
  • 原文地址:https://www.cnblogs.com/wmc245376374/p/15603040.html
Copyright © 2020-2023  润新知