WORK/Android

[Android] KeyEvent 처리 | KeyEvent 순서 | Framework Key event

호이호이호잇 2022. 4. 25. 20:00
728x90
반응형

로지난번에 AccessibilityService를 이용해 앱이 상단에 있는 상태가 아니여도, 

KeyEvent 를 받는 방법에 대해 알아보았다.

https://codingstorywithme.tistory.com/42

 

[Android] 화면 OFF 상태에서 Key Event 받기 | AcessibilityService 상속 서비스 | KeyEvent 서비스 | 접근성 서

안드로이드 단말에는 다양한 키들이 존재한다. 볼륨 Up/Down, 전원 등등의 키가 있고 이 키들이 press/release 되는 이벤트를 앱에서 받아 처리 할 수 있다. 아래 링크에서 안드로이드가 제공해주는 기

codingstorywithme.tistory.com

 

근데!

위의 서비스를 이용하거나 app에서 keyEvent를 받을 때, 모든 Key에 대한 이벤트를 받을 수 있는 것은 아니다.

어떻게 알 수 있을까?

그래서 이번에는 KeyEvent 에 대해 좀 더 정리하려고 한다.

 

#1. KeyEvent 전달 순서

KeyEvent 가 발생 했을 때, 사용자가 확인 가능하도록 앱, 앱 서비스 등 어플리케이션으로 올라오기까지 여러 단계를 거치게 된다.

이에 대한 내용은 안드로이드 개발자 페이지에도 잘 나와있지만, 내가 한 번 더 정리하려고 한다.!

https://source.android.com/devices/input?hl=ko#input-pipeline 

 

입력  |  Android 오픈소스 프로젝트  |  Android Open Source Project

입력 Android 입력 하위 시스템은 명목상으로 시스템의 여러 레이어를 통과하는 이벤트 파이프라인으로 구성됩니다. 입력 파이프라인 실제 입력 기기는 최하위 레이어에서 키 누름 및 터치 접점

source.android.com

 

-1. [Kernel] 키 이벤트 발생

제일 먼저 커널에서 키 이벤트가 발생한다.

이때 KeyEvent는 adb 로 확인이 가능하다.

 

- cmd 실행

- $adb shell getevent

를 이용해 실시간 event를 확인 할 수 있다.

아래는 터치 및 뒤로가기 버튼 클릭한 화면이다.

 

adb shell getevent에 대한 내용은 아래 문서에서 자세히 확인 가능하다. 

https://source.android.com/devices/input/getevent?hl=ko

커널에서 올려주는 keyCode는 kl파일에 정의 되어 있다.

파일에 대한 설명은

https://source.android.com/devices/input/key-layout-files?hl=ko 를 참고하면 좋다.

 

키 레이아웃 파일  |  Android 오픈소스 프로젝트  |  Android Open Source Project

키 레이아웃 파일 키 레이아웃 파일(.kl 파일)은 Linux 키 코드와 축 코드를 Android 키 코드 및 축 코드에 매핑하고 관련 정책 플래그를 지정합니다. 기기별 키 레이아웃 파일은 다음과 같습니다. 음

source.android.com

 

-2. [Native Service] 키 이벤트 Eventhub로 전달

* 위치 : frameworks/native/services/inputflinger/EventHub.cpp

커널에서 input_event를 얻어오기 위해서는 디바이스 파일을 읽어내야 한다. 디바이스 파일 경로는 “/dev/input/*” 형태로 되어있다. 안드로이드에서 이를 읽어내서 처리하는 부분을 담당한다.

각 입력 기기에 연결된 evdev 드라이버를 열어 커널의 입력 이벤트를 읽은 후, 입력장치별 Key Layout 에 따라 Kernel keycode 를 Android Keycode로 변환한다. 다시말하면 struct input_event 타입으로 Kernel keycode 를 읽어와서 RawEvent로 변환시켜  Android Keycode를 InputReader에 전달하는 것이다.

 

-3. [Native Service] 키 이벤트 InputReader로 전달 

* 위치 : frameworks/native/services/inputflinger/InputReader.cpp

Input device의 데이터를 읽고 처리하는 class로 입력 이벤트를 InputDispatcher로 전송한다.

 

-4. [Native Service] 키 이벤트 InputDispatcher로 전달 

* 위치 : frameworks/native/services/inputflinger/InputDispatcher.cpp

InputReader 클래스에서 처리된 데이터를 내부의 큐에 저장 한 뒤, input event를 보낼 input target을 찾아 event를 dispatch한다.

 

-5. [System Service] 키 이벤트 PhoneWindowManager로 전달 

* 위치 : frameworks/./base/services/core/java/com/android/server/policy/PhoneWindowManager.java

키 이벤트를 내부적으로 처리할지, USER (APP단) 에게 올려 사용 가능하게 할지 등을 포함한 키 이벤트의 동작을 결정한다.

 

소스에서 확인을 해보자.

https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-release/services/core/java/com/android/server/policy/PhoneWindowManager.java 

 

services/core/java/com/android/server/policy/PhoneWindowManager.java - platform/frameworks/base - Git at Google

 

android.googlesource.com

 

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) 부분을 확인해 보면 아래 쪽에 

여기에서 result = ACTION_PASS_TO_USER; 이 부분이 중요하다.

result 에 ACTION_PASS_TO_USER 이 있으면 USER 즉 App 에서 해당 key event를 확인 할 수 있고, 없으면 app 에서 getKeyEvent 등으로 확인이 불가능하다.

 

하나 더 확인을 해보면,

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) 함수에서

위에 표시 되어 있는 부분처럼 

result &= ~ACTION_PASS_TO_USER; 라고 표시하면, 현재 result 에서 ACTION_PASS_TO_USER 표시를 없애는 것이 되므로 해당 key event는 User 즉 app 에서 확인이 불가하게 된다.

 

ACTION_PASS_TO_USER 와 같은 result 값들은 

./base/services/core/java/com/android/server/policy/WindowManagerPolicy.java 에서 확인 할 수 있다.

/**
 * Pass this event to the user / app.  To be returned from
 * {@link #interceptKeyBeforeQueueing}.
 */
int ACTION_PASS_TO_USER = 0x00000001;
/** Layout state may have changed (so another layout will be performed) */
int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
/** Configuration state may have changed */
int FINISH_LAYOUT_REDO_CONFIG = 0x0002;
/** Wallpaper may need to move */
int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
/** Need to recompute animations */
int FINISH_LAYOUT_REDO_ANIM = 0x0008;
/** Layer for the screen off animation */
int COLOR_FADE_LAYER = 0x40000001;

 

끄읐!

728x90
반응형