Skip to content

Commit 6ec6f79

Browse files
author
Jeff Brown
committed
Support loading keyboard layout overlays from resources.
Added the concept of a keyboard layout overlay, which is a key character map file that has "type OVERLAY". Added support for loading keyboard layout overlays from resources dynamically. The layouts are reloaded whenever they are changed in the Settings application or an application is installed. This is somewhat more aggressive than necessary so we might want to optimize it later. Before system-ready, the input system uses just the generic keyboard layouts that are included on the device system image. After system-ready, it considers the user's selected keyboard layout overlay and attempts to load it as necessary. We need to wait until system-ready before doing this because we need to be in a state where it is safe to start applications or access their resources. Bug: 6110399 Change-Id: Iae0886d3356649b0d2440aa00910a888cedd8323
1 parent a3bc565 commit 6ec6f79

File tree

18 files changed

+423
-115
lines changed

18 files changed

+423
-115
lines changed

include/androidfw/KeyCharacterMap.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ class KeyCharacterMap : public RefBase {
4949
KEYBOARD_TYPE_ALPHA = 3,
5050
KEYBOARD_TYPE_FULL = 4,
5151
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
52+
KEYBOARD_TYPE_OVERLAY = 6,
53+
};
54+
55+
enum Format {
56+
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
57+
FORMAT_BASE = 0,
58+
// Overlay keyboard layout, more restrictive, may be published by applications,
59+
// cannot override device-specific options.
60+
FORMAT_OVERLAY = 1,
61+
// Either base or overlay layout ok.
62+
FORMAT_ANY = 2,
5263
};
5364

5465
// Substitute key code and meta state for fallback action.
@@ -58,7 +69,15 @@ class KeyCharacterMap : public RefBase {
5869
};
5970

6071
/* Loads a key character map from a file. */
61-
static status_t load(const String8& filename, sp<KeyCharacterMap>* outMap);
72+
static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
73+
74+
/* Loads a key character map from its string contents. */
75+
static status_t loadContents(const String8& filename,
76+
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
77+
78+
/* Combines a base key character map and an overlay. */
79+
static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
80+
const sp<KeyCharacterMap>& overlay);
6281

