summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/androidfw/KeyCharacterMap.h29
-rw-r--r--libs/androidfw/KeyCharacterMap.cpp127
-rw-r--r--libs/androidfw/Keyboard.cpp3
-rw-r--r--packages/InputDevices/Android.mk30
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_english_us.kcm7
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm15
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_german.kcm6
-rw-r--r--services/input/EventHub.cpp18
-rw-r--r--services/input/EventHub.h5
-rw-r--r--services/input/InputReader.cpp8
-rw-r--r--services/input/InputReader.h6
-rw-r--r--services/input/tests/InputReader_test.cpp8
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java2
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/java/com/android/server/input/InputManagerService.java211
-rw-r--r--services/jni/Android.mk3
-rw-r--r--services/jni/com_android_server_input_InputManagerService.cpp46
-rw-r--r--tools/validatekeymaps/Main.cpp3
18 files changed, 423 insertions, 115 deletions
diff --git a/include/androidfw/KeyCharacterMap.h b/include/androidfw/KeyCharacterMap.h
index 3cc1cb2..e0e6ffa 100644
--- a/include/androidfw/KeyCharacterMap.h
+++ b/include/androidfw/KeyCharacterMap.h
@@ -49,6 +49,17 @@ public:
KEYBOARD_TYPE_ALPHA = 3,
KEYBOARD_TYPE_FULL = 4,
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
+ KEYBOARD_TYPE_OVERLAY = 6,
+ };
+
+ enum Format {
+ // Base keyboard layout, may contain device-specific options, such as "type" declaration.
+ FORMAT_BASE = 0,
+ // Overlay keyboard layout, more restrictive, may be published by applications,
+ // cannot override device-specific options.
+ FORMAT_OVERLAY = 1,
+ // Either base or overlay layout ok.
+ FORMAT_ANY = 2,
};
// Substitute key code and meta state for fallback action.
@@ -58,7 +69,15 @@ public:
};
/* Loads a key character map from a file. */
- static status_t load(const String8& filename, sp<KeyCharacterMap>* outMap);
+ static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
+
+ /* Loads a key character map from its string contents. */
+ static status_t loadContents(const String8& filename,
+ const char* contents, Format format, sp<KeyCharacterMap>* outMap);
+
+ /* Combines a base key character map and an overlay. */
+ static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
+ const sp<KeyCharacterMap>& overlay);
/* Returns an empty key character map. */
static sp<KeyCharacterMap> empty();
@@ -115,6 +134,7 @@ protected:
private:
struct Behavior {
Behavior();
+ Behavior(const Behavior& other);
/* The next behavior in the list, or NULL if none. */
Behavior* next;
@@ -131,6 +151,7 @@ private:
struct Key {
Key();
+ Key(const Key& other);
~Key();
/* The single character label printed on the key, or 0 if none. */
@@ -166,11 +187,12 @@ private:
KeyCharacterMap* mMap;
Tokenizer* mTokenizer;
+ Format mFormat;
State mState;
int32_t mKeyCode;
public:
- Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
+ Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
~Parser();
status_t parse();
@@ -188,6 +210,7 @@ private:
int mType;
KeyCharacterMap();
+ KeyCharacterMap(const KeyCharacterMap& other);
bool getKey(int32_t keyCode, const Key** outKey) const;
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -195,6 +218,8 @@ private:
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
+ static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
+
static void addKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
static void addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/androidfw/KeyCharacterMap.cpp b/libs/androidfw/KeyCharacterMap.cpp
index 9abbf38..66fdfd7 100644
--- a/libs/androidfw/KeyCharacterMap.cpp
+++ b/libs/androidfw/KeyCharacterMap.cpp
@@ -89,6 +89,13 @@ KeyCharacterMap::KeyCharacterMap() :
mType(KEYBOARD_TYPE_UNKNOWN) {
}
+KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
+ RefBase(), mType(other.mType) {
+ for (size_t i = 0; i < other.mKeys.size(); i++) {
+ mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
+ }
+}
+
KeyCharacterMap::~KeyCharacterMap() {
for (size_t i = 0; i < mKeys.size(); i++) {
Key* key = mKeys.editValueAt(i);
@@ -96,7 +103,8 @@ KeyCharacterMap::~KeyCharacterMap() {
}
}
-status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* outMap) {
+status_t KeyCharacterMap::load(const String8& filename,
+ Format format, sp<KeyCharacterMap>* outMap) {
outMap->clear();
Tokenizer* tokenizer;
@@ -104,31 +112,77 @@ status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* out
if (status) {
ALOGE("Error %d opening key character map file %s.", status, filename.string());
} else {
- sp<KeyCharacterMap> map = new KeyCharacterMap();
- if (!map.get()) {
- ALOGE("Error allocating key character map.");
- status = NO_MEMORY;
- } else {
+ status = load(tokenizer, format, outMap);
+ delete tokenizer;
+ }
+ return status;
+}
+
+status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
+ Format format, sp<KeyCharacterMap>* outMap) {
+ outMap->clear();
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening key character map.", status);
+ } else {
+ status = load(tokenizer, format, outMap);
+ delete tokenizer;
+ }
+ return status;
+}
+
+status_t KeyCharacterMap::load(Tokenizer* tokenizer,
+ Format format, sp<KeyCharacterMap>* outMap) {
+ status_t status = OK;
+ sp<KeyCharacterMap> map = new KeyCharacterMap();
+ if (!map.get()) {
+ ALOGE("Error allocating key character map.");
+ status = NO_MEMORY;
+ } else {
#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
- Parser parser(map.get(), tokenizer);
- status = parser.parse();
+ Parser parser(map.get(), tokenizer, format);
+ status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
#endif
- if (!status) {
- *outMap = map;
- }
+ if (!status) {
+ *outMap = map;
}
- delete tokenizer;
}
return status;
}
+sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
+ const sp<KeyCharacterMap>& overlay) {
+ if (overlay == NULL) {
+ return base;
+ }
+ if (base == NULL) {
+ return overlay;
+ }
+
+ sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
+ for (size_t i = 0; i < overlay->mKeys.size(); i++) {
+ int32_t keyCode = overlay->mKeys.keyAt(i);
+ Key* key = overlay->mKeys.valueAt(i);
+ ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
+ if (oldIndex >= 0) {
+ delete map->mKeys.valueAt(oldIndex);
+ map->mKeys.editValueAt(oldIndex) = new Key(*key);
+ } else {
+ map->mKeys.add(keyCode, new Key(*key));
+ }
+ }
+ return map;
+}
+
sp<KeyCharacterMap> KeyCharacterMap::empty() {
return sEmpty;
}
@@ -508,6 +562,11 @@ KeyCharacterMap::Key::Key() :
label(0), number(0), firstBehavior(NULL) {
}
+KeyCharacterMap::Key::Key(const Key& other) :
+ label(other.label), number(other.number),
+ firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
+}
+
KeyCharacterMap::Key::~Key() {
Behavior* behavior = firstBehavior;
while (behavior) {
@@ -524,11 +583,17 @@ KeyCharacterMap::Behavior::Behavior() :
next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
}
+KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
+ next(other.next ? new Behavior(*other.next) : NULL),
+ metaState(other.metaState), character(other.character),
+ fallbackKeyCode(other.fallbackKeyCode) {
+}
+
// --- KeyCharacterMap::Parser ---
-KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
- mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
+KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
+ mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
}
KeyCharacterMap::Parser::~Parser() {
@@ -588,10 +653,24 @@ status_t KeyCharacterMap::Parser::parse() {
return BAD_VALUE;
}
- if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
- ALOGE("%s: Missing required keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
+ if (mFormat == FORMAT_BASE) {
+ if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+ ALOGE("%s: Base keyboard layout missing required keyboard 'type' declaration.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
+ ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ } else if (mFormat == FORMAT_OVERLAY) {
+ if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
+ ALOGE("%s: Overlay keyboard layout missing required keyboard "
+ "'type OVERLAY' declaration.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
}
return NO_ERROR;
@@ -616,6 +695,8 @@ status_t KeyCharacterMap::Parser::parseType() {
type = KEYBOARD_TYPE_FULL;
} else if (typeToken == "SPECIAL_FUNCTION") {
type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+ } else if (typeToken == "OVERLAY") {
+ type = KEYBOARD_TYPE_OVERLAY;
} else {
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
typeToken.string());
diff --git a/libs/androidfw/Keyboard.cpp b/libs/androidfw/Keyboard.cpp
index a84a8c7..4ddbeab 100644
--- a/libs/androidfw/Keyboard.cpp
+++ b/libs/androidfw/Keyboard.cpp
@@ -129,7 +129,8 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
return NAME_NOT_FOUND;
}
- status_t status = KeyCharacterMap::load(path, &keyCharacterMap);
+ status_t status = KeyCharacterMap::load(path,
+ KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
if (status) {
return status;
}
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
index 446b47e..37f2428 100644
--- a/packages/InputDevices/Android.mk
+++ b/packages/InputDevices/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -12,5 +26,17 @@ LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
-########################
-include $(call all-makefiles-under,$(LOCAL_PATH))
+# Validate all key maps.
+include $(CLEAR_VARS)
+
+validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
+files := frameworks/base/packages/InputDevices/res/raw/*.kcm
+
+LOCAL_MODULE := validate_input_devices_keymaps
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := validatekeymaps
+
+validate_input_devices_keymaps: $(files)
+ $(hide) $(validatekeymaps) $(files)
+
+include $(BUILD_PHONY_PACKAGE)
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_us.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_us.kcm
index a7823fd..2c663bc 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_english_us.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_us.kcm
@@ -12,4 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# PLACEHOLDER CONTENT #
+#
+# English (US) keyboard layout.
+# Assumes that the base keyboard layout is already English (US).
+#
+
+type OVERLAY
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm
index a7823fd..b5bf2ef 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm
@@ -12,4 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# PLACEHOLDER CONTENT #
+#
+# English (US), Dvorak keyboard layout.
+# Assumes that the base keyboard layout is already English (US).
+#
+
+type OVERLAY
+
+# Test
+key A {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+ ctrl, alt, meta: none
+}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_german.kcm b/packages/InputDevices/res/raw/keyboard_layout_german.kcm
index a7823fd..6b3b3b4 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_german.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_german.kcm
@@ -12,4 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# PLACEHOLDER CONTENT #
+#
+# German keyboard layout.
+#
+
+type OVERLAY
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index c0eb1b9..b788e16 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -531,11 +531,29 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device) {
+ if (device->combinedKeyMap != NULL) {
+ return device->combinedKeyMap;
+ }
return device->keyMap.keyCharacterMap;
}
return NULL;
}
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
+ const sp<KeyCharacterMap>& map) {
+ AutoMutex _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device) {
+ if (map != device->overlayKeyMap) {
+ device->overlayKeyMap = map;
+ device->combinedKeyMap = KeyCharacterMap::combine(
+ device->keyMap.keyCharacterMap, map);
+ return true;
+ }
+ }
+ return false;
+}
+
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 51d2bac..f885f4d 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -221,6 +221,7 @@ public:
Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
/* Control the vibrator. */
virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
@@ -283,6 +284,7 @@ public:
Vector<VirtualKeyDefinition>& outVirtualKeys) const;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
virtual void vibrate(int32_t deviceId, nsecs_t duration);
virtual void cancelVibrate(int32_t deviceId);
@@ -321,6 +323,9 @@ private:
VirtualKeyMap* virtualKeyMap;
KeyMap keyMap;
+ sp<KeyCharacterMap> overlayKeyMap;
+ sp<KeyCharacterMap> combinedKeyMap;
+
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 8c37fbb..95e56bf 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -962,6 +962,14 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config
mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
}
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
+ sp<KeyCharacterMap> keyboardLayout =
+ mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier.descriptor);
+ if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
+ bumpGeneration();
+ }
+ }
+
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index ed57596..e5897e7 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -67,6 +67,9 @@ struct InputReaderConfiguration {
// The visible touches option changed.
CHANGE_SHOW_TOUCHES = 1 << 3,
+ // The keyboard layouts must be reloaded.
+ CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -222,6 +225,9 @@ public:
* and provides information about all current input devices.
*/
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
+
+ /* Gets the keyboard layout for a particular input device. */
+ virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0;
};
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 94d4189..602eb34 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -170,6 +170,10 @@ private:
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
mInputDevices = inputDevices;
}
+
+ virtual sp<KeyCharacterMap> getKeyboardLayout(const String8& inputDeviceDescriptor) {
+ return NULL;
+ }
};
@@ -646,6 +650,10 @@ private:
return NULL;
}
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
+ return false;
+ }
+
virtual void vibrate(int32_t deviceId, nsecs_t duration) {
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index ca7241c..a474cec 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -118,7 +118,7 @@ import java.util.TreeMap;
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
static final boolean DEBUG = false;
- static final String TAG = "InputManagerService";
+ static final String TAG = "InputMethodManagerService";
static final int MSG_SHOW_IM_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 75dcf8c..02c4d5a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -51,6 +51,7 @@ import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.widget.LockSettingsService;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
+import com.android.server.input.InputManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.pm.PackageManagerService;
@@ -137,6 +138,7 @@ class ServerThread extends Thread {
ThrottleService throttle = null;
NetworkTimeUpdateService networkTimeUpdater = null;
CommonTimeManagementService commonTimeMgmtService = null;
+ InputManagerService inputManager = null;
// Critical services...
try {
@@ -224,7 +226,8 @@ class ServerThread extends Thread {
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
- ServiceManager.addService(Context.INPUT_SERVICE, wm.getInputManagerService());
+ inputManager = wm.getInputManagerService();
+ ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
@@ -722,6 +725,7 @@ class ServerThread extends Thread {
final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
final DreamManagerService dreamyF = dreamy;
+ final InputManagerService inputManagerF = inputManager;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -833,6 +837,11 @@ class ServerThread extends Thread {
} catch (Throwable e) {
reportWtf("making DreamManagerService ready", e);
}
+ try {
+ if (inputManagerF != null) inputManagerF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making InputManagerService ready", e);
+ }
}
});
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 7640eff..a4ed31c 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -26,15 +26,18 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.Manifest;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
@@ -60,13 +63,11 @@ import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
-import android.view.KeyCharacterMap.UnavailableException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -77,13 +78,14 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import libcore.io.IoUtils;
+import libcore.io.Streams;
import libcore.util.Objects;
/*
@@ -91,7 +93,7 @@ import libcore.util.Objects;
*/
public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
static final String TAG = "InputManager";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
@@ -103,6 +105,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
private final Context mContext;
private final Callbacks mCallbacks;
private final InputManagerHandler mHandler;
+ private boolean mSystemReady;
// Persistent data store. Must be locked each time during use.
private final PersistentDataStore mDataStore = new PersistentDataStore();
@@ -163,6 +166,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
int repeat, int token);
private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
+ private static native void nativeReloadKeyboardLayouts(int ptr);
private static native String nativeDump(int ptr);
private static native void nativeMonitor(int ptr);
@@ -212,7 +216,33 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
}
-
+
+ public void systemReady() {
+ if (DEBUG) {
+ Slog.d(TAG, "System ready.");
+ }
+ mSystemReady = true;
+ reloadKeyboardLayouts();
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Packages changed, reloading keyboard layouts.");
+ }
+ reloadKeyboardLayouts();
+ }
+ }, filter, null, mHandler);
+ }
+
+ private void reloadKeyboardLayouts() {
+ nativeReloadKeyboardLayouts(mPtr);
+ }
+
public void setDisplaySize(int displayId, int width, int height,
int externalWidth, int externalHeight) {
if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
@@ -528,14 +558,14 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
@Override // Binder call
public KeyboardLayout[] getKeyboardLayouts() {
- ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
-
- final PackageManager pm = mContext.getPackageManager();
- Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
- for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA)) {
- loadKeyboardLayouts(pm, resolveInfo.activityInfo, list, null);
- }
+ final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
+ visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+ @Override
+ public void visitKeyboardLayout(Resources resources,
+ String descriptor, String label, int kcmResId) {
+ list.add(new KeyboardLayout(descriptor, label));
+ }
+ });
return list.toArray(new KeyboardLayout[list.size()]);
}
@@ -545,37 +575,57 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
}
- KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
- if (d == null) {
- return null;
+ final KeyboardLayout[] result = new KeyboardLayout[1];
+ visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
+ @Override
+ public void visitKeyboardLayout(Resources resources,
+ String descriptor, String label, int kcmResId) {
+ result[0] = new KeyboardLayout(descriptor, label);
+ }
+ });
+ if (result[0] == null) {
+ Log.w(TAG, "Could not get keyboard layout with descriptor '"
+ + keyboardLayoutDescriptor + "'.");
}
+ return result[0];
+ }
+ private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
final PackageManager pm = mContext.getPackageManager();
- try {
- ActivityInfo receiver = pm.getReceiverInfo(
- new ComponentName(d.packageName, d.receiverName),
- PackageManager.GET_META_DATA);
- return loadKeyboardLayouts(pm, receiver, null, d.keyboardLayoutName);
- } catch (NameNotFoundException ex) {
- Log.w(TAG, "Could not load keyboard layout '" + d.keyboardLayoutName
- + "' from receiver " + d.packageName + "/" + d.receiverName, ex);
- return null;
+ Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
+ for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA)) {
+ visitKeyboardLayoutsInPackage(pm, resolveInfo.activityInfo, null, visitor);
+ }
+ }
+
+ private void visitKeyboardLayout(String keyboardLayoutDescriptor,
+ KeyboardLayoutVisitor visitor) {
+ KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
+ if (d != null) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ ActivityInfo receiver = pm.getReceiverInfo(
+ new ComponentName(d.packageName, d.receiverName),
+ PackageManager.GET_META_DATA);
+ visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, visitor);
+ } catch (NameNotFoundException ex) {
+ }
}
}
- private KeyboardLayout loadKeyboardLayouts(
- PackageManager pm, ActivityInfo receiver,
- List<KeyboardLayout> list, String keyboardName) {
+ private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
+ String keyboardName, KeyboardLayoutVisitor visitor) {
Bundle metaData = receiver.metaData;
if (metaData == null) {
- return null;
+ return;
}
int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
if (configResId == 0) {
Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
+ "' on receiver " + receiver.packageName + "/" + receiver.name);
- return null;
+ return;
}
try {
@@ -608,12 +658,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
} else {
String descriptor = KeyboardLayoutDescriptor.format(
receiver.packageName, receiver.name, name);
- KeyboardLayout c = new KeyboardLayout(descriptor, label);
- if (keyboardName != null && name.equals(keyboardName)) {
- return c;
- }
- if (list != null) {
- list.add(c);
+ if (keyboardName == null || name.equals(keyboardName)) {
+ visitor.visitKeyboardLayout(resources, descriptor,
+ label, kcmResId);
}
}
} finally {
@@ -629,16 +676,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
parser.close();
}
} catch (Exception ex) {
- Log.w(TAG, "Could not load keyboard layout resource from receiver "
+ Log.w(TAG, "Could not parse keyboard layout resource from receiver "
+ receiver.packageName + "/" + receiver.name, ex);
- return null;
}
- if (keyboardName != null) {
- Log.w(TAG, "Could not load keyboard layout '" + keyboardName
- + "' from receiver " + receiver.packageName + "/" + receiver.name
- + " because it was not declared in the keyboard layout resource.");
- }
- return null;
}
@Override // Binder call
@@ -664,51 +704,23 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
}
+ final boolean changed;
synchronized (mDataStore) {
try {
- mDataStore.setKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor);
+ changed = mDataStore.setKeyboardLayout(
+ inputDeviceDescriptor, keyboardLayoutDescriptor);
} finally {
mDataStore.saveIfNeeded();
}
}
- }
-
- /**
- * Loads the key character map associated with the keyboard layout.
- *
- * @param pm The package manager.
- * @return The key character map, or null if it could not be loaded for any reason.
- *
- public KeyCharacterMap loadKeyCharacterMap(PackageManager pm) {
- if (pm == null) {
- throw new IllegalArgumentException("pm must not be null");
- }
- if (mKeyCharacterMap == null) {
- KeyboardLayoutDescriptor d = InputManager.parseKeyboardLayoutDescriptor(mDescriptor);
- if (d == null) {
- Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
- + "' because the descriptor could not be parsed.");
- return null;
- }
-
- CharSequence cs = pm.getText(d.packageName, mKeyCharacterMapResId, null);
- if (cs == null) {
- Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
- + "' because its associated resource could not be loaded.");
- return null;
- }
-
- try {
- mKeyCharacterMap = KeyCharacterMap.load(cs);
- } catch (UnavailableException ex) {
- Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
- + "' due to an error while parsing.", ex);
- return null;
+ if (changed) {
+ if (DEBUG) {
+ Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
}
+ reloadKeyboardLayouts();
}
- return mKeyCharacterMap;
- }*/
+ }
public void setInputWindows(InputWindowHandle[] windowHandles) {
nativeSetInputWindows(mPtr, windowHandles);
@@ -1076,6 +1088,40 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
return PointerIcon.getDefaultIcon(mContext);
}
+ // Native callback.
+ private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
+ if (!mSystemReady) {
+ return null;
+ }
+
+ String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+ if (keyboardLayoutDescriptor == null) {
+ return null;
+ }
+
+ final String[] result = new String[2];
+ visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
+ @Override
+ public void visitKeyboardLayout(Resources resources,
+ String descriptor, String label, int kcmResId) {
+ try {
+ result[0] = descriptor;
+ result[1] = Streams.readFully(new InputStreamReader(
+ resources.openRawResource(kcmResId)));
+ } catch (IOException ex) {
+ } catch (NotFoundException ex) {
+ }
+ }
+ });
+ if (result[0] == null) {
+ Log.w(TAG, "Could not get keyboard layout with descriptor '"
+ + keyboardLayoutDescriptor + "'.");
+ return null;
+ }
+ return result;
+ }
+
+
/**
* Callback interface implemented by the Window Manager.
*/
@@ -1169,6 +1215,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
}
}
+ private interface KeyboardLayoutVisitor {
+ void visitKeyboardLayout(Resources resources,
+ String descriptor, String label, int kcmResId);
+ }
+
private final class InputDevicesChangedListenerRecord implements DeathRecipient {
private final int mPid;
private final IInputDevicesChangedListener mListener;
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index ac4fd15..e2bd622 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -22,7 +22,8 @@ LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
frameworks/base/services \
frameworks/base/core/jni \
- external/skia/include/core
+ external/skia/include/core \
+ libcore/include
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index 3795074..b361a26 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -46,6 +46,9 @@
#include <android_view_PointerIcon.h>
#include <android/graphics/GraphicsJNI.h>
+#include <ScopedLocalRef.h>
+#include <ScopedUtfChars.h>
+
#include "com_android_server_PowerManagerService.h"
#include "com_android_server_input_InputApplicationHandle.h"
#include "com_android_server_input_InputWindowHandle.h"
@@ -79,6 +82,7 @@ static struct {
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
+ jmethodID getKeyboardLayoutOverlay;
} gServiceClassInfo;
static struct {
@@ -179,12 +183,14 @@ public:
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
void setShowTouches(bool enabled);
+ void reloadKeyboardLayouts();
/* --- InputReaderPolicyInterface implementation --- */
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
+ virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -522,6 +528,29 @@ void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>
checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged");
}
+sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
+ const String8& inputDeviceDescriptor) {
+ JNIEnv* env = jniEnv();
+
+ sp<KeyCharacterMap> result;
+ ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+ ScopedLocalRef<jobjectArray> arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj,
+ gServiceClassInfo.getKeyboardLayoutOverlay, descriptorObj.get())));
+ if (arrayObj.get()) {
+ ScopedLocalRef<jstring> filenameObj(env,
+ jstring(env->GetObjectArrayElement(arrayObj.get(), 0)));
+ ScopedLocalRef<jstring> contentsObj(env,
+ jstring(env->GetObjectArrayElement(arrayObj.get(), 1)));
+ ScopedUtfChars filenameChars(env, filenameObj.get());
+ ScopedUtfChars contentsChars(env, contentsObj.get());
+
+ KeyCharacterMap::loadContents(String8(filenameChars.c_str()),
+ String8(contentsChars.c_str()), KeyCharacterMap::FORMAT_OVERLAY, &result);
+ }
+ checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay");
+ return result;
+}
+
void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
int32_t switchValue, uint32_t policyFlags) {
#if DEBUG_INPUT_DISPATCHER_POLICY
@@ -728,6 +757,11 @@ void NativeInputManager::setShowTouches(bool enabled) {
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
}
+void NativeInputManager::reloadKeyboardLayouts() {
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
+}
+
bool NativeInputManager::isScreenOn() {
return android_server_PowerManagerService_isScreenOn();
}
@@ -1258,6 +1292,13 @@ static void nativeCancelVibrate(JNIEnv* env,
im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
}
+static void nativeReloadKeyboardLayouts(JNIEnv* env,
+ jclass clazz, jint ptr) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->reloadKeyboardLayouts();
+}
+
static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1323,6 +1364,8 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) nativeVibrate },
{ "nativeCancelVibrate", "(III)V",
(void*) nativeCancelVibrate },
+ { "nativeReloadKeyboardLayouts", "(I)V",
+ (void*) nativeReloadKeyboardLayouts },
{ "nativeDump", "(I)Ljava/lang/String;",
(void*) nativeDump },
{ "nativeMonitor", "(I)V",
@@ -1418,6 +1461,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
+ "getKeyboardLayoutOverlay", "(Ljava/lang/String;)[Ljava/lang/String;");
+
// InputDevice
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 563225e..91e4fda 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -89,7 +89,8 @@ static bool validateFile(const char* filename) {
case FILETYPE_KEYCHARACTERMAP: {
sp<KeyCharacterMap> map;
- status_t status = KeyCharacterMap::load(String8(filename), &map);
+ status_t status = KeyCharacterMap::load(String8(filename),
+ KeyCharacterMap::FORMAT_ANY, &map);
if (status) {
fprintf(stderr, "Error %d parsing key character map file.\n\n", status);
return false;