InputManagerService 是在 SystemServer 中启动的。它包装了 C++ InputManager 并提供其回调。它分为 Java 层和 Native 层两部分。Java 层负责与 WindowManagerService 的通信。而 Native 层则是 InputReader 和 InputDispatcher 两个输入系统关键组件的运行容器。
InputManagerService 在 startOtherServices() 方法中启动,先创建 InputManagerService 对象,接着创建了 WindowManagerService 对象,创建 WindowManagerService 对象时传入了 InputManagerService 对象的引用,而 InputManagerService 对象调用了 setWindowManagerCallbacks 和 WindowManagerService 建立了联系。其中 ActivityManagerService 对象也调用了 setWindowManager 传入了 WindowManagerService 对象的引用从而建立了联系。最后调用 InputManagerService start 方法启动它。
frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer {
......
public static void main(String[] args) {
new SystemServer().run();
}
......
private void run() {
......
try {
......
startOtherServices();
} catch (Throwable ex) {
......
}
......
}
......
private void startOtherServices() {
......
WindowManagerService wm = null;
......
InputManagerService inputManager = null;
......
try {
......
Slog.i(TAG, "Input Manager");
// 1. 创建 InputManagerService 对象
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.启动 InputManagerService
inputManager.start();
......
} catch (RuntimeException e) {
......
}
}
......
}
创建 InputManagerService 对象的时候,传入了参数 context,它代表 SystemServer 上下文。接着创建 InputManagerHandler 对象,通过这个 Handler 可以向 DisplayThread 线程发送消息。然后调用 nativeInit 完成 native 初始化。
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.创建 InputManagerHandler Handler 类型对象,注意 Looper 是从 DisplayThread 获取的。
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
// 是否将 dev/input/event 或 uevent 子系统用于音频插孔。
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
// 2.native 初始化,返回指向本地输入管理器服务对象的指针。
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
......
}
nativeInit 方法中首先将传递来的 messageQueueObj java 对象转化为 native MessageQueue 对象。接着创建了 NativeInputManager 对象。
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);
}
下面是 NativeInputManager 构造函数完成的主要工作:
1.将 contextObj 和 serviceObj 提升为 jni 全局变量;
2.Locked 结构体赋值;
3.创建 EventHub 对象——需要重点关注!
4.InputManager 对象创建。
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();
// 将 contextObj 和 serviceObj 提升为 jni 全局变量
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();
mInputManager = new InputManager(eventHub, this, this);
}
NativeInputManager 类继承了 RefBase、InputReaderPolicyInterface、InputDispatcherPolicyInterface 和 PointerControllerPolicyInterface。可实现引用计数,以及相关策略。Locked 是其私有结构体。
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 {
// 显示尺寸信息
DisplayViewport internalViewport;
DisplayViewport externalViewport;
// 系统 UI 可见性
int32_t systemUiVisibility;
// 指针速度。
int32_t pointerSpeed;
// 如果启用指针手势,则为真。
bool pointerGesturesEnabled;
// 显示触摸功能启用/禁用。
bool showTouches;
// SpriteController 单例
sp<SpriteController> spriteController;
// PointerController 单例
wp<PointerController> pointerController;
} mLocked;
......
};
EventHub 构造函数中出现了 INotify 与 Epoll,以及管道 Pipe,在理解下面代码前,需要复习 INotify 与 Epoll,以及管道 Pipe 知识点。
INotify 是一个 Linux 内核所提供的一种文件系统变化通知机制。它可以为应用程序监控文件系统的变化,如文件的新建、删除和读写等。INotify 机制有两个基本对象,分别为 inotify 对象与 watch 对象,都使用文件描述符表示。
inotify 对象对应了一个队列,应用程序可以向 inotify 对象添加多个监听。当被监听的事件发生时,可以通过 read() 函数从 inotify 对象中将事件信息读取出来。Inotify 对象可以通过以下方式创建:
int inotifyFd = inotify_init();
而 watch 对象则用来描述文件系统的变化事件的监听。它是一个二元组,包括监听目标和事件掩码两个元素。监听目标是文件系统的一个路径,可以是文件也可以是文件夹。而事件掩码则表示了需要需要监听的事件类型,掩码中的每一位代表一种事件。可以监听的事件种类很多,其中就包括文件的创建(IN_CREATE)与删除(IN_DELETE)等。以下代码即可将一个用于监听输入设备节点的创建与删除的 watch 对象添加到 inotify 对象中:
int wd = inotify_add_watch (inotifyFd, "/dev/input/*", IN_CREATE | IN_DELETE);
完成上述 watch 对象的添加后,当 /dev/input/ 下的设备节点发生创建与删除操作时,都会将相应的事件信息写入到 inotifyFd 所描述的 inotify 对象中,此时可以通过 read() 函数从 inotifyFd 描述符中将事件信息读取出来。
事件信息使用结构体 inotify_event 进行描述:
struct inotify_event {
__s32 wd;/* 事件对应的 Watch 对象的描述符 */
__u32 mask;/* 事件类型,例如文件被删除,此处值为 IN_DELETE */
__u32 cookie;
__u32 len;/* name 字段的长度 */
char name[0]; /* 可变长的字段,用于存储产生此事件的文件路径*/
};
当没有监听事件发生时,可以通过如下方式将一个或多个未读取的事件信息读取出来:
size_t len = read (inotifyFd, events_buf, BUF_LEN);
其中 events_buf 是 inotify_event 的数组指针,能够读取的事件数量由取决于数组的长度。成功读取事件信息后,便可根据 inotify_event 结构体的字段判断事件类型以及产生事件的文件路径了。
总结一下 INotify 机制的使用过程:
通过 inotify_init() 创建一个 inotify 对象。
通过 inotify_add_watch 将一个或多个监听添加到 inotify 对象中。
通过 read() 函数从 inotify 对象中读取监听事件。当没有新事件发生时,inotify 对象中无任何可读数据。
INotify 机制并不是通过回调的方式通知事件,而需要使用者主动从 inotify 对象中进行事件读取。那么何时才是读取的最佳时机呢?这就需要借助 Linux 的另一个机制 Epoll 了。
无论是从设备节点中获取原始输入事件还是从 inotify 对象中读取文件系统事件,都面临一个问题,就是这些事件都是偶发的。也就是说,大部分情况下设备节点、inotify 对象这些文件描述符中都是无数据可读的,同时又希望有事件到来时可以尽快地对事件作出反应。为解决这个问题,我们不希望不断地轮询这些描述符,也不希望为每个描述符创建一个单独的线程进行阻塞时的读取,因为这都将会导致资源的极大浪费。
此时最佳的办法是使用 Epoll 机制。Epoll 可以使用一次等待监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符或自定义的数据,使用者可以据此读取所需的数据后可以再次进入等待。因此不需要为每个描述符创建独立的线程进行阻塞读取,避免了资源浪费的同时又可以获得较快的响应速度。
Epoll机制的接口只有三个函数:
epoll_create(int max_fds):创建一个 epoll 对象的描述符,之后对 epoll 的操作均使用这个描述符完成。max_fds 参数表示了此 epoll 对象可以监听的描述符的最大数量。
epoll_ctl(int epfd, int op,int fd, struct epoll_event *event):用于管理注册事件的函数。这个函数可以增加/删除/修改事件的注册。
int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout):用于等待事件的到来。当此函数返回时,events 数组参数中将会包含产生事件的文件描述符。
接下来以监控若干描述符可读事件为例介绍一下 epoll 的用法。
(1) 创建 epoll 对象
首先通过 epoll_create() 函数创建一个epoll对象:
int epfd = epoll_create(MAX_FDS)
(2) 填充epoll_event结构体
接着为每一个需监控的描述符填充 epoll_event 结构体,以描述监控事件,并通 过epoll_ctl() 函数将此描述符与 epoll_event 结构体注册进 epoll 对象。epoll_event 结构体的定义如下:
struct epoll_event {
__uint32_tevents; /* 事件掩码,指明了需要监听的事件种类*/
epoll_data_t data; /* 使用者自定义的数据,当此事件发生时该数据将原封不动地返回给使用者 */
};
epoll_data_t 联合体的定义如下,当然,同一时间使用者只能使用一个字段:
typedef union epoll_data {
void*ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
epoll_event 结构中的 events 字段是一个事件掩码,用以指明需要监听的事件种类,同 INotify 一样,掩码的每一位代表了一种事件。常用的事件有 EPOLLIN(可读),EPOLLOUT(可写),EPOLLERR(描述符发生错误),EPOLLHUP(描述符被挂起)等。
data 字段是一个联合体,它让使用者可以将一些自定义数据加入到事件通知中,当此事件发生时,用户设置的 data 字段将会返回给使用者。在实际使用中常设置 epoll_event.data.fd 为需要监听的文件描述符,事件发生时便可以根据 epoll_event.data.fd 得知引发事件的描述符。
填充 epoll_event 的方法如下:
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN | EPOLLERR | EPOLLHUP; // 监听描述符可读以及出错的事件
eventItem.data.fd= listeningFd; // 填写自定义数据为需要监听的描述符
接下来就可以使用 epoll_ctl() 将事件注册进 epoll 对象。epoll_ctl() 的参数有四个:
epfd 是由 epoll_create() 函数所创建的 epoll 对象的描述符。
op 表示了何种操作,包括 EPOLL_CTL_ADD/DEL/MOD 三种,分别表示增加/删除/修改注册事件。
fd 表示了需要监听的描述符。
event 参数是描述了监听事件的详细信息的 epoll_event 结构体。
注册方法如下:
// 将事件监听添加到epoll对象中去
result = epoll_ctl(epfd, EPOLL_CTL_ADD, listeningFd, &eventItem);
重复这个步骤可以将多个文件描述符的多种事件监听注册到 epoll 对象中。完成了监听的注册之后,便可以通过 epoll_wait() 函数等待事件的到来了。
(3) 使用 epoll_wait() 函数等待事件
epoll_wait() 函数将会使调用者陷入等待状态,直到其注册的事件之一发生之后才会返回,并且携带了刚刚发生的事件的详细信息。其签名如下:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epfd 是由 epoll_create() 函数所创建的 epoll 对象描述符。
events 是一个 epoll_event 的数组,此函数返回时,事件的信息将被填充至此。
maxevents 表示此次调用最多可以获取多少个事件,当然,events 参数必须能够足够容纳这么多事件。
timeout 表示等待超时的事件。
epoll_wait() 函数返回值表示获取了多少个事件。
(4)处理事件
epoll_wait 返回后,便可以根据 events 数组中所保存的所有 epoll_event 结构体的 events 字段与 data 字段识别事件的类型与来源。
Epoll 的使用步骤总结如下:
通过 epoll_create() 创建一个 epoll 对象。
为需要监听的描述符填充 epoll_events 结构体,并使用 epoll_ctl() 注册到 epoll 对象中。
使用 epoll_wait() 等待事件的发生。
根据 epoll_wait() 返回的 epoll_events 结构体数组判断事件的类型与来源并进行处理。
继续使用 epoll_wait() 等待新事件的发生。
管道是一种最基本的 IPC 机制,作用于有血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一个管道。有如下特质:
1.其本质是一个伪文件(实为内核缓冲区)
2.由两个文件描述符引用,一个表示读端,一个表示写端。
3.规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区实现。
管道的局限性:
1.数据自己读不能自己写;
2.数据一旦被读走,便不在管道中存在,不可反复读取;
3.由于管道采用半双工通信方式。因此,数据只能在一个方向上流动;
4.只能在有公共祖先的进程间使用管道。
创建管道:
int pipe(int pipefd[2]); //成功:0;失败:-1,设置 errno
函数调用成功返回 r/w 两个文件描述符。无需 open,但需手动 close。规定:fd[0] → r; fd[1] → w,就像 0 对应标准输入,1 对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤:
1.父进程调用 pipe 函数创建管道,得到两个文件描述符 fd[0]、fd[1] 指向管道的读端和写端。
2.父进程调用 fork 创建子进程,那么子进程也有两个文件描述符指向同一管道。
3.父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
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);
// 创建一个 epoll 对象的描述符
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
// 创建一个 inotify 对象
mINotifyFd = inotify_init();
// 通过 inotify_add_watch 将一个或多个监听添加到 inotify 对象中
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);
// 填充 epoll_event
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;// 监听可读事件
eventItem.data.u32 = EPOLL_ID_INOTIFY;// 自定义 ID,当事件返回时可知是以上定义的 inotify 相关
// 将 inotify 事件监听添加到 epoll 对象中去
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];
// 创建管道
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
// 读端文件描述符
mWakeReadPipeFd = wakeFds[0];
// 写端文件描述符
mWakeWritePipeFd = wakeFds[1];
// 将管道读端设为非阻塞模式
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
// 将管道写端设为非阻塞模式
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;// 自定义 ID,当事件返回时可知是以上定义的 Pipe 相关
// 将管道事件监听添加到 epoll 对象中去
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;
// 获取内核版本号
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP 是内核 3.5 中引入的
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
以上代码中注释详尽的说明了创建 EventHub 对象都干了什么?接下来分析 InputManager 对象创建。
InputManager 是系统事件处理的核心,它使用了两个线程。
1.InputReaderThread(称为“ InputReader”)读取并预处理原始输入事件,应用策略,并将消息发布到由 DispatcherThread 管理的队列中。
2.InputDispatcherThread(称为“ InputDispatcher”)线程在队列上等待新事件,并异步将其分配给应用程序。
根据设计,InputReaderThread 类和 InputDispatcherThread 类不共享任何内部状态。而且,所有通信都是从 InputReaderThread 到 InputDispatcherThread 的一种方式,绝不会反转。但是,这两个类都可以与 InputDispatchPolicy 交互。
InputManager 类从不对 Java 本身进行任何调用。相反,InputDispatchPolicy 负责执行与系统的所有外部交互,包括调用 DVM 服务。
frameworks/native/services/inputflinger/InputManager.h
namespace android {
class InputManagerInterface : public virtual RefBase {
protected:
InputManagerInterface() { }
virtual ~InputManagerInterface() { }
public:
/* 启动 InputManager 线程 */
virtual status_t start() = 0;
/* 停止 InputManager 线程并等待它们退出 */
virtual status_t stop() = 0;
/* 获取 InputReader */
virtual sp<InputReaderInterface> getReader() = 0;
/* 获取 InputDispatcher */
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface {
protected:
virtual ~InputManager();
public:
InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
// (测试目的)
InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& dispatcher);
virtual status_t start();
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
virtual sp<InputDispatcherInterface> getDispatcher();
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
void initialize();
};
} // namespace android
在 InputManager 构造器中创建了 InputDispatcher 和 InputReader 对象,initialize 方法启动了两个线程 InputReaderThread 和 InputDispatcherThread。
frameworks/native/services/inputflinger/InputManager.cpp
namespace android {
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 1.创建 InputDispatcher 对象
mDispatcher = new InputDispatcher(dispatcherPolicy);
// 2.创建 InputReader 对象
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
......
void InputManager::initialize() {
// 3.创建 InputReaderThread 对象
mReaderThread = new InputReaderThread(mReader);
// 4.创建 InputDispatcherThread 对象
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
......
} // namespace android
1.创建 InputDispatcher 对象,初始化了一系列属性,新建了一个 Looper 对象。
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);
}
......
}
2.创建 InputReader 对象,初始化了一系列属性,创建 QueuedInputListener 对象(排队并推迟解码事件的派发,直到刷新为止),刷新配置,并更新了全局 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();
} // release lock
}
3.创建 InputReaderThread 对象
frameworks/native/services/inputflinger/InputReader.cpp
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
Thread(/*canCallJava*/ true), mReader(reader) {
}
4.创建 InputDispatcherThread 对象
frameworks/native/services/inputflinger/InputDispatcher.cpp
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}