Skip to content

Commit 2b6c32c

Browse files
author
Jeff Brown
committed
Fix spurious ANRs in native activities.
Some native activities experienced ANRs when the input consumer deferred an input event due to client-side batching. If the input channel was fully emptied then the client had no way of knowing that it should wake up to handle the deferred input event. This patch also fixes some lock issues in the native activity input queue implementation. In at least one error case, it was possible for a function to exit without releasing the lock. Bug: 6051176 Change-Id: I4d9d843237e69b9834f8d8b360031b677fcab8c3
1 parent b267948 commit 2b6c32c

File tree

6 files changed

+135
-66
lines changed

6 files changed

+135
-66
lines changed

core/jni/android_app_NativeActivity.cpp

Lines changed: 32 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ int32_t AInputQueue::hasEvents() {
162162
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
163163
*outEvent = NULL;
164164

165-
bool finishNow = false;
166-
167165
char byteread;
168166
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
167+
168+
Mutex::Autolock _l(mLock);
169+
169170
if (nRead == 1) {
170-
mLock.lock();
171171
if (mDispatchingKeys.size() > 0) {
172172
KeyEvent* kevent = mDispatchingKeys[0];
173173
*outEvent = kevent;
@@ -178,6 +178,8 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
178178
inflight.finishSeq = 0;
179179
mInFlightEvents.push(inflight);
180180
}
181+
182+
bool finishNow = false;
181183
if (mFinishPreDispatches.size() > 0) {
182184
finish_pre_dispatch finish(mFinishPreDispatches[0]);
183185
mFinishPreDispatches.removeAt(0);
@@ -193,7 +195,6 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
193195
ALOGW("getEvent couldn't find inflight for seq %d", finish.seq);
194196
}
195197
}
196-
mLock.unlock();
197198

198199
if (finishNow) {
199200
finishEvent(*outEvent, true, false);
@@ -206,7 +207,8 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
206207

207208
uint32_t consumerSeq;
208209
InputEvent* myEvent = NULL;
209-
status_t res = mConsumer.consume(this, true /*consumeBatches*/, &consumerSeq, &myEvent);
210+
status_t res = mConsumer.consume(&mPooledInputEventFactory, true /*consumeBatches*/,
211+
&consumerSeq, &myEvent);
210212
if (res != android::OK) {
211213
if (res != android::WOULD_BLOCK) {
212214
ALOGW("channel '%s' ~ Failed to consume input event. status=%d",
@@ -215,6 +217,10 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
215217
return -1;
216218
}
217219

220+
if (mConsumer.hasDeferredEvent()) {
221+
wakeupDispatchLocked();
222+
}
223+
218224
in_flight_event inflight;
219225
inflight.event = myEvent;
220226
inflight.seq = -1;
@@ -255,7 +261,8 @@ void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultH
255261
return;
256262
}
257263

258-
mLock.lock();
264+
Mutex::Autolock _l(mLock);
265+
259266
const size_t N = mInFlightEvents.size();
260267
for (size_t i=0; i<N; i++) {
261268
const in_flight_event& inflight(mInFlightEvents[i]);
@@ -267,111 +274,82 @@ void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultH
267274
mConsumer.getChannel()->getName().string(), res);
268275
}
269276
}
270-
if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
271-
mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
272-
} else {
273-
mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
274-
}
277+
mPooledInputEventFactory.recycle(static_cast<InputEvent*>(event));
275278
mInFlightEvents.removeAt(i);
276-
mLock.unlock();
277279
return;
278280
}
279281
}
280-
mLock.unlock();
281-
282+
282283
ALOGW("finishEvent called for unknown event: %p", event);
283284
}
284285

285286
void AInputQueue::dispatchEvent(android::KeyEvent* event) {
286-
mLock.lock();
287+
Mutex::Autolock _l(mLock);
288+
287289
LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
288290
mDispatchKeyWrite);
289291
mDispatchingKeys.add(event);
290-
wakeupDispatch();
291-
mLock.unlock();
292+
wakeupDispatchLocked();
292293
}
293294

294295
void AInputQueue::finishPreDispatch(int seq, bool handled) {
295-
mLock.lock();
296+
Mutex::Autolock _l(mLock);
297+
296298
LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
297299
finish_pre_dispatch finish;
298300
finish.seq = seq;
299301
finish.handled = handled;
300302
mFinishPreDispatches.add(finish);
301-
wakeupDispatch();
302-
mLock.unlock();
303+
wakeupDispatchLocked();
303304
}
304305

305306
KeyEvent* AInputQueue::consumeUnhandledEvent() {
306-
KeyEvent* event = NULL;
307+
Mutex::Autolock _l(mLock);
307308

308-
mLock.lock();
309+
KeyEvent* event = NULL;
309310
if (mUnhandledKeys.size() > 0) {
310311
event = mUnhandledKeys[0];
311312
mUnhandledKeys.removeAt(0);
312313
}
313-
mLock.unlock();
314314

315315
LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
316-
317316
return event;
318317
}
319318

