Scancode到Keycode的映射

之前分析的InputReader读取底层事件可以得知 InputReaderThead启动之后会通过mEventHub->getEvents读取设备节点的所有事件,通过parsekey方法解析kl文件存入数据结构map,在读取节点事件之前先扫描设备,如果没有打开则打开设备,在打开设备时会将scancode和keycode一一映射,此篇文章记录他们是如何建立映射关系的起来的 /frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() {
         ......
343      size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
344  
345      { // acquire lock
346          AutoMutex _l(mLock);
347          mReaderIsAliveCondition.broadcast();
348  
349          if (count) {
350              processEventsLocked(mEventBuffer, count);
351          }
352  

/frameworks/native/services/inputflinger/EventHub.cpp
mEventHub->getEvents

851  size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
852          ......
860      bool awoken = false;
861      for (;;) {
862          nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
             ......
875          // Report any devices that had last been added/removed.
876          while (mClosingDevices) {
877              Device* device = mClosingDevices;
878              ALOGV("Reporting device closed: id=%d, name=%s\n",
879                   device->id, device->path.c_str());
880          ......
891          }
892  
893          if (mNeedToScanDevices) {
894              mNeedToScanDevices = false;
895              scanDevicesLocked();
896              mNeedToSendFinishedDeviceScan = true;
897          }
             ......
   }

/frameworks/native/services/inputflinger/EventHub.cpp
扫描设备"/dev/input"

static const char *DEVICE_PATH = "/dev/input";
1130  void EventHub::scanDevicesLocked() {
          //DEVICE_PATH = "/dev/input"
1131      status_t result = scanDirLocked(DEVICE_PATH);
1132      ......
1144  }
1908  status_t EventHub::scanDirLocked(const char *dirname)
1909  {
1910      ......
          //扫描/dev/input/下的所有设备
1920      while((de = readdir(dir))) {
1921          if(de->d_name[0] == '.' &&
1922             (de->d_name[1] == '\0' ||
1923              (de->d_name[1] == '.' && de->d_name[2] == '\0')))
1924              continue;
1925          strcpy(filename, de->d_name);
              //打开设备
1926          openDeviceLocked(devname);
1927      }
1928      closedir(dir);
1929      return 0;
1930  }

打开设备

1237  status_t EventHub::openDeviceLocked(const char* devicePath) {
1238        //省略了很多代码,主要看loadKeyMapLocked
1258        ......
1350        ......
1429      // Load the key map.
1430      // We need to do this for joysticks too because the key layout may specify axes.
1431      status_t keyMapStatus = NAME_NOT_FOUND;
1432      if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
1433          // Load the keymap for the device.
1434          keyMapStatus = loadKeyMapLocked(device);
1435      }
}

主要看scancode和keycode如何一一映射的

1685  status_t EventHub::loadKeyMapLocked(Device* device) {
1686      return device->keyMap.load(device->identifier, device->configuration);
1687  }

继续调到Device的keyMap的load函数,Device是EventHub内部的结构体
/frameworks/native/services/inputflinger/EventHub.h

325  private:
326      struct Device {
327          Device* next;
             .......
328          KeyMap keyMap;
             .......
      }

继续看KeyMap的load函数
/frameworks/native/libs/input/Keyboard.cpp

41  status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
42          const PropertyMap* deviceConfiguration) {
43      // Use the configured key layout if available.
44      if (deviceConfiguration) {
45          String8 keyLayoutName;
            //通过设备的配置文件去加载配置文件内制定好的映射表
46          if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
47                  keyLayoutName)) {
48              status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str());
49           if (status == NAME_NOT_FOUND) {
52                deviceIdenfifier.name.c_str(), keyLayoutName.string());
53              }
54          }
55  
56          String8 keyCharacterMapName;
57          if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
58                  keyCharacterMapName)) {
59              status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
60              if (status == NAME_NOT_FOUND) {
61                  ALOGE("Configuration for keyboard device '%s' requested keyboard character "
62                          "map '%s' but it was not found.",
63                          deviceIdenfifier.name.c_str(), keyLayoutName.string());
64              }
65          }
67          if (isComplete()) {
68              return OK;
69          }
70      }
72      // Try searching by device identifier.通过设备信息查找对应的映射表
73      if (probeKeyMap(deviceIdenfifier, "")) {
74          return OK;
75      }
77      // Fall back on the Generic key map.
78      // TODO Apply some additional heuristics here to figure out what kind of
79      //      generic key map to use (US English, etc.) for typical external keyboards.查找通用的映射表
80      if (probeKeyMap(deviceIdenfifier, "Generic")) {
81          return OK;
82      }
83  
84      // Try the Virtual key map as a last resort.查找虚拟映射表
85      if (probeKeyMap(deviceIdenfifier, "Virtual")) {
86          return OK;
87      }
88  
89      // Give up!
90      ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
91              deviceIdenfifier.name.c_str());
92      return NAME_NOT_FOUND;
93  }