6382
/* Returns an empty key character map. */
6483
static sp<KeyCharacterMap> empty();
@@ -115,6 +134,7 @@ class KeyCharacterMap : public RefBase {
115134
private:
116135
struct Behavior {
117136
Behavior();
137+
Behavior(const Behavior& other);
118138

119139
/* The next behavior in the list, or NULL if none. */
120140
Behavior* next;
@@ -131,6 +151,7 @@ class KeyCharacterMap : public RefBase {
131151

132152
struct Key {
133153
Key();
154+
Key(const Key& other);
134155
~Key();
135156

136157
/* The single character label printed on the key, or 0 if none. */
@@ -166,11 +187,12 @@ class KeyCharacterMap : public RefBase {
166187

167188
KeyCharacterMap* mMap;
168189
Tokenizer* mTokenizer;
190+
Format mFormat;
169191
State mState;
170192
int32_t mKeyCode;
171193

172194
public:
173-
Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
195+
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
174196
~Parser();
175197
status_t parse();
176198

@@ -188,13 +210,16 @@ class KeyCharacterMap : public RefBase {
188210
int mType;
189211

190212
KeyCharacterMap();
213+
KeyCharacterMap(const KeyCharacterMap& other);
191214

192215
bool getKey(int32_t keyCode, const Key** outKey) const;
193216
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
194217
const Key** outKey, const Behavior** outBehavior) const;
195218

196219
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
197220

221+
static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
222+
198223
static void addKey(Vector<KeyEvent>& outEvents,
199224
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
200225
static void addMetaKeys(Vector<KeyEvent>& outEvents,

libs/androidfw/KeyCharacterMap.cpp

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -89,46 +89,100 @@ KeyCharacterMap::KeyCharacterMap() :
8989
mType(KEYBOARD_TYPE_UNKNOWN) {
9090
}
9191

92+
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
93+
RefBase(), mType(other.mType) {
94+
for (size_t i = 0; i < other.mKeys.size(); i++) {
95+
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
96+
}
97+
}
98+
9299
KeyCharacterMap::~KeyCharacterMap() {
93100
for (size_t i = 0; i < mKeys.size(); i++) {
94101
Key* key = mKeys.editValueAt(i);
95102
delete key;
96103
}
97104
}
98105

99-
status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* outMap) {
106+
status_t KeyCharacterMap::load(const String8& filename,
107+
Format format, sp<KeyCharacterMap>* outMap) {
100108
outMap->clear();
101109

102110
Tokenizer* tokenizer;
103111
status_t status = Tokenizer::open(filename, &tokenizer);
104112
if (status) {
105113
ALOGE("Error %d opening key character map file %s.", status, filename.string());
106114
} else {
107-
sp<KeyCharacterMap> map = new KeyCharacterMap();
108-
if (!map.get()) {
109-
ALOGE("Error allocating key character map.");
110-
status = NO_MEMORY;
111-
} else {
115+
status = load(tokenizer, format, outMap);
116+
delete tokenizer;
117+
}
118+
return status;
119+
}
120+
121+
status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
122+
Format format, sp<KeyCharacterMap>* outMap) {
123+
outMap->clear();
124+
125+
Tokenizer* tokenizer;
126+
status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
127+
if (status) {
128+
ALOGE("Error %d opening key character map.", status);
129+
} else {
130+
status = load(tokenizer, format, outMap);
131+
delete tokenizer;
132+
}
133+
return status;
134+
}
135+
136+
status_t KeyCharacterMap::load(Tokenizer* tokenizer,
137+
Format format, sp<KeyCharacterMap>* outMap) {
138+
status_t status = OK;
139+
sp<KeyCharacterMap> map = new KeyCharacterMap();
140+
if (!map.get()) {
141+
ALOGE("Error allocating key character map.");
142+
status = NO_MEMORY;
143+
} else {
112144
#if DEBUG_PARSER_PERFORMANCE
113-
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
145+
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
114146
#endif
115-
Parser parser(map.get(), tokenizer);
116-
status = parser.parse();
147+
Parser parser(map.get(), tokenizer, format);
148+
status = parser.parse();
117149
#if DEBUG_PARSER_PERFORMANCE
118-
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
119-
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
120-
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
121-
elapsedTime / 1000000.0);
150+
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
151+
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
152+
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
153+
elapsedTime / 1000000.0);
122154
#endif
123-
if (!status) {
124-
*outMap = map;
125-
}
155+
if (!status) {
156+
*outMap = map;
126157
}
127-
delete tokenizer;
128158
}
129159
return status;
130160
}
131161

162+
sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
163+
const sp<KeyCharacterMap>& overlay) {
164+
if (overlay == NULL) {
165+
return base;
166+
}
167+
if (base == NULL) {
168+
return overlay;
169+
}
170+
171+
sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
172+
for (size_t i = 0; i < overlay->mKeys.size(); i++) {
173+
int32_t keyCode = overlay->mKeys.keyAt(i);
174+
Key* key = overlay->mKeys.valueAt(i);
175+
ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
176+
if (oldIndex >= 0) {
177+
delete map->mKeys.valueAt(oldIndex);
178+
map->mKeys.editValueAt(oldIndex) = new Key(*key);
179+
} else {
180+
map->mKeys.add(keyCode, new Key(*key));
181+
}
182+
}
183+
return map;
184+
}
185+
132186
sp<KeyCharacterMap> KeyCharacterMap::empty() {
133187
return sEmpty;
134188
}
@@ -508,6 +562,11 @@ KeyCharacterMap::Key::Key() :
508562
label(0), number(0), firstBehavior(NULL) {
509563
}
510564

565+
KeyCharacterMap::Key::Key(const Key& other) :
566+
label(other.label), number(other.number),
567+
firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
568+
}
569+
511570
KeyCharacterMap::Key::~Key() {
512571
Behavior* behavior = firstBehavior;
513572
while (behavior) {
@@ -524,11 +583,17 @@ KeyCharacterMap::Behavior::Behavior() :
524583
next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
525584
}
526585

586+
KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
587+
next(other.next ? new Behavior(*other.next) : NULL),
588+
metaState(other.metaState), character(other.character),
589+
fallbackKeyCode(other.fallbackKeyCode) {
590+
}
591+
527592

528593
// --- KeyCharacterMap::Parser ---
529594

530-
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
531-
mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
595+
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
596+
mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
532597
}
533598

534599
KeyCharacterMap::Parser::~Parser() {
@@ -588,10 +653,24 @@ status_t KeyCharacterMap::Parser::parse() {
588653
return BAD_VALUE;
589654
}
590655

591-
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
592-
ALOGE("%s: Missing required keyboard 'type' declaration.",
593-
mTokenizer->getLocation().string());
594-
return BAD_VALUE;
656+
if (mFormat == FORMAT_BASE) {
657+
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
658+
ALOGE("%s: Base keyboard layout missing required keyboard 'type' declaration.",
659+
mTokenizer->getLocation().string());
660+
return BAD_VALUE;
661+
}
662+
if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
663+
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
664+
mTokenizer->getLocation().string());
665+
return BAD_VALUE;
666+
}
667+
} else if (mFormat == FORMAT_OVERLAY) {
668+
if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
669+
ALOGE("%s: Overlay keyboard layout missing required keyboard "
670+
"'type OVERLAY' declaration.",
671+
mTokenizer->getLocation().string());
672+
return BAD_VALUE;
673+
}
595674
}
596675

