简述
主要是通过按键的方式来注入一个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);