InputManagerService is started in SystemServer. It wraps C++ InputManager and provides its callbacks. It is divided into Java layer and Native layer. The Java layer is responsible for communicating with Windows ManagerService. The Native layer is the operation container of two key components of the input system, InputReader and InputDispatcher.
InputManagerService is started in the startOtherServices() method. The InputManagerService object is created, followed by the WindowManagerService object. A reference to the InputManagerService object is passed when the WindowManagerService object is created. And call the setWindowManagerCallbacks InputManagerService object and WindowManagerService connection is established. The ActivityManagerService object also calls setWindowManager and passes in a reference to the WindowManagerService object to establish the connection. Finally, the InputManagerService start method is called to start it.
frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer {...public static void main(String[] args) {
newSystemServer().run(); }...private void run(a) {...try{... startOtherServices(); }catch(Throwable ex) { ...... }... }...private void startOtherServices(a) {... WindowManagerService wm =null; . InputManagerService inputManager =null; .try{... Slog.i(TAG,"Input Manager");
1. Create an InputManagerService object
inputManager = new InputManagerService(context);
Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode ! = FactoryTest.FACTORY_TEST_LOW_LEVEL, ! mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor());// 2. Start InputManagerServiceinputManager.start(); . }catch(RuntimeException e) { ...... }}... }Copy the code
The InputManagerService object is created with the context parameter, which represents the SystemServer context. We then create the InputManagerHandler object, through which we can send messages to the DisplayThread. NativeInit is then called to complete native initialization.
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {...public InputManagerService(Context context) {
this.mContext = context;
// 1. Create an InputManagerHandler Handler object. Note that Looper is obtained from DisplayThread.
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
// Whether the dev/input/event or uEvent subsystem is used for the audio jack.
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
// 2. Native initialization returns a pointer to the local input manager service object.
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, newLocalService()); }... }Copy the code
The nativeInit method first converts the messageQueueObj Java object passed to the native MessageQueue object. The NativeInputManager object is then created.
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
Copy the code
Here’s what the NativeInputManager constructor does:
1. Upgrade contextObj and serviceObj to JNI global variables;
2. Assign values to Locked structures;
3. Create an EventHub object — Focus!
4. Create InputManager objects.
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv(a);// Promote contextObj and serviceObj to jNI global variables
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
mInteractive = true;
sp<EventHub> eventHub = new EventHub(a); mInputManager =new InputManager(eventHub, this.this);
}
Copy the code
NativeInputManager class inherits RefBase, InputReaderPolicyInterface, InputDispatcherPolicyInterface and PointerControllerPolicyInterface. Reference counting can be implemented, as well as related policies. Locked is its private structure.
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
class NativeInputManager : public virtual RefBase,
public virtual InputReaderPolicyInterface,
public virtual InputDispatcherPolicyInterface,
public virtual PointerControllerPolicyInterface {
......
private:...struct Locked {
// Display size information
DisplayViewport internalViewport;
DisplayViewport externalViewport;
// System UI visibility
int32_t systemUiVisibility;
// Pointer speed.
int32_t pointerSpeed;
True if pointer gestures are enabled.
bool pointerGesturesEnabled;
// Display touch enabled/disabled.
bool showTouches;
/ / SpriteController singleton
sp<SpriteController> spriteController;
/ / PointerController singletonwp<PointerController> pointerController; } mLocked; . };Copy the code
The EventHub constructor shows up INotify and Epoll, as well as Pipe. Review INotify and Epoll, as well as Pipe, before understanding the following code.
1. Introduction and use of INotify
INotify is a filesystem change notification mechanism provided by the Linux kernel. It monitors file system changes for applications, such as file creation, deletion, and reading and writing. The INotify mechanism has two basic objects, the INotify object and the Watch object, both represented by file descriptors.
The inotify object corresponds to a queue, and applications can add multiple listeners to the inotify object. When a monitored event occurs, you can read the event information from the inotify object using the read() function. Inotify objects can be created by:
int inotifyFd = inotify_init(a);Copy the code
The Watch object describes listening for file system changes. It is a tuple consisting of two elements, the listener target and the event mask. The listening target is a path to the file system, which can be a file or folder. The event mask represents the type of event that needs to be listened for, and each of the masks represents an event. There are many types of events that can be listened for, including file creation (IN_CREATE) and deletion (IN_DELETE). The following code adds a watch object to the inotify object that listens for the creation and deletion of input device nodes:
int wd = inotify_add_watch (inotifyFd, "/dev/input/*", IN_CREATE | IN_DELETE);
Copy the code
After the above watch object is added, when the device node under /dev/in/ is created or deleted, the corresponding event information will be written into the inotify object described by inotifyFd. The event information can be read from the inotifyFd descriptor using the read() function.
Event information is described using the inotify_event structure:
struct inotify_event {
__s32 wd;/* The descriptor of the Watch object corresponding to the event */
__u32 mask;/* The event type, such as file deletion, is IN_DELETE */
__u32 cookie;
__u32 len;/* The length of the name field */
char name[0]; /* Variable length field, used to store the file path that generated this event */
};
Copy the code
When no listening event occurs, one or more unread events can be read in the following ways:
size_t len = read (inotifyFd, events_buf, BUF_LEN);
Copy the code
Events_buf is an array pointer to inotify_event. The number of events that can be read depends on the length of the array. After the event information is successfully read, you can determine the event type and the file path that generated the event based on the fields in the inotify_event structure.
To summarize how INotify works:
Create an inotify object with inotify_init().
Add one or more listeners to an inotify object via inotify_add_watch.
Listen events are read from the inotify object by the read() function. When no new events occur, there is no data in the inotify object that can be read.
Instead of notifying events through callbacks, the INotify mechanism requires the user to actively read the events from the INotify object. So when is the best time to read? That’s where Epoll, another Linux mechanism, comes in.
2. Introduction and use of Epoll
One problem with getting raw input events from device nodes or reading file system events from inotify objects is that these events are accidental. That is, in most cases, file descriptors such as device nodes and inotify objects have no data to read and want to be able to react to events as soon as they arrive. To solve this problem, we do not want to constantly poll these descriptors, nor do we want to create a separate thread for each descriptor to block reads, as this would be a huge waste of resources.
The best solution in this case is to use the Epoll mechanism. Epoll can use a wait to listen for the readable/writable state of multiple descriptors. The wait returns with a readable descriptor or custom data that the user can read and then enter the wait again. Therefore, there is no need to create an independent thread for each descriptor to block reading, avoiding resource waste and getting faster response speed.
The Epoll interface has only three functions:
Epoll_create (int max_fds) : Creates a descriptor for an epoll object. All subsequent operations on epoll are performed using this descriptor. The max_fds parameter indicates the maximum number of descriptors that this epoll object can listen on.
Epoll_ctl (int epfd, int op,int fd, struct epoll_event *event) : function used to manage registration events. This function can add/remove/modify the registration of events.
Int epoll_wait(int epfd, structepoll_event * events, int maxEvents, int timeout) : used to wait for an event to arrive. When this function returns, the Events array parameter will contain the file descriptor that generated the event.
The use of epoll is illustrated by monitoring several descriptor-readable events.
(1) Create an epoll object
First create an epoll object with the epoll_create() function:
int epfd = epoll_create(MAX_FDS)
Copy the code
(2) Populate the epoll_event structure
Each descriptor to be monitored is then populated with an epoll_event structure to describe the monitoring event, and this descriptor and the epoll_event structure are registered with the epoll object through the epoll_ctl() function. The epoll_event structure is defined as follows:
struct epoll_event {
__uint32_tevents; /* Event mask, which specifies the type of event to listen for */
epoll_data_t data; /* User - defined data that is returned to the user unchanged when this event occurs
};
Copy the code
The union of epoll_data_t is defined as follows. Of course, only one field can be used ata time:
typedef union epoll_data {
void*ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
Copy the code
The events field in the epoll_event structure is an event mask that specifies the type of event to listen for, and like INotify, each part of the mask represents a type of event. Common events are EPOLLIN (readable), EPOLLOUT (writable), EPOLLERR (descriptor error), EPOLLHUP (descriptor suspension), and so on.
The Data field is a federation that allows the user to add custom data to the event notification, and when the event occurs, the user-set data field will be returned to the user. In practice, epoll_event.data.fd is usually set as the file descriptor to be monitored. When an event occurs, you can learn the event descriptor based on epoll_event.data.fd.
Epoll_event can be populated as follows:
struct epoll_event eventItem;
memset(&eventItem, 0.sizeof(eventItem));
eventItem.events = EPOLLIN | EPOLLERR | EPOLLHUP; // Listener descriptor readable and error events
eventItem.data.fd= listeningFd; // Enter custom data as the descriptor to listen on
Copy the code
You can then use epoll_ctl() to register the event into the epoll object. Epoll_ctl () takes four arguments:
Epfd is the descriptor of the epoll object created by the epoll_create() function.
Op indicates the operation, including EPOLL_CTL_ADD, DEL, and MOD, which add, delete, and modify registration events respectively.
Fd represents the descriptor to listen on.
The event parameter is an epoll_event structure that describes the details of the listening event.
The registration method is as follows:
// Add event listeners to the epoll object
result = epoll_ctl(epfd, EPOLL_CTL_ADD, listeningFd, &eventItem);
Copy the code
Repeat this step to register multiple event listeners for multiple file descriptors into an epoll object. Once the listener is registered, you can wait for the event to arrive using the epoll_wait() function.
(3) Use the epoll_wait() function to wait for events
The epoll_wait() function puts the caller in a wait state and does not return until one of its registered events has occurred, carrying details about the event that just happened. The signatures are as follows:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Copy the code
Epfd is the epoll object descriptor created by the epoll_create() function.
Events is an array of epoll_events that will be populated when this function returns.
Maxevents indicates the maximum number of events that can be retrieved by this call. Of course, the Events parameter must be large enough to hold this number of events.
Timeout Indicates the event waiting for timeout.
The epoll_wait() function returns a value indicating how many events were retrieved.
(4) Event handling
After epoll_wait returns, you can identify the type and source of the event from the Events and data fields of all epoll_event structures stored in the Events array.
The steps to use Epoll are summarized as follows:
Create an epoll object with epoll_create().
Populate the epoll_events structure with the descriptors you want to listen on and register them with the epoll object using epoll_ctl().
Use epoll_wait() to wait for the event to occur.
Determine the type and source of the event based on the epoll_events structure array returned by epoll_wait() and process it.
Continue to use epoll_wait() to wait for new events to occur.
3. The Pipe is introduced
The pipeline is a basic IPC mechanism that operates between related processes to complete data transfer. A pipe is created by calling the PIPE system function. It has the following characteristics:
1. It is essentially a pseudo file (actually a kernel buffer)
2. It is referenced by two file descriptors, one representing the read end and the other representing the write end.
3. Specify that data flows into the pipe from the write end and flows out from the read end.
The principle of the pipeline: the pipeline uses the ring queue mechanism for the kernel, with the kernel buffer implementation.
Limitations of pipelines:
1. Data can not be written and read by oneself;
2. Once the data is read away, it does not exist in the pipeline and cannot be read repeatedly;
3. Because the pipeline adopts half-duplex communication mode. Therefore, data can only flow in one direction;
4. Pipes can only be used between processes that have a common ancestor.
Create a pipe:
int pipe(int pipefd[2]); // Success: 0; Failure: -1, set errno
Copy the code
The function call returns r/ W file descriptors on success. No need to open, but need to close manually. Fd [0] → r; Fd [1] → W, just as 0 corresponds to standard input and 1 to standard output. Reading or writing data to a pipe file is actually reading or writing to the kernel buffer.
Once a pipe is successfully created, the process that created the pipe (the parent process) controls both the read and write sides of the pipe. How do you implement parent-child communication? Usually the following steps can be taken:
File descriptors fd[0] and fd[1] point to the read and write ends of the pipe.
2. The parent process calls fork to create a child process that also has two file descriptors pointing to the same pipe.
3. The parent process closes the pipe read end, and the child process closes the pipe write end. The parent process can write data to the pipe and the child process can read data from the pipe. Since the pipe is implemented using a circular queue, data flows into the pipe from the write side and out from the read side, thus enabling interprocess communication.
frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
// Create a descriptor for an epoll object
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0."Could not create epoll instance. errno=%d", errno);
// Create an inotify object
mINotifyFd = inotify_init(a);Add one or more listeners to the inotify object via inotify_add_watch
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0."Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
/ / fill epoll_event
struct epoll_event eventItem;
memset(&eventItem, 0.sizeof(eventItem));
eventItem.events = EPOLLIN;// Listen for readable events
eventItem.data.u32 = EPOLL_ID_INOTIFY;// A custom ID that is associated with inotify defined above when the event returns
// Add the inotify event listener to the epoll object
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result ! =0."Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
// Create a pipe
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result ! =0."Could not create wake pipe. errno=%d", errno);
// Read-end file descriptor
mWakeReadPipeFd = wakeFds[0];
// Write the file descriptor
mWakeWritePipeFd = wakeFds[1];
// Set the pipe reader to non-blocking mode
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result ! =0."Could not make wake read pipe non-blocking. errno=%d",
errno);
// Set the pipe write to non-blocking mode
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result ! =0."Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.u32 = EPOLL_ID_WAKE;// A custom ID that is associated with the Pipe defined above when the event returns
// Add the pipe event listener to the epoll object
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result ! =0."Could not add wake read pipe to epoll instance. errno=%d",
errno);
int major, minor;
// Get the kernel version number
getLinuxRelease(&major, &minor);
EPOLLWAKEUP was introduced in kernel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
Copy the code
What do you do to create an EventHub object? Next, look at InputManager object creation.
InputManager is the heart of system event processing and uses two threads.
1.InputReaderThread (called InputReader) reads and preprocesses raw input events, applies policies, and publishes messages to queues managed by DispatcherThread.
2.InputDispatcherThread (called “InputDispatcher”) threads wait on queues for new events and asynchronously assign them to applications.
By design, the InputReaderThread and InputDispatcherThread classes do not share any internal state. In addition, all communication goes from InputReaderThread to InputDispatcherThread in one way and is never reversed. However, both classes can interact with InputDispatchPolicy.
The InputManager class never makes any calls to Java itself. Instead, InputDispatchPolicy is responsible for performing all external interactions with the system, including invoking DVM services.
frameworks/native/services/inputflinger/InputManager.h
namespace android {
class InputManagerInterface : public virtual RefBase {
protected:
InputManagerInterface() {}virtual ~InputManagerInterface() {}public:
/* Start the InputManager thread */
virtual status_t start(a) = 0;
/* Stop InputManager threads and wait for them to exit */
virtual status_t stop(a) = 0;
/* Get InputReader */
virtual sp<InputReaderInterface> getReader(a) = 0;
/* Get InputDispatcher */
virtual sp<InputDispatcherInterface> getDispatcher(a) = 0;
};
class InputManager : public InputManagerInterface {
protected:
virtual ~InputManager(a);public:
InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
// (test purpose)
InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& dispatcher);
virtual status_t start(a);
virtual status_t stop(a);
virtual sp<InputReaderInterface> getReader(a);
virtual sp<InputDispatcherInterface> getDispatcher(a);
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
void initialize(a);
};
} // namespace android
Copy the code
InputDispatcher and InputReader objects are created in the InputManager constructor. The Initialize method starts two threads, InputReaderThread and InputDispatcherThread.
frameworks/native/services/inputflinger/InputManager.cpp
namespace android {
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
1. Create an InputDispatcher object
mDispatcher = new InputDispatcher(dispatcherPolicy);
// 2. Create InputReader objects
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize(a); }...void InputManager::initialize(a) {
3. Create an InputReaderThread object
mReaderThread = new InputReaderThread(mReader);
Create an InputDispatcherThread object
mDispatcherThread = new InputDispatcherThread(mDispatcher); }... }// namespace android
Copy the code
1. Create an InputDispatcher object, initialize a set of properties, and create a Looper object.
frameworks/native/services/inputflinger/InputDispatcher.cpp
namespace android {
......
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
mKeyRepeatState.lastKeyEntry = NULL;
policy->getDispatcherConfiguration(&mConfig); }... }Copy the code
2. Create an InputReader object, initialize a set of properties, create a QueuedInputListener object (queue and defer dispatch of decode events until refreshed), refresh the configuration, and update global MetaState.
frameworks/native/services/inputflinger/InputReader.cpp
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
AutoMutex _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked(a); }// release lock
}
Copy the code
3. Create an InputReaderThread object
frameworks/native/services/inputflinger/InputReader.cpp
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
Thread(/*canCallJava*/ true), mReader(reader) {
}
Copy the code
4. Create an InputDispatcherThread object
frameworks/native/services/inputflinger/InputDispatcher.cpp
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}
Copy the code
The class diagram summarizes the relationships between these classes: