diff options
-rw-r--r-- | include/androidfw/KeyCharacterMap.h | 29 | ||||
-rw-r--r-- | libs/androidfw/KeyCharacterMap.cpp | 127 | ||||
-rw-r--r-- | libs/androidfw/Keyboard.cpp | 3 | ||||
-rw-r--r-- | packages/InputDevices/Android.mk | 30 | ||||
-rw-r--r-- | packages/InputDevices/res/raw/keyboard_layout_english_us.kcm | 7 | ||||
-rw-r--r-- | packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm | 15 | ||||
-rw-r--r-- | packages/InputDevices/res/raw/keyboard_layout_german.kcm | 6 | ||||
-rw-r--r-- | services/input/EventHub.cpp | 18 | ||||
-rw-r--r-- | services/input/EventHub.h | 5 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 8 | ||||
-rw-r--r-- | services/input/InputReader.h | 6 | ||||
-rw-r--r-- | services/input/tests/InputReader_test.cpp | 8 | ||||
-rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 2 | ||||
-rw-r--r-- | services/java/com/android/server/SystemServer.java | 11 | ||||
-rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 211 | ||||
-rw-r--r-- | services/jni/Android.mk | 3 | ||||
-rw-r--r-- | services/jni/com_android_server_input_InputManagerService.cpp | 46 | ||||
-rw-r--r-- | tools/validatekeymaps/Main.cpp | 3 |
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; |