2012년 3월 5일 월요일

[Android] adb를 이용해 터치 이벤트 생성하기.

0. 배경 지식
안드로이드의 사용자 이벤트 처리 과정은 크게, H/W(device) > Linux kernel > Android input system 정도의 3단계로 나눠 볼 수 있다.

터치스크린, 키보드, 센서 등의 입력 기기는 사용자의 실제 입력을 받게 되고, 이는 각 기기의 드라이버를 통해 Linux 커널에게 Linux 표준 방식으로 자신의 이벤트를 알리게 된다.
Linux 커널의 표준 방식이란, 널리 알려진 대로 기기 자신을 커널의 파일 시스템 상에 존재하는 하나의 파일로 추상화 시키고, 해당 기기를 사용하고자 하는 사용자는 해당 파일을 open 하고, read / write / ioctl 과 같은 표준 방식으로 조작하는 방식이다. 이때, 사용되는 각 이벤트의 데이터 형식은 Linux 표준을 따른다.

안드로이드 input system 은 EventHub 라는 모듈이 직접 각 입력 기기의 device file 에 접근, 이벤트를 읽어들이고, 이는 InputReader 라는 모듈에게 Linux kernel 표준 이벤트 형태로 전달된다.
이 이벤트는 InputReader 에서 Android 에서 사용하는 형식의 이벤트로 다듬어지고, Input dispatcher 라는 모듈에게 전달된다. Input dispatcher 는 현재 시스템에서 이벤트를 받아야 할 윈도우에게 Android 에서 사용하는 형식의 이벤트를 던져 준다.

안드로이드 기기는 자신의 파일시스템 상에 getevent, sendevent 라는 native 프로그램을 내장하고 있으며, adb shell 명령을 이용해 안드로이드 기기의 shell 에 접근 하면 두 프로그램을 실행 할 수 있다.

getevent 는 위에서 말한 input device file 들의 내용을 실시간으로 읽어서 표시해 주는 일을 하고, sendevent 는 사용자가 전달한 인자를 가지고 이벤트를 만들어 input device file 에 넣어준다. 즉, 사용자가 의도한 이벤트를 발생시킨다.
때문에, getevent 를 가지고 end-user 의 입력이 어떤 모습으로 커널에 전달되는지 확인 할 수 있고, 이를 분석함으로써 얻은 구조에 대한 이해를 가지고 sendevent 로 원하는 이벤트를 발생 시킬 수 있다.


1. 터치 이벤트 분석하기
# adb shell getevent
를 입력하면, 현재 adb 에 연결된 기기 또는 에뮬레이터의 입력 기기 목록과 해당 기기들의 device file 경로가 나오고, 이후 몇몇 기기의 이벤트가 계속해서 실시간으로 화면에 나타날 것이다.
다음은 Nexus One 을 연결하고 getevent 했을 때의 출력 내용이다.


$ adb shell getevent
add device 1: /dev/input/event6
  name:     "mahimahi-nav"
add device 2: /dev/input/event5
  name:     "mahimahi-keypad"
add device 3: /dev/input/event4
  name:     "proximity"
add device 4: /dev/input/event3
  name:     "synaptics-rmi-touchscreen"
add device 5: /dev/input/event2
  name:     "compass"
add device 6: /dev/input/event1
  name:     "h2w headset"
add device 7: /dev/input/event0
  name:     "lightsensor-level"
/dev/input/event2: 0003 0001 fffffd3e
/dev/input/event2: 0003 0002 00000008
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0000 ffffffef
/dev/input/event2: 0003 0002 0000000b
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0000 ffffffec
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0000 ffffffe9
/dev/input/event2: 0003 0001 fffffd41
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0001 fffffd3e
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0002 00000008
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0000 ffffffec
/dev/input/event2: 0003 0001 fffffd3b
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0000 ffffffe9


아무짓도 하지 않고 있는데 계속 이벤트를 뿌려주는 입력 기기는 대부분 light sensor 등과 같은 센서들이다.
여기서 직접 화면에 손가락을 대서 터치 이벤트를 발생시키면, 그때만 이벤트를 뿌리는 device file 이 있을 것이다. 그게 터치스크린 device file 이다.
꼭 그러지 않더라도, 최초의 기기 목록의 이름을 가지고도 어느정도 짐작은 할 수 있다. 위의 출력 내용에서는 /dev/input/event3 가 터치스크린일 것임을 짐작 할 수 있다.

이벤트의 내용은 좌측부터 순서대로 device file path, type, code, value 이다.

위의 명령은 light sensor 등과 같은 필요치 않은 기기의 이벤트를 뿌려 정신을 산만하게 만드니까 grep 명령 등을 이용하면 좋다.

이제, getevent 를 통해 터치스크린의 이벤트에 집중하면서 짧게 화면을 터치해 보자.
다음과 같은 이벤트 내용을 얻을 수 있을 것이다.


$ adb shell getevent | grep event0
add device 7: /dev/input/event0
/dev/input/event0: 0003 0035 0000035d
/dev/input/event0: 0003 0036 000000e0
/dev/input/event0: 0003 003a 00000044
/dev/input/event0: 0003 0030 00000007
/dev/input/event0: 0003 0039 00000000
/dev/input/event0: 0000 0002 00000000
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0000 0002 00000000
/dev/input/event0: 0000 0000 00000000



이번엔 터치 위치를 좌우로 또는 아래로 약간 움직여 한번 더 터치해 보자.
여기선 x축 좌표는 거의 그대로 두고, 꽤 아래 쪽을 터치했다.

$ adb shell getevent | grep event0
add device 7: /dev/input/event0
/dev/input/event0: 0003 0035 0000035a
/dev/input/event0: 0003 0036 00000286
/dev/input/event0: 0003 003a 00000037
/dev/input/event0: 0003 0030 00000007
/dev/input/event0: 0003 0039 00000000
/dev/input/event0: 0000 0002 00000000
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0000 0002 00000000
/dev/input/event0: 0000 0000 00000000




참고로, getevent 가 표시해 주는 값들은 16진수다. 10진수로 바꿔주면 가지고 있는 기기의 해상도와 터치한 손가락의 위치를 바탕으로 보다 쉽게 어느 type / code 가 어느 값을 의미하는지 알 수 있다.
보면 중복되는 값들이 있고, x/y축의 이동을 생각해 보면, 최초 두개의 이벤트는 x/y 축을 의미하며, 세번째는 터치 압력을 의미한다. 나머지는 두 이벤트가 똑같다.

이제, 위와 같은 내용의 이벤트를 x / y 축의 값만 바꿔 입력하면, 원하는 위치에 터치 이벤트를 만들어 줄 수 있다.
setevent 는 10진수를 입력받으므로, 위의 값들은 10진수로 변환해야 한다.
파이썬을 사용한다면 다음과 같은 스크립트를 만들 수 있을 것이다.


os.system("adb shell sendevent /dev/input/event1 3 53 " + x_axis)
os.system("adb shell sendevent /dev/input/event1 3 54 " + y_axis)
os.system("adb shell sendevent /dev/input/event1 3 58 55")
os.system("adb shell sendevent /dev/input/event1 3 48 7")
os.system("adb shell sendevent /dev/input/event1 3 57 0")
os.system("adb shell sendevent /dev/input/event1 0 2 0")
os.system("adb shell sendevent /dev/input/event1 0 0 0")
os.system("adb shell sendevent /dev/input/event1 0 2 0")
os.system("adb shell sendevent /dev/input/event1 0 0 0")



참고 자료:

댓글 없음:

댓글 쓰기