320319
KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
321-
KeyEvent* event = NULL;
320+
Mutex::Autolock _l(mLock);
322321

323-
mLock.lock();
322+
KeyEvent* event = NULL;
324323
if (mPreDispatchingKeys.size() > 0) {
325324
const in_flight_event& inflight(mPreDispatchingKeys[0]);
326325
event = static_cast<KeyEvent*>(inflight.event);
327326
*outSeq = inflight.seq;
328327
mPreDispatchingKeys.removeAt(0);
329328
}
330-
mLock.unlock();
331329

332330
LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
333-
334331
return event;
335332
}
336333

337334
KeyEvent* AInputQueue::createKeyEvent() {
338-
mLock.lock();
339-
KeyEvent* event;
340-
if (mAvailKeyEvents.size() <= 0) {
341-
event = new KeyEvent();
342-
} else {
343-
event = mAvailKeyEvents.top();
344-
mAvailKeyEvents.pop();
345-
}
346-
mLock.unlock();
347-
return event;
348-
}
335+
Mutex::Autolock _l(mLock);
349336

350-
MotionEvent* AInputQueue::createMotionEvent() {
351-
mLock.lock();
352-
MotionEvent* event;
353-
if (mAvailMotionEvents.size() <= 0) {
354-
event = new MotionEvent();
355-
} else {
356-
event = mAvailMotionEvents.top();
357-
mAvailMotionEvents.pop();
358-
}
359-
mLock.unlock();
360-
return event;
337+
return mPooledInputEventFactory.createKeyEvent();
361338
}
362339

363340
void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
364-
mLock.lock();
341+
Mutex::Autolock _l(mLock);
342+
365343
LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
366344
if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
367345
write_work(mWorkWrite, CMD_DEF_KEY);
368346
}
369347
mUnhandledKeys.add(keyEvent);
370-
mLock.unlock();
371348
}
372349

373350
bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
374-
mLock.lock();
351+
Mutex::Autolock _l(mLock);
352+
375353
LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
376354
const size_t N = mInFlightEvents.size();
377355
for (size_t i=0; i<N; i++) {
@@ -380,7 +358,6 @@ bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
380358
if (inflight.seq >= 0) {
381359
// This event has already been pre-dispatched!
382360
LOG_TRACE("Event already pre-dispatched!");
383-
mLock.unlock();
384361
return false;
385362
}
386363
mSeq++;
@@ -391,7 +368,6 @@ bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
391368
write_work(mWorkWrite, CMD_DEF_KEY);
392369
}
393370
mPreDispatchingKeys.add(inflight);
394-
mLock.unlock();
395371
return true;
396372
}
397373
}
@@ -400,7 +376,7 @@ bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
400376
return false;
401377
}
402378

403-
void AInputQueue::wakeupDispatch() {
379+
void AInputQueue::wakeupDispatchLocked() {
404380
restart:
405381
char dummy = 0;
406382
int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));