597676
return NO_ERROR;
@@ -616,6 +695,8 @@ status_t KeyCharacterMap::Parser::parseType() {
616695
type = KEYBOARD_TYPE_FULL;
617696
} else if (typeToken == "SPECIAL_FUNCTION") {
618697
type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
698+
} else if (typeToken == "OVERLAY") {
699+
type = KEYBOARD_TYPE_OVERLAY;
619700
} else {
620701
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
621702
typeToken.string());

libs/androidfw/Keyboard.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
129129
return NAME_NOT_FOUND;
130130
}
131131

132-
status_t status = KeyCharacterMap::load(path, &keyCharacterMap);
132+
status_t status = KeyCharacterMap::load(path,
133+
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
133134
if (status) {
134135
return status;
135136
}

packages/InputDevices/Android.mk

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright (C) 2012 The Android Open Source Project
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
LOCAL_PATH:= $(call my-dir)
216
include $(CLEAR_VARS)
317

@@ -12,5 +26,17 @@ LOCAL_CERTIFICATE := platform
1226

1327
include $(BUILD_PACKAGE)
1428

15-
########################
16-
include $(call all-makefiles-under,$(LOCAL_PATH))
29+
# Validate all key maps.
30+
include $(CLEAR_VARS)
31+
32+
validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
33+
files := frameworks/base/packages/InputDevices/res/raw/*.kcm
34+
35+
LOCAL_MODULE := validate_input_devices_keymaps
36+
LOCAL_MODULE_TAGS := optional
37+
LOCAL_REQUIRED_MODULES := validatekeymaps
38+
39+
validate_input_devices_keymaps: $(files)
40+
$(hide) $(validatekeymaps) $(files)
41+
42+
include $(BUILD_PHONY_PACKAGE)

packages/InputDevices/res/raw/keyboard_layout_english_us.kcm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# English (US) keyboard layout.
17+
# Assumes that the base keyboard layout is already English (US).
18+
#
19+
20+
type OVERLAY

packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,17 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# English (US), Dvorak keyboard layout.
17+
# Assumes that the base keyboard layout is already English (US).
18+
#
19+
20+
type OVERLAY
21+
22+
# Test
23+
key A {
24+
label: 'X'
25+
base: 'x'
26+
shift, capslock: 'X'
27+
ctrl, alt, meta: none
28+
}

packages/InputDevices/res/raw/keyboard_layout_german.kcm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# German keyboard layout.
17+
#
18+
19+
type OVERLAY

services/input/EventHub.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,29 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
531531
AutoMutex _l(mLock);
532532
Device* device = getDeviceLocked(deviceId);
533533
if (device) {
534+
if (device->combinedKeyMap != NULL) {
535+
return device->combinedKeyMap;
536+
}
534537
return device->keyMap.keyCharacterMap;
535538
}
536539
return NULL;
537540
}
538541

542+
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
543+
const sp<KeyCharacterMap>& map) {
544+
AutoMutex _l(mLock);
545+
Device* device = getDeviceLocked(deviceId);
546+
if (device) {
547+
if (map != device->overlayKeyMap) {
548+
device->overlayKeyMap = map;
549+
device->combinedKeyMap = KeyCharacterMap::combine(
550+
device->keyMap.keyCharacterMap, map);
551+
return true;
552+
}
553+
}
554+
return false;
555+
}
556+
539557
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
540558
AutoMutex _l(mLock);
541559
Device* device = getDeviceLocked(deviceId);

0 commit comments

Comments
 (0)