可以看到我这个设备有四个设备节点,所以
mtk-tpd,ACCDET,fts_ts,mtk-kpd以及通用表都会解析的

Tokyo_TF:/system/usr $ getevent
add device 1: /dev/input/event3
  name:     "mtk-tpd"
add device 2: /dev/input/event0
  name:     "ACCDET"
add device 3: /dev/input/event1
  name:     "mtk-kpd"
add device 4: /dev/input/event2
  name:     "fts_ts"
/dev/input/event1: 0001 0074 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0074 00000000
/dev/input/event1: 0000 0000 00000000
Tokyo_TF:/system/usr $ 
Tokyo_TF:/system/usr $ find -name "mtk-kpd*"                                                                                             
./keylayout/mtk-kpd.kl
Tokyo_TF:/system/usr $ 

probeKeyMap函数

95  bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
96          const std::string& keyMapName) {
97      if (!haveKeyLayout()) {
98          loadKeyLayout(deviceIdentifier, keyMapName);
99      }
100      if (!haveKeyCharacterMap()) {
101          loadKeyCharacterMap(deviceIdentifier, keyMapName);
102      }
103      return isComplete();
104  }

loadKeyLayout()函数

106  status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
107          const std::string& name) {
108      std::string path(getPath(deviceIdentifier, name,
109              INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
110      if (path.empty()) {
111          return NAME_NOT_FOUND;
112      }
113  
114      status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
115      if (status) {
116          return status;
117      }
118  
119      keyLayoutFile = path;
120      return OK;
121  }

继续KeyLayoutMap::load
/frameworks/native/libs/input/KeyLayoutMap.cpp

52  status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
54        ......
55      Tokenizer* tokenizer;
56      status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
57      if (status) {
58        ......
59      } else {
60          sp<KeyLayoutMap> map = new KeyLayoutMap();
61          if (!map.get()) {
62              ......
64          } else {
68              Parser parser(map.get(), tokenizer);
                //解析map
69              status = parser.parse();
70              ......
82      return status;
83  }

继续parser.parse(),开始解析映射表

199  status_t KeyLayoutMap::Parser::parse() {
     //while循环,一行一行地解析映射表项
200  while (!mTokenizer->isEof()) {
201  #if DEBUG_PARSER
      /*
 11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:160: '## key 138 "KEY_HELP"'.
11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:161: 'key 139   MENU'.
11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:151: '## key 129 "KEY_AGAIN"'.
11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:152: '## key 130 "KEY_PROPS"'.
11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:153: '## key 131 "KEY_UNDO"'.
11-22 04:27:06.719   908  1162 D KeyLayoutMap: Parsing /system/usr/keylayout/Generic.kl:154: '## key 132 "KEY_FRONT"'.
    */
    //这条log的输入,部分
202          ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
203                  mTokenizer->peekRemainderOfLine().string());
204  #endif
206          mTokenizer->skipDelimiters(WHITESPACE);
208          if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
209              String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
210              if (keywordToken == "key") {
211                  mTokenizer->skipDelimiters(WHITESPACE);
212                  status_t status = parseKey();
213                  if (status) return status;
214              } else if (keywordToken == "axis") {
215                  mTokenizer->skipDelimiters(WHITESPACE);
216                  status_t status = parseAxis();
217                  if (status) return status;
218              } else if (keywordToken == "led") {
219                  mTokenizer->skipDelimiters(WHITESPACE);
220                  status_t status = parseLed();
221                  if (status) return status;
222              } else {
223                  ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
224                          keywordToken.string());
225                  return BAD_VALUE;
226              }
227  
228              mTokenizer->skipDelimiters(WHITESPACE);
229              if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
230                  ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
231                          mTokenizer->getLocation().string(),
232                          mTokenizer->peekRemainderOfLine().string());
233                  return BAD_VALUE;
234              }
235          }
237          mTokenizer->nextLine();
238      }
239      return NO_ERROR;
240  }