include/android_runtime/android_app_NativeActivity.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ extern void android_NativeActivity_hideSoftInput(
6565
* b. Java sends event through default key handler.
6666
* c. event is finished.
6767
*/
68-
struct AInputQueue : public android::InputEventFactoryInterface {
68+
struct AInputQueue {
6969
public:
7070
/* Creates a consumer associated with an input channel. */
7171
explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
@@ -96,16 +96,16 @@ struct AInputQueue : public android::InputEventFactoryInterface {
9696
android::KeyEvent* consumeUnhandledEvent();
9797
android::KeyEvent* consumePreDispatchingEvent(int* outSeq);
9898

99-
virtual android::KeyEvent* createKeyEvent();
100-
virtual android::MotionEvent* createMotionEvent();
99+
android::KeyEvent* createKeyEvent();
101100

102101
int mWorkWrite;
103102

104103
private:
105104
void doUnhandledKey(android::KeyEvent* keyEvent);
106105
bool preDispatchKey(android::KeyEvent* keyEvent);
107-
void wakeupDispatch();
106+
void wakeupDispatchLocked();
108107

108+
android::PooledInputEventFactory mPooledInputEventFactory;
109109
android::InputConsumer mConsumer;
110110
android::sp<android::Looper> mLooper;
111111

@@ -127,11 +127,6 @@ struct AInputQueue : public android::InputEventFactoryInterface {
127127

128128
int mSeq;
129129

130-
// Cache of previously allocated key events.
131-
android::Vector<android::KeyEvent*> mAvailKeyEvents;
132-
// Cache of previously allocated motion events.
133-
android::Vector<android::MotionEvent*> mAvailMotionEvents;
134-
135130
// All input events that are actively being processed.
136131
android::Vector<in_flight_event> mInFlightEvents;
137132

include/androidfw/Input.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,26 @@ class PreallocatedInputEventFactory : public InputEventFactoryInterface {
615615
MotionEvent mMotionEvent;
616616
};
617617

618+
/*
619+
* An input event factory implementation that maintains a pool of input events.
620+
*/
621+
class PooledInputEventFactory : public InputEventFactoryInterface {
622+
public:
623+
PooledInputEventFactory(size_t maxPoolSize = 20);
624+
virtual ~PooledInputEventFactory();
625+
626+
virtual KeyEvent* createKeyEvent();
627+
virtual MotionEvent* createMotionEvent();
628+
629+
void recycle(InputEvent* event);
630+
631+
private:
632+
const size_t mMaxPoolSize;
633+
634+
Vector<KeyEvent*> mKeyEventPool;
635+
Vector<MotionEvent*> mMotionEventPool;
636+
};
637+
618638
/*
619639
* Calculates the velocity of pointer movements over time.
620640
*/

include/androidfw/InputTransport.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,29 @@ class InputConsumer {
292292
*/
293293
status_t sendFinishedSignal(uint32_t seq, bool handled);
294294

295-
/* Returns true if there is a pending batch. */
295+
/* Returns true if there is a deferred event waiting.
296+
*
297+
* Should be called after calling consume() to determine whether the consumer
298+
* has a deferred event to be processed. Deferred events are somewhat special in
299+
* that they have already been removed from the input channel. If the input channel
300+
* becomes empty, the client may need to do extra work to ensure that it processes
301+
* the deferred event despite the fact that the inptu channel's file descriptor
302+
* is not readable.
303+
*
304+
* One option is simply to call consume() in a loop until it returns WOULD_BLOCK.
305+
* This guarantees that all deferred events will be processed.
306+
*
307+
* Alternately, the caller can call hasDeferredEvent() to determine whether there is
308+
* a deferred event waiting and then ensure that its event loop wakes up at least
309+
* one more time to consume the deferred event.
310+
*/
311+
bool hasDeferredEvent() const;
312+
313+
/* Returns true if there is a pending batch.
314+
*
315+
* Should be called after calling consume() with consumeBatches == false to determine
316+
* whether consume() should be called again later on with consumeBatches == true.
317+
*/
296318
bool hasPendingBatch() const;
297319

298320
private:

libs/androidfw/Input.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,58 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
683683
}
684684

685685

686+
// --- PooledInputEventFactory ---
687+
688+
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
689+
mMaxPoolSize(maxPoolSize) {
690+
}
691+
692+
PooledInputEventFactory::~PooledInputEventFactory() {
693+
for (size_t i = 0; i < mKeyEventPool.size(); i++) {
694+
delete mKeyEventPool.itemAt(i);
695+
}
696+
for (size_t i = 0; i < mMotionEventPool.size(); i++) {
697+
delete mMotionEventPool.itemAt(i);
698+
}
699+
}
700+
701+
KeyEvent* PooledInputEventFactory::createKeyEvent() {
702+
if (!mKeyEventPool.isEmpty()) {
703+
KeyEvent* event = mKeyEventPool.top();
704+
mKeyEventPool.pop();
705+
return event;
706+
}
707+
return new KeyEvent();
708+
}
709+
710+
MotionEvent* PooledInputEventFactory::createMotionEvent() {
711+
if (!mMotionEventPool.isEmpty()) {
712+
MotionEvent* event = mMotionEventPool.top();
713+
mMotionEventPool.pop();
714+
return event;
715+
}
716+
return new MotionEvent();
717+
}
718+
719+
void PooledInputEventFactory::recycle(InputEvent* event) {
720+
switch (event->getType()) {
721+
case AINPUT_EVENT_TYPE_KEY:
722+
if (mKeyEventPool.size() < mMaxPoolSize) {
723+
mKeyEventPool.push(static_cast<KeyEvent*>(event));
724+
return;
725+
}
726+
break;
727+
case AINPUT_EVENT_TYPE_MOTION:
728+
if (mMotionEventPool.size() < mMaxPoolSize) {
729+
mMotionEventPool.push(static_cast<MotionEvent*>(event));
730+
return;
731+
}
732+
break;
733+
}
734+
delete event;
735+
}
736+
737+
686738
// --- VelocityTracker ---
687739

688740
const uint32_t VelocityTracker::DEFAULT_DEGREE;

libs/androidfw/InputTransport.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,10 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled)
527527
return mChannel->sendMessage(&msg);
528528
}
529529

530+
bool InputConsumer::hasDeferredEvent() const {
531+
return mMsgDeferred;
532+
}
533+
530534
bool InputConsumer::hasPendingBatch() const {
531535
return !mBatches.isEmpty();
532536
}

0 commit comments

Comments
 (0)