在Android系統中,鍵盤按鍵事件是由WindowManagerService服務來管理的,然後再以消息的形式來分發給應用程序處理,不過和普通消息不一樣,它是由硬件中斷觸發的;在上一篇文章《Android應用程序消息處理機制(Looper、Handler)分析》中,我們分析了Android應用程序的消息處理機制,本文將結合這種消息處理機制來詳細分析Android應用程序是如何獲得鍵盤按鍵消息的。
在系統啓動的時候,SystemServer會啓動窗口管理服務WindowManagerService,WindowManagerService在啓動的時候就會通過系統輸入管理器InputManager來總負責監控鍵盤消息。這些鍵盤消息一般都是分發給當前激活的Activity窗口來處理的,因此,當前激活的Activity窗口在創建的時候,會到WindowManagerService中去註冊一個接收鍵盤消息的通道,表明它要處理鍵盤消息,而當InputManager監控到有鍵盤消息時,就會分給給它處理。噹噹前激活的Activity窗口不再處於激活狀態時,它也會到WindowManagerService中去反註冊之前的鍵盤消息接收通道,這樣,InputManager就不會再把鍵盤消息分發給它來處理。
由於本文的內容比較多,在接下面的章節中,我們將分爲五個部分來詳細描述Android應用程序獲得鍵盤按鍵消息的過程,每一個部分都是具體描述鍵盤消息處理過程中的一個過程。結合上面的鍵盤消息處理框架,這四個過程分別是InputManager的啓動過程、應用程序註冊鍵盤消息接收通道的過程、InputManager分發鍵盤消息給應用程序的過程以及應用程序註銷鍵盤消息接收通道的過程。爲了更好地理解Android應用程序獲得鍵盤按鍵消息的整個過程,建議讀者首先閱讀Android應用程序消息處理機制(Looper、Handler)分析一文,理解了Android應用程序的消息處理機制後,就能很好的把握本文的內容。
1. InputManager的啓動過程分析
前面說過,Android系統的鍵盤事件是由InputManager來監控的,而InputManager是由窗口管理服務WindowManagerService來啓動的。
從前面一篇文章Android系統進程Zygote啓動過程的源代碼分析中,我們知道在Android系統中,Zygote進程負責啓動系統服務進程SystemServer,而系統服務進程SystemServer負責啓動系統中的各種關鍵服務,例如我們在前面兩篇文章Android應用程序安裝過程源代碼分析和Android系統默認Home應用程序(Launcher)的啓動過程源代碼分析中提到的Package管理服務PackageManagerService和Activity管理服務ActivityManagerService。這裏我們所討論的窗口管理服務WindowManagerService也是由SystemServer來啓動的,具體的啓動過程這裏就不再詳述了,具體可以參考PackageManagerService和ActivityManagerService的啓動過程。
瞭解了WindowManagerService的啓動過程之後,我們就可以繼續分析InputManager的啓動過程了。我們先來看一下InputManager啓動過程的序列圖,然後根據這個序列圖來一步步分析它的啓動過程:
Step 1. WindowManagerService.main
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- public static WindowManagerService main(Context context,
- PowerManagerService pm, boolean haveInputMethods) {
- WMThread thr = new WMThread(context, pm, haveInputMethods);
- thr.start();
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- return thr.mService;
- }
- }
- ......
- }
Step 2. WMThread.run
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- static class WMThread extends Thread {
- ......
- public void run() {
- ......
- WindowManagerService s = new WindowManagerService(mContext, mPM,
- mHaveInputMethods);
- ......
- }
- }
- ......
- }
Step 3. WindowManagerService<init>
WindowManagerService類的構造函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- final InputManager mInputManager;
- ......
- private WindowManagerService(Context context, PowerManagerService pm,
- boolean haveInputMethods) {
- ......
- mInputManager = new InputManager(context, this);
- ......
- mInputManager.start();
- ......
- }
- ......
- }
Step 4. InputManager<init>@java
Java層的InputManager類的構造函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- public InputManager(Context context, WindowManagerService windowManagerService) {
- this.mContext = context;
- this.mWindowManagerService = windowManagerService;
- this.mCallbacks = new Callbacks();
- init();
- }
- ......
- }
Step 5. InputManager.init
這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- private void init() {
- Slog.i(TAG, "Initializing input manager");
- nativeInit(mCallbacks);
- }
- ......
- }
Step 6. InputManager.nativeInit
這個函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- 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.");
- }
- }
Step 7. NativeInputManager<init>
NativeInputManager類的構造函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- NativeInputManager::NativeInputManager(jobject callbacksObj) :
- mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
- mMaxEventsPerSecond(-1),
- mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0) {
- JNIEnv* env = jniEnv();
- mCallbacksObj = env->NewGlobalRef(callbacksObj);
- sp<EventHub> eventHub = new EventHub();
- mInputManager = new InputManager(eventHub, this, this);
- }
Step 8. InputManager<init>@C++
C++層的InputManager類的構造函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:
- InputManager::InputManager(
- const sp<EventHubInterface>& eventHub,
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
- mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- initialize();
- }
Step 9. InputManager.initialize
這個函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:
- void InputManager::initialize() {
- mReaderThread = new InputReaderThread(mReader);
- mDispatcherThread = new InputDispatcherThread(mDispatcher);
- }
至此,InputManager的初始化工作就完成了,在回到Step 3中繼續分析InputManager的進一步啓動過程之前,我們先來作一個小結,看看這個初始化過程都做什麼事情:
A. 在Java層中的WindowManagerService中創建了一個InputManager對象,由它來負責管理Android應用程序框架層的鍵盤消息處理;
B. 在C++層也相應地創建一個InputManager本地對象來負責監控鍵盤事件;
C. 在C++層中的InputManager對象中,分別創建了一個InputReader對象和一個InputDispatcher對象,前者負責讀取系統中的鍵盤消息,後者負責把鍵盤消息分發出去;
D. InputReader對象和一個InputDispatcher對象分別是通過InputReaderThread線程實例和InputDispatcherThread線程實例來實鍵盤消息的讀取和分發的。
有了這些對象之後,萬事就俱備了,回到Step 3中,調用InputManager類的start函數來執行真正的啓動操作。
Step 10. InputManager.start
這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- public void start() {
- Slog.i(TAG, "Starting input manager");
- nativeStart();
- }
- ......
- }
Step 11. InputManager.nativeStart
這個函數定義在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- 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.");
- }
- }
Step 12. InputManager.start
這個函數定義在frameworks/base/libs/ui/InputManager.cpp 文件中:
- status_t InputManager::start() {
- status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
- if (result) {
- LOGE("Could not start InputDispatcher thread due to error %d.", result);
- return result;
- }
- 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;
- }
我們先來分析InputDispatcherThread線程分發消息的過程,然後再回過頭來分析InputReaderThread線程讀取消息的過程。
Step 13. InputDispatcherThread.threadLoop
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
- }
Step 14. InputDispatcher.dispatchOnce
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::dispatchOnce() {
- nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
- nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- if (runCommandsLockedInterruptible()) {
- nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
- } // release lock
- // Wait for callback or timeout or wake. (make sure we round up, not down)
- nsecs_t currentTime = now();
- int32_t timeoutMillis;
- if (nextWakeupTime > currentTime) {
- uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
- timeout = (timeout + 999999LL) / 1000000LL;
- timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
- } else {
- timeoutMillis = 0;
- }
- mLooper->pollOnce(timeoutMillis);
- }
Step 15. Looper.pollOnce
這個函數定義在frameworks/base/libs/utils/Looper.cpp文件中,具體可以參考前面Android應用程序消息處理機制(Looper、Handler)分析一文,這裏就不再詳述了。總的來說,就是在Looper類中,會創建一個管道,當調用Looper類的pollOnce函數時,如果管道中沒有內容可讀,那麼當前線程就會進入到空閒等待狀態;當有鍵盤事件發生時,InputReader就會往這個管道中寫入新的內容,這樣就會喚醒前面正在等待鍵盤事件發生的線程。
InputDispatcher類分發消息的過程就暫時分析到這裏,後面會有更進一步的分析,現在,我們回到Step 12中,接着分析InputReader類讀取鍵盤事件的過程。在調用了InputReaderThread線程類的run就函數後,同樣會進入到InputReaderThread線程類的threadLoop函數中去。
Step 16. InputReaderThread.threadLoop
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
- }
Step 17. InputReader.loopOnce
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void InputReader::loopOnce() {
- RawEvent rawEvent;
- mEventHub->getEvent(& rawEvent);
- #if DEBUG_RAW_EVENTS
- LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
- rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
- rawEvent.value);
- #endif
- process(& rawEvent);
- }
Step 18. EventHub.getEvent
這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:
- bool EventHub::getEvent(RawEvent* outEvent)
- {
- outEvent->deviceId = 0;
- outEvent->type = 0;
- outEvent->scanCode = 0;
- outEvent->keyCode = 0;
- outEvent->flags = 0;
- outEvent->value = 0;
- outEvent->when = 0;
- // Note that we only allow one caller to getEvent(), so don't need
- // to do locking here... only when adding/removing devices.
- if (!mOpened) {
- mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
- mOpened = true;
- mNeedToSendFinishedDeviceScan = true;
- }
- for (;;) {
- // Report any devices that had last been added/removed.
- if (mClosingDevices != NULL) {
- device_t* device = mClosingDevices;
- LOGV("Reporting device closed: id=0x%x, name=%s\n",
- device->id, device->path.string());
- mClosingDevices = device->next;
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_REMOVED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- delete device;
- mNeedToSendFinishedDeviceScan = true;
- return true;
- }
- if (mOpeningDevices != NULL) {
- device_t* device = mOpeningDevices;
- LOGV("Reporting device opened: id=0x%x, name=%s\n",
- device->id, device->path.string());
- mOpeningDevices = device->next;
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_ADDED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- mNeedToSendFinishedDeviceScan = true;
- return true;
- }
- if (mNeedToSendFinishedDeviceScan) {
- mNeedToSendFinishedDeviceScan = false;
- outEvent->type = FINISHED_DEVICE_SCAN;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- }
- // Grab the next input event.
- for (;;) {
- // Consume buffered input events, if any.
- if (mInputBufferIndex < mInputBufferCount) {
- const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
- const device_t* device = mDevices[mInputDeviceIndex];
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = iev.type;
- outEvent->scanCode = iev.code;
- if (iev.type == EV_KEY) {
- status_t err = device->layoutMap->map(iev.code,
- & outEvent->keyCode, & outEvent->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, outEvent->keyCode, outEvent->flags, err);
- if (err != 0) {
- outEvent->keyCode = AKEYCODE_UNKNOWN;
- outEvent->flags = 0;
- }
- } else {
- outEvent->keyCode = iev.code;
- }
- outEvent->value = iev.value;
- // Use an event timestamp in the same timebase as
- // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
- // as expected by the rest of the system.
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- }
- // Finish reading all events from devices identified in previous poll().
- // This code assumes that mInputDeviceIndex is initially 0 and that the
- // revents member of pollfd is initialized to 0 when the device is first added.
- // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
- mInputDeviceIndex += 1;
- if (mInputDeviceIndex >= mFDCount) {
- break;
- }
- const struct pollfd& pfd = mFDs[mInputDeviceIndex];
- if (pfd.revents & POLLIN) {
- int32_t readSize = read(pfd.fd, mInputBufferData,
- sizeof(struct input_event) * INPUT_BUFFER_SIZE);
- if (readSize < 0) {
- if (errno != EAGAIN && errno != EINTR) {
- LOGW("could not get event (errno=%d)", errno);
- }
- } else if ((readSize % sizeof(struct input_event)) != 0) {
- LOGE("could not get event (wrong size: %d)", readSize);
- } else {
- mInputBufferCount = readSize / sizeof(struct input_event);
- mInputBufferIndex = 0;
- }
- }
- }
- ......
- mInputDeviceIndex = 0;
- // Poll for events. Mind the wake lock dance!
- // We hold a wake lock at all times except during poll(). This works due to some
- // subtle choreography. When a device driver has pending (unread) events, it acquires
- // a kernel wake lock. However, once the last pending event has been read, the device
- // driver will release the kernel wake lock. To prevent the system from going to sleep
- // when this happens, the EventHub holds onto its own user wake lock while the client
- // is processing events. Thus the system can only sleep if there are no events
- // pending or currently being processed.
- release_wake_lock(WAKE_LOCK_ID);
- int pollResult = poll(mFDs, mFDCount, -1);
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- if (pollResult <= 0) {
- if (errno != EINTR) {
- LOGW("poll failed (errno=%d)\n", errno);
- usleep(100000);
- }
- }
- }
- }
首先,如果是第一次進入到這個函數中時,成員變量mOpened的值爲false,於是就會調用openPlatformInput函數來打開系統輸入設備,在本文中,我們主要討論的輸入設備就是鍵盤了。打開了這些輸入設備文件後,就可以對這些輸入設備進行是監控了。如果不是第一次進入到這個函數,那麼就會分析當前有沒有輸入事件發生,如果有,就返回這個事件,否則就會進入等待狀態,等待下一次輸入事件的發生。在我們這個場景中,就是等待下一次鍵盤事件的發生了。
我們先分析openPlatformInput函數的實現,然後回過頭來分析這個getEvent函數的具體的實現。
Step 19. EventHub.openPlatformInput
這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:
- bool EventHub::openPlatformInput(void)
- {
- ......
- res = scanDir(device_path);
- if(res < 0) {
- LOGE("scan dir failed for %s\n", device_path);
- }
- return true;
- }
- static const char *device_path = "/dev/input";
Step 20. EventHub.scanDir
這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:
- int EventHub::scanDir(const char *dirname)
- {
- char devname[PATH_MAX];
- char *filename;
- DIR *dir;
- struct dirent *de;
- dir = opendir(dirname);
- if(dir == NULL)
- return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while((de = readdir(dir))) {
- if(de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' ||
- (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDevice(devname);
- }
- closedir(dir);
- return 0;
- }
Step 21. EventHub.openDevice
這個函數定義在frameworks/base/libs/ui/EventHub.cpp文件中:
- int EventHub::openDevice(const char *deviceName) {
- int version;
- int fd;
- struct pollfd *new_mFDs;
- device_t **new_devices;
- char **new_device_names;
- char name[80];
- char location[80];
- char idstr[80];
- struct input_id id;
- LOGV("Opening device: %s", deviceName);
- AutoMutex _l(mLock);
- fd = open(deviceName, O_RDWR);
- if(fd < 0) {
- LOGE("could not open %s, %s\n", deviceName, strerror(errno));
- return -1;
- }
- ......
- int devid = 0;
- while (devid < mNumDevicesById) {
- if (mDevicesById[devid].device == NULL) {
- break;
- }
- devid++;
- }
- ......
- mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if (mDevicesById[devid].seq == 0) {
- mDevicesById[devid].seq = 1<<SEQ_SHIFT;
- }
- new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
- new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
- if (new_mFDs == NULL || new_devices == NULL) {
- LOGE("out of memory");
- return -1;
- }
- mFDs = new_mFDs;
- mDevices = new_devices;
- ......
- device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
- if (device == NULL) {
- LOGE("out of memory");
- return -1;
- }
- device->fd = fd;
- mFDs[mFDCount].fd = fd;
- mFDs[mFDCount].events = POLLIN;
- mFDs[mFDCount].revents = 0;
- // Figure out the kinds of events the device reports.
- uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
- memset(key_bitmask, 0, sizeof(key_bitmask));
- LOGV("Getting keys...");
- if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
- // See if this is a keyboard. Ignore everything in the button range except for
- // gamepads which are also considered keyboards.
- if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
- || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
- sizeof_bit_array(BTN_DIGI))
- || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX + 1))) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
- device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
- if (device->keyBitmask != NULL) {
- memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
- } else {
- delete device;
- LOGE("out of memory allocating key bitmask");
- return -1;
- }
- }
- }
- ......
- if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
- char tmpfn[sizeof(name)];
- char keylayoutFilename[300];
- // a more descriptive name
- device->name = name;
- // replace all the spaces with underscores
- strcpy(tmpfn, name);
- for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
- *p = '_';
- // find the .kl file we need for this device
- const char* root = getenv("ANDROID_ROOT");
- snprintf(keylayoutFilename, sizeof(keylayoutFilename),
- "%s/usr/keylayout/%s.kl", root, tmpfn);
- bool defaultKeymap = false;
- if (access(keylayoutFilename, R_OK)) {
- snprintf(keylayoutFilename, sizeof(keylayoutFilename),
- "%s/usr/keylayout/%s", root, "qwerty.kl");
- defaultKeymap = true;
- }
- status_t status = device->layoutMap->load(keylayoutFilename);
- if (status) {
- LOGE("Error %d loading key layout.", status);
- }
- // tell the world about the devname (the descriptive name)
- if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
- // the built-in keyboard has a well-known device ID of 0,
- // this device better not go away.
- mHaveFirstKeyboard = true;
- mFirstKeyboardId = device->id;
- property_set("hw.keyboards.0.devname", name);
- } else {
- // ensure mFirstKeyboardId is set to -something-.
- if (mFirstKeyboardId == 0) {
- mFirstKeyboardId = device->id;
- }
- }
- char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", device->id);
- property_set(propName, name);
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
- }
- // See if this device has a DPAD.
- if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
- device->classes |= INPUT_DEVICE_CLASS_DPAD;
- }
- // See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
- device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
- break;
- }
- }
- LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
- device->id, name, propName, keylayoutFilename);
- }
- ......
- mDevicesById[devid].device = device;
- device->next = mOpeningDevices;
- mOpeningDevices = device;
- mDevices[mFDCount] = device;
- mFDCount++;
- return 0;
- }
- fd = open(deviceName, O_RDWR);
- mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if (mDevicesById[devid].seq == 0) {
- mDevicesById[devid].seq = 1<<SEQ_SHIFT;
- }
- mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if (mDevicesById[devid].seq == 0) {
- mDevicesById[devid].seq = 1<<SEQ_SHIFT;
- }
- new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
- new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
- if (new_mFDs == NULL || new_devices == NULL) {
- LOGE("out of memory");
- return -1;
- }
- mFDs = new_mFDs;
- mDevices = new_devices;
- ......
- device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
- if (device == NULL) {
- LOGE("out of memory");
- return -1;
- }
- device->fd = fd;
同時,這個設備文件還會保存在數組mFDs中:
- mFDs[mFDCount].fd = fd;
- mFDs[mFDCount].events = POLLIN;
- mFDs[mFDCount].revents = 0;
- // Figure out the kinds of events the device reports.
- uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
- memset(key_bitmask, 0, sizeof(key_bitmask));
- LOGV("Getting keys...");
- if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
- // See if this is a keyboard. Ignore everything in the button range except for
- // gamepads which are also considered keyboards.
- if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
- || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
- sizeof_bit_array(BTN_DIGI))
- || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX + 1))) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
- device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
- if (device->keyBitmask != NULL) {
- memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
- } else {
- delete device;
- LOGE("out of memory allocating key bitmask");
- return -1;
- }
- }
- }
如果是鍵盤設備,初始化工作還未完成,還要繼續設置鍵盤的佈局等信息:
- if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
- char tmpfn[sizeof(name)];
- char keylayoutFilename[300];
- // a more descriptive name
- device->name = name;
- // replace all the spaces with underscores
- strcpy(tmpfn, name);
- for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
- *p = '_';
- // find the .kl file we need for this device
- const char* root = getenv("ANDROID_ROOT");
- snprintf(keylayoutFilename, sizeof(keylayoutFilename),
- "%s/usr/keylayout/%s.kl", root, tmpfn);
- bool defaultKeymap = false;
- if (access(keylayoutFilename, R_OK)) {
- snprintf(keylayoutFilename, sizeof(keylayoutFilename),
- "%s/usr/keylayout/%s", root, "qwerty.kl");
- defaultKeymap = true;
- }
- status_t status = device->layoutMap->load(keylayoutFilename);
- if (status) {
- LOGE("Error %d loading key layout.", status);
- }
- // tell the world about the devname (the descriptive name)
- if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
- // the built-in keyboard has a well-known device ID of 0,
- // this device better not go away.
- mHaveFirstKeyboard = true;
- mFirstKeyboardId = device->id;
- property_set("hw.keyboards.0.devname", name);
- } else {
- // ensure mFirstKeyboardId is set to -something-.
- if (mFirstKeyboardId == 0) {
- mFirstKeyboardId = device->id;
- }
- }
- char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", device->id);
- property_set(propName, name);
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycodeLocked(device, AKEYCODE_Q)) {
- device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
- }
- // See if this device has a DPAD.
- if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
- device->classes |= INPUT_DEVICE_CLASS_DPAD;
- }
- // See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
- device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
- break;
- }
- }
- LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
- device->id, name, propName, keylayoutFilename);
- }
回到Step 18中,我們繼續分析EventHub.getEvent函數的實現。
在中間的for循環裏面,首先會檢查當前是否有輸入設備被關閉,如果有,就返回一個設備移除的事件給調用方:
- // Report any devices that had last been added/removed.
- if (mClosingDevices != NULL) {
- device_t* device = mClosingDevices;
- LOGV("Reporting device closed: id=0x%x, name=%s\n",
- device->id, device->path.string());
- mClosingDevices = device->next;
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_REMOVED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- delete device;
- mNeedToSendFinishedDeviceScan = true;
- return true;
- }
- if (mOpeningDevices != NULL) {
- device_t* device = mOpeningDevices;
- LOGV("Reporting device opened: id=0x%x, name=%s\n",
- device->id, device->path.string());
- mOpeningDevices = device->next;
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_ADDED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- mNeedToSendFinishedDeviceScan = true;
- return true;
- }
- if (mNeedToSendFinishedDeviceScan) {
- mNeedToSendFinishedDeviceScan = false;
- outEvent->type = FINISHED_DEVICE_SCAN;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- }
- // Grab the next input event.
- for (;;) {
- // Consume buffered input events, if any.
- if (mInputBufferIndex < mInputBufferCount) {
- const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
- const device_t* device = mDevices[mInputDeviceIndex];
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = iev.type;
- outEvent->scanCode = iev.code;
- if (iev.type == EV_KEY) {
- status_t err = device->layoutMap->map(iev.code,
- & outEvent->keyCode, & outEvent->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, outEvent->keyCode, outEvent->flags, err);
- if (err != 0) {
- outEvent->keyCode = AKEYCODE_UNKNOWN;
- outEvent->flags = 0;
- }
- } else {
- outEvent->keyCode = iev.code;
- }
- outEvent->value = iev.value;
- // Use an event timestamp in the same timebase as
- // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
- // as expected by the rest of the system.
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- }
- // Finish reading all events from devices identified in previous poll().
- // This code assumes that mInputDeviceIndex is initially 0 and that the
- // revents member of pollfd is initialized to 0 when the device is first added.
- // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
- mInputDeviceIndex += 1;
- if (mInputDeviceIndex >= mFDCount) {
- break;
- }
- const struct pollfd& pfd = mFDs[mInputDeviceIndex];
- if (pfd.revents & POLLIN) {
- int32_t readSize = read(pfd.fd, mInputBufferData,
- sizeof(struct input_event) * INPUT_BUFFER_SIZE);
- if (readSize < 0) {
- if (errno != EAGAIN && errno != EINTR) {
- LOGW("could not get event (errno=%d)", errno);
- }
- } else if ((readSize % sizeof(struct input_event)) != 0) {
- LOGE("could not get event (wrong size: %d)", readSize);
- } else {
- mInputBufferCount = readSize / sizeof(struct input_event);
- mInputBufferIndex = 0;
- }
- }
- }
- int pollResult = poll(mFDs, mFDCount, -1);
這裏的mFDs包含了我們所要監控的輸入設備的打開文件描述符,這是在前面的openPlatformInput函數中初始化的。
Step 22. poll
這是一個Linux系統的文件操作系統調用,它用來查詢指定的文件列表是否有有可讀寫的,如果有,就馬上返回,否則的話,就阻塞線程,並等待驅動程序喚醒,重新調用poll函數,或超時返回。在我們的這個場景中,就是要查詢是否有鍵盤事件發生,如果有的話,就返回,否則的話,當前線程就睡眠等待鍵盤事件的發生了。
這樣,InputManager的啓動過程就分析完了,下面我們再分析應用程序註冊鍵盤消息接收通道的過程。
2. 應用程序註冊鍵盤消息接收通道的過程分析
InputManager啓動以後,就開始負責監控鍵盤輸入事件了。當InputManager監控到鍵盤輸入事件時,它應該把這個鍵盤事件分發給誰呢?當然是要把這個鍵盤消息分發給當前激活的Activity窗口了,不過,當前激活的Activity窗口還需要主動註冊一個鍵盤消息接收通道到InputManager中去,InputManager才能把這個鍵盤消息分發給它處理。那麼,當前被激活的Activity窗口又是什麼時候去註冊這個鍵盤消息接收通道的呢?在前面一篇文章Android應用程序啓動過程源代碼分析中,我們分析Android應用程序的啓動過程時,在Step 33中分析到ActivityThread類的handleLaunchActivity函數中,我們曾經說過,當函數handleLaunchActivity調用performLaunchActivity函數來加載這個完畢應用程序的默認Activity後,再次回到handleLaunchActivity函數時,會調用handleResumeActivity函數來使這個Activity進入Resumed狀態。在調用handleResumeActivity函數的過程中,ActivityThread會通過android.view.WindowManagerImpl類爲該Activity創建一個ViewRoot實例,並且會通過調用ViewRoot類的setView成員函數把與該Activity關聯的View設置到這個ViewRoot中去,而Activity正是通過ViewRoot類的setView成員函數來註冊鍵盤消息接收通道的。
有了這些背影知識後,接下來,我們就可以從ViewRoot.setView函數開始分析應用程序註冊鍵盤消息接收通道的過程了。首先看一下這個註冊過程的序列圖,然後再詳細分析每一個步驟:
Step 1. ViewRoot.setView
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- public void setView(View view, WindowManager.LayoutParams attrs,
- View panelParentView) {
- ......
- synchronized (this) {
- if (mView == null) {
- ......
- // Schedule the first layout -before- adding to the window
- // manager, to make sure we do the relayout before receiving
- // any other events from the system.
- requestLayout();
- mInputChannel = new InputChannel();
- try {
- res = sWindowSession.add(mWindow, mWindowAttributes,
- getHostVisibility(), mAttachInfo.mContentInsets,
- mInputChannel);
- } catch (RemoteException e) {
- ......
- } finally {
- ......
- }
- ......
- if (view instanceof RootViewSurfaceTaker) {
- mInputQueueCallback =
- ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
- }
- if (mInputQueueCallback != null) {
- mInputQueue = new InputQueue(mInputChannel);
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- } else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
- }
- ......
- }
- }
- }
- }
這個函數中與註冊鍵盤消息接收通道(InputChannel)相關的邏輯主要有三處,一是調用requestLayout函數來通知InputManager,這個Activity窗口是當前被激活的窗口,二是調用sWindowSession(WindowManagerService內部類Session的遠程接口)的add成員函數來把鍵盤消息接收通道的一端註冊在InputManager中,三是調用InputQueue的registerInputChannel成員函數來把鍵盤消息接收通道的另一端註冊在本應用程序的消息循環(Looper)中。這樣,當InputManager監控到有鍵盤消息時,就會先找到當前被激活的窗口,然後找到其在InputManager中對應的鍵盤消息接收通道,通過這個通道在InputManager中的一端來通知在應用程序消息循環中的另一端,就把鍵盤消息分發給當前激活的Activity窗口了。
在接下來的內容中,我們首先描述requestLayout函數是如何告訴InputManager當前的Activity窗口便是激活窗口的,接着再回過頭來分析應用程序是如何把鍵盤消息接收通道的一端註冊到InputManager中去的,最後分析應用程序是如何鍵盤消息接收通道的另一端註冊到本應用程序的消息循環中去了。
Step 2. ViewRoot.requestLayout
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- public void requestLayout() {
- ......
- mLayoutRequested = true;
- scheduleTraversals();
- }
- ......
- }
Step 3. WindowManagerService.Session.relayout
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- private final class Session extends IWindowSession.Stub
- implements IBinder.DeathRecipient {
- ......
- public int relayout(IWindow window, WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewFlags,
- boolean insetsPending, Rect outFrame, Rect outContentInsets,
- Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
- //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
- int res = relayoutWindow(this, window, attrs,
- requestedWidth, requestedHeight, viewFlags, insetsPending,
- outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
- //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
- return res;
- }
- ......
- }
- ......
- }
這個函數只是簡單地調用WindowManagerService的成員函數relayoutWIndow來進一步處理。
Step 4. WindowManagerService.relayoutWIndow
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- public int relayoutWindow(Session session, IWindow client,
- WindowManager.LayoutParams attrs, int requestedWidth,
- int requestedHeight, int viewVisibility, boolean insetsPending,
- Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
- Configuration outConfig, Surface outSurface) {
- ......
- synchronized(mWindowMap) {
- ......
- mInputMonitor.updateInputWindowsLw();
- }
- ......
- }
- ......
- }
Step 5. InputMonitor.updateInputWindowsLw
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- final class InputMonitor {
- ......
- /* Updates the cached window information provided to the input dispatcher. */
- public void updateInputWindowsLw() {
- // Populate the input window list with information about all of the windows that
- // could potentially receive input.
- // As an optimization, we could try to prune the list of windows but this turns
- // out to be difficult because only the native code knows for sure which window
- // currently has touch focus.
- final ArrayList<WindowState> windows = mWindows;
- final int N = windows.size();
- for (int i = N - 1; i >= 0; i--) {
- final WindowState child = windows.get(i);
- if (child.mInputChannel == null || child.mRemoved) {
- // Skip this window because it cannot possibly receive input.
- continue;
- }
- ......
- // Add a window to our list of input windows.
- final InputWindow inputWindow = mTempInputWindows.add();
- ......
- }
- // Send windows to native code.
- mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
- ......
- }
- ......
- }
- ......
- }
Step 6. InputManager.setInputWindows
這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- public void setInputWindows(InputWindow[] windows) {
- nativeSetInputWindows(windows);
- }
- ......
- }
Step 7. InputManager.nativeSetInputWindows
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
- jobjectArray windowObjArray) {
- if (checkInputManagerUnitialized(env)) {
- return;
- }
- gNativeInputManager->setInputWindows(env, windowObjArray);
- }
Step 8. NativeInputManager.setInputWindows
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
- Vector<InputWindow> windows;
- jsize length = env->GetArrayLength(windowObjArray);
- for (jsize i = 0; i < length; i++) {
- jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
- if (! inputTargetObj) {
- break; // found null element indicating end of used portion of the array
- }
- windows.push();
- InputWindow& window = windows.editTop();
- bool valid = populateWindow(env, inputTargetObj, window);
- if (! valid) {
- windows.pop();
- }
- env->DeleteLocalRef(inputTargetObj);
- }
- mInputManager->getDispatcher()->setInputWindows(windows);
- }
Step 9. InputDispatcher.setInputWindows
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
- ......
- { // acquire lock
- AutoMutex _l(mLock);
- // Clear old window pointers.
- sp<InputChannel> oldFocusedWindowChannel;
- if (mFocusedWindow) {
- oldFocusedWindowChannel = mFocusedWindow->inputChannel;
- mFocusedWindow = NULL;
- }
- mWindows.clear();
- // Loop over new windows and rebuild the necessary window pointers for
- // tracking focus and touch.
- mWindows.appendVector(inputWindows);
- size_t numWindows = mWindows.size();
- for (size_t i = 0; i < numWindows; i++) {
- const InputWindow* window = & mWindows.itemAt(i);
- if (window->hasFocus) {
- mFocusedWindow = window;
- break;
- }
- }
- ......
- } // release lock
- ......
- }
這樣,InputManager就把當前激活的Activity窗口保存在InputDispatcher中了,後面就可以把鍵盤消息分發給它來處理。
回到Step 1中的ViewRoot.setView函數中,接下來就調用下面語句來註冊鍵盤消息接收通道的一端到InputManager中去:
- mInputChannel = new InputChannel();
- try {
- res = sWindowSession.add(mWindow, mWindowAttributes,
- getHostVisibility(), mAttachInfo.mContentInsets,
- mInputChannel);
- } catch (RemoteException e) {
- ......
- } finally {
- ......
- }
Step 10. WindowManagerService.Session.add
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- private final class Session extends IWindowSession.Stub
- implements IBinder.DeathRecipient {
- ......
- public int add(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
- return addWindow(this, window, attrs, viewVisibility, outContentInsets,
- outInputChannel);
- }
- ......
- }
- ......
- }
Step 11. WindowManagerService.addWindow
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java 文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- public int addWindow(Session session, IWindow client,
- WindowManager.LayoutParams attrs, int viewVisibility,
- Rect outContentInsets, InputChannel outInputChannel) {
- ......
- WindowState win = null;
- synchronized(mWindowMap) {
- ......
- win = new WindowState(session, client, token,
- attachedWindow, attrs, viewVisibility);
- ......
- if (outInputChannel != null) {
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.mInputChannel = inputChannels[0];
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel);
- }
- ......
- }
- ......
- }
- ......
- }
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
創建輸入通道之前,WindowManagerService會爲當前Activity窗口創建一個WindowState對象win,用來記錄這個Activity窗口的狀態信息。當創建這對輸入管道成功以後,也會把其中的一個管道保存在這個WindowState對象win的成員變量mInputChannel中,後面要註銷這個管道的時候,就是從這個WindownState對象中取回這個管道的:
- win.mInputChannel = inputChannels[0];
接下來我們就看一下InputChannel.openInputChannelPair函數的實現。
Step 12. InputChannel.openInputChannelPair
這個函數定義在frameworks/base/core/java/android/view/InputChannel.java文件中:
- public final class InputChannel implements Parcelable {
- ......
- /**
- * Creates a new input channel pair. One channel should be provided to the input
- * dispatcher and the other to the application's input queue.
- * @param name The descriptive (non-unique) name of the channel pair.
- * @return A pair of input channels. They are symmetric and indistinguishable.
- */
- public static InputChannel[] openInputChannelPair(String name) {
- ......
- return nativeOpenInputChannelPair(name);
- }
- ......
- }
Step 13. InputChannel.nativeOpenInputChannelPair
這個函數定義在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:
- static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
- jclass clazz, jstring nameObj) {
- const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
- String8 name(nameChars);
- env->ReleaseStringUTFChars(nameObj, nameChars);
- sp<InputChannel> serverChannel;
- sp<InputChannel> clientChannel;
- status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- if (result) {
- LOGE("Could not open input channel pair. status=%d", result);
- jniThrowRuntimeException(env, "Could not open input channel pair.");
- return NULL;
- }
- // TODO more robust error checking
- jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(serverChannel));
- jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(clientChannel));
- jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
- env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
- env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
- return channelPair;
- }
Step 14. InputChannel.openInputChannelPair
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputChannel::openInputChannelPair(const String8& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
- status_t result;
- int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
- if (serverAshmemFd < 0) {
- ......
- } else {
- result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
- if (result < 0) {
- ......
- } else {
- // Dup the file descriptor because the server and client input channel objects that
- // are returned may have different lifetimes but they share the same shared memory region.
- int clientAshmemFd;
- clientAshmemFd = dup(serverAshmemFd);
- if (clientAshmemFd < 0) {
- ......
- } else {
- int forward[2];
- if (pipe(forward)) {
- ......
- } else {
- int reverse[2];
- if (pipe(reverse)) {
- ......
- } else {
- String8 serverChannelName = name;
- serverChannelName.append(" (server)");
- outServerChannel = new InputChannel(serverChannelName,
- serverAshmemFd, reverse[0], forward[1]);
- String8 clientChannelName = name;
- clientChannelName.append(" (client)");
- outClientChannel = new InputChannel(clientChannelName,
- clientAshmemFd, forward[0], reverse[1]);
- return OK;
- }
- ......
- }
- ......
- }
- }
- }
- ......
- }
- InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
- int32_t sendPipeFd) :
- mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
- ......
- }
具體來說,Server端和Client端的InputChannel分別是這樣構成的:
Server Input Channel: ashmem - reverse(read) - forward(write)
Client Input Channel: ashmem - forward(read) - reverse(write)
前面我們在Android應用程序消息處理機制(Looper、Handler)分析一文中學習Android應用程序的消息處理機制時知道,管道可以用作進程間通信,其中一個進程在管道的讀端等待新的內空可讀,另一個進程在管道的寫端寫入新的內容以喚醒在管道讀端等待的進程,這樣就實現了進程間通信。在我們這個情景中,Client端可以在前向管道(forward pipe)的讀端睡眠等待新的內容可讀,而Server端可以通過向前向管道(forward
pipe)的寫端寫入新的內容來喚醒Client端,同樣,把前向管道(forward pipe)換成反向管道(reverse pipe),也能實現Client端喚醒Server端。在後面我們分析InputDispatcher分發鍵盤消息時,會看到它們的用法。
有了這些背景知識後,相信上面的openInputChannelPair的代碼就容易理解了,這裏就不再詳述了。
創建好了這兩個輸入通道後,回到Step 11中的WindowManagerService.addWindow函數中,一方面它把剛纔創建的Client端的輸入通道通過outInputChannel參數返回到應用程序中:
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
另一方面,它還要把剛纔創建的Server端的輸入通道註冊到InputManager中:
- mInputManager.registerInputChannel(win.mInputChannel);
這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- /**
- * Registers an input channel so that it can be used as an input event target.
- * @param inputChannel The input channel to register.
- */
- public void registerInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null.");
- }
- nativeRegisterInputChannel(inputChannel, false);
- }
- ......
- }
Step 16. InputManager.nativeRegisterInputChannel
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj, jboolean monitor) {
- ......
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- status_t status = gNativeInputManager->registerInputChannel(
- env, inputChannel, inputChannelObj, monitor);
- ......
- }
Step 17. NativeInputManager.registerInputChannel
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
- status_t NativeInputManager::registerInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
- ......
- status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);
- ......
- }
Step 18. InputDispatcher.registerInputChannel
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
- ......
- { // acquire lock
- AutoMutex _l(mLock);
- if (getConnectionIndexLocked(inputChannel) >= 0) {
- LOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
- sp<Connection> connection = new Connection(inputChannel);
- status_t status = connection->initialize();
- if (status) {
- LOGE("Failed to initialize input publisher for input channel '%s', status=%d",
- inputChannel->getName().string(), status);
- return status;
- }
- int32_t receiveFd = inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd, connection);
- if (monitor) {
- mMonitoringChannels.push(inputChannel);
- }
- mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- runCommandsLockedInterruptible();
- } // release lock
- return OK;
- }
道的讀端描述符。回憶一下Step 14中的InputChannel.openInputChannelPair函數,我們創建了一個Server端的InputChannel,就是對應這裏的inputChannel了,這個inputChannel的Receive Pipe Fd就是我們前面說的反向管道的讀端描述符了。有了這個Receive Pipe Fd後,就以它作爲Key值來把前面創建的Connection對象保存在InputDispatcher中,這樣就基本完成鍵盤消息接收通道的註冊了。但是,註冊的工作還未完成,最後,還要把這個Receive Pipe Fd添加到InputDispatcher的成員變量mLooper中去,這裏的成員變量mLooper的類型爲Looper,我們在前面介紹InputManager的啓動過程的Step 15中已經見過了,這裏就不再詳述了,不過這裏仍然值得介紹一下它的addFd函數。
在前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析中,我們在介紹到Android應用程序的消息循環一節時,曾經說過,在Looper類內部,會創建一個管道,然後Looper會睡眠在這個管道的讀端,等待另外一個線程來往這個管道的寫端寫入新的內容,從而喚醒等待在這個管道讀端的線程,除此之外,Looper還可以同時睡眠等待在其它的文件描述符上,因爲它是通過Linux系統的epoll機制來批量等待指定的文件有新的內容可讀的。這些其它的文件描述符就是通過Looper類的addFd成函數添加進去的了,在添加的時候,還可以指定回調函數,即當這個文件描述符所指向的文件有新的內容可讀時,Looper就會調用這個hanldeReceiveCallback函數,有興趣的讀者可以自己研究一下Looper類的addFd函數的實現,它位於frameworks/base/libs/utils/Looper.cpp文件中。
分析到這裏,Server端的InputChannel就註冊完成了。回憶一下前面介紹InputManager啓動過程的Step 14,這時InputDispatcherThread同時睡眠在InputDispatcher的成員變量mLooper內部的管道的讀端以及這裏的Server端InputChannel裏面的反向管道的讀端上,mLooper內部的管道的讀端等待鍵盤事件的發生而被喚醒,而Server端InputChannel裏面的反向管道的讀端等待Client端InputChannel裏面的反向管道的寫端被寫入新的內容而被喚醒。
Server端的InputChannel註冊完成後,回到Step 11中的WindowManagerService.addWindow函數,接下來就是把Client端的InputChannel轉換成addWindow的參數outInputChannel中,然後返回到Step 1中的ViewRoot.setView函數中,繼續執行Client端的InputChannel的註冊過程,即爲應用程序這一側註冊鍵盤消息接收通道:
- if (view instanceof RootViewSurfaceTaker) {
- mInputQueueCallback =
- ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
- }
- if (mInputQueueCallback != null) {
- mInputQueue = new InputQueue(mInputChannel);
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- } else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
- }
這裏的變量view一般不爲RootViewSurfaceTaker的實例,因此,最後會執行下面語句:
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
Step 19. InputQueue.registerInputChannel
這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:
- public final class InputQueue {
- ......
- public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,
- MessageQueue messageQueue) {
- ......
- synchronized (sLock) {
- ......
- nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);
- }
- }
- ......
- }
Step 20. InputQueue.nativeRegisterInputChannel
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) {
- status_t status = gNativeInputQueue.registerInputChannel(
- env, inputChannelObj, inputHandlerObj, messageQueueObj);
- ......
- }
Step 21. NativeInputQueue.registerInputChannel
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,
- jobject inputHandlerObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
- { // acquire lock
- AutoMutex _l(mLock);
- if (getConnectionIndex(inputChannel) >= 0) {
- LOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
- uint16_t connectionId = mNextConnectionId++;
- sp<Connection> connection = new Connection(connectionId, inputChannel, looper);
- status_t result = connection->inputConsumer.initialize();
- if (result) {
- LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
- inputChannel->getName().string(), result);
- return result;
- }
- connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
- int32_t receiveFd = inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd, connection);
- looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- } // release lock
- ......
- return OK;
- }
介紹到這裏,應用程序註冊鍵盤消息接收通道的過程就分析完成了。這個過程比較複雜,這裏小結一下:
A. 即將會被激活的Activity窗口,會通知InputManager,它是當前激活的窗口,因此,一旦發生鍵盤事件的時候,InputManager就把這個鍵盤事件拋給這個Activity處理;
B. 應用程序會爲這個Activity窗口和InputManager之間創建一個鍵盤消息接收通道,這個通道的一端由一個Server端的InputChannel構成,另一端由Client端的InputChannel構成,Server端的InputChannel註冊在由InputManager所管理的InputDispatcher中,而Client端的InputChannel註冊在由應用程序主線程的消息循環對象Looper中;
C. 註冊在InputDispatcher中的InputChannel由一個反向管道的讀端和一個前向管道的寫端組成,而註冊在應用程序主線程的消息循環對象Looper中的InputChannel由這個前向管道的讀端和反向管道的寫端組成,這種交叉結構使得當有鍵盤事件發生時,InputDispatcher可以把這個事件通知給應用程序。
應用程序註冊好鍵盤消息接收通道後,接下來就開始分析InputManager分發鍵盤消息給應用程序的過程了。
3. InputManager分發鍵盤消息給應用程序的過程分析
在分析InputManager分發鍵盤消息給應用程序的過程之前,我們先假設現在沒有鍵盤事件發生,因此,InputManager中的InputReader正在睡眠等待鍵盤事件的發生,而InputManager中的InputDispatcher正在等待InputReader從睡眠中醒過來並且喚醒它,而應用程序也正在消息循環中等待InputDispatcher從睡眠中醒過來並且喚醒它。這時候,用戶按下鍵盤中的一個鍵,於是,一系列喚醒的事件就依次發生了,一直到應用程序中正在顯示的Activity得到通知,有鍵盤事件發生了。我們先來看這個過程的序列圖,然後再詳細分析每一個步驟:
Step 1. InputReader.pollOnce
Step 2. EventHub.getEvent
這兩個函數分別定義在frameworks/base/libs/ui/InputReader.cpp和frameworks/base/libs/ui/EventHub.cpp文件中,前面我們在分析InputManager的啓動過程的Step 17和Step 18時,已經看到過這兩個函數了。InputReaderThread線程會不民地循環調用InputReader.pollOnce函數來讀入鍵盤事件,而實際的鍵盤事件讀入操作是由EventHub.getEvent函數來進行的。如果當前沒有鍵盤事件發生,InputReaderThread線程就會睡眠在EventHub.getEvent函數上,而當鍵盤事件發生後,就會把這個事件封裝成一個RawEvent對象,然後返回到pollOnce函數中,執行process函數進一步處理:
- void InputReader::loopOnce() {
- RawEvent rawEvent;
- mEventHub->getEvent(& rawEvent);
- ......
- process(& rawEvent);
- }
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void InputReader::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EventHubInterface::DEVICE_ADDED:
- addDevice(rawEvent->deviceId);
- break;
- case EventHubInterface::DEVICE_REMOVED:
- removeDevice(rawEvent->deviceId);
- break;
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChanged(rawEvent->when);
- break;
- default:
- consumeEvent(rawEvent);
- break;
- }
- }
當鍵盤事件發生時,rawEvent->type的值爲EV_KEY,這是一個宏定義,具體可以參考bionic/libc/kernel/common/linux/input.h文件:
- #define EV_KEY 0x01
Step 4. InputReader.consumeEvent
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void InputReader::consumeEvent(const RawEvent* rawEvent) {
- int32_t deviceId = rawEvent->deviceId;
- { // acquire device registry reader lock
- RWLock::AutoRLock _rl(mDeviceRegistryLock);
- ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
- if (deviceIndex < 0) {
- LOGW("Discarding event for unknown deviceId %d.", deviceId);
- return;
- }
- InputDevice* device = mDevices.valueAt(deviceIndex);
- if (device->isIgnored()) {
- //LOGD("Discarding event for ignored deviceId %d.", deviceId);
- return;
- }
- device->process(rawEvent);
- } // release device registry reader lock
- }
Step 5. InputDevice.process
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void InputDevice::process(const RawEvent* rawEvent) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
- }
- }
Step 6. KeyboardInputMapper.process
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void KeyboardInputMapper::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EV_KEY: {
- int32_t scanCode = rawEvent->scanCode;
- if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,
- rawEvent->flags);
- }
- break;
- }
- }
- }
Step 7. KeyboardInputMapper.processKey
這個函數定義在frameworks/base/libs/ui/InputReader.cpp文件中:
- void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
- int32_t scanCode, uint32_t policyFlags) {
- int32_t newMetaState;
- nsecs_t downTime;
- bool metaStateChanged = false;
- { // acquire lock
- AutoMutex _l(mLock);
- if (down) {
- // Rotate key codes according to orientation if needed.
- // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
- if (mAssociatedDisplayId >= 0) {
- int32_t orientation;
- if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
- return;
- }
- keyCode = rotateKeyCode(keyCode, orientation);
- }
- // Add key down.
- ssize_t keyDownIndex = findKeyDownLocked(scanCode);
- if (keyDownIndex >= 0) {
- // key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- } else {
- // key down
- if ((policyFlags & POLICY_FLAG_VIRTUAL)
- && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {
- return;
- }
- mLocked.keyDowns.push();
- KeyDown& keyDown = mLocked.keyDowns.editTop();
- keyDown.keyCode = keyCode;
- keyDown.scanCode = scanCode;
- }
- mLocked.downTime = when;
- } else {
- // Remove key down.
- ssize_t keyDownIndex = findKeyDownLocked(scanCode);
- if (keyDownIndex >= 0) {
- // key up, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- mLocked.keyDowns.removeAt(size_t(keyDownIndex));
- } else {
- // key was not actually down
- LOGI("Dropping key up from device %s because the key was not down. "
- "keyCode=%d, scanCode=%d",
- getDeviceName().string(), keyCode, scanCode);
- return;
- }
- }
- int32_t oldMetaState = mLocked.metaState;
- newMetaState = updateMetaState(keyCode, down, oldMetaState);
- if (oldMetaState != newMetaState) {
- mLocked.metaState = newMetaState;
- metaStateChanged = true;
- }
- downTime = mLocked.downTime;
- } // release lock
- if (metaStateChanged) {
- getContext()->updateGlobalMetaState();
- }
- getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
- }
- // Rotate key codes according to orientation if needed.
- // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
- if (mAssociatedDisplayId >= 0) {
- int32_t orientation;
- if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
- return;
- }
- keyCode = rotateKeyCode(keyCode, orientation);
- }
- // Add key down.
- ssize_t keyDownIndex = findKeyDownLocked(scanCode);
- if (keyDownIndex >= 0) {
- // key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- } else {
- // key down
- if ((policyFlags & POLICY_FLAG_VIRTUAL)
- && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {
- return;
- }
- mLocked.keyDowns.push();
- KeyDown& keyDown = mLocked.keyDowns.editTop();
- keyDown.keyCode = keyCode;
- keyDown.scanCode = scanCode;
- }
如果是鬆開鍵盤上的某個鍵,就把它從mLocked.keyDowns裏面刪除:
- // Remove key down.
- ssize_t keyDownIndex = findKeyDownLocked(scanCode);
- if (keyDownIndex >= 0) {
- // key up, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
- mLocked.keyDowns.removeAt(size_t(keyDownIndex));
- } else {
- // key was not actually down
- LOGI("Dropping key up from device %s because the key was not down. "
- "keyCode=%d, scanCode=%d",
- getDeviceName().string(), keyCode, scanCode);
- return;
- }
最後,KeyboardInputMappger函數通知InputDispatcher,有鍵盤事件發生了:
- getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
- uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
- ......
- if (! validateKeyEvent(action)) {
- return;
- }
- /* According to http://source.android.com/porting/keymaps_keyboard_input.html
- * Key definitions: Key definitions follow the syntax key SCANCODE KEYCODE [FLAGS...],
- * where SCANCODE is a number, KEYCODE is defined in your specific keylayout file
- * (android.keylayout.xxx), and potential FLAGS are defined as follows:
- * SHIFT: While pressed, the shift key modifier is set
- * ALT: While pressed, the alt key modifier is set
- * CAPS: While pressed, the caps lock key modifier is set
- * Since KeyEvent.java doesn't check if Cap lock is ON and we don't have a
- * modifer state for cap lock, we will not support it.
- */
- if (policyFlags & POLICY_FLAG_ALT) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;
- }
- if (policyFlags & POLICY_FLAG_ALT_GR) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;
- }
- if (policyFlags & POLICY_FLAG_SHIFT) {
- metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;
- }
- policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
- keyCode, scanCode, /*byref*/ policyFlags);
- bool needWake;
- { // acquire lock
- AutoMutex _l(mLock);
- int32_t repeatCount = 0;
- KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
- deviceId, source, policyFlags, action, flags, keyCode, scanCode,
- metaState, repeatCount, downTime);
- needWake = enqueueInboundEventLocked(newEntry);
- } // release lock
- if (needWake) {
- mLooper->wake();
- }
- }
- static bool isValidKeyAction(int32_t action) {
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- case AKEY_EVENT_ACTION_UP:
- return true;
- default:
- return false;
- }
- }
- static bool validateKeyEvent(int32_t action) {
- if (! isValidKeyAction(action)) {
- LOGE("Key event has invalid action code 0x%x", action);
- return false;
- }
- return true;
- }
參數action檢查通過後,還通過policyFlags參數來檢查一下同時是否有ALT和SHIFT鍵被按下:
- if (policyFlags & POLICY_FLAG_ALT) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;
- }
- if (policyFlags & POLICY_FLAG_ALT_GR) {
- metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;
- }
- if (policyFlags & POLICY_FLAG_SHIFT) {
- metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;
- }
- bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
- bool needWake = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(entry);
- switch (entry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
- if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
- mAppSwitchSawKeyDown = true;
- } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- if (mAppSwitchSawKeyDown) {
- <span style="white-space:pre"> </span> ......
- mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
- mAppSwitchSawKeyDown = false;
- needWake = true;
- }
- }
- }
- break;
- }
- }
- return needWake;
- }
回到前面的notifyKey函數中,根據enqueueInboundEventLocked函數的返回值來決定是否要喚醒InputDispatccherThread線程:
- if (needWake) {
- mLooper->wake();
- }
Step 9. Looper.wake
這個函數定義在frameworks/base/libs/utils/Looper.cpp文件中,在前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析中,我們已經分析過這個函數了,這裏不再詳述,簡單來說,它的作用就是用來喚醒睡眠在Looper對象內部的管道讀端的線程,在我們的這個場景中,睡眠在Looper對象內部的管道讀端的線程就是InputDispatccherThread線程了。
從上面InputManager啓動過程的Step 15中,我們知道,此時InputDispatccherThread線程正在InputDispatcher類的dispatchOnceb函數中通過調用mLooper->loopOnce函數進入睡眠狀態。當它被喚醒以後,它就會從InputDispatcher類的dispatchOnceb函數返回到InputDispatcherThread類的threadLoop函數,而InputDispatcherThread類的threadLoop函數是循環執行的,於是,它又會再次進入到InputDispatcher類的dispatchOnce函數來處理當前發生的鍵盤事件。
Step 10. InputDispatcher.dispatchOnce
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::dispatchOnce() {
- nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
- nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
- { // acquire lock
- AutoMutex _l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- ......
- } // release lock
- ......
- }
Step 11. InputDispatcher.dispatchOnceInnerLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
- nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
- ......
- // Ready to start a new event.
- // If we don't already have a pending event, go grab one.
- if (! mPendingEvent) {
- if (mInboundQueue.isEmpty()) {
- ......
- } else {
- // Inbound queue has at least one entry.
- EventEntry* entry = mInboundQueue.headSentinel.next;
- ......
- mInboundQueue.dequeue(entry);
- mPendingEvent = entry;
- }
- ......
- }
- ......
- switch (mPendingEvent->type) {
- ......
- case EventEntry::TYPE_KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
- ......
- done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
- &dropReason, nextWakeupTime);
- break;
- }
- ......
- }
- ......
- }
我們忽略了這個函數的次要邏輯,主要關注鍵盤事件的主要處理流程。首先,如果前面發生的鍵盤事件都已經處理完畢,那麼這裏的mPendingEvent就爲NULL,又因爲前面我們把剛剛發生的鍵盤事件加入了mInboundQueue隊列,因此,這裏mInboundQueue不爲NULL,於是,這裏就把mInboundQueue隊列中的鍵盤事件取出來,放在mPendingEvent變量中:
- mInboundQueue.dequeue(entry);
- mPendingEvent = entry;
Step 12. InputDispatcher.dispatchKeyLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- bool InputDispatcher::dispatchKeyLocked(
- nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
- ......
- // Identify targets.
- if (! mCurrentInputTargetsValid) {
- int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
- entry, nextWakeupTime);
- ......
- }
- // Dispatch the key.
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
- return true;
- }
我們先來看一InputDispatcher是如何找到當前激活的Activity窗口的,然後再分析它把鍵盤事件分發給當前激活Activity窗口的過程。
Step 13. InputDispatcher.findFocusedWindowTargetsLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry* entry, nsecs_t* nextWakeupTime) {
- mCurrentInputTargets.clear();
- int32_t injectionResult;
- // If there is no currently focused window and no focused application
- // then drop the event.
- if (! mFocusedWindow) {
- if (mFocusedApplication) {
- ......
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplication, NULL, nextWakeupTime);
- goto Unresponsive;
- }
- ......
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
- }
- // Check permissions.
- if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- goto Failed;
- }
- // If the currently focused window is paused then keep waiting.
- if (mFocusedWindow->paused) {
- ......
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplication, mFocusedWindow, nextWakeupTime);
- goto Unresponsive;
- }
- // If the currently focused window is still working on previous events then keep waiting.
- if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
- ......
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplication, mFocusedWindow, nextWakeupTime);
- goto Unresponsive;
- }
- // Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
- ......
- return injectionResult;
- }
第二個if語句檢查權限問題,原來,這個鍵盤事件除了是由硬件觸發的外,也可以由其它進程注入進來的,如果這個鍵盤事件是由其它進程注入進來的,那麼entry->injectState就不爲NULL,它裏面包含了事件註冊者的進程ID和用戶ID,於是,這裏就會調用checkInjectionPermission來檢查這個事件注入者的進程ID和用戶ID,看看它是否具有這個權限。這裏我們不考慮這種情況,因此,這裏的entry->injectState爲NULL,於是,這個if語句的檢查也通過了。
第三個if語句檢查當前激活的Activity窗口是否是處於paused狀態,如果是的話,也不用進一步處理了。一般情況下,當前激活的Activity窗口都是處於resumed狀態的,於是,這個if語句的檢查也通過了。
第四個if語句檢查當前激活的Activity窗口是否還正在處理前一個鍵盤事件,如果是的話,那就要等待它處理完前一個鍵盤事件後再來處理新的鍵盤事件了。這裏我們也假設當前激活的Activity窗口不是正在處理前面的鍵盤事件,因此,這個if語句的檢查也通過了。
最後,就調用addWindowTargetLocked函數把當前激活的Activity窗口添加到InputDispatcher類的mCurrentInputTargets成員變量中去。
Step 14. InputDispatcher.addWindowTargetLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
- BitSet32 pointerIds) {
- mCurrentInputTargets.push();
- InputTarget& target = mCurrentInputTargets.editTop();
- target.inputChannel = window->inputChannel;
- target.flags = targetFlags;
- target.xOffset = - window->frameLeft;
- target.yOffset = - window->frameTop;
- target.pointerIds = pointerIds;
- }
回到Step 12中的dispatchKeyLocked函數,它接下來就調用dispatchEventToCurrentInputTargetsLocked來進一步處理了。
Step 15. InputDispatcher.dispatchEventToCurrentInputTargetsLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
- EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
- ......
- for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
- const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
- ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
- resumeWithAppendedMotionSample);
- } else {
- ......
- }
- }
前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封裝成了一個Connection,然後以這個InputChannel中的Receive Pipe Fd作爲鍵值把這個Connection對象保存在mConnectionsByReceiveFd中。這裏,既然我們已經通過mCurrentInputTargets得到了表示當前需要接收鍵盤事件的Activity窗口的InputTarget對象,而且這個InputTarget對象的inputChannel就表示當初在InputDispatcher中註冊的Server端InputChannel,因此,這裏就可以把這個Connection對象取出來,最後調用prepareDispatchCycleLocked函數來進一步處理。
Step 16. InputDispatcher.prepareDispatchCycleLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
- bool resumeWithAppendedMotionSample) {
- ......
- // Resume the dispatch cycle with a freshly appended motion sample.
- // First we check that the last dispatch entry in the outbound queue is for the same
- // motion event to which we appended the motion sample. If we find such a dispatch
- // entry, and if it is currently in progress then we try to stream the new sample.
- bool wasEmpty = connection->outboundQueue.isEmpty();
- if (! wasEmpty && resumeWithAppendedMotionSample) {
- ......
- return;
- }
- // This is a new event.
- // Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
- inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
- ......
- // Enqueue the dispatch entry.
- connection->outboundQueue.enqueueAtTail(dispatchEntry);
- // If the outbound queue was previously empty, start the dispatch cycle going.
- if (wasEmpty) {
- ......
- startDispatchCycleLocked(currentTime, connection);
- }
- }
在開始處理鍵盤事件之前,這個函數會檢查一下傳進來的參數connection中的outboundQueue事件隊列是否爲空,如果不爲空,就要看看當前要處理的事件和outboundQueue隊列中的最後一個事件是不是同一個motion事件,如果是的話,並且從上面傳進來的resumeWithAppendedMotionSample參數爲true,這時候就要以流水線的方式來處理這些motion事件了。在我們這個情景中,要處理的是鍵盤事件,因此在上面Step 12中傳進來的resumeWithAppendedMotionSample參數爲false,因此,我們略過這種情況。
接下來,就會把當前的鍵盤事件封裝成一個DispatchEntry對象,然後添加到connection對象的outboundQueue隊列中去,表示當前鍵盤事件是一個待處理的鍵盤事件。
當connection中的outboundQueue事件隊列不爲空,即wasEmpty爲false時,說明當前這個Activity窗口正在處鍵盤事件了,因此,就不需要調用startDispatchCycleLocked來啓動Activity窗口來處理這個事件了,因爲一旦這個Activity窗口正在處鍵盤事件,它就會一直處理下去,直到它裏的connection對象的outboundQueue爲空爲止。當connection中的outboundQueue事件隊列爲空時,就需要調用startDispatchCycleLocked來通知這個Activity窗口來執行鍵盤事件處理的流程了。
Step 17. InputDispatcher.startDispatchCycleLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
- ......
- DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
- // Mark the dispatch entry as in progress.
- dispatchEntry->inProgress = true;
- // Update the connection's input state.
- EventEntry* eventEntry = dispatchEntry->eventEntry;
- ......
- // Publish the event.
- status_t status;
- switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- // Apply target flags.
- int32_t action = keyEntry->action;
- int32_t flags = keyEntry->flags;
- // Publish the key event.
- status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
- action, flags, keyEntry->keyCode, keyEntry->scanCode,
- keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
- ......
- break;
- }
- ......
- }
- // Send the dispatch signal.
- status = connection->inputPublisher.sendDispatchSignal();
- ......
- }
Step 18. InputPublisher.publishKeyEvent
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputPublisher::publishKeyEvent(
- int32_t deviceId,
- int32_t source,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- ......
- status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);
- if (result < 0) {
- return result;
- }
- mSharedMessage->key.action = action;
- mSharedMessage->key.flags = flags;
- mSharedMessage->key.keyCode = keyCode;
- mSharedMessage->key.scanCode = scanCode;
- mSharedMessage->key.metaState = metaState;
- mSharedMessage->key.repeatCount = repeatCount;
- mSharedMessage->key.downTime = downTime;
- mSharedMessage->key.eventTime = eventTime;
- return OK;
- }
這個匿名共享內存是什麼時候創建的呢?前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封裝成一個 Connection對象時,會調用它的initialize成員函數來執行一些初始化工作,就是在這個時候創建這個匿名共享內存的了:
- sp<Connection> connection = new Connection(inputChannel);
- status_t status = connection->initialize();
- status_t InputPublisher::initialize() {
- ......
- int ashmemFd = mChannel->getAshmemFd();
- int result = ashmem_get_size_region(ashmemFd);
- ......
- mAshmemSize = (size_t) result;
- mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
- PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
- ......
- mPinned = true;
- mSharedMessage->consumed = false;
- return reset();
- }
這個匿名共享內存mSharedMessage的作用是什麼呢?原來,在InputChannel中,前向管道和反向管道的作用只是用來在Server端和Client端之間相互通知有事件發生了,但是具體是什麼樣的事件,還需要去讀取這個匿名共享內存的內容才知道。前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 14中(InputChannel.openInputChannelPair)創建Server端和Client端的InputChannel對時,創建一個匿名共享內存,這個匿名共享內存有兩個文件描述符同時指向它,其中一個放在Server端的InputChannel中,另外一個放在Client端的InputChannel中。這樣,當InputDispatcher通過Server端的InputChannel的前向管道來通知Client端有鍵盤事件發生時,Client端只要通過它的InputChannel中的匿名共享內存文件描述符去讀取匿名共享內存中的內容,就可以知道發生了什麼事情了。有關匿名共享內存的相關知識,請參考Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃一文。
回到Step 17中,接下來就是調用InputPublisher的成員函數sendDispatchSignal來通知Activity窗口處理鍵盤事件了。
Step 19. InputPublishe.sendDispatchSignal
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputPublisher::sendDispatchSignal() {
- ......
- mWasDispatched = true;
- return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
- }
Step 20. InputChannel.sendSignal
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputChannel::sendSignal(char signal) {
- ssize_t nWrite;
- do {
- nWrite = ::write(mSendPipeFd, & signal, 1);
- } while (nWrite == -1 && errno == EINTR);
- if (nWrite == 1) {
- ......
- return OK;
- }
- return -errno;
- }
在前面分析應用程序註冊鍵盤消息接收通道過程的Step 21中,我們也說過,當應用程序的主線程因爲這個InputChannel中的前向管道的寫端喚醒時,NativeInputQueue的成員函數handleReceiveCallback就會被回調,因此,接下來,應用程序的主線程就會被喚醒,然後執行NativeInputQueue的成員函數handleReceiveCallback。
Step 21. NativeInputQueue.handleReceiveCallback
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- sp<Connection> connection;
- InputEvent* inputEvent;
- jobject inputHandlerObjLocal;
- jlong finishedToken;
- { // acquire lock
- AutoMutex _l(q->mLock);
- ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- ......
- status_t status = connection->inputConsumer.receiveDispatchSignal();
- if (status) {
- ......
- return 0; // remove the callback
- }
- ......
- status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
- ......
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- } // release lock
- ......
- int32_t inputEventType = inputEvent->getType();
- jobject inputEventObj;
- jmethodID dispatchMethodId;
- switch (inputEventType) {
- case AINPUT_EVENT_TYPE_KEY:
- ......
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
- break;
- }
- ......
- }
- ......
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
- ......
- return 1;
- }
在InputChannel內部中,分別有一個InputPublisher對象和一個InputConsumer對象,對於Server端的InputChannel來說,它使用的是InputPublisher對象,通過它進行鍵盤消息的分發,而對於Client端的InputChannel來說,它使用的是InputConsumer對象,通過它進行鍵盤消息的讀取。
獲得了這個InputConsumer對象後首先是調用它的receiveDispatchSignal來確認是否是接收到了鍵盤消息的通知,如果是的話,再調用它的consume函數來把鍵盤事件讀取出來,最後,調用Java層的回調對象InputQueue的DispatchKeyEvent來處理這個鍵盤事件。下面,我們就依次來分析這些過程。
Step 22. InputConsumer.receiveDispatchSignal
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputConsumer::receiveDispatchSignal() {
- ......
- char signal;
- status_t result = mChannel->receiveSignal(& signal);
- if (result) {
- return result;
- }
- if (signal != INPUT_SIGNAL_DISPATCH) {
- ......
- return UNKNOWN_ERROR;
- }
- return OK;
- }
InputChannel類的receiveSignal函數也是定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputChannel::receiveSignal(char* outSignal) {
- ssize_t nRead;
- do {
- nRead = ::read(mReceivePipeFd, outSignal, 1);
- } while (nRead == -1 && errno == EINTR);
- if (nRead == 1) {
- ......
- return OK;
- }
- ......
- return -errno;
- }
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
- ......
- *outEvent = NULL;
- int ashmemFd = mChannel->getAshmemFd();
- int result = ashmem_pin_region(ashmemFd, 0, 0);
- ......
- if (mSharedMessage->consumed) {
- ......
- return INVALID_OPERATION;
- }
- // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal
- // to the publisher that the message has been consumed (or is in the process of being
- // consumed). Eventually the publisher will reinitialize the semaphore for the next message.
- result = sem_wait(& mSharedMessage->semaphore);
- ......
- mSharedMessage->consumed = true;
- switch (mSharedMessage->type) {
- case AINPUT_EVENT_TYPE_KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (! keyEvent) return NO_MEMORY;
- populateKeyEvent(keyEvent);
- *outEvent = keyEvent;
- break;
- }
- ......
- }
- return OK;
- }
回到Step 21中的handleReceiveCallback函數中,從InputConsumer中獲得了鍵盤事件的內容(保存在本地變量inputEvent中)後,就開始要通知Java層的應用程序了。在前面分析應用程序註冊鍵盤消息接收通道的過程時,在Step 21中(NativeInputQueue.registerInputChannel),會把傳進來的對象inputHandlerObj保存在Connection對象中:
- connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:
- public final class InputQueue {
- ......
- private static void dispatchKeyEvent(InputHandler inputHandler,
- KeyEvent event, long finishedToken) {
- Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
- inputHandler.handleKey(event, finishedCallback);
- }
- ......
- }
這裏的inputHandler對象是在前面分析應用程序註冊鍵盤消息接收通道的過程時,在Step 1(ViewRoot.setView)中傳進來的:
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
Step 25. InputHandler.handleKey
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private final InputHandler mInputHandler = new InputHandler() {
- public void handleKey(KeyEvent event, Runnable finishedCallback) {
- startInputEvent(finishedCallback);
- dispatchKey(event, true);
- }
- ......
- };
- ......
- }
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private void startInputEvent(Runnable finishedCallback) {
- ......
- mFinishedCallback = finishedCallback;
- }
- ......
- }
Step 26. ViewRoot.dispatchKey
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private void dispatchKey(KeyEvent event, boolean sendDone) {
- ......
- Message msg = obtainMessage(DISPATCH_KEY);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- ......
- sendMessageAtTime(msg, event.getEventTime());
- }
- ......
- }
Step 27. ViewRoot.deliverKeyEvent
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- // If mView is null, we just consume the key event because it doesn't
- // make sense to do anything else with it.
- boolean handled = mView != null
- ? mView.dispatchKeyEventPreIme(event) : true;
- ......
- // If it is possible for this window to interact with the input
- // method window, then we want to first dispatch our key events
- // to the input method.
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mView != null) {
- ......
- imm.dispatchKeyEvent(mView.getContext(), seq, event,
- mInputMethodCallback);
- return;
- }
- }
- ......
- }
- ......
- }
Step 28. InputMethodManager.dispatchKeyEvent
這個函數定義在frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java文件中。這是一個輸入法相關的類,我們這裏就不關注了,只要知道當輸入法處理完成之後,它就會調用ViewRoot類的mInputMehtodCallback對象的finishedEvent成員函數。
Step 29. InputMethodCallack.finishedEvent
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- static class InputMethodCallback extends IInputMethodCallback.Stub {
- private WeakReference<ViewRoot> mViewRoot;
- public InputMethodCallback(ViewRoot viewRoot) {
- mViewRoot = new WeakReference<ViewRoot>(viewRoot);
- }
- public void finishedEvent(int seq, boolean handled) {
- final ViewRoot viewRoot = mViewRoot.get();
- if (viewRoot != null) {
- viewRoot.dispatchFinishedEvent(seq, handled);
- }
- }
- ......
- }
- ......
- }
Step 30. ViewRoot.dispatchFinishedEvent
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- public void dispatchFinishedEvent(int seq, boolean handled) {
- Message msg = obtainMessage(FINISHED_EVENT);
- msg.arg1 = seq;
- msg.arg2 = handled ? 1 : 0;
- sendMessage(msg);
- }
- ......
- }
Step 31. ViewRoot.handleFinishedEvent
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- void handleFinishedEvent(int seq, boolean handled) {
- final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
- ......
- if (event != null) {
- final boolean sendDone = seq >= 0;
- if (!handled) {
- deliverKeyEventToViewHierarchy(event, sendDone);
- return;
- } else if (sendDone) {
- ......
- } else {
- ......
- }
- }
- }
- ......
- }
Step 32. ViewRoot.deliverKeyEventToViewHierarchy
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
- try {
- if (mView != null && mAdded) {
- ......
- boolean keyHandled = mView.dispatchKeyEvent(event);
- }
- ......
- } finally {
- if (sendDone) {
- finishInputEvent();
- }
- }
- }
- ......
- }
ViewRoot類的成員變量mView的類型爲DecorView,它是由ActivityThread類第一次Resume當前的Activity窗口時創建的,具體可以參考ActivityThread類的handleResumeActivity成員函數,這裏就不關注了。
Step 33. DecorView.dispatchKeyEvent
這個函數定義在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java文件中,它是PhoneWindow類的一個內部類:
- public class PhoneWindow extends Window implements MenuBuilder.Callback {
- ......
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
- ......
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- ......
- final Callback cb = getCallback();
- final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
- : super.dispatchKeyEvent(event);
- ......
- }
- ......
- }
- ......
- }
Step 34. Activity.dispatchKeyEvent
這個函數定義在frameworks/base/core/java/android/app/Activity.java文件中:
- public class Activity extends ContextThemeWrapper
- implements LayoutInflater.Factory,
- Window.Callback, KeyEvent.Callback,
- OnCreateContextMenuListener, ComponentCallbacks {
- ......
- public boolean dispatchKeyEvent(KeyEvent event) {
- ......
- View decor = mDecor;
- if (decor == null) decor = win.getDecorView();
- return event.dispatch(this, decor != null
- ? decor.getKeyDispatcherState() : null, this);
- }
- ......
- }
Step 35. KeyEvent.dispatch
這個函數定義在frameworks/base/core/java/android/view/KeyEvent.java文件中:
- public class KeyEvent extends InputEvent implements Parcelable {
- ......
- public final boolean dispatch(Callback receiver, DispatcherState state,
- Object target) {
- switch (mAction) {
- case ACTION_DOWN: {
- ......
- boolean res = receiver.onKeyDown(mKeyCode, this);
- ......
- return res;
- }
- case ACTION_UP:
- ......
- return receiver.onKeyUp(mKeyCode, this);
- case ACTION_MULTIPLE:
- final int count = mRepeatCount;
- final int code = mKeyCode;
- if (receiver.onKeyMultiple(code, count, this)) {
- return true;
- }
- ......
- return false;
- }
- return false;
- }
- ......
- }
Activity窗口處理完這個鍵盤事件後,層層返回,最後回到Step 32中,調用finishInputEvent事件來處理一些手尾工,下面我們將會看到這些手尾工是什麼。
Step 36. ViewRoot.finishInputEvent
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- private void finishInputEvent() {
- ......
- if (mFinishedCallback != null) {
- mFinishedCallback.run();
- mFinishedCallback = null;
- } else {
- ......
- }
- }
- ......
- }
ViewRoot類裏面的成員變量mFinishedCallback是在前面Step 25中由InputQueue設置的,它是一個Runnable對象,實際類型是定義在InputQueue的內部類FinishedCallback,因此,這裏調用它的run方法時,接下來就會調用InputQueue的內部類FinishedCallback的run成員函數:
- public final class InputQueue {
- ......
- private static class FinishedCallback implements Runnable {
- ......
- public void run() {
- synchronized (sLock) {
- ......
- nativeFinished(mFinishedToken);
- ......
- }
- }
- ......
- }
- ......
- }
Step 37. InputQueue.nativeFinished
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
- jlong finishedToken) {
- status_t status = gNativeInputQueue.finished(
- env, finishedToken, false /*ignoreSpuriousFinish*/);
- ......
- }
Step 38. NativeInputQueue.finished
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
- int32_t receiveFd;
- uint16_t connectionId;
- uint16_t messageSeqNum;
- parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- ......
- connection->messageInProgress = false;
- status_t status = connection->inputConsumer.sendFinishedSignal();
- ......
- } // release lock
- return OK;
- }
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
- jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
- uint16_t messageSeqNum) {
- return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
- }
因此,在上面的finished函數裏面,首先就是要對參數值finishedToken進行解碼,把receiveFd、connectionId和messageSeqNum三個值分別取回來:
- parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
- void NativeInputQueue::parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
- *outReceiveFd = int32_t(finishedToken >> 32);
- *outConnectionId = uint16_t(finishedToken >> 16);
- *outMessageIndex = uint16_t(finishedToken);
- }
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- status_t status = connection->inputConsumer.sendFinishedSignal();
Step 39. InputConsumer.sendFinishedSignal
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputConsumer::sendFinishedSignal() {
- ......
- return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);
- }
前面我們在分析應用程序註冊鍵盤消息接收通道的過程時,在Step 18(InputDispatcher.registerInputChannel)中,說到InputDispatcher把一個反向管道的讀端文件描述符添加到WindowManagerService所運行的線程中的Looper對象中去,然後就會在這個反向管道的讀端上睡眠等待有這個管道有新的內容可讀。現在,InputConsumer往這個反向管道寫入新的內容了,於是,InputDispatcher就被喚醒過來了,喚醒過來後,它所調用的函數是InputDispatcher.handleReceiveCallback函數,這與前面的Step 21的邏輯是一樣的。
Step 40. InputDispatcher.handleReceiveCallack
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
- InputDispatcher* d = static_cast<InputDispatcher*>(data);
- { // acquire lock
- AutoMutex _l(d->mLock);
- ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- nsecs_t currentTime = now();
- sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
- ......
- status_t status = connection->inputPublisher.receiveFinishedSignal();
- if (status) {
- ......
- return 0; // remove the callback
- }
- d->finishDispatchCycleLocked(currentTime, connection);
- ......
- return 1;
- } // release lock
- }
- ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- ......
- sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
Step 41. InputPublisher.receiverFinishedSignal
這個函數定義在frameworks/base/libs/ui/InputTransport.cpp文件中:
- status_t InputPublisher::receiveFinishedSignal() {
- ....
- char signal;
- status_t result = mChannel->receiveSignal(& signal);
- if (result) {
- return result;
- }
- if (signal != INPUT_SIGNAL_FINISHED) {
- .......
- return UNKNOWN_ERROR;
- }
- return OK;
- }
回到前面的Step 40中,確認了是真的收到了鍵盤事件處理完成的信號後,就調用InputDispatcher的finishDispatchCycleLocked函數來執行一些善後工作了。
Step 42. InputDispatcher.finishDispatchCycleLocked
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
- ......
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection);
- // Reset the publisher since the event has been consumed.
- // We do this now so that the publisher can release some of its internal resources
- // while waiting for the next dispatch cycle to begin.
- status_t status = connection->inputPublisher.reset();
- ......
- startNextDispatchCycleLocked(currentTime, connection);
- }
一是通知其它系統,InputDispatcher完成了一次鍵盤事件的處理:
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection);
- // Reset the publisher since the event has been consumed.
- // We do this now so that the publisher can release some of its internal resources
- // while waiting for the next dispatch cycle to begin.
- status_t status = connection->inputPublisher.reset();
- startNextDispatchCycleLocked(currentTime, connection);
至此,InputManager分發鍵盤消息給應用程序的過程就分析完成了,這是一個比較複雜的過程,不過,只要我們抓住主要的線索,就不難理解了,現在我們就小結一下這個過程的四個主要線索:
A. 鍵盤事件發生,InputManager中的InputReader被喚醒,此前InputReader睡眠在/dev/input/event0這個設備文件上;
B. InputReader被喚醒後,它接着喚醒InputManager中的InputDispatcher,此前InputDispatcher睡眠在InputManager所運行的線程中的Looper對象裏面的管道的讀端上;
C. InputDispatcher被喚醒後,它接着喚醒應用程序的主線程來處理這個鍵盤事件,此前應用程序的主線程睡眠在Client端InputChannel中的前向管道的讀端上;
D. 應用程序處理處理鍵盤事件之後,它接着喚醒InputDispatcher來執行善後工作,此前InputDispatcher睡眠在Server端InputChannel的反向管道的讀端上,注意這裏與第二個線索處的區別。
4. 應用程序註銷鍵盤消息接收通道的過程分析
當Activity窗口創建時,它會向InputManager註冊鍵盤消息接收通道,而當Activity窗口銷燬時,它就會向InputManager註銷前面註冊的鍵盤消息接收通道了,本節內容就來看看應用程序註銷鍵盤消息接收通道的過程。
當我們按下鍵盤上的Back鍵時,當前激活的Activity窗口就會被失去焦點,但是這時候它還沒有被銷燬,它的狀態被設置爲Stopped;當新的Activity窗口即將要顯示時,它會通知WindowManagerService,這時候WindowManagerService就會處理當前處理Stopped狀態的Activity窗口了,要執行的操作就是銷燬它們了,在銷燬的時候,就會註銷它們之前所註冊的鍵盤消息接收通道。
新的Activity窗口通知WindowManagerService它即將要顯示的過程比較複雜,但是它與我們本節要介紹的內容不是很相關,因此,這裏就略過大部過程了,我們從ActvitiyRecord的windowsVisible函數開始分析。注意,這裏的ActivityRecord是新的Activity窗口在ActivityManangerService的代表,而那些處於Stopped狀態的Activity窗口
會放在ActivityStack類的一個等待可見的mWaitingVisibleActivities列表裏面,事實於,對於那些Stopped狀態的Activity窗口來說,它們是等待銷燬,而不是等待可見。
像前面一樣,我們先來看一張應用程序註銷鍵盤消息接收通道的過程的序列圖,然後根據這個序列圖來詳細分析互一個步驟:
Step 1. ActivityRecord.windowsVisible
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityRecord.java文件中:
- class ActivityRecord extends IApplicationToken.Stub {
- ......
- boolean nowVisible; // is this activity's window visible?
- boolean idle; // has the activity gone idle?
- ......
- public void windowsVisible() {
- synchronized(service) {
- ......
- if (!nowVisible) {
- nowVisible = true;
- if (!idle) {
- .......
- } else {
- // If this activity was already idle, then we now need to
- // make sure we perform the full stop of any activities
- // that are waiting to do so. This is because we won't
- // do that while they are still waiting for this one to
- // become visible.
- final int N = stack.mWaitingVisibleActivities.size();
- if (N > 0) {
- for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)
- stack.mWaitingVisibleActivities.get(i);
- r.waitingVisible = false;
- ......
- }
- stack.mWaitingVisibleActivities.clear();
- Message msg = Message.obtain();
- msg.what = ActivityStack.IDLE_NOW_MSG;
- stack.mHandler.sendMessage(msg);
- }
- }
- ......
- }
- }
- }
- ......
- }
- final int N = stack.mWaitingVisibleActivities.size();
- if (N > 0) {
- for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)
- stack.mWaitingVisibleActivities.get(i);
- r.waitingVisible = false;
- ......
- }
- stack.mWaitingVisibleActivities.clear();
- Message msg = Message.obtain();
- msg.what = ActivityStack.IDLE_NOW_MSG;
- stack.mHandler.sendMessage(msg);
- }
Step 2. ActivityStack.activityIdleInternal
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- final void activityIdleInternal(IBinder token, boolean fromTimeout,
- Configuration config) {
- ......
- ArrayList<ActivityRecord> stops = null;
- ......
- int NS = 0;
- ......
- synchronized (mService) {
- ......
- // Atomically retrieve all of the other things to do.
- stops = processStoppingActivitiesLocked(true);
- NS = stops != null ? stops.size() : 0;
- ......
- }
- int i;
- ......
- // Stop any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NS; i++) {
- ActivityRecord r = (ActivityRecord)stops.get(i);
- synchronized (mService) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
- } else {
- ......
- }
- }
- }
- ......
- }
- ......
- }
Step 3. ActivityStack.finishCurrentActivityLocked
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int mode) {
- ......
- return finishCurrentActivityLocked(r, index, mode);
- }
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int index, int mode) {
- ......
- // make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
- ......
- final ActivityState prevState = r.state;
- r.state = ActivityState.FINISHING;
- if (mode == FINISH_IMMEDIATELY
- || prevState == ActivityState.STOPPED
- || prevState == ActivityState.INITIALIZING) {
- // If this activity is already stopped, we can just finish
- // it right now.
- return destroyActivityLocked(r, true) ? null : r;
- } else {
- ......
- }
- return r;
- }
- ......
- }
Step 4. ActivityStack.destroyActivityLocked
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- final boolean destroyActivityLocked(ActivityRecord r,
- boolean removeFromApp) {
- ......
- boolean removedFromHistory = false;
- ......
- final boolean hadApp = r.app != null;
- if (hadApp) {
- ......
- try {
- ......
- r.app.thread.scheduleDestroyActivity(r, r.finishing,
- r.configChangeFlags);
- } catch (Exception e) {
- ......
- }
- ......
- } else {
- ......
- }
- ......
- return removedFromHistory;
- }
- ......
- }
- r.app.thread.scheduleDestroyActivity(r, r.finishing,
- r.configChangeFlags);
Step 5. ApplicationThread.scheduleDestroyActivity
這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- public final class ActivityThread {
- ......
- private final class ApplicationThread extends ApplicationThreadNative {
- ......
- public final void scheduleDestroyActivity(IBinder token, boolean finishing,
- int configChanges) {
- queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
- configChanges);
- }
- ......
- }
- ......
- }
Step 6. ActivityThread.handleDestroyActivity
這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- public final class ActivityThread {
- ......
- private final void handleDestroyActivity(IBinder token, boolean finishing,
- int configChanges, boolean getNonConfigInstance) {
- ......
- ActivityClientRecord r = performDestroyActivity(token, finishing,
- configChanges, getNonConfigInstance);
- if (r != null) {
- WindowManager wm = r.activity.getWindowManager();
- View v = r.activity.mDecor;
- if (v != null) {
- ......
- if (r.activity.mWindowAdded) {
- wm.removeViewImmediate(v);
- }
- ......
- }
- ......
- }
- ......
- }
- ......
- }
Step 7. LocalWindowManager.removeViewImmediate
這個函數定義在frameworks/base/core/java/android/view/Window.java文件中:
- public abstract class Window {
- ......
- private class LocalWindowManager implements WindowManager {
- ......
- public final void removeViewImmediate(View view) {
- mWindowManager.removeViewImmediate(view);
- }
- ......
- private final WindowManager mWindowManager;
- }
- ......
- }
Step 8. WndowManagerImpl.removeViewImmediate
這個函數定義在frameworks/base/core/java/android/view/WindowManagerImpl.java文件中:
- public class WindowManagerImpl implements WindowManager {
- ......
- public void removeViewImmediate(View view) {
- synchronized (this) {
- int index = findViewLocked(view, true);
- ViewRoot root = mRoots[index];
- ......
- root.die(true);
- ......
- }
- }
- ......
- }
Step 9. ViewRoot.die
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- public void die(boolean immediate) {
- if (immediate) {
- doDie();
- } else {
- ......
- }
- }
- ......
- }
Step 10. ViewRoot.doDie
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- void doDie() {
- ......
- synchronized (this) {
- ......
- if (mAdded) {
- mAdded = false;
- dispatchDetachedFromWindow();
- }
- }
- }
- ......
- }
Step 11. ViewRoot.ispatchDetachedFromWindow
這個函數定義在frameworks/base/core/java/android/view/ViewRoot.java文件中:
- public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.Callbacks {
- ......
- void dispatchDetachedFromWindow() {
- ......
- if (mInputChannel != null) {
- if (mInputQueueCallback != null) {
- ......
- } else {
- InputQueue.unregisterInputChannel(mInputChannel);
- }
- }
- try {
- sWindowSession.remove(mWindow);
- } catch (RemoteException e) {
- }
- ......
- }
- ......
- }
我們先來看註銷註冊在應用程序這一側的Client端InputChannel,然後再回過頭來分析註銷註冊在InputManager這一側的Server端InputChannel。
Step 12. InputQueue.unregisterInputChannel
這個函數定義在frameworks/base/core/java/android/view/InputQueue.java文件中:
- public final class InputQueue {
- ......
- public static void unregisterInputChannel(InputChannel inputChannel) {
- ......
- synchronized (sLock) {
- ......
- nativeUnregisterInputChannel(inputChannel);
- }
- }
- ......
- }
Step 13. InputQueue.nativeUnregisterInputChannel
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
- status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
- ......
- }
Step 14. NativeInputQueue.unregisterInputChannel
這個函數定義在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
- status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t connectionIndex = getConnectionIndex(inputChannel);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
- connection->status = Connection::STATUS_ZOMBIE;
- connection->looper->removeFd(inputChannel->getReceivePipeFd());
- env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
- connection->inputHandlerObjGlobal = NULL;
- ......
- } // release lock
- ......
- return OK;
- }
- ssize_t connectionIndex = getConnectionIndex(inputChannel);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
- connection->looper->removeFd(inputChannel->getReceivePipeFd());
最後將Connection對象中的回調對象inputHandlerOjbGlobal對象刪除:
- env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
- connection->inputHandlerObjGlobal = NULL;
註冊在應用程序這一側的Client端InputChannel被註銷以後,回到前面的Step 11中,我們繼續分析註銷註冊在InputManager這一側的Server端InputChannel。
Step 15. WindowManagerService.Session.remove
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- private final class Session extends IWindowSession.Stub
- implements IBinder.DeathRecipient {
- ......
- public void remove(IWindow window) {
- removeWindow(this, window);
- }
- ......
- }
- ......
- }
Step 16. WindowManagerService.removeWindow
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- public void removeWindow(Session session, IWindow client) {
- synchronized(mWindowMap) {
- WindowState win = windowForClientLocked(session, client, false);
- if (win == null) {
- return;
- }
- removeWindowLocked(session, win);
- }
- }
- ......
- }
回憶一下前面我們在分析應用程序註冊鍵盤消息管道的過程時,在Step 11(WindowManagerService.addWindow)中,WindowManagerService爲這個即將要激活的Activity窗口創建了一個WindowState對象win,創建的時候,使用了從ViewRoot中傳過來的兩個參數,分別是一個Session對象session和一個IWindow對象client。
在這個函數中,ViewRoot傳過來的兩個參數session和client和上面說的兩個參數是一致的,因此,這個函數首先通過參數session和client得到一個WindowState對象win,然後調用removeWindowLocked來把它從WindowManagerService刪除。
Step 17. WindowManagerService.removeWindowLocked
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- public void removeWindowLocked(Session session, WindowState win) {
- ......
- win.disposeInputChannel();
- ......
- }
- ......
- }
Step 18. WindowState.disposeInputChannel
這個函數定義在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor {
- ......
- private final class WindowState implements WindowManagerPolicy.WindowState {
- ......
- void disposeInputChannel() {
- if (mInputChannel != null) {
- mInputManager.unregisterInputChannel(mInputChannel);
- mInputChannel.dispose();
- mInputChannel = null;
- }
- }
- ......
- }
- ......
- }
Step 19. InputManager.unregisterInputChannel
這個函數定義在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- public class InputManager {
- ......
- public void unregisterInputChannel(InputChannel inputChannel) {
- ......
- nativeUnregisterInputChannel(inputChannel);
- }
- ......
- }
Step 20. InputManager.nativeUnregisterInputChannel
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:
- static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
- ......
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
- ......
- }
Step 21. NativeInputManager.unregisterInputChannel
這個函數定義在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:
- status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel) {
- ......
- return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
- }
Step 22. InputDispatcher.unregisterInputChannel
這個函數定義在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
- ......
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
- ......
- mLooper->removeFd(inputChannel->getReceivePipeFd());
- .....
- } // release lock
- ......
- return OK;
- }
- ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
- ......
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
這樣, 應用程序註銷鍵盤消息接收通道的過程就分析完成了,整個應用程序鍵盤消息處理機制也分析完成了,這是一個比較複雜的過程,要完全理解它還需要花費一些努力和時間,不過,理解了這個過程之後,對Android應用程序框架層的理解就更進一步了。