InputFlinger崩溃问题分析报告
【NE现场】
DEBUG : pid: 2034, tid: 3409, name: InputReader >>> system_server <<< DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x6fa1bc44 DEBUG : r0 4c37003b r1 746e6658 r2 718a8178 r3 6fa1bc40 DEBUG : r4 9de8234c r5 9fd0e884 r6 9de823c4 r7 9de822e0 DEBUG : r8 9fd0e88c r9 00000000 sl 00000420 fp 9de822e0 DEBUG : ip 9fd0e818 sp 9dcbe7c0 lr 9de82354 pc b6bed43a cpsr a0070030 DEBUG : DEBUG : backtrace: DEBUG : #00 pc 0002b43a /system/lib/libinputflinger.so (android::RawPointerData::copyFrom(android::RawPointerData const&)+99) DEBUG : #01 pc 00030ff9 /system/lib/libinputflinger.so (android::TouchInputMapper::processRawTouches(bool)+136) DEBUG : #02 pc 000315a1 /system/lib/libinputflinger.so (android::MultiTouchInputMapper::process(android::RawEvent const*)+6) DEBUG : #03 pc 0002a75b /system/lib/libinputflinger.so (android::InputDevice::process(android::RawEvent const*, unsigned int)+106) DEBUG : #04 pc 0002a7b7 /system/lib/libinputflinger.so (android::InputReader::processEventsForDeviceLocked(int, android::RawEvent const*, unsigned int)+70) DEBUG : #05 pc 0002a9c3 /system/lib/libinputflinger.so (android::InputReader::processEventsLocked(android::RawEvent const*, unsigned int)+50) DEBUG : #06 pc 0002aad7 /system/lib/libinputflinger.so (android::InputReader::loopOnce()+182) DEBUG : #07 pc 000274a3 /system/lib/libinputflinger.so (android::InputReaderThread::threadLoop()+8) DEBUG : #08 pc 0001006d /system/lib/libutils.so (android::Thread::_threadLoop(void*)+112) DEBUG : #09 pc 0005fbef /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+70) DEBUG : #10 pc 0003f54f /system/lib/libc.so (__pthread_start(void*)+30) DEBUG : #11 pc 00019c2f /system/lib/libc.so (__start_thread+6)
这个问题在多个机型上出现过且概率较高。
由于每次调用栈都一样,且都是system_server的InputRead线程Crash,
且每次都是系统启动的时,Input系统初始化的时候挂掉的。
【问题分析】
用gdb分析core:
(gdb) bt #0 0xb6ba45b8 in android::RawPointerData::copyFrom (this=this@entry=0xa9c88ae0, other=...) at frameworks/native/services/inputflinger/InputReader.cpp:1534 #1 0xb6baa17c in copyFrom (other=..., this=0xa9c88ad8) at frameworks/native/services/inputflinger/InputReader.h:1410 #2 android::TouchInputMapper::processRawTouches (this=0xa9c88800, timeout=<optimized out>) at frameworks/native/services/inputflinger/InputReader.cpp:3986 #3 0xb6baa724 in android::MultiTouchInputMapper::process (this=0xa9c88800, rawEvent=0xae9a0a58) at frameworks/native/services/inputflinger/InputReader.cpp:6488 #4 0xb6ba38dc in android::InputDevice::process (this=0x9df57bc0, rawEvents=rawEvents@entry=0xae9a0908, count=0, count@entry=15) at frameworks/native/services/inputflinger/InputReader.cpp:1065 #5 0xb6ba393a in android::InputReader::processEventsForDeviceLocked (this=0xae9a0800, deviceId=6, rawEvents=0xae9a0908, count=15) at frameworks/native/services/inputflinger/InputReader.cpp:523 #6 0xb6ba3b46 in android::InputReader::processEventsLocked (this=this@entry=0xae9a0800, rawEvents=rawEvents@entry=0xae9a0908, count=count@entry=15) at frameworks/native/services/inputflinger/InputReader.cpp:358 #7 0xb6ba3c5a in android::InputReader::loopOnce (this=0xae9a0800) at frameworks/native/services/inputflinger/InputReader.cpp:307 #8 0xb6ba0624 in android::InputReaderThread::threadLoop (this=<optimized out>) at frameworks/native/services/inputflinger/InputReader.cpp:919 #9 0xb6f2f06e in android::Thread::_threadLoop (user=0x9dfcde40) at system/core/libutils/Threads.cpp:758 #10 0xb6e58d48 in android::AndroidRuntime::javaThreadShell (args=<optimized out>) at frameworks/base/core/jni/AndroidRuntime.cpp:1215 #11 0xb6d07550 in __pthread_start (arg=0x9dbba930, arg@entry=<error reading variable: value has been optimized out>) at bionic/libc/bionic/pthread_create.cpp:199 #12 0xb6ce1c30 in __start_thread (fn=<optimized out>, arg=<optimized out>) at bionic/libc/bionic/clone.cpp:41 #13 0x00000000 in ?? ()
查看源码,崩溃的地方是:
void RawPointerData::copyFrom(const RawPointerData& other) { pointerCount = other.pointerCount; hoveringIdBits = other.hoveringIdBits; touchingIdBits = other.touchingIdBits; for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; int id = pointers[i].id; idToIndex[id] = other.idToIndex[id]; } }
这里挂掉,要么是other值有问题,要么是id值有问题。
(gdb) p &other $95 = (const android::RawPointerData *) 0x9ea21c18 (gdb) p id $96 = 1953391990
显然,是id值异常了。
这个问题在同样一个模块高概率出现,说明很可能这部分逻辑有问题,所以得分析代码流程。
走到它的上一级函数,查看这个other及id是怎么来的:
void TouchInputMapper::processRawTouches(bool timeout) { ... const size_t N = mRawStatesPending.size(); size_t count; for(count = 0; count < N; count++) { const RawState& next = mRawStatesPending[count]; ... mCurrentRawState.copyFrom(next); if (mCurrentRawState.when < mLastRawState.when) { mCurrentRawState.when = mLastRawState.when; } cookAndDispatch(mCurrentRawState.when); }
是从mRawStatesPending里面来的。继续往上推导,看这个mRawStatesPending是怎么来的:
void TouchInputMapper::sync(nsecs_t when) { const RawState* last = mRawStatesPending.isEmpty() ? &mCurrentRawState : &mRawStatesPending.top(); // Push a new state. mRawStatesPending.push(); RawState* next = &mRawStatesPending.editTop(); next->clear(); next->when = when; // Sync button state. next->buttonState = mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); // Sync scroll next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); mCursorScrollAccumulator.finishSync(); // Sync touch syncTouch(when, next); // Assign pointer ids. if (!mHavePointerIds) { assignPointerIds(last, next); } ... processRawTouches(false /*timeout*/); }
mRawStatesPending是个缓冲区,这里先调用push来获取一个RawState,并调用clear()来初始化RawState。
然后调用syncTouch()来对新的RawState赋值,crash的RawState就是这个新构建的RawState。
要看这个新的RawState如何被构建的,那得看syncTouch()的实现:
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); size_t outCount = 0; BitSet32 newPointerIdBits; for (size_t inIndex = 0; inIndex < inCount; inIndex++) { const MultiTouchMotionAccumulator::Slot* inSlot = mMultiTouchMotionAccumulator.getSlot(inIndex); if (!inSlot->isInUse()) { continue; } ... RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; outPointer.x = inSlot->getX(); outPointer.y = inSlot->getY(); outPointer.pressure = inSlot->getPressure(); outPointer.touchMajor = inSlot->getTouchMajor(); outPointer.touchMinor = inSlot->getTouchMinor(); outPointer.toolMajor = inSlot->getToolMajor(); outPointer.toolMinor = inSlot->getToolMinor(); outPointer.orientation = inSlot->getOrientation(); outPointer.distance = inSlot->getDistance(); outPointer.tiltX = 0; outPointer.tiltY = 0; outPointer.toolType = inSlot->getToolType(); ... bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && (mTouchButtonAccumulator.isHovering() || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); outPointer.isHovering = isHovering; // Assign pointer id using tracking id if available. mHavePointerIds = true; int32_t trackingId = inSlot->getTrackingId(); int32_t id = -1; if (trackingId >= 0) { for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { uint32_t n = idBits.clearFirstMarkedBit(); if (mPointerTrackingIdMap[n] == trackingId) { id = n; } } if (id < 0 && !mPointerIdBits.isFull()) { id = mPointerIdBits.markFirstUnmarkedBit(); mPointerTrackingIdMap[id] = trackingId; } } if (id < 0) { mHavePointerIds = false; outState->rawPointerData.clearIdBits(); newPointerIdBits.clear(); } else { outPointer.id = id; outState->rawPointerData.idToIndex[id] = outCount; outState->rawPointerData.markIdBit(id, isHovering); newPointerIdBits.markBit(id); } outCount += 1; } outState->rawPointerData.pointerCount = outCount; mPointerIdBits = newPointerIdBits; mMultiTouchMotionAccumulator.finishSync(); }
出问题的Pointer的具体值就是在这里赋值的,
从mMultiTouchMotionAccumulator里找到isInUse为true的时候就把对应slot里的内容拷贝给pointer里。
注意,我们关注的的id也是这里赋值的。
通过GDB对比mMultiTouchMotionAccumulator和最终Crash时候的Pointer.
(gdb) p mMultiTouchMotionAccumulator.mSlotCount $71 = 10 (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*0) $81 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 0, mAbsMTTouchMajor = 11, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*1) $82 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 483, mAbsMTPositionY = 115, mAbsMTTouchMajor = 8, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*2) $83 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 207, mAbsMTTouchMajor = 10, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*3) $84 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 641, mAbsMTPositionY = 579, mAbsMTTouchMajor = 9, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*4) $85 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 435, mAbsMTPositionY = 493, mAbsMTTouchMajor = 7, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*5) $86 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 20, mAbsMTTouchMajor = 0, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*6) $87 = { mInUse = true, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 598, mAbsMTPositionY = 870, mAbsMTTouchMajor = 3, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = 71, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*7) $97 = { mInUse = false, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 0, mAbsMTTouchMajor = 0, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*8) $98 = { mInUse = false, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 0, mAbsMTTouchMajor = 0, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 } (gdb) p *(android::MultiTouchMotionAccumulator::Slot *) (0x9ed3fa00+sizeof(android::MultiTouchMotionAccumulator::Slot)*9) $99 = { mInUse = false, mHaveAbsMTTouchMinor = false, mHaveAbsMTWidthMinor = false, mHaveAbsMTToolType = false, mAbsMTPositionX = 0, mAbsMTPositionY = 0, mAbsMTTouchMajor = 0, mAbsMTTouchMinor = 0, mAbsMTWidthMajor = 0, mAbsMTWidthMinor = 0, mAbsMTOrientation = 0, mAbsMTTrackingId = -1, mAbsMTPressure = 0, mAbsMTDistance = 0, mAbsMTToolType = 0 }
可以看出,一共有10个slot,其中前7个是mInUse=true的。
对应Pointer的数据如下:
$89 = (const android::RawPointerData &) @0x9ea21c18: {
pointerCount = 7,
pointers = {{
id = 0,
x = 0,
y = 0,
pressure = 0,
touchMajor = 11,
touchMinor = 11,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 1953391990,
x = 483,
y = 115,
pressure = 0,
touchMajor = 8,
touchMinor = 8,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 0,
x = 0,
y = 207,
pressure = 0,
touchMajor = 10,
touchMinor = 10,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 0,
x = 641,
y = 579,
pressure = 0,
touchMajor = 9,
touchMinor = 9,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 0,
x = 435,
y = 493,
pressure = 0,
touchMajor = 7,
touchMinor = 7,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 0,
x = 0,
y = 20,
pressure = 0,
touchMajor = 0,
touchMinor = 0,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
}, {
id = 0,
x = 598,
y = 870,
pressure = 0,
touchMajor = 3,
touchMinor = 3,
toolMajor = 0,
toolMinor = 0,
orientation = 0,
distance = 0,
tiltX = 0,
tiltY = 0,
toolType = 1,
isHovering = false
},
...
}},
hoveringIdBits = {
value = 0
},
touchingIdBits = {
value = 2147483648
},
idToIndex = {6, 0 <repeats 31 times>}
}
发现有效数据pointerCount刚好是7个,且数据也是非常相近。除了第二个Pinter里的id值,这个id值就是crash时的1953391990。
所以得重点看这个id是怎么来的,简化MultiTouchInputMapper::syncTouch():
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); size_t outCount = 0; BitSet32 newPointerIdBits; for (size_t inIndex = 0; inIndex < inCount; inIndex++) { const MultiTouchMotionAccumulator::Slot* inSlot = mMultiTouchMotionAccumulator.getSlot(inIndex); if (!inSlot->isInUse()) { continue; } ... // Assign pointer id using tracking id if available. mHavePointerIds = true; int32_t trackingId = inSlot->getTrackingId(); int32_t id = -1; if (trackingId >= 0) { for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { uint32_t n = idBits.clearFirstMarkedBit(); if (mPointerTrackingIdMap[n] == trackingId) { id = n; } } if (id < 0 && !mPointerIdBits.isFull()) { id = mPointerIdBits.markFirstUnmarkedBit(); mPointerTrackingIdMap[id] = trackingId; } } if (id < 0) { mHavePointerIds = false; outState->rawPointerData.clearIdBits(); newPointerIdBits.clear(); } else { outPointer.id = id; outState->rawPointerData.idToIndex[id] = outCount; outState->rawPointerData.markIdBit(id, isHovering); newPointerIdBits.markBit(id); } outCount += 1; } outState->rawPointerData.pointerCount = outCount; mPointerIdBits = newPointerIdBits; mMultiTouchMotionAccumulator.finishSync(); }
从代码中可以看出,当我们trackingId为负值时,这里不会更新id值!
而我们从其上一级函数的语义可以看出来,这里的outState里的初始值都是无效的。
而我们mMultiTouchMotionAccumulator的前6个trackingId都是-1,也就是说前6个都不会更新id。
对比SingleTouchInputMapper::syncTouch()里是有对id赋初值的。
void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { ... RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; outPointer.id = 0; outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); outPointer.touchMajor = 0; ... outPointer.isHovering = isHovering; } }
很可能就是这里的问题了,看起来是Android原生问题。为此去查Android的gerrit,发现确实有在这里做了修改。
【解决方案】
https://android-review.googlesource.com/#/c/174790/
只不过它的改法不是简单的赋初值,它的逻辑如下:
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); size_t outCount = 0; BitSet32 newPointerIdBits; bool needRecomputePointerIds = false; for (size_t inIndex = 0; inIndex < inCount; inIndex++) { const MultiTouchMotionAccumulator::Slot* inSlot = mMultiTouchMotionAccumulator.getSlot(inIndex); if (!inSlot->isInUse()) { continue; } ... // Assign pointer id using tracking id if available. mHavePointerIds = true; int32_t trackingId = inSlot->getTrackingId(); int32_t id = -1; if (trackingId >= 0) { for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { uint32_t n = idBits.clearFirstMarkedBit(); if (mPointerTrackingIdMap[n] == trackingId) { id = n; } } if (id < 0 && !mPointerIdBits.isFull()) { id = mPointerIdBits.markFirstUnmarkedBit(); mPointerTrackingIdMap[id] = trackingId; } } if (id < 0) { needRecomputePointerIds = true; //mHavePointerIds = false; outState->rawPointerData.clearIdBits(); newPointerIdBits.clear(); } else { outPointer.id = id; outState->rawPointerData.idToIndex[id] = outCount; outState->rawPointerData.markIdBit(id, isHovering); newPointerIdBits.markBit(id); } outCount += 1; } if (needRecomputePointerIds) { mHavePointerIds = false; } outState->rawPointerData.pointerCount = outCount; mPointerIdBits = newPointerIdBits; mMultiTouchMotionAccumulator.finishSync(); }
原先的逻辑里,最后一个slot的id>0,mHavePointerIds值就是true。
修改后的逻辑是,只要有一个id<0也就是trackingId为负数,则mHavePointerIds值是false。
对应我们的case,最后一个slot,其trackingId是74,这里对应的mHavePointerIds就是true了。
而如果按照修改后的逻辑,这里mHavePointerIds应该就是false了。
这个mHavePointerIds有啥用呢?
void TouchInputMapper::sync(nsecs_t when) { const RawState* last = mRawStatesPending.isEmpty() ? &mCurrentRawState : &mRawStatesPending.top(); // Push a new state. mRawStatesPending.push(); RawState* next = &mRawStatesPending.editTop(); next->clear(); next->when = when; ... // Sync touch syncTouch(when, next); // Assign pointer ids. if (!mHavePointerIds) { assignPointerIds(last, next); } ... processRawTouches(false /*timeout*/); }
syncTouch()后会判断mHavePointerIds,如果是false,则重新分配id。
这样就不会出现crash了。