mTokenizer->getLocation()这代表哪一张映射表,mTokenizer->peekRemainderOfLine()代表映射表中的scancode, 解析的规则如下: keyword是"key",调用parseKey解析,如果解析出错则返回错误 keyword是"axis",调用parseAxis解析,如果解析出错则返回错误 keyword是"led",调用parseAxis解析,如果解析出错则返回错误 如果还有其他不按规则的符号则返回BAD_VALUE

继续parseKey()函数

242  status_t KeyLayoutMap::Parser::parseKey() {
243      ......
256      //此函数解析得到keycode         
267      int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
268      ......
     //11-22 04:55:50.301   852  1162 D KeyLayoutMap: Parsed key scan code: code=85, keyCode=211, flags=0x00000000.
294  #if DEBUG_PARSER
295      ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
296              mapUsage ? "usage" : "scan code", code, keyCode, flags);
297  #endif
298      Key key;
299      key.keyCode = keyCode;
300      key.flags = flags;
301      map.add(code, key);
302      return NO_ERROR;
303  }

打开上面的log开关得到如下输出:scancode和keycode的映射终于得到了,继续看函数getKeyCodeByLabel

11-22 04:55:50.301   852  1162 D KeyLayoutMap: Parsed key scan code: code=83, keyCode=158, flags=0x00000000.
11-22 04:55:50.301   852  1162 D KeyLayoutMap: Parsed key scan code: code=85, keyCode=211, flags=0x00000000.
11-22 04:55:50.306   852  1162 D KeyLayoutMap: Parsed key scan code: code=173, keyCode=285, flags=0x00000000.
11-22 04:55:50.316   852  1162 D KeyLayoutMap: Parsed key scan code: code=483, keyCode=47, flags=0x00000004.

/frameworks/native/include/input/InputEventLabels.h

434  static inline int32_t getKeyCodeByLabel(const char* label) {
435      return int32_t(lookupValueByLabel(label, KEYCODES));
436  }

调用了lookupValueByLabel函数,我们来看 KEYCODES是什么,里面是所有的键

