android input system(frameworks) analysis -- InputManager (1)
Inputシステムのframeworkレイヤでの解析は、デバイスノード/dev/input/input 0が存在し、geteventがタッチスクリーンの動作に応答できるため、EventHubとInputManagerのレイヤに問題を特定します.
InputManagerの構造は簡単で、外部に開放されています
プライベートメンバーを含む4つのメンバー関数
====================================================================================================================
次にInputReaderを分析します.
InputReader Thread run以降に繰り返される呼び出し
すなわちmReader->loopOnce();
InputReaderはEventHubを探してデータ(getEvent())を求めますので、まずEventHubのgetEventがどんな仕事をしているかを見てみましょう.
次にscanDir()に回ります.この関数は/dev/inputというディレクトリの各ファイルに対してopenDevice()を実行することにほかならません.後者は本当に働いています.
初期化が振り回された後、getEventに戻り、getEventは最初にデバイスを追加する必要があるほか、その主体は大きなサイクルです.
上のコードセグメントから分かるように、mOpeningDevicesのデバイスに対してgetEvent()呼び出しは、読み込みごとにすぐにDEVICE_に報告されるADDEDイベントを返します.したがって、システムにkeypadとtouchscreenがある場合、スキャンフェーズでgetEvent()が3回返されます.キーボードのDEVICE_ADDED、タッチパネルのDEVICE_ADDEDとスキャン完了を示すFINISHED_DEVICE_SCAN.
InputManagerの構造は簡単で、外部に開放されています
virtual status_t start();
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
virtual sp<InputDispatcherInterface> getDispatcher();
プライベートメンバーを含む4つのメンバー関数
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
は構造も簡単です. mDispatcher = new InputDispatcher(dispatcherPolicy); -- dispatcher
mReader = new InputReader(eventHub, readerPolicy, mDispatcher); -- inputreader
initialize();
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader); -- reader input event
mDispatcherThread = new InputDispatcherThread(mDispatcher); -- dispachter event
}
InputManagerのstartは主にこの2つのスレッドを走らせます.status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
...
return OK;
}
====================================================================================================================
次にInputReaderを分析します.
InputReader Thread run以降に繰り返される呼び出し
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
すなわちmReader->loopOnce();
void InputReader::loopOnce() {
RawEvent rawEvent;
mEventHub->getEvent(& rawEvent);
process(& rawEvent);
}
InputReaderはEventHubを探してデータ(getEvent())を求めますので、まずEventHubのgetEventがどんな仕事をしているかを見てみましょう.
bool EventHub::getEvent(RawEvent* outEvent)
{
outEvent->deviceId = 0;
...
// getEvent , openPlatformInput
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
mOpened = true;
mNeedToSendFinishedDeviceScan = true;
}
}
bool EventHub::openPlatformInput(void)
{
// poll()
mFDCount = 1;
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
mFDs[0].revents = 0;
mDevices[0] = NULL;
...
// device_path = "/dev/input"
res = scanDir(device_path);
...
return true;
}
次にscanDir()に回ります.この関数は/dev/inputというディレクトリの各ファイルに対してopenDevice()を実行することにほかならません.後者は本当に働いています.
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);
// version, id, name
fd = open(deviceName, O_RDWR);
if(fd < 0) ...
if(ioctl(fd, EVIOCGVERSION, &version))
...
if(ioctl(fd, EVIOCGID, &id))
...
if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1)
...
// check to see if the device is on our excluded list - ,
List<String8>::iterator iter = mExcludedDevices.begin();
List<String8>::iterator end = mExcludedDevices.end();
for ( ; iter != end; iter++) {
...
}
// location, idstr
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1)
...
if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1)
...
//
if (fcntl(fd, F_SETFL, O_NONBLOCK))
...
// mDeviceById “ ”, mDeviceById device
int devid = 0;
while (devid < mNumDevicesById) {
if (mDevicesById[devid].device == NULL) {
break;
}
devid++;
}
if (devid >= mNumDevicesById) {
device_ent* new_devids = (device_ent*)realloc(mDevicesById,
sizeof(mDevicesById[0]) * (devid + 1));
if (new_devids == NULL) {
LOGE("out of memory");
return -1;
}
mDevicesById = new_devids;
mNumDevicesById = devid+1;
mDevicesById[devid].device = NULL;
mDevicesById[devid].seq = 0;
}
mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
if (mDevicesById[devid].seq == 0) {
mDevicesById[devid].seq = 1<<SEQ_SHIFT;
}
// poll() kernel input event
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.
// device : keypad? touchscreen? gamepad?
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
// device , key_bitmask
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
...
}
//
// See if this is a trackball (or mouse).
if (test_bit(BTN_MOUSE, key_bitmask)) {
...
}
// touchscreen,
// See if this is a touch pad.
uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
...
}
#ifdef EV_SW
// switch ,
#endif
// , keypad-layout, /usr/keylayout/ xxx.kl
if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
...
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) - keyboard
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;
}
}
// , , , classes
if (hasKeycodeLocked(device, AKEYCODE_Q))
...
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'
",
device->id, name, propName, keylayoutFilename);
}
// device ,
if (device->classes == 0) {
...
}
// mDevicesById mOpeningDevices , getEvent() mOpenningDevices device
mDevicesById[devid].device = device;
device->next = mOpeningDevices;
mOpeningDevices = device;
mDevices[mFDCount] = device;
mFDCount++;
return 0;
}
初期化が振り回された後、getEventに戻り、getEventは最初にデバイスを追加する必要があるほか、その主体は大きなサイクルです.
bool EventHub::getEvent(RawEvent* outEvent)
{
....
// main loop here -
for (;;) {
// Report any devices that had last been removed. -
if (mClosingDevices != NULL) {
...
}
// Report any devices that had last been added -
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s
",
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; // DEVICE_ADDED
}
// After finish scanning all input devices in system, send finished siganal at boot time
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
outEvent->type = FINISHED_DEVICE_SCAN;
outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
return true;
}
// event
... ...
}
}
上のコードセグメントから分かるように、mOpeningDevicesのデバイスに対してgetEvent()呼び出しは、読み込みごとにすぐにDEVICE_に報告されるADDEDイベントを返します.したがって、システムにkeypadとtouchscreenがある場合、スキャンフェーズでgetEvent()が3回返されます.キーボードのDEVICE_ADDED、タッチパネルのDEVICE_ADDEDとスキャン完了を示すFINISHED_DEVICE_SCAN.