Andriod启动过程中是如何正确加载.kl和.kcm的键盘布局文件?本文就从Honeycomb代码入手,详细介绍开机启动时键盘布局文件的加载过程。 Honeycom相较与之前的版本,加入了一个.idc后缀的配置文件,使在不修改系统代码的前提下,我们就可以使用自定义的键盘布局文件,系统中与键盘布局相关的目录为/system/usr/keychars,/system/usr/keylayout,/system/usr/idc 一、系统启动过程中SystemServer添加WindowManagerService Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ((ActivityManagerService)ServiceManager.getService("activity")) .setWindowManager(wm); 二、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已经正确加载了键盘布局文件,那么我们如何定制和使用自己的键盘布局文件呢? 附件:qwerty.idc配置文件内容 # Copyright (C) 2010 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. # # Emulator keyboard configuration file #1. # touch.deviceType = touchScreen touch.orientationAware = 1 keyboard.layout = qwerty keyboard.characterMap = qwerty keyboard.orientationAware = 1 keyboard.builtIn = 1 cursor.mode = navigation cursor.orientationAware = 1
Android 加载键盘布局文件过程
原文作者:MMLoveMeMM
原文地址: http://www.cnblogs.com/MMLoveMeMM/articles/3853794.html
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
原文地址: http://www.cnblogs.com/MMLoveMeMM/articles/3853794.html
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。