23  #define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
39  static const InputEventLabel KEYCODES[] = {
40      // NOTE: If you add a new keycode here you must also add it to several other files.
41      //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
44      ......  
45      DEFINE_KEYCODE(HOME),
46      DEFINE_KEYCODE(BACK),
47      DEFINE_KEYCODE(CALL),
48      DEFINE_KEYCODE(ENDCALL),
49      DEFINE_KEYCODE(0),
50      DEFINE_KEYCODE(1),
51      DEFINE_KEYCODE(2),
52      DEFINE_KEYCODE(3),
53      DEFINE_KEYCODE(4),
54      DEFINE_KEYCODE(5),
55      DEFINE_KEYCODE(6),
56      DEFINE_KEYCODE(7),
57      DEFINE_KEYCODE(8),
58      DEFINE_KEYCODE(9),
59      DEFINE_KEYCODE(STAR),
60      DEFINE_KEYCODE(POUND),
61      DEFINE_KEYCODE(DPAD_UP),
62      DEFINE_KEYCODE(DPAD_DOWN),
63      DEFINE_KEYCODE(DPAD_LEFT),
64      DEFINE_KEYCODE(DPAD_RIGHT),
65      DEFINE_KEYCODE(DPAD_CENTER),
66      DEFINE_KEYCODE(VOLUME_UP),
67      DEFINE_KEYCODE(VOLUME_DOWN),
68      DEFINE_KEYCODE(POWER),
69      DEFINE_KEYCODE(CAMERA),
70      DEFINE_KEYCODE(CLEAR),
        ......

DEFINE_KEYCODE这个宏定义作用是将传进去的键名称拼接为AKEYCODE_XXX,比如我们传一个POWER进去得到的就是{ “POWER”, AKEYCODE_POWER }

而AKEYCODE_POWER,我们全局搜索一下,可以发现定义在 /frameworks/native/include/android/keycodes.h

51  /**
52   * Key codes.
53   */
54  enum {
55      /** Unknown key code. */
56      AKEYCODE_UNKNOWN         = 0,
57      /** Soft Left key.
58       * Usually situated below the display on phones and used as a multi-function
59       * feature key for selecting a software defined function shown on the bottom left
60       * of the display. */
61      AKEYCODE_SOFT_LEFT       = 1,
62      /** Soft Right key.
63       * Usually situated below the display on phones and used as a multi-function
64       * feature key for selecting a software defined function shown on the bottom right
65       * of the display. */
66      AKEYCODE_SOFT_RIGHT      = 2,
67      /** Home key.
68       * This key is handled by the framework and is never delivered to applications. */
69      AKEYCODE_HOME            = 3,
70      /** Back key. */
71      AKEYCODE_BACK            = 4,
72      /** Call key. */
73      AKEYCODE_CALL            = 5,
74      /** End Call key. */
75      AKEYCODE_ENDCALL         = 6,
76      /** '0' key. */
77      AKEYCODE_0               = 7,
78      /** '1' key. */
79      AKEYCODE_1               = 8,
80      /** '2' key. */
81      AKEYCODE_2               = 9,
82      /** '3' key. */
83      AKEYCODE_3               = 10,
84      /** '4' key. */
85      AKEYCODE_4               = 11,
86      /** '5' key. */
87      AKEYCODE_5               = 12,
88      /** '6' key. */
89      AKEYCODE_6               = 13,
90      /** '7' key. */
91      AKEYCODE_7               = 14,
92      /** '8' key. */
93      AKEYCODE_8               = 15,
94      /** '9' key. */
95      AKEYCODE_9               = 16,
96      /** '*' key. */
97      AKEYCODE_STAR            = 17,
98      /** '#' key. */
99      AKEYCODE_POUND           = 18,
100      /** Directional Pad Up key.
101       * May also be synthesized from trackball motions. */
102      AKEYCODE_DPAD_UP         = 19,
103      /** Directional Pad Down key.
104       * May also be synthesized from trackball motions. */
105      AKEYCODE_DPAD_DOWN       = 20,
106      /** Directional Pad Left key.
107       * May also be synthesized from trackball motions. */
108      AKEYCODE_DPAD_LEFT       = 21,
109      /** Directional Pad Right key.
110       * May also be synthesized from trackball motions. */
111      AKEYCODE_DPAD_RIGHT      = 22,
112      /** Directional Pad Center key.
113       * May also be synthesized from trackball motions. */
114      AKEYCODE_DPAD_CENTER     = 23,
115      /** Volume Up key.
116       * Adjusts the speaker volume up. */
117      AKEYCODE_VOLUME_UP       = 24,
118      /** Volume Down key.
119       * Adjusts the speaker volume down. */
120      AKEYCODE_VOLUME_DOWN     = 25,
121      /** Power key. */
122      AKEYCODE_POWER           = 26,
         ......
  }

并且在java层也是用的映射的keycode,这都是和native层一致的
frameworks/base/core/java/android/view/KeyEvent.java

88  public class KeyEvent extends InputEvent implements Parcelable {
89      /** Key code constant: Unknown key code. */
90      public static final int KEYCODE_UNKNOWN         = 0;
91      /** Key code constant: Soft Left key.
92       * Usually situated below the display on phones and used as a multi-function
93       * feature key for selecting a software defined function shown on the bottom left
94       * of the display. */
95      public static final int KEYCODE_SOFT_LEFT       = 1;
96      /** Key code constant: Soft Right key.
97       * Usually situated below the display on phones and used as a multi-function
98       * feature key for selecting a software defined function shown on the bottom right
99       * of the display. */
100      public static final int KEYCODE_SOFT_RIGHT      = 2;
101      /** Key code constant: Home key.
102       * This key is handled by the framework and is never delivered to applications. */
103      public static final int KEYCODE_HOME            = 3;
104      /** Key code constant: Back key. */
105      public static final int KEYCODE_BACK            = 4;
106      /** Key code constant: Call key. */
107      public static final int KEYCODE_CALL            = 5;
108      /** Key code constant: End Call key. */
109      public static final int KEYCODE_ENDCALL         = 6;
110      /** Key code constant: '0' key. */
111      public static final int KEYCODE_0               = 7;
112      /** Key code constant: '1' key. */
113      public static final int KEYCODE_1               = 8;
114      /** Key code constant: '2' key. */
115      public static final int KEYCODE_2               = 9;
116      /** Key code constant: '3' key. */
117      public static final int KEYCODE_3               = 10;
118      /** Key code constant: '4' key. */
119      public static final int KEYCODE_4               = 11;
120      /** Key code constant: '5' key. */
121      public static final int KEYCODE_5               = 12;
122      /** Key code constant: '6' key. */
123      public static final int KEYCODE_6               = 13;
124      /** Key code constant: '7' key. */
125      public static final int KEYCODE_7               = 14;
126      /** Key code constant: '8' key. */
127      public static final int KEYCODE_8               = 15;
128      /** Key code constant: '9' key. */
129      public static final int KEYCODE_9               = 16;
130      /** Key code constant: '*' key. */
131      public static final int KEYCODE_STAR            = 17;
132      /** Key code constant: '#' key. */
133      public static final int KEYCODE_POUND           = 18;
134      /** Key code constant: Directional Pad Up key.
135       * May also be synthesized from trackball motions. */
136      public static final int KEYCODE_DPAD_UP         = 19;
137      /** Key code constant: Directional Pad Down key.
138       * May also be synthesized from trackball motions. */
139      public static final int KEYCODE_DPAD_DOWN       = 20;
140      /** Key code constant: Directional Pad Left key.
141       * May also be synthesized from trackball motions. */
142      public static final int KEYCODE_DPAD_LEFT       = 21;
143      /** Key code constant: Directional Pad Right key.
144       * May also be synthesized from trackball motions. */
145      public static final int KEYCODE_DPAD_RIGHT      = 22;
146      /** Key code constant: Directional Pad Center key.
147       * May also be synthesized from trackball motions. */
148      public static final int KEYCODE_DPAD_CENTER     = 23;
149      /** Key code constant: Volume Up key.
150       * Adjusts the speaker volume up. */
151      public static final int KEYCODE_VOLUME_UP       = 24;
152      /** Key code constant: Volume Down key.
153       * Adjusts the speaker volume down. */
154      public static final int KEYCODE_VOLUME_DOWN     = 25;
155      /** Key code constant: Power key. */
156      public static final int KEYCODE_POWER           = 26;
         ......
}

到这里scancode与keycode的映射就创建完成,
用一个实际例子来总结一下映射关系

Tokyo_TF:/ $ getevent
add device 1: /dev/input/event3
  name:     "mtk-tpd"
add device 2: /dev/input/event0
  name:     "ACCDET"
add device 3: /dev/input/event1
  name:     "mtk-kpd"
add device 4: /dev/input/event2
  name:     "fts_ts"
/dev/input/event1: 0001 0074 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0074 00000000
/dev/input/event1: 0000 0000 00000000
Tokyo_TF:/ $ cat system/usr/keylayout/mtk-kpd.kl |grep -i POWER                                                                      
key 116   POWER

16进制74对应10进制116,按键名称POWER,经过scancode到keycode的映射,POWER事件传到native层变为了AKEYCODE_POWER,到java层变为了KEYCODE_POWER,keycode都一样等于26,到此底层对上层事件的映射就已经完成,之后会接着InputReader读取设备事件,分析InputDispatcher是如何将事件给到应用窗口的。

评论