WindowManagerService的主要两大作用:
1 和surfaceflinger交互,创建surface, 通知surfacelinger窗口的层级、大小、位置等属性。
2 和inputflinger交互, 告知inputflinger当前窗口大小位置,是否可以接受input事件以及窗口可以处理什么类型的事件。
surfacelinger和inputflinger都是系统里面两个负载较重的服务。为了分析WindowManagerService我准备先从inputflinger这端着手,因为这端相对比较容易,采用自顶向下的方式来进行分析。所以今天先看下inputflinger如何将根据WMS告知的窗口情况来派发input事件。
dumpsys是比较好的工具,我们此次不考虑WMS如何告知inputflinger窗口情况,只关心inputflinger如何根据窗口情况派发输入事件。下面是我在桌面启动了一个全屏应用的()场景,使用adb shell dumpsys input 输出如下:
Input Dispatcher State:
DispatchEnabled: 1
DispatchFrozen: 0
FocusedApplication: name='AppWindowToken{9ab95fe token=Token{867fdb9 ActivityRecord{c189380 u0 com.android.contacts/.activities.CompactContactEditorActivity t6}}}', dispatchingTimeout=5000.000ms
FocusedWindow: name='Window{c7ac47b u0 com.android.contacts/com.android.contacts.activities.CompactContactEditorActivity}'
TouchStates: <no displays touched>
Windows:
0: name='Window{6887dc9 u0 NavigationBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x01840068, type=0x000007e3, layer=211000, frame=[0,432][320,480], scale=1.000000, touchableRegion=[0,432][320,480], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms
1: name='Window{7d5502a u0 StatusBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x81840048, type=0x000007d0, layer=161000, frame=[0,0][320,24], scale=1.000000, touchableRegion=[0,0][320,24], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms
2: name='Window{25377a1 u0 KeyguardScrim}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01110900, type=0x000007ed, layer=141000, frame=[0,0][320,432], scale=1.000000, touchableRegion=[0,0][320,432], inputFeatures=0x00000000, ownerPid=1303, ownerUid=1000, dispatchingTimeout=5000.000ms
3: name='Window{3dc7d4 u0 AssistPreviewPanel}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01000118, type=0x000007f1, layer=41000, frame=[0,432][320,432], scale=1.000000, touchableRegion=<empty>, inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms
4: name='Window{c7ac47b u0 com.android.contacts/com.android.contacts.activities.CompactContactEditorActivity}', displayId=0, paused=false, hasFocus=true, hasWallpaper=false, visible=true, canReceiveKeys=true, flags=0x81810120, type=0x00000001, layer=21015, frame=[0,0][320,480], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1993, ownerUid=10002, dispatchingTimeout=5000.000ms
5: name='Window{a245c67 u0 com.android.contacts/com.android.contacts.activities.PeopleActivity}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x81810120, type=0x00000001, layer=21010, frame=[0,0][320,480], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1993, ownerUid=10002, dispatchingTimeout=5000.000ms
6: name='Window{e911fed u0 com.android.launcher/com.android.launcher2.Launcher}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01910120, type=0x00000001, layer=21005, frame=[0,0][320,432], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1601, ownerUid=10008, dispatchingTimeout=5000.000ms
7: name='Window{5df78f9 u0 com.android.systemui.ImageWallpaper}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00000318, type=0x000007dd, layer=21000, frame=[0,0][960,800], scale=1.000000, touchableRegion=[0,0][960,800], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms
输出中windows的信息都来自InputDispatcher的成员变量mWindowHandles,数据结构第一如下
Vector<sp<InputWindowHandle> > mWindowHandles;
mWindowHandles为数组结构,每个元素为一个InputWindowHandle结构,代表一个窗口, 这些InputWindowHandle是Wms告诉inputflinger的,按照z-order从高到低的顺序存储在mWindowHandles数组中。每个InputWindowHandle的成员变量mInfo 描述了window的一些情况。mInfo的类型为InputWindowInfo:
struct InputWindowInfo {
sp<InputChannel> inputChannel; // 事件派发通道
String8 name; // window 名称
int32_t layoutParamsFlags; // flags
int32_t layoutParamsType; // 类型
nsecs_t dispatchingTimeout; // anr超时时间
int32_t frameLeft; // 坐标
int32_t frameTop;
int32_t frameRight;
int32_t frameBottom;
float scaleFactor; // 缩放
Region touchableRegion; // 可触摸区域
bool visible; // 是否可见
bool canReceiveKeys; // 是否可以接收key时间
bool hasFocus; // 是否是焦点window (默认key事件发给焦点window)
bool hasWallpaper; // 是否能显示壁纸
bool paused; // 是否处于暂停状态
int32_t layer; // layer 也就是层级z-order
int32_t ownerPid; // 所属应用进程的pid
int32_t ownerUid; // 所属应用进程的uid
int32_t inputFeatures; // 支持的feature
int32_t displayId; // 所属的屏幕(display) id
}
layoutParamsFlags变量描述了window能处理哪些事件。frameLeft、frameTop、frameRight、frameBottom、scaleFactor、touchableRegion描述了window的坐标,一般情况下window只能处理落在自身范围内的输入事件。visible则表示window是否可见,不可见的window也不能处理input事件。hasFocus变量则表示window是否是焦点window,一般key事件都发送到焦点window。displayId则用于标识window所属的屏幕(display),window只处理落在自己屏幕上的input事件。有了这些背景只是我们来分析代码实现。
对于一个MotionEvent 可以能处理该事件的Window并不是只有焦点窗口,因为Window有一些特殊的layoutParamsFlags可以,可以用于接收到触摸到window外部的事件,我们比较熟悉的PopupWindow.setOutsideTouchable(boolean touchable)就会设置一个比较特殊的layoutParamsFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)。设置该参数的Flags就会收到点击到Window外部的事件。 另外Dialog也有一个函数Dialog.setCanceledOnTouchOutside(boolean cancel) 函数如果参数cancel为true,那么input系统就会把不在Dialog window范围内的事件发送给它,这是通过window的layoutParamsFlags另外一个表示WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL实现的。
下面我列出几个在window事件处理中比较常见的flag。这些flag都是WindowManager.LayoutParams下的常亮。
FLAG | 描述 |
---|---|
FLAG_NOT_FOCUSABLE | 该标志表示window不会获取焦点,不接收key事件 |
FLAG_NOT_TOUCHABLE | 该标志表示window不会处理touch事件 |
FLAG_NOT_TOUCH_MODAL | 该标志表示该window允许将超出该窗口的事件发送给后面的窗口处理,否则事件不会发送到后面窗口,该窗口会处理所有事件,当设置了FLAG_NOT_FOCUSABLE标志,该标志自动置1 |
FLAG_WATCH_OUTSIDE_TOUCH | 当点击了window外部区域,该window可以收到一个ACTION_OUTSIDE事件 |
对于真正处理事件的window,对于相同的MotionEvent,假设是一个ACTION_DOWN事件,目标window要收到一个ACTION_DOWN事件,但是其他window可能要收到ACTION_OUTSIDE事件,这样同一个MotionEvent对多个window产生了不同的事件,这是通过给该window一个TargetFlags来实现的,有些TargetFlag是一次性的。关于TargetFlag描述如下:
FLAG | 描述 |
---|---|
FLAG_DISPATCH_AS_IS | 表示序列事件应该一直发送给该window,没有该标志只需要发送一个事件给该window |
FLAG_DISPATCH_AS_OUTSIDE | 发送一个ACTION_OUTSIDE给该window |
FLAG_DISPATCH_AS_HOVER_EXIT | 发送一个ACTION_HOVER_EXIT事件给该window |
FLAG_DISPATCH_AS_HOVER_ENTER | 发送一个ACTION_HOVER_ENTER给该window |
FLAG_DISPATCH_AS_SLIPPERY_EXIT | 发送一个ACTION_CANCEL给该window |
FLAG_DISPATCH_AS_SLIPPERY_ENTER | 发送一个ACTION_DOWN给该window |
除了上述知识点外,我们还有一点需要理解,一个Motion事件序列一般是: ACTION_DOWN->一系列ACTION_MOVE->ACTION_UP|ACTION_CANCEL。一般在收到ACTION_DOWN的时候确定目标window,后续ACTION_MOVE和ACTION_UP|ACTION_CANCEL事件都应该发送给该窗口,即使我们的ACTION_MOVE已经超出了该window的范围也应该将该事件派发给该window,所以一般确定派发window的在序列事件开始的地方。
下面我们来通过代码来详细解读Input系统如何选择目标窗口,代码是InputDispatcher的findTouchdWindowTargetsLocked
frameworks/native/services/inputflinger/InputDispatcher.cpp
1129 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
1130 const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
1131 bool* outConflictingPointerActions) {
1132 enum InjectionPermission {
1133 INJECTION_PERMISSION_UNKNOWN,
1134 INJECTION_PERMISSION_GRANTED,
1135 INJECTION_PERMISSION_DENIED
1136 };
1137
1138 nsecs_t startTime = now();
1139
1140 // For security reasons, we defer updating the touch state until we are sure that
1141 // event injection will be allowed.
1142 int32_t displayId = entry->displayId;
1143 int32_t action = entry->action;
1144 int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
1145
1146 // Update the touch state as needed based on the properties of the touch event.
1147 int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
1148 InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
1149 sp<InputWindowHandle> newHoverWindowHandle;
1150
1151 // Copy current touch state into mTempTouchState.
1152 // This state is always reset at the end of this function, so if we don't find state
1153 // for the specified display then our initial state will be empty.
1154 const TouchState* oldState = NULL;
1155 ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
1156 if (oldStateIndex >= 0) {
1157 oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
// 复制屏幕处理input事件的状态到mTempTouchState中
1158 mTempTouchState.copyFrom(*oldState);
1159 }
1160
1161 bool isSplit = mTempTouchState.split;
1162 bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0
1163 && (mTempTouchState.deviceId != entry->deviceId
1164 || mTempTouchState.source != entry->source
1165 || mTempTouchState.displayId != displayId);
1166 bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
1167 || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
1168 || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
1169 bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
1170 || maskedAction == AMOTION_EVENT_ACTION_SCROLL
1171 || isHoverAction);
1172 bool wrongDevice = false;
1173 if (newGesture) {
// 对于新的操作序列开始,清空旧的事件处理状态mTempTouchState。
1174 bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
1175 if (switchedDevice && mTempTouchState.down && !down) {
1176 #if DEBUG_FOCUS
1177 ALOGW("Dropping event because a pointer for a different device is already down.");
1178 #endif
1179 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1180 switchedDevice = false;
1181 wrongDevice = true;
1182 goto Failed;
1183 }
1184 mTempTouchState.reset();
1185 mTempTouchState.down = down;
1186 mTempTouchState.deviceId = entry->deviceId;
1187 mTempTouchState.source = entry->source;
1188 mTempTouchState.displayId = displayId;
1189 isSplit = false;
1190 }
1191
1192 if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
1193 /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
// case 1 处理新的操作序列开始,对于当前操作状态为isSplit的状态的情况,AMOTION_EVENT_ACTION_POINTER_DOWN
// 函数将被当新操作序列开始处理
......
1285 } else {
1286 /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// case 2: mov,up,或者cancel或者pointer down事件,应该发送给之前处理case1的对应window上。
......
1341 }
findTouchedWindowTargetsLocked函数比较长我们分段去看,函数的参数currentTime表示当前时间,entry表示要派发的input事件,这里类型为MotionEntry表示为触摸事件。inputTargets是一个输出变量,用于保存最终要处理该事件(entry)对应的window集合。nextWakeupTime也是一个输出变量,用于通知InputDispatcher没有事件处理的时候休眠多久。outConflictingPointerActions也是一个输出变量,用于反馈上级函数当前事件是否有冲突。
对于input事件的处理Android要维护一个状态,要根据当前状态去处理下一个事件。比如我们的一次滑动事件,起始位置落在了普通应用的窗口上,但是我们一直向上滑动,一直滑动到statusbar所在的位置,那么这个事件应该继续派发给普通应用程序而不应该派发给statusbar,因为我们需要事件的连续性,即使statusbar在普通应用窗口上面。 为了实现这个功能就需要做动作序列的追踪。Android的InputDispatcher为每个屏幕创建一个TouchState变量来追踪落在该屏幕的序列事件状态。
struct TouchState {
bool down;
bool split;
int32_t deviceId; // id of the device that is currently down, others are rejected
uint32_t source; // source of the device that is current down, others are rejected
int32_t displayId; // id to the display that currently has a touch, others are rejected
}
down 表示该屏幕已经开始处理一个事件序列(一个input序列一般开始于down事件结束于up或者cancel)。deviceId、source、displayId都是down事件对应的设备信息。split表示当前屏幕上AMOTION_EVENT_ACTION_POINTER_DOWN事件应当当做一个事件序列的开始来处理。当前屏幕上有接受input事件的窗口包含WindowManager.FLAG_SPLIT_TOUCH标志,就会设置split为true。 FLAG_SPLIT_TOUCH这个flag的描述如下:
frameworks/base/core/java/android/view/WindowManager.java
/** Window flag: when set the window will accept for touch events
* outside of its bounds to be sent to other windows that also
* support split touch. When this flag is not set, the first pointer
* that goes down determines the window to which all subsequent touches
* go until all pointers go up. When this flag is set, each pointer
* (not necessarily the first) that goes down determines the window
* to which all subsequent touches of that pointer will go until that
* pointer goes up thereby enabling touches with multiple pointers
* to be split across multiple windows.
*/
public static final int FLAG_SPLIT_TOUCH = 0x00800000;
也就是说AMOTION_EVENT_ACTION_POINTER_DOWN(支持多点触控的屏幕第二个手指落下就会上报该事件)事件将会重新选择派发的window。
回到findTouchedWindowTargetsLocked函数,首先1155-1159行代码找到当前屏幕处理事件序列的状态,保存在mTempTouchState中。
1161-1172行代码isSplit表示该屏幕上有可以接收input事件的window是否设置了WindowManager.FLAG_SPLIT_TOUCH标志,如果isSplit为true后面需要为AMOTION_EVENT_ACTION_POINTER_DOWN事件寻找新的目标window处理。switchedDevice表示新的输入设备的事件,此display应该处理不了,需要丢掉该事件。isHoverAction表示是否是悬停设备(如鼠标)指针的移入移出事件。newGesture表示新的事件序列开始,对于第一个手指落到该设备的情况(ACTION_DOWN),或者鼠标移入移除,或者开始滚动滚轮设备,都被认为是新的事件序列开始。
1173-1190行代码在一个新输入序列的开始的情况下, 如果发生了设备切换,也就是当前display无法处理这个事件,那么直接直接丢掉这个事件。否则的话新事件序列的开始要重置屏幕的TouchState,1184-1189行代码就是用于重置该屏幕的TouchState。
下面1192-1341行代码,分为两个case来找到处理查找处理事件的目标window,case 1的代码为1192-1284行包含两种情况会进入该case1处理:
- 一个事件序列的开始:hover 、 scroll、AMOTION_EVENT_ACTION_DOWN事件。
- 拆分多点触控事件的情况下AMOTION_EVENT_ACTION_POINTER_DOWN事件。
case2的代码为1286-1341行,case2表示该事件序列的前边inpu事件已经找到目标处理窗口,后续事件也应该使用该窗口来处理。包含两种情况会进入该case2处理:
- mov、up、cancel 事件。
- 不拆分多点触控事件的情况下AMOTION_EVENT_ACTION_POINTER_DOWN事件。
其实case1对应的情况是要寻找新的window作为事件处理的window,而case2的情况是直接使用case1中找到的window作为目标窗口。case1对应的事件为一个事件序列的开始,case2对应事件序列开始之后后续事件的处理。
下面我们就按照这两个case,首先来看case1的处理:
1193 /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
1194
1195 int32_t pointerIndex = getMotionEventActionPointerIndex(action);
1196 int32_t x = int32_t(entry->pointerCoords[pointerIndex].
1197 getAxisValue(AMOTION_EVENT_AXIS_X));
1198 int32_t y = int32_t(entry->pointerCoords[pointerIndex].
1199 getAxisValue(AMOTION_EVENT_AXIS_Y));
1200 sp<InputWindowHandle> newTouchedWindowHandle;
1201 bool isTouchModal = false;
1202
1203 // Traverse windows from front to back to find touched window and outside targets.
1204 size_t numWindows = mWindowHandles.size();
1205 for (size_t i = 0; i < numWindows; i++) {
1206 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
1207 const InputWindowInfo* windowInfo = windowHandle->getInfo();
1208 if (windowInfo->displayId != displayId) {
1209 continue; // wrong display
1210 }
1211
1212 int32_t flags = windowInfo->layoutParamsFlags;
1213 if (windowInfo->visible) { // 窗口可见
1214 if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { // 窗口可以接收touch事件
1215 isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
1216 | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
1217 if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
// 该窗口可以消费掉所有后面窗口的事件 或者
// 点击到了窗口区可接收触摸事件区域 那么该窗口为前台窗口,后面的窗口将无法接收
// touch event。保存在newTouchedWindowHandle中,该窗口为事件处主理窗口,后面
// 我们叫它前台窗口( foreground window)
1218 newTouchedWindowHandle = windowHandle;
1219 break; // found touched window, exit window loop
1220 }
1221 }
1222
1223 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
1224 && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
// 在事件处理窗口之上的窗口,如果设置了FLAG_WATCH_OUTSIDE_TOUCH标志,则对于
// AMOTION_EVENT_ACTION_DOWN事件,需要发送给该窗口一个ACTION_OUTSIDE或者
// ACTION_PARTIALLY_OBSCURED事件通知该窗口点击其他窗口的通知。
1225 int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
1226 if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
1227 outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1228 } else if (isWindowObscuredLocked(windowHandle)) {
1229 outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
1230 }
1231
// 添加该窗口到mTempTouchState,用于后续事件派发
1232 mTempTouchState.addOrUpdateWindow(
1233 windowHandle, outsideTargetFlags, BitSet32(0));
1234 }
1235 }
1236 }
1237
1238 // Figure out whether splitting will be allowed for this window.
1239 if (newTouchedWindowHandle != NULL
1240 && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
1241 // New window supports splitting.
// 前台窗口包含需要拆分多点触控事件,设置isSplit为真
1242 isSplit = true;
1243 } else if (isSplit) {
1244 // New window does not support splitting but we have already split events.
1245 // Ignore the new window.
// 新找到的前台窗口不能处理拆分多点触控事件,则设置为null
1246 newTouchedWindowHandle = NULL;
1247 }
1248
1249 // Handle the case where we did not find a window.
1250 if (newTouchedWindowHandle == NULL) {
1251 // Try to assign the pointer to the first foreground window we find, if there is one.
// 如果没有找到前台窗口,则尝试使用之前的前台窗口,没有的话则拒绝派发该事件
1252 newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
1253 if (newTouchedWindowHandle == NULL) {
1254 ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
1255 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1256 goto Failed;
1257 }
1258 }
1259
// 设置targetFlags表示该窗口需要发送什么事件给应用。
1260 // Set target flags.
1261 int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
1262 if (isSplit) {
1263 targetFlags |= InputTarget::FLAG_SPLIT;
1264 }
1265 if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
1266 targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1267 } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
1268 targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
1269 }
1270
1271 // Update hover state.
1272 if (isHoverAction) {
1273 newHoverWindowHandle = newTouchedWindowHandle;
1274 } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
1275 newHoverWindowHandle = mLastHoverWindowHandle;
1276 }
1277
1278 // Update the temporary touch state.
1279 BitSet32 pointerIds;
1280 if (isSplit) {
1281 uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
1282 pointerIds.markBit(pointerId);
1283 }
// 添加前台窗口和targetFlags到mTempTouchState用于后续事件派发
1284 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
针对case 1, 会按照z-order自上而下的遍历window来寻找处理input事件的目标window,要成为目标window要同时满足如下条件:
- window 可见
- window不包含InputWindowInfo::FLAG_NOT_TOUCHABLE标志。
- 点击动作落在了该window区域 或者 该window没有设置FLAG_NOT_FOCUSABLE 和InputWindowInfo::FLAG_NOT_TOUCH_MODAL标志。
条件1 中InputWindowInfo::FLAG_NOT_TOUCHABLE为真表示wndow不能处理touch事件。所以input系统不会把MotionEvent 事件发送给这种类型的window。 在满足条件1 的前提下点击坐标落到window上一定是目标window。另外如果window的标志同时不包含InputWindowInfo::FLAG_NOT_FOCUSABLE| InputWindowInfo::FLAG_NOT_TOUCH_MODAL 标志则该window可以处理没有落在自己可点击区域上面的事件,这种情况input事件也会发送到这个window处理。注意如果找到这样的window,后面的window就不去看了,因为事件不需要派发给后面的window了。我们后面称该window为前台window。
/** Window flag: even when this window is focusable (its
* {@link #FLAG_NOT_FOCUSABLE} is not set), allow any pointer events
* outside of the window to be sent to the windows behind it. Otherwise
* it will consume all pointer events itself, regardless of whether they
* are inside of the window. */
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
代码1223-1235行代码,在前台window之上,并且设置了 InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH标志的window,它们关心点击自身外部区域 的input事件。点击了它们后面window的区域手指落下的时候需要发送给它们一个ACTION_OUTSIDE事件或者 ACTION_PARTIALLY_OBSCURED 事件。所以当input事件类型为AMOTION_EVENT_ACTION_DOWN时,需要把这些window挑出来放到mTempTouchState中,并且设置上InputTarget::FLAG_WINDOW_IS_OBSCURED或者InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED标志。
这些window最终会收到相关的input事件。
最后1239-1282行代码对目标window做一些处理添后将该window添加到TouchState中。添加window到TouchState中使用的方法为
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds) {
if (targetFlags & InputTarget::FLAG_SPLIT) {
split = true;
}
for (size_t i = 0; i < windows.size(); i++) {
TouchedWindow& touchedWindow = windows.editItemAt(i);
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
}
touchedWindow.pointerIds.value |= pointerIds.value;
return;
}
}
windows.push();
TouchedWindow& touchedWindow = windows.editTop();
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
touchedWindow.pointerIds = pointerIds;
}
addOrUpdateWindow 有三个参数,newTouchedWindowHandle表示处理MontionEvent的窗口, targetFlags描述了该窗口该如何处理该MotionEvent,比如InputTarget::FLAG_WINDOW_IS_OBSCURED标志会给该window发送一个ACTION_OUTSIDE事件 。pointerIds 描述了落在该window上多点触控的点。
看完findTouchedWindowTargetsLocked的case1的处理,再来看下case2 的处理,由于case2的目标window比较明确代码也比case1要简单不少:
1286 /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
1287
1288 // If the pointer is not currently down, then ignore the event.
1289 if (! mTempTouchState.down) {
// 没有down事件无法进入该状态,move,up,cacel 和 point action都需要TouchStat先进入down状态。
1290 #if DEBUG_FOCUS
1291 ALOGW("Dropping event because the pointer is not down or we previously "
1292 "dropped the pointer down event.");
1293 #endif
1294 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1295 goto Failed;
1296 }
1297
1298 // Check whether touches should slip outside of the current foreground window.
1299 if (maskedAction == AMOTION_EVENT_ACTION_MOVE
1300 && entry->pointerCount == 1
1301 && mTempTouchState.isSlippery()) {
// 对于支持拆分多点触控事件的window,需要检查移动事件是否将坐标移动到了目标window之外,
// 如果移动到了目标window之外,需要给新的处理该事件的window发送一个ACTION_DOWN事件
1302 int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
1303 int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
1304
1305 sp<InputWindowHandle> oldTouchedWindowHandle =
1306 mTempTouchState.getFirstForegroundWindowHandle();
1307 sp<InputWindowHandle> newTouchedWindowHandle =
1308 findTouchedWindowAtLocked(displayId, x, y);
1309 if (oldTouchedWindowHandle != newTouchedWindowHandle
1310 && newTouchedWindowHandle != NULL) {
// 处理该事件的window发生变化,就的window需要接收ACTION_CANCEL事件,给旧的window的
// target设置InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT标志,将会发送ACTION_CANCEL
// 事件给它
1311 #if DEBUG_FOCUS
1312 ALOGW("Touch is slipping out of window %s into window %s.",
1313 oldTouchedWindowHandle->getName().string(),
1314 newTouchedWindowHandle->getName().string());
1315 #endif
1316 // Make a slippery exit from the old window.
1317 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
1318 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
1319
// 设置新的前台窗口FLAG_DISPATCH_AS_SLIPPERY_ENTER标志将是它收到
// ACTION_DOWN事件。
1320 // Make a slippery entrance into the new window.
1321 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
1322 isSplit = true;
1323 }
1324
1325 int32_t targetFlags = InputTarget::FLAG_FOREGROUND
1326 | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
1327 if (isSplit) {
1328 targetFlags |= InputTarget::FLAG_SPLIT;
1329 }
1330 if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
1331 targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1332 }
1333
1334 BitSet32 pointerIds;
1335 if (isSplit) {
1336 pointerIds.markBit(entry->pointerProperties[0].id);
1337 }
1338 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
1339 } // 其他情况使用现有mTempTouchState中保存的目标window
1340 }
1341 }
case2 的代码比较简单,1289-1296 行代码对于还没有接收down事件就进到case2的情况是不正常的,也就是事件序列还没开始,就要处理后续事件,所以这里直接拒绝派发该事件。1302-1338行代码大多数情况都不会被执行到,所以默认是使用case 1中计算好的mTempTouchState中的目标窗口来处理事件。
对于1302-1338行代码代码,支持拆分多点触控事件的window,如果要处理的事件是ACTION_MOVE事件,并且该事件落下的点只有一个,那么这种情况下如果ACTION_MOVE事件如果移出了目标window,则需要给目标window发送一个ACTION_CANCEL事件,表示该事件移除了window,还要找到能处理该事件的下一个window,如果可以找的到,则需要将ACTION_MOVE事件变为ACTION_DOWN事件,防止接收方直接收到ACTION_MOVE事件无法处理。
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER 标志会将给window发送一个ACTION_DOWN(这里把ACTION_MOVE变为ACTION_DOWN),InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT标志会将给window发送一个ACTION_DOWN(这里把ACTION_MOVE变为ACTION_CANCEL),
处理完case1和case2基本上目标window就找到了,下面做一些额外的处理
1342
1343 if (newHoverWindowHandle != mLastHoverWindowHandle) {
1344 // Let the previous window know that the hover sequence is over.
1345 if (mLastHoverWindowHandle != NULL) {
1346 #if DEBUG_HOVER
1347 ALOGD("Sending hover exit event to window %s.",
1348 mLastHoverWindowHandle->getName().string());
1349 #endif
// 处理光标事件的window发生变化,要给旧的window一个ACTION_HOVER_EXIT事件
1350 mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
1351 InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
1352 }
1353
1354 // Let the new window know that the hover sequence is starting.
1355 if (newHoverWindowHandle != NULL) {
1356 #if DEBUG_HOVER
1357 ALOGD("Sending hover enter event to window %s.",
1358 newHoverWindowHandle->getName().string());
1359 #endif
// 需要给新的光标处理窗口一个ACTION_HOVER_ENTER事件。
1360 mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
1361 InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
1362 }
1363 }
1364
1365 // Check permission to inject into all touched foreground windows and ensure there
1366 // is at least one touched foreground window.
1367 {
1368 bool haveForegroundWindow = false;
1369 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
1370 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1371 if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
1372 haveForegroundWindow = true;
// 权限检查(处理inject event的权限),没有权限再该事件不能派发。
1373 if (! checkInjectionPermission(touchedWindow.windowHandle,
1374 entry->injectionState)) {
1375 injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
1376 injectionPermission = INJECTION_PERMISSION_DENIED;
1377 goto Failed;
1378 }
1379 }
1380 }
1381 if (! haveForegroundWindow) {
// 没有前台窗口,也应该决绝派发该事件
1382 #if DEBUG_FOCUS
1383 ALOGW("Dropping event because there is no touched foreground window to receive it.");
1384 #endif
1385 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1386 goto Failed;
1387 }
1388
1389 // Permission granted to injection into all touched foreground windows.
1390 injectionPermission = INJECTION_PERMISSION_GRANTED;
1391 }
1392
1393 // Check whether windows listening for outside touches are owned by the same UID. If it is
1394 // set the policy flag that we will not reveal coordinate information to this window.
1395 if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// 如果能处理该input事件的其他窗口与前台窗口不在相同应用进程,则不给它们发送坐标信息,只发送事件。
// 设置了FLAG_ZERO_COORDS标志的目标窗口收到的事件坐标都是0,0
1396 sp<InputWindowHandle> foregroundWindowHandle =
1397 mTempTouchState.getFirstForegroundWindowHandle();
1398 const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
1399 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
1400 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1401 if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
1402 sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
1403 if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
1404 mTempTouchState.addOrUpdateWindow(inputWindowHandle,
1405 InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
1406 }
1407 }
1408 }
1409 }
1410
1411 // Ensure all touched foreground windows are ready for new input.
1412 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
1413 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1414 if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
// 检查前台窗口是否准备好处理事件,如果没准备好调到Unresponsive标号处执行。
1415 // Check whether the window is ready for more input.
1416 String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
1417 touchedWindow.windowHandle, entry, "touched");
1418 if (!reason.isEmpty()) {
// 处理前台window没有准备好的情况,这里可能上报anr
1419 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
1420 NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
1421 goto Unresponsive;
1422 }
1423 }
1424 }
1425
1426 // If this is the first pointer going down and the touched window has a wallpaper
1427 // then also add the touched wallpaper windows so they are locked in for the duration
1428 // of the touch gesture.
1429 // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
1430 // engine only supports touch events. We would need to add a mechanism similar
1431 // to View.onGenericMotionEvent to enable wallpapers to handle these events.
1432 if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// 如果前台窗口使用了墙纸,那么墙纸顶用需要收到后续ACTION_PARTIALLY_OBSCURED事件,做一些特效处理。
1433 sp<InputWindowHandle> foregroundWindowHandle =
1434 mTempTouchState.getFirstForegroundWindowHandle();
1435 if (foregroundWindowHandle->getInfo()->hasWallpaper) {
1436 for (size_t i = 0; i < mWindowHandles.size(); i++) {
1437 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
1438 const InputWindowInfo* info = windowHandle->getInfo();
1439 if (info->displayId == displayId
1440 && windowHandle->getInfo()->layoutParamsType
1441 == InputWindowInfo::TYPE_WALLPAPER) {
1442 mTempTouchState.addOrUpdateWindow(windowHandle,
1443 InputTarget::FLAG_WINDOW_IS_OBSCURED
1444 | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED
1445 | InputTarget::FLAG_DISPATCH_AS_IS,
1446 BitSet32(0));
1447 }
1448 }
1449 }
1450 }
1451
1452 // Success! Output targets.
1453 injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
1454
// 将收集到的事件处理窗口添加到 inputTargets的集合中,用于结果输出。
1455 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
1456 const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
1457 addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
1458 touchedWindow.pointerIds, inputTargets);
1459 }
1460
1461 // Drop the outside or hover touch windows since we will not care about them
1462 // in the next iteration.
// filterNonAsIsTouchWindows函数清除那些不需要再跟踪后续事件的窗口。
1463 mTempTouchState.filterNonAsIsTouchWindows();
1464
1465 Failed:
1466 // Check injection permission once and for all.
// 检查注入权限
1467 if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
1468 if (checkInjectionPermission(NULL, entry->injectionState)) {
1469 injectionPermission = INJECTION_PERMISSION_GRANTED;
1470 } else {
1471 injectionPermission = INJECTION_PERMISSION_DENIED;
1472 }
1473 }
1474
1475 // Update final pieces of touch state if the injector had permission.
1476 if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
1477 if (!wrongDevice) {
1478 if (switchedDevice) {
1479 #if DEBUG_FOCUS
1480 ALOGW("Conflicting pointer actions: Switched to a different device.");
1481 #endif
// 切换设备导致事件冲突
1482 *outConflictingPointerActions = true;
1483 }
1484
1485 if (isHoverAction) {
1486 // Started hovering, therefore no longer down.
1487 if (oldState && oldState->down) {
// 发生事件冲突,指针已经按下又收到了指针悬停事件
1488 #if DEBUG_FOCUS
1489 ALOGW("Conflicting pointer actions: Hover received while pointer was down.");
1490 #endif
1491 *outConflictingPointerActions = true;
1492 }
1493 mTempTouchState.reset();
1494 if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
1495 || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
1496 mTempTouchState.deviceId = entry->deviceId;
1497 mTempTouchState.source = entry->source;
1498 mTempTouchState.displayId = displayId;
// 事件序列开始,更新设备信息
1499 }
1500 } else if (maskedAction == AMOTION_EVENT_ACTION_UP
1501 || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
1502 // All pointers up or canceled.
1503 mTempTouchState.reset();
// AMOTION_EVENT_ACTION_UP 和 AMOTION_EVENT_ACTION_CANCEL表示事件序列已经结束
// 清空mTempTouchState
1504 } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// 点击事件序列开始
1505 // First pointer went down.
1506 if (oldState && oldState->down) {
1507 #if DEBUG_FOCUS
1508 ALOGW("Conflicting pointer actions: Down received while already down.");
1509 #endif
// 事件冲突
1510 *outConflictingPointerActions = true;
1511 }
1512 } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
1513 // One pointer went up.
// 多点触控指针抬起
1514 if (isSplit) {
// 清空处理该事件序列的window
1515 int32_t pointerIndex = getMotionEventActionPointerIndex(action);
1516 uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
1517
1518 for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
1519 TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
1520 if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
1521 touchedWindow.pointerIds.clearBit(pointerId);
1522 if (touchedWindow.pointerIds.isEmpty()) {
1523 mTempTouchState.windows.removeAt(i);
1524 continue;
1525 }
1526 }
1527 i += 1;
1528 }
1529 }
1530 }
1531
1532 // Save changes unless the action was scroll in which case the temporary touch
1533 // state was only valid for this one action.
1534 if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
// 更新该屏幕上的TouchState
1535 if (mTempTouchState.displayId >= 0) {
1536 if (oldStateIndex >= 0) {
1537 mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
1538 } else {
1539 mTouchStatesByDisplay.add(displayId, mTempTouchState);
1540 }
1541 } else if (oldStateIndex >= 0) {
1542 mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
1543 }
1544 }
1545
1546 // Update hover state.
1547 mLastHoverWindowHandle = newHoverWindowHandle;
1548 }
1549 } else {
1550 #if DEBUG_FOCUS
1551 ALOGW("Not updating touch focus because injection was denied.");
1552 #endif
1553 }
1554
1555 Unresponsive:
1556 // Reset temporary touch state to ensure we release unnecessary references to input channels.
// TouchState已经更新到mTouchStatesByDisplay中,这里恢复mTempTouchState
1557 mTempTouchState.reset();
1558
1559 nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
// 更新统计数据
1560 updateDispatchStatisticsLocked(currentTime, entry,
1561 injectionResult, timeSpentWaitingForApplication);
1562 #if DEBUG_FOCUS
1563 ALOGW("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
1564 "timeSpentWaitingForApplication=%0.1fms",
1565 injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
1566 #endif
// 返回最终结果。
1567 return injectionResult;
1568 }
前边case1和case2已经将可以处理相关事件的window都添加到了mTempTouchState中,后续再做一些通用处理。1343-1363行代码如果hover事件处理窗口变化,需要发送给旧的处理hover事件window一个ACTION_HOVER_EXIT事件,这是通过设置InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT 标志来完成的。新的处理hover事件的window需要接收一个ACTION_HOVER_ENTER事件,这是通过设置一个InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER标志来完成的。
1369-1380行代码检查注入事件能否发送给目标应用,主要通过PhoneWindowManager来进行权限检查。
1395-1409行代码如果能接收input的window和前台window不在相同的应用进程中,则后续发给给它的事件将不包含真实坐标,通过设置InputTarget::FLAG_ZERO_COORDS标志来实现的。
1412-1424行代码检查前台窗口有没有准备好处理input事件,如果没有准备好就会调用handleTargetsNotReadyLocked函数来处理,注意应用anr事件也是在这里上报的。
1432-1450 行代码如果前台窗口需要显示墙纸,那么需要发送给墙纸窗口ACTION_PARTIALLY_OBSCURED事件,来实现一些特效。所以给墙纸窗口添加一个InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED标志。
1455-1459行代码将收集到的接收input事件的窗口添加到输出参数inputTargets中,用于给上层函数进行事件派发。
1643行代码 filterNonAsIsTouchWindows函数清除那些不需要再跟踪后续事件的窗口。
void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
for (size_t i = 0 ; i < windows.size(); ) {
TouchedWindow& window = windows.editItemAt(i);
if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
| InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
i += 1;
} else {
windows.removeAt(i);
}
}
}
filterNonAsIsTouchWindows函数如果一个处理事件没有FLAG_DISPATCH_AS_IS标志或者FLAG_DISPATCH_AS_SLIPPERY_ENTER标志,那么该window不需要处理后续事件,就直接删除。后续MOVE, UP, CANCEL等事件将不再发送给该窗口处理。另外除了FLAG_DISPATCH_AS_IS标志会保留外其他标志也会被清除。也就是说后续的ACTION_PARTIALLY_OBSCURED,ACTION_OBSCURED事件也无法收到(只能收到down的事件)。
1500-1503行代码AMOTION_EVENT_ACTION_UP 和AMOTION_EVENT_ACTION_CANCEL事件,表示一个事件序列结束,所以清除到mTempTouchState中保存的事件序列信息。
1512-1530行代码AMOTION_EVENT_ACTION_POINTER_UP事件,如果拆分touch事件的情况,代表处理该事件序列也已经结束,清除对应处理该事件的window。
1534-1544行代码更新TouchStat到mTouchStatesByDisplay。
1557行 TouchState已经更新到mTouchStatesByDisplay中,这里重置mTempTouchState
1559-1565行做一些统计工作,最终返回injectionResult,表示该如何处理该事件。
收集到处理事件的window后,最终事件通过dispatchEventLocked(currentTime, entry, inputTargets)函数派发, dispatchEventLocked函数又会调用prepareDispatchCycleLocked函数, prepareDispatchCycleLocked函数调用enqueueDispatchEntriesLocked函数来将事件转化为发送给应用窗口的对应事件。
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
}
对于不同inputTargetFlage会产生不同的事件派发给window。有
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,InputTarget::FLAG_DISPATCH_AS_OUTSIDE,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,InputTarget::FLAG_DISPATCH_AS_IS,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER前边我们已经介绍过了。
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) { // 如果targetFlag不包含dispatchMode则不会产生相应的事件,直接返回。
return;
}
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
if (!connection->inputState.trackKey(keyEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
connection->getInputChannelName());
#endif
delete dispatchEntry;
return; // skip the inconsistent event
}
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
dispatchEntry->resolvedAction = motionEntry->action;
}
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
&& !connection->inputState.isHovering(
motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
connection->getInputChannelName());
#endif
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
dispatchEntry->resolvedFlags = motionEntry->flags;
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
if (!connection->inputState.trackMotion(motionEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
connection->getInputChannelName());
#endif
delete dispatchEntry;
return; // skip the inconsistent event
}
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
incrementPendingForegroundDispatchesLocked(eventEntry);
}
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
traceOutboundQueueLengthLocked(connection);
}
enqueueDispatchEntryLocked函数根据目标窗口的targetFlag和dispatchMode来设置事件的dispatchEntry->resolvedAction,也就是要发送给该窗口的事件类型。