am a4ca8ea0: Fix reference cycle in InputEventReceiver and Sender.

* commit 'a4ca8ea0ad14c509d1ced46482e83c1b8a518982':
  Fix reference cycle in InputEventReceiver and Sender.
This commit is contained in:
Jeff Brown
2013-04-02 20:05:03 -07:00
committed by Android Git Automerger
5 changed files with 64 additions and 39 deletions

View File

@@ -23,6 +23,8 @@ import android.os.MessageQueue;
import android.util.Log;
import android.util.SparseIntArray;
import java.lang.ref.WeakReference;
/**
* Provides a low-level mechanism for an application to receive input events.
* @hide
@@ -42,7 +44,7 @@ public abstract class InputEventReceiver {
// Map from InputEvent sequence numbers to dispatcher sequence numbers.
private final SparseIntArray mSeqMap = new SparseIntArray();
private static native int nativeInit(InputEventReceiver receiver,
private static native int nativeInit(WeakReference<InputEventReceiver> receiver,
InputChannel inputChannel, MessageQueue messageQueue);
private static native void nativeDispose(int receiverPtr);
private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled);
@@ -65,7 +67,8 @@ public abstract class InputEventReceiver {
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}

View File

@@ -22,6 +22,8 @@ import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* Provides a low-level mechanism for an application to send input events.
* @hide
@@ -38,7 +40,7 @@ public abstract class InputEventSender {
private InputChannel mInputChannel;
private MessageQueue mMessageQueue;
private static native int nativeInit(InputEventSender sender,
private static native int nativeInit(WeakReference<InputEventSender> sender,
InputChannel inputChannel, MessageQueue messageQueue);
private static native void nativeDispose(int senderPtr);
private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event);
@@ -60,7 +62,8 @@ public abstract class InputEventSender {
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mSenderPtr = nativeInit(this, inputChannel, mMessageQueue);
mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}

View File

@@ -93,14 +93,6 @@ static struct debug_offsets_t
// ----------------------------------------------------------------------------
static struct weakreference_offsets_t
{
// Class state.
jclass mClass;
jmethodID mGet;
} gWeakReferenceOffsets;
static struct error_offsets_t
{
jclass mClass;
@@ -570,7 +562,7 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet);
jobject res = jniGetReferent(env, object);
if (res != NULL) {
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
@@ -1211,13 +1203,6 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
{
jclass clazz;
clazz = env->FindClass("java/lang/ref/WeakReference");
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.ref.WeakReference");
gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gWeakReferenceOffsets.mGet
= env->GetMethodID(clazz, "get", "()Ljava/lang/Object;");
assert(gWeakReferenceOffsets.mGet);
clazz = env->FindClass("java/lang/Error");
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error");
gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

View File

@@ -34,6 +34,8 @@
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
#include <ScopedLocalRef.h>
namespace android {
static struct {
@@ -47,7 +49,7 @@ static struct {
class NativeInputEventReceiver : public LooperCallback {
public:
NativeInputEventReceiver(JNIEnv* env,
jobject receiverObj, const sp<InputChannel>& inputChannel,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue);
status_t initialize();
@@ -59,7 +61,7 @@ protected:
virtual ~NativeInputEventReceiver();
private:
jobject mReceiverObjGlobal;
jobject mReceiverWeakGlobal;
InputConsumer mInputConsumer;
sp<MessageQueue> mMessageQueue;
PreallocatedInputEventFactory mInputEventFactory;
@@ -74,9 +76,9 @@ private:
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
jobject receiverObj, const sp<InputChannel>& inputChannel,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue) :
mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel), mMessageQueue(messageQueue),
mBatchedInputEventPending(false) {
#if DEBUG_DISPATCH_CYCLE
@@ -86,7 +88,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
NativeInputEventReceiver::~NativeInputEventReceiver() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mReceiverObjGlobal);
env->DeleteGlobalRef(mReceiverWeakGlobal);
}
status_t NativeInputEventReceiver::initialize() {
@@ -151,6 +153,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
mBatchedInputEventPending = false;
}
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
@@ -162,12 +165,21 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
if (!skipCallbacks && !mBatchedInputEventPending
&& mInputConsumer.hasPendingBatch()) {
// There is a pending batch. Come back later.
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
ALOGW("channel '%s' ~ Receiver object was finalized "
"without being disposed.", getInputChannelName());
return DEAD_OBJECT;
}
}
mBatchedInputEventPending = true;
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
getInputChannelName());
#endif
env->CallVoidMethod(mReceiverObjGlobal,
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching batched input events.");
@@ -183,6 +195,15 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
assert(inputEvent);
if (!skipCallbacks) {
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
ALOGW("channel '%s' ~ Receiver object was finalized "
"without being disposed.", getInputChannelName());
return DEAD_OBJECT;
}
}
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -210,12 +231,13 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
#endif
env->CallVoidMethod(mReceiverObjGlobal,
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
skipCallbacks = true;
@@ -229,7 +251,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
}
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
@@ -245,7 +267,7 @@ static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
}
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverObj, inputChannel, messageQueue);
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
@@ -293,7 +315,7 @@ static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint rece
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
"(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
(void*)nativeInit },
{ "nativeDispose", "(I)V",
(void*)nativeDispose },

View File

@@ -35,6 +35,8 @@
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
#include <ScopedLocalRef.h>
namespace android {
static struct {
@@ -47,7 +49,7 @@ static struct {
class NativeInputEventSender : public LooperCallback {
public:
NativeInputEventSender(JNIEnv* env,
jobject senderObj, const sp<InputChannel>& inputChannel,
jobject senderWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue);
status_t initialize();
@@ -59,7 +61,7 @@ protected:
virtual ~NativeInputEventSender();
private:
jobject mSenderObjGlobal;
jobject mSenderWeakGlobal;
InputPublisher mInputPublisher;
sp<MessageQueue> mMessageQueue;
KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
@@ -75,9 +77,9 @@ private:
NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
jobject senderObj, const sp<InputChannel>& inputChannel,
jobject senderWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue) :
mSenderObjGlobal(env->NewGlobalRef(senderObj)),
mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
mInputPublisher(inputChannel), mMessageQueue(messageQueue),
mNextPublishedSeq(1) {
#if DEBUG_DISPATCH_CYCLE
@@ -87,7 +89,7 @@ NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
NativeInputEventSender::~NativeInputEventSender() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mSenderObjGlobal);
env->DeleteGlobalRef(mSenderWeakGlobal);
}
status_t NativeInputEventSender::initialize() {
@@ -178,6 +180,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
#endif
ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t publishedSeq;
@@ -205,7 +208,16 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
#endif
if (!skipCallbacks) {
env->CallVoidMethod(mSenderObjGlobal,
if (!senderObj.get()) {
senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
ALOGW("channel '%s' ~ Sender object was finalized "
"without being disposed.", getInputChannelName());
return DEAD_OBJECT;
}
}
env->CallVoidMethod(senderObj.get(),
gInputEventSenderClassInfo.dispatchInputEventFinished,
jint(seq), jboolean(handled));
if (env->ExceptionCheck()) {
@@ -218,7 +230,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
}
static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj,
static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
@@ -234,7 +246,7 @@ static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj,
}
sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
senderObj, inputChannel, messageQueue);
senderWeak, inputChannel, messageQueue);
status_t status = sender->initialize();
if (status) {
String8 message;
@@ -277,7 +289,7 @@ static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr,
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Landroid/view/InputEventSender;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
"(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
(void*)nativeInit },
{ "nativeDispose", "(I)V",
(void*)nativeDispose },