android实现injectInputEvent的流程分析

简述

主要是通过按键的方式来注入一个KeyEvent按键事件,通过KeyEvent.java中可以查询到系统中自带的最后一个按键码,如下所示:

  public static final int KEYCODE_MACRO_4 = 316;

   /**
     * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
     * @hide
     */
    @TestApi
    public static final int LAST_KEYCODE = KEYCODE_MACRO_4;

如果想自定义一个新的按键keycode,只需要比上面的最后一个按键值大就好了。

实现流程

需要调用到InputManager中的injectInputEvent方法,源码如下所示

/**
*...
* Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into windows that are owned by    * other applications.
* ...
* @hide
*/
 @UnsupportedAppUsage
    public boolean injectInputEvent(InputEvent event, int mode) {
       ...
        try {
            return mIm.injectInputEvent(event, mode);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

注入的是一个系统事件,隐藏的方法,需要系统权限的INJECT_EVENTS事件,这里我们使用系统platform签名,使用自定义的keycode=400

1.INJECT_EVENTS事件

在应用相关的清单文件中加入系统权限的申请

<uses-permission android:name="android.permission.INJECT_EVENTS" />

2.KeyEvent.ACTION_DOWN

此时实现的是当按键按下时,需要注册的按键事件以及keycode值

 long time = SystemClock.uptimeMillis();
 Log.d(TAG, "start btn_down,time = " + time);
 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, 400, 0);
 boolean flag = mContext.getSystemService(InputManager.class)
             .injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC);
Log.d(TAG, "end btn_down,flag = " + flag);

在activity中会回调到onKeyDown方法

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d(TAG, "onKeyDown keyCode = " + keyCode + ",event.code = " + event.getAction());
        return super.onKeyDown(keyCode, event);
    }

日志输出

05-20 13:24:53.884  3444  3444 D TestActivity: start btn_down,time = 180417
05-20 13:24:53.887  3444  3444 D TestActivity: end btn_down,flag = true
05-20 13:24:53.890  1118  1617 D PowerManagerService: scheduleUserInactivityTimeoutCustom: timeMs = 240417
05-20 13:24:53.891  1118  1617 V InputDispatcher: Asynchronous input event injection succeeded.
05-20 13:24:53.892  1118  2445 D PowerManagerService: scheduleUserInactivityTimeoutCustom: timeMs = 240417
05-20 13:24:53.894  1118  1201 D PowerManagerService: scheduleUserInactivityTimeoutCustom: timeMs = 240417
05-20 13:24:53.921  3444  3444 D TestActivity: onKeyDown keyCode = 400,event.code = 0

3.KeyEvent.ACTION_UP

此时实现的是当按键被松开时,需要注册的按键事件以及keycode值

long time = SystemClock.uptimeMillis();
Log.d(TAG, "start btn_up time = " + time);
KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_UP, 400, 0);
boolean flag = mContext.getSystemService(InputManager.class)
              .injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC);
Log.d(TAG, "end btn_up,flag = " + flag);

在activity中会回调到onKeyUp方法

   @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d(TAG, "onKeyUp keyCode = " + keyCode + ",event.code = " + event.getAction());
        return super.onKeyUp(keyCode, event);
    }

日志输出

05-20 13:24:55.804  3444  3444 D TestActivity: start btn_up time = 182338
05-20 13:24:55.806  1118  1617 D PowerManagerService: scheduleUserInactivityTimeoutCustom: timeMs = 242331
05-20 13:24:55.807  3444  3444 D TestActivity: end btn_up,flag = true
05-20 13:24:55.813  1118  1617 D PowerManagerService: scheduleUserInactivityTimeoutCustom: timeMs = 242338
05-20 13:24:55.814  1118  1617 V InputDispatcher: Asynchronous input event injection succeeded.
05-20 13:24:55.820  3444  3444 D TestActivity: onKeyUp keyCode = 400,event.code = 1

injectInputEvent调用分析

针对android 10的源码分析

// 调用到最外层的方法
 @UnsupportedAppUsage
    public boolean injectInputEvent(InputEvent event, int mode) {
     ...
        try {
            return mIm.injectInputEvent(event, mode);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
    // 调用IInputManager中的方法injectInputEvent
    mIm.injectInputEvent
    // IInputManager是个aidl文件,所以就调用到InputManagerService中去了
    public class InputManagerService extends IInputManager.Stub
    // 然后调用到InputManagerService中的injectInputEvent-->injectInputEventInternal
    private boolean injectInputEventInternal(InputEvent event, int mode) {
       ..
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        final int result;
        try {
            result = nativeInjectInputEvent(mPtr, event, pid, uid, mode,
                    INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
     	...
    }    
    // 最后调用到了nativeInjectInputEvent方法,后面是native层的,里面涉及到的内容比较多,
    // native层主要是针对按键的时间有个判断、按键的动作行为必须是ACTION_DOWN 或者是 ACTION_UP中的一个 ,会将KeyEvent事件与InputEvent事件分开进行处理。
    

最后是将注册的系统事件给input进程处理的,重点是看下以下两行代码

// 表示清除身份,相当于就是服务端进程自己调用了,调用getCallingUid的值变成自己进程的UID
final long ident = Binder.clearCallingIdentity();
// 表示恢复身份,其实本质就是对前面clearCallingIdentity后,需要对当前线程的UID进行恢复
Binder.restoreCallingIdentity(ident);
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值