summaryrefslogtreecommitdiff
path: root/modules/input/evdev/InputDevice.cpp
blob: a1bce643da19bc2337165f2127c360f32fc54ee3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "InputDevice"
//#define LOG_NDEBUG 0

// Enables debug output for processing input events
#define DEBUG_INPUT_EVENTS 0

#include "InputDevice.h"

#include <linux/input.h>

#define __STDC_FORMAT_MACROS
#include <cinttypes>
#include <cstdlib>
#include <string>

#include <utils/Log.h>
#include <utils/Timers.h>

#include "InputHost.h"
#include "InputHub.h"
#include "MouseInputMapper.h"
#include "SwitchInputMapper.h"


namespace android {

static InputBus getInputBus(const std::shared_ptr<InputDeviceNode>& node) {
    switch (node->getBusType()) {
        case BUS_USB:
            return INPUT_BUS_USB;
        case BUS_BLUETOOTH:
            return INPUT_BUS_BT;
        case BUS_RS232:
            return INPUT_BUS_SERIAL;
        default:
            // TODO: check for other linux bus types that might not be built-in
            return INPUT_BUS_BUILTIN;
    }
}

static uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
    // Touch devices get dibs on touch-related axes.
    if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
        switch (axis) {
            case ABS_X:
            case ABS_Y:
            case ABS_PRESSURE:
            case ABS_TOOL_WIDTH:
            case ABS_DISTANCE:
            case ABS_TILT_X:
            case ABS_TILT_Y:
            case ABS_MT_SLOT:
            case ABS_MT_TOUCH_MAJOR:
            case ABS_MT_TOUCH_MINOR:
            case ABS_MT_WIDTH_MAJOR:
            case ABS_MT_WIDTH_MINOR:
            case ABS_MT_ORIENTATION:
            case ABS_MT_POSITION_X:
            case ABS_MT_POSITION_Y:
            case ABS_MT_TOOL_TYPE:
            case ABS_MT_BLOB_ID:
            case ABS_MT_TRACKING_ID:
            case ABS_MT_PRESSURE:
            case ABS_MT_DISTANCE:
                return INPUT_DEVICE_CLASS_TOUCH;
        }
    }

    // External stylus gets the pressure axis
    if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
        if (axis == ABS_PRESSURE) {
            return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
        }
    }

    // Joystick devices get the rest.
    return INPUT_DEVICE_CLASS_JOYSTICK;
}

EvdevDevice::EvdevDevice(InputHostInterface* host, const std::shared_ptr<InputDeviceNode>& node) :
    mHost(host), mDeviceNode(node), mDeviceDefinition(mHost->createDeviceDefinition()) {

    InputBus bus = getInputBus(node);
    mInputId = mHost->createDeviceIdentifier(
            node->getName().c_str(),
            node->getProductId(),
            node->getVendorId(),
            bus,
            node->getUniqueId().c_str());

    createMappers();
    configureDevice();

    // If we found a need for at least one mapper, register the device with the
    // host. If there were no mappers, this device is effectively ignored, as
    // the host won't know about it.
    if (mMappers.size() > 0) {
        mDeviceHandle = mHost->registerDevice(mInputId, mDeviceDefinition);
        for (const auto& mapper : mMappers) {
            mapper->setDeviceHandle(mDeviceHandle);
        }
    }
}

