二、WindowManagerService.java的构造函数,在加载键盘布局方面做了两件事情:1.初始化,构造一个InputManager实例;2.启动,由InputManager.java
start()函数实现
private
WindowManagerService(Context
context, PowerManagerService pm,
……..
……..
mInputManager = new
InputManager(context,
this);
//构造InputManager实例
PolicyThread thr
= new PolicyThread(mPolicy, this, context, pm);
thr.start();
synchronized (thr)
{
while
(!thr.mRunning) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
mInputManager.start();
//调用InputManager.java start()函数
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
三、InputManager.java是本地c代码的包装类,对com_android_server_InputManager.cpp接口函数进行包装,以提供其他java文件调取。
1.初始化,InputManager.java构造函数中的init()最后调用nativeInit(mCallbacks),
public
InputManager(Context context,
WindowManagerService windowManagerService) {
this.mContext =
context;
this.mWindowManagerService = windowManagerService;
this.mCallbacks =
new Callbacks();
init();
//调用init()函数
}
private void init()
{
Slog.i(TAG, "Initializing input manager");
nativeInit(mCallbacks);
//java接口,由本地函数实现
}
2.
启动,InputManager.java的start()最后调用nativeStart():
public void start()
{
Slog.i(TAG, "Starting input manager");
nativeStart();
//java接口,由本地函数实现
}
四、com_android_server_InputManager.cpp实现InutManager.java的nativeInit(mCallbacks和nativeStart(),当然还实现了其他功能的接口函数,这里不再介绍,对于android如何实现java和c之间的转换,我想对于了解jni的来说不难理解。不懂的可以看此文章学习:http://hi.baidu.com/kellyvivian/blog/item/09cfb541179d2f3387947397.html
1.初始化,android_server_InputManager_nativeInit在被执行的时候会new一个NativeInputManager(callbacks)实例,NativeInputManager(callbacks)接着又会new一个InputManager(eventHub,
this, this)实例
static void
android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject callbacks) {
if (gNativeInputManager == NULL) {
gNativeInputManager
= new NativeInputManager(callbacks);
} else {
LOGE("Input manager already
initialized.");
jniThrowRuntimeException(env, "Input
manager already initialized.");
}
}
NativeInputManager::NativeInputManager(jobject
callbacksObj) :
mFilterTouchEvents(-1),
mFilterJumpyTouchEvents(-1),
mVirtualKeyQuietTime(-1),
mMaxEventsPerSecond(-1)
{
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
…….
sp eventHub = new
EventHub();
mInputManager = new
InputManager(eventHub, this,
this);
}
2.启动,android_server_InputManager_nativeStart中gNativeInputManager->getInputManager()->start()最终调用的是InputManager.cpp的start()函数
static void
android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
if (checkInputManagerUnitialized(env)) {
return;
}
status_t result
= gNativeInputManager->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input
manager could not be started.");
}
}
五、InputManager.cpp中主要有三个函数:initialize()初始化函数,在构造函数中调用;start()开启线程函数;stop()取消线程函数,在虚构函数中调用。
1.初始化,InputManager.cpp构造函数调用initialize(),期间new一个InputReaderThread线程
InputManager::InputManager(
const sp& eventHub,
const sp& readerPolicy,
const sp& dispatcherPolicy) {
mDispatcher = new
InputDispatcher(dispatcherPolicy);
mReader = new
InputReader(eventHub,
readerPolicy, mDispatcher);
initialize();
}
void
InputManager::initialize() {
mReaderThread = new
InputReaderThread(mReader);
mDispatcherThread = new
InputDispatcherThread(mDispatcher);
}
2.启动,mReaderThread->run("InputReader",
PRIORITY_URGENT_DISPLAY)开启初始化时new的InputReaderThread线程
status_t InputManager::start()
{
……..
result = mReaderThread->run("InputReader",
PRIORITY_URGENT_DISPLAY);
if (result) {
LOGE("Could not start
InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return
result;
}
return
OK;
}
六、InputReader.cpp中定义了InputReaderThread类,继承于Thread类
1.初始化,InputReaderThread构造函数,初始化一个Thread类
InputReaderThread::InputReaderThread(const
sp& reader) :
Thread( true),
mReader(reader)
{
}
2.启动,run启动线程,Thread
run()方法又调用InputReaderThread
的虚函数threadLoop(),接着调用InputReader的loopOnce()方法,最后调用EventHub.cpp的getEvent(& rawEvent)方法
bool
InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
void InputReader::loopOnce()
{
RawEvent rawEvent;
mEventHub->getEvent(&
rawEvent);
#if
DEBUG_RAW_EVENTS
LOGD("Input event: device=%d
type=0x%x scancode=%d keycode=%d value=%d",
rawEvent.deviceId, rawEvent.type, rawEvent.scanCode,
rawEvent.keyCode,
rawEvent.value);
#endif
process(&
rawEvent);
}
七、EventHub.cpp是android输入系统的硬件抽象层,维护输入设备的运行,包括Keyboard、
TouchScreen、TraceBall等。
EventHub.cpp中依次执行getEvent()–>openPlatformInput()–>scanDir(DEVICE_PATH)–> openDevice(devname)
bool
EventHub::openPlatformInput(void) {
int res, fd;
………
// Reserve fd index 0 for inotify.
struct pollfd pollfd;
pollfd.fd = fd;
pollfd.events =
POLLIN;
pollfd.revents =
0;
mFds.push(pollfd);
mDevices.push(NULL);
res = scanDir(DEVICE_PATH);
//DEVICE_PATH =
"/dev/input"
if(res < 0) {
LOGE("scan dir failed for
%s\n",
DEVICE_PATH);
}
return
true;
}
int
EventHub::scanDir(const char
*dirname)
{
……
openDevice(devname);
}
closedir(dir);
return 0;
}
openDevice方法会打开/dev/input目录下的所有设备文件,读取name、version、id等设备信息,然后执行loadConfiguration()方法,如果键盘设备就会执行loadKeyMap()这个方法
int
EventHub::openDevice(const char
*devicePath) {
……
// Load the configuration file for the device.
loadConfiguration(device);
……
if
((device->classes &
INPUT_DEVICE_CLASS_KEYBOARD) !=
0) {
// Load the keymap for the device.
status_t status
= loadKeyMap(device);
……
}
……
}
Honeycomb与之前版本不同之处是加入loadConfiguration()方法,它获取与当前设备驱动Vendor、Product、Version匹配的配置文件名,或者是Vendor、Product匹配的配置文件名,具体可查看Input.cpp中getInputDeviceConfigurationFilePathByDeviceIdentifie和getInputDeviceConfigurationFilePathByName方法。
如: kernel/
drivers/input/keyboard/atkbd.c键盘驱动中定义了
input_dev->id.vendor = 0×0001;
input_dev->id.product = 0×0001;
input_dev->id.version = 0xab41,那么与之对应的配置名为Vendor_0001_Product_0001_Version_ad41.idc,返回这个文件的全路径并赋值给device->configurationFile。如果/system/user/idc下存在此文件,接下来调用PropertyMap.cpp的load()方法解析该配置文件并将解析后的信息保存到device->configuration中。
void
EventHub::loadConfiguration(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.isEmpty())
{
LOGD("No input device
configuration file found for device ‘%s’.",
device->identifier.name.string());
} else {
status_t status
= PropertyMap::load(device->configurationFile,
&device->configuration);
if
(status) {
LOGE("Error loading input
device configuration file for device ‘%s’. "
"Using default
configuration.",
device->identifier.name.string());
}
}
}
EventHub.cpp中loadKeyMap又调用了Keyboard.cpp的KeyMap::load()方法
status_t
EventHub::loadKeyMap(Device*
device) {
return device->keyMap.load(device->identifier,
device->configuration);
}
八、在Keyboard.cpp的load方法中,首先判断deviceConfiguration参数是否为空,deviceConfiguration的赋值就是上面loadConfiguration()方法所做的工作。
如果有.idc的配置文件,那么获取key为keyboard.layout的value给keyLayoutName和key为keyboard.characterMap的value给keyCharacterMapName,最后调用loadKeyLayout和loadKeyCharacterMap方法加载此键盘布局文件;如果没有对应的.idc配置文件,则deviceConfiguration为空,就会接着执行probeKeyMap(deviceIdenfifier,
String8("Generic"))方法
status_t
KeyMap::load(const
InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if
(deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status
= loadKeyLayout(deviceIdenfifier, keyLayoutName);
if
(status == NAME_NOT_FOUND) {
LOGE("Configuration for
keyboard device ‘%s’ requested keyboard layout ‘%s’ but
"
"it was not found.",
deviceIdenfifier.name.string(),
keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if
(deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status
= loadKeyCharacterMap(deviceIdenfifier,
keyCharacterMapName);
if
(status == NAME_NOT_FOUND) {
LOGE("Configuration for
keyboard device ‘%s’ requested keyboard character "
"map ‘%s’ but it was not
found.",
deviceIdenfifier.name.string(),
keyLayoutName.string());
}
}
if
(isComplete()) {
return
OK;
}
}
……
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return
OK;
}
……
}
probeKeyMap方法判断名为Gerneric的布局文件是否存在,若存在就会调用loadKeyLayout和loadKeyCharacterMap方法加载此键盘布局文件
bool
KeyMap::probeKeyMap(const
InputDeviceIdentifier& deviceIdentifier,
const String8& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier,
keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
至此,Android
Honeycomb已经正确加载了键盘布局文件,那么我们如何定制和使用自己的键盘布局文件呢?