1#define LOG_TAG "KeyCharacterMap"
2
3#include <ui/KeyCharacterMap.h>
4#include <cutils/properties.h>
5
6#include <utils/Log.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <stdlib.h>
10#include <fcntl.h>
11#include <limits.h>
12#include <string.h>
13
14struct Header
15{
16    char magic[8];
17    unsigned int endian;
18    unsigned int version;
19    unsigned int keycount;
20    unsigned char kbdtype;
21    char padding[11];
22};
23
24KeyCharacterMap::KeyCharacterMap()
25{
26}
27
28KeyCharacterMap::~KeyCharacterMap()
29{
30    free(m_keys);
31}
32
33unsigned short
34KeyCharacterMap::get(int keycode, int meta)
35{
36    Key* k = find_key(keycode);
37    if (k != NULL) {
38        return k->data[meta & META_MASK];
39    }
40    return 0;
41}
42
43unsigned short
44KeyCharacterMap::getNumber(int keycode)
45{
46    Key* k = find_key(keycode);
47    if (k != NULL) {
48        return k->number;
49    }
50    return 0;
51}
52
53unsigned short
54KeyCharacterMap::getMatch(int keycode, const unsigned short* chars,
55                          int charsize, uint32_t modifiers)
56{
57    Key* k = find_key(keycode);
58    modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it
59    if (k != NULL) {
60        const uint16_t* data = k->data;
61        for (int j=0; j<charsize; j++) {
62            uint16_t c = chars[j];
63            for (int i=0; i<(META_MASK + 1); i++) {
64                if ((modifiers == 0) || ((modifiers & i) != 0)) {
65                    if (c == data[i]) {
66                        return c;
67                    }
68                }
69            }
70        }
71    }
72    return 0;
73}
74
75unsigned short
76KeyCharacterMap::getDisplayLabel(int keycode)
77{
78    Key* k = find_key(keycode);
79    if (k != NULL) {
80        return k->display_label;
81    }
82    return 0;
83}
84
85bool
86KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel,
87                            unsigned short *number, unsigned short* results)
88{
89    Key* k = find_key(keycode);
90    if (k != NULL) {
91        memcpy(results, k->data, sizeof(short)*(META_MASK + 1));
92        *number = k->number;
93        *displayLabel = k->display_label;
94        return true;
95    } else {
96        return false;
97    }
98}
99
100bool
101KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods)
102{
103    uint32_t N = m_keyCount;
104    for (int j=0; j<(META_MASK + 1); j++) {
105        Key const* keys = m_keys;
106        for (uint32_t i=0; i<N; i++) {
107            if (keys->data[j] == c) {
108                *key = keys->keycode;
109                *mods = j;
110                return true;
111            }
112            keys++;
113        }
114    }
115    return false;
116}
117
118bool
119KeyCharacterMap::getEvents(uint16_t* chars, size_t len,
120                           Vector<int32_t>* keys, Vector<uint32_t>* modifiers)
121{
122    for (size_t i=0; i<len; i++) {
123        uint32_t k, mods;
124        if (find_char(chars[i], &k, &mods)) {
125            keys->add(k);
126            modifiers->add(mods);
127        } else {
128            return false;
129        }
130    }
131    return true;
132}
133
134KeyCharacterMap::Key*
135KeyCharacterMap::find_key(int keycode)
136{
137    Key* keys = m_keys;
138    int low = 0;
139    int high = m_keyCount - 1;
140    int mid;
141    int n;
142    while (low <= high) {
143        mid = (low + high) / 2;
144        n = keys[mid].keycode;
145        if (keycode < n) {
146            high = mid - 1;
147        } else if (keycode > n) {
148            low = mid + 1;
149        } else {
150            return keys + mid;
151        }
152    }
153    return NULL;
154}
155
156KeyCharacterMap*
157KeyCharacterMap::load(int id)
158{
159    KeyCharacterMap* rv = NULL;
160    char path[PATH_MAX];
161    char propName[100];
162    char dev[PROPERTY_VALUE_MAX];
163    char tmpfn[PROPERTY_VALUE_MAX];
164    int err;
165    const char* root = getenv("ANDROID_ROOT");
166
167    sprintf(propName, "hw.keyboards.%u.devname", id);
168    err = property_get(propName, dev, "");
169    if (err > 0) {
170        // replace all the spaces with underscores
171        strcpy(tmpfn, dev);
172        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
173            *p = '_';
174        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn);
175        //LOGD("load: dev='%s' path='%s'\n", dev, path);
176        rv = try_file(path);
177        if (rv != NULL) {
178            return rv;
179        }
180        LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
181    } else {
182        LOGW("No keyboard for id %d", id);
183    }
184
185    snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
186    rv = try_file(path);
187    if (rv == NULL) {
188        LOGE("Can't find any keycharmaps (also tried %s)", path);
189        return NULL;
190    }
191    LOGW("Using default keymap: %s", path);
192
193    return rv;
194}
195
196KeyCharacterMap*
197KeyCharacterMap::try_file(const char* filename)
198{
199    KeyCharacterMap* rv = NULL;
200    Key* keys;
201    int fd;
202    off_t filesize;
203    Header header;
204    int err;
205
206    fd = open(filename, O_RDONLY);
207    if (fd == -1) {
208        LOGW("Can't open keycharmap file");
209        return NULL;
210    }
211
212    filesize = lseek(fd, 0, SEEK_END);
213    lseek(fd, 0, SEEK_SET);
214
215    // validate the header
216    if (filesize <= (off_t)sizeof(header)) {
217        LOGW("Bad keycharmap - filesize=%d\n", (int)filesize);
218        goto cleanup1;
219    }
220
221    err = read(fd, &header, sizeof(header));
222    if (err == -1) {
223        LOGW("Error reading keycharmap file");
224        goto cleanup1;
225    }
226
227    if (0 != memcmp(header.magic, "keychar", 8)) {
228        LOGW("Bad keycharmap magic token");
229        goto cleanup1;
230    }
231    if (header.endian != 0x12345678) {
232        LOGW("Bad keycharmap endians");
233        goto cleanup1;
234    }
235    if ((header.version & 0xff) != 2) {
236        LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version);
237        goto cleanup1;
238    }
239    if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) {
240        LOGW("Bad keycharmap file size\n");
241        goto cleanup1;
242    }
243
244    // read the key data
245    keys = (Key*)malloc(sizeof(Key)*header.keycount);
246    err = read(fd, keys, sizeof(Key)*header.keycount);
247    if (err == -1) {
248        LOGW("Error reading keycharmap file");
249        free(keys);
250        goto cleanup1;
251    }
252
253    // return the object
254    rv = new KeyCharacterMap;
255    rv->m_keyCount = header.keycount;
256    rv->m_keys = keys;
257    rv->m_type = header.kbdtype;
258
259cleanup1:
260    close(fd);
261
262    return rv;
263}
264