void EvdevDevice::createMappers() {
    // See if this is a cursor device such as a trackball or mouse.
    if (mDeviceNode->hasKey(BTN_MOUSE)
            && mDeviceNode->hasRelativeAxis(REL_X)
            && mDeviceNode->hasRelativeAxis(REL_Y)) {
        mClasses |= INPUT_DEVICE_CLASS_CURSOR;
        mMappers.push_back(std::make_unique<MouseInputMapper>());
    }

    bool isStylus = false;
    bool haveGamepadButtons = mDeviceNode->hasKeyInRange(BTN_MISC, BTN_MOUSE) ||
            mDeviceNode->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI);

    // See if this is a touch pad or stylus.
    // Is this a new modern multi-touch driver?
    if (mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_X)
            && mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_Y)) {
        // Some joysticks such as the PS3 controller report axes that conflict
        // with the ABS_MT range. Try to confirm that the device really is a
        // touch screen.
        if (mDeviceNode->hasKey(BTN_TOUCH) || !haveGamepadButtons) {
            mClasses |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
            //mMappers.push_back(std::make_unique<MultiTouchInputMapper>());
        }
    // Is this an old style single-touch driver?
    } else if (mDeviceNode->hasKey(BTN_TOUCH)
            && mDeviceNode->hasAbsoluteAxis(ABS_X)
            && mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
        mClasses |= INPUT_DEVICE_CLASS_TOUCH;
        //mMappers.push_back(std::make_unique<SingleTouchInputMapper>());
    // Is this a BT stylus?
    } else if ((mDeviceNode->hasAbsoluteAxis(ABS_PRESSURE) || mDeviceNode->hasKey(BTN_TOUCH))
            && !mDeviceNode->hasAbsoluteAxis(ABS_X) && !mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
        mClasses |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
        //mMappers.push_back(std::make_unique<ExternalStylusInputMapper>());
        isStylus = true;
        mClasses &= ~INPUT_DEVICE_CLASS_KEYBOARD;
    }

    // See if this is a keyboard. Ignore everything in the button range except
    // for joystick and gamepad buttons which are handled like keyboards for the
    // most part.
    // Keyboard will try to claim some of the stylus buttons but we really want
    // to reserve those so we can fuse it with the touch screen data. Note this
    // means an external stylus cannot also be a keyboard device.
    if (!isStylus) {
        bool haveKeyboardKeys = mDeviceNode->hasKeyInRange(0, BTN_MISC) ||
            mDeviceNode->hasKeyInRange(KEY_OK, KEY_CNT);
        if (haveKeyboardKeys || haveGamepadButtons) {
            mClasses |= INPUT_DEVICE_CLASS_KEYBOARD;
            //mMappers.push_back(std::make_unique<KeyboardInputMapper>());
        }
    }

    // See if this device is a joystick.
    // Assumes that joysticks always have gamepad buttons in order to
    // distinguish them from other devices such as accelerometers that also have
    // absolute axes.
    if (haveGamepadButtons) {
        uint32_t assumedClasses = mClasses | INPUT_DEVICE_CLASS_JOYSTICK;
        for (int i = 0; i < ABS_CNT; ++i) {
            if (mDeviceNode->hasAbsoluteAxis(i)
                    && getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) {
                mClasses = assumedClasses;
                //mMappers.push_back(std::make_unique<JoystickInputMapper>());
                break;
            }
        }
    }

    // Check whether this device has switches.
    for (int i = 0; i < SW_CNT; ++i) {
        if (mDeviceNode->hasSwitch(i)) {
            mClasses |= INPUT_DEVICE_CLASS_SWITCH;
            mMappers.push_back(std::make_unique<SwitchInputMapper>());
            break;
        }
    }

    // Check whether this device supports the vibrator.
    // TODO: decide if this is necessary.
    if (mDeviceNode->hasForceFeedback(FF_RUMBLE)) {
        mClasses |= INPUT_DEVICE_CLASS_VIBRATOR;
        //mMappers.push_back(std::make_unique<VibratorInputMapper>());
    }

    ALOGD("device %s classes=0x%x %zu mappers", mDeviceNode->getPath().c_str(), mClasses,
            mMappers.size());
}

void EvdevDevice::configureDevice() {
    for (const auto& mapper : mMappers) {
        auto reportDef = mHost->createInputReportDefinition();
        if (mapper->configureInputReport(mDeviceNode.get(), reportDef)) {
            mDeviceDefinition->addReport(reportDef);
        } else {
            mHost->freeReportDefinition(reportDef);
        }

        reportDef = mHost->createOutputReportDefinition();
        if (mapper->configureOutputReport(mDeviceNode.get(), reportDef)) {
            mDeviceDefinition->addReport(reportDef);
        } else {
            mHost->freeReportDefinition(reportDef);
        }
    }
}

void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) {
#if DEBUG_INPUT_EVENTS
    std::string log;
    log.append("---InputEvent for device %s---\n");
    log.append("   when:  %" PRId64 "\n");
    log.append("   type:  %d\n");
    log.append("   code:  %d\n");
    log.append("   value: %d\n");
    ALOGD(log.c_str(), mDeviceNode->getPath().c_str(), event.when, event.type, event.code,
            event.value);
#endif

    // Bug 7291243: Add a guard in case the kernel generates timestamps
    // that appear to be far into the future because they were generated
    // using the wrong clock source.
    //
    // This can happen because when the input device is initially opened
    // it has a default clock source of CLOCK_REALTIME.  Any input events
    // enqueued right after the device is opened will have timestamps
    // generated using CLOCK_REALTIME.  We later set the clock source
    // to CLOCK_MONOTONIC but it is already too late.
    //
    // Invalid input event timestamps can result in ANRs, crashes and
    // and other issues that are hard to track down.  We must not let them
    // propagate through the system.
    //
    // Log a warning so that we notice the problem and recover gracefully.
    if (event.when >= currentTime + s2ns(10)) {
        // Double-check. Time may have moved on.
        auto time = systemTime(SYSTEM_TIME_MONOTONIC);
        if (event.when > time) {
            ALOGW("An input event from %s has a timestamp that appears to have "
                    "been generated using the wrong clock source (expected "
                    "CLOCK_MONOTONIC): event time %" PRId64 ", current time %" PRId64
                    ", call time %" PRId64 ". Using current time instead.",
                    mDeviceNode->getPath().c_str(), event.when, time, currentTime);
            event.when = time;
        } else {
            ALOGV("Event time is ok but failed the fast path and required an extra "
                    "call to systemTime: event time %" PRId64 ", current time %" PRId64
                    ", call time %" PRId64 ".", event.when, time, currentTime);
        }
    }

    for (size_t i = 0; i < mMappers.size(); ++i) {
        mMappers[i]->process(event);
    }
}

}  // namespace android