Merge "Support loading keyboard layout overlays from resources."

This commit is contained in:
Jeff Brown
2012-04-17 17:59:03 -07:00
committed by Android (Google) Code Review
18 changed files with 429 additions and 121 deletions

View File

@@ -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,

View File

@@ -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 {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
Parser parser(map.get(), tokenizer);
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);
#endif
if (!status) {
*outMap = map;
}
}
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);
#endif
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);
#endif
if (!status) {
*outMap = map;
}
}
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());

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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];

View File

@@ -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;
};

View File

@@ -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) {
}

View File

@@ -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;

View File

@@ -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);
}
}
});

View File

@@ -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 KeyboardLayout loadKeyboardLayouts(
PackageManager pm, ActivityInfo receiver,
List<KeyboardLayout> list, String keyboardName) {
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 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,52 +704,24 @@ 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();
}
}
if (changed) {
if (DEBUG) {
Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
}
reloadKeyboardLayouts();
}
}
/**
* 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;
}
}
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;

View File

@@ -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 \

View File

@@ -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");

View File

@@ -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;