Fix leaks due to GC circular references.
The DisplayEventReceiver and SensorManager event queue both get
leaked when the Looper thread they are attached to dies because
the Java object holds a strong reference to its native peer and
meanwhile the native peer holds a strong reference to the Java
object through JNI.
Fixed the issue by indirecting through a weak reference as was
done for InputEventReceiver some time ago.
Bug: 12455729
Change-Id: I3d80a2a190192d1a2981bf5ae0cad30f0f7688a5
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 88fa339..7ad3a68 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -26,6 +26,7 @@
import android.util.SparseIntArray;
import dalvik.system.CloseGuard;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -224,8 +225,8 @@
* the queues and the listeners.
*/
private static abstract class BaseEventQueue {
- private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
- float[] scratch, String packageName);
+ private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak,
+ MessageQueue msgQ, float[] scratch, String packageName);
private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
int maxBatchReportLatencyUs);
private static native int nativeDisableSensor(long eventQ, int handle);
@@ -240,7 +241,8 @@
protected final SystemSensorManager mManager;
BaseEventQueue(Looper looper, SystemSensorManager manager) {
- nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch,
+ nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this),
+ looper.getQueue(), mScratch,
manager.mPackageName);
mCloseGuard.open("dispose");
mManager = manager;
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index de46a4a..5a9a1ea 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -22,6 +22,8 @@
import android.os.MessageQueue;
import android.util.Log;
+import java.lang.ref.WeakReference;
+
/**
* Provides a low-level mechanism for an application to receive display events
* such as vertical sync.
@@ -42,7 +44,7 @@
// GC'd while the native peer of the receiver is using them.
private MessageQueue mMessageQueue;
- private static native long nativeInit(DisplayEventReceiver receiver,
+ private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
MessageQueue messageQueue);
private static native void nativeDispose(long receiverPtr);
private static native void nativeScheduleVsync(long receiverPtr);
@@ -58,7 +60,7 @@
}
mMessageQueue = looper.getQueue();
- mReceiverPtr = nativeInit(this, mMessageQueue);
+ mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);
mCloseGuard.open("dispose");
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 7d12230..16e5b3c 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -19,6 +19,7 @@
#include <map>
#include <ScopedUtfChars.h>
+#include <ScopedLocalRef.h>
#include <utils/Log.h>
#include <utils/Looper.h>
@@ -178,21 +179,21 @@
class Receiver : public LooperCallback {
sp<SensorEventQueue> mSensorQueue;
sp<MessageQueue> mMessageQueue;
- jobject mReceiverObject;
+ jobject mReceiverWeakGlobal;
jfloatArray mScratch;
public:
Receiver(const sp<SensorEventQueue>& sensorQueue,
const sp<MessageQueue>& messageQueue,
- jobject receiverObject, jfloatArray scratch) {
+ jobject receiverWeak, jfloatArray scratch) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
mSensorQueue = sensorQueue;
mMessageQueue = messageQueue;
- mReceiverObject = env->NewGlobalRef(receiverObject);
+ mReceiverWeakGlobal = env->NewGlobalRef(receiverWeak);
mScratch = (jfloatArray)env->NewGlobalRef(scratch);
}
~Receiver() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mReceiverObject);
+ env->DeleteGlobalRef(mReceiverWeakGlobal);
env->DeleteGlobalRef(mScratch);
}
sp<SensorEventQueue> getSensorEventQueue() const {
@@ -213,6 +214,8 @@
virtual int handleEvent(int fd, int events, void* data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
sp<SensorEventQueue> q = reinterpret_cast<SensorEventQueue *>(data);
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+
ssize_t n;
ASensorEvent buffer[16];
while ((n = q->read(buffer, 16)) > 0) {
@@ -228,9 +231,11 @@
if (buffer[i].type == SENSOR_TYPE_META_DATA) {
// This is a flush complete sensor event. Call dispatchFlushCompleteEvent
// method.
- env->CallVoidMethod(mReceiverObject,
- gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
- buffer[i].meta_data.sensor);
+ if (receiverObj.get()) {
+ env->CallVoidMethod(receiverObj.get(),
+ gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
+ buffer[i].meta_data.sensor);
+ }
} else {
int8_t status;
switch (buffer[i].type) {
@@ -247,12 +252,14 @@
status = SENSOR_STATUS_ACCURACY_HIGH;
break;
}
- env->CallVoidMethod(mReceiverObject,
- gBaseEventQueueClassInfo.dispatchSensorEvent,
- buffer[i].sensor,
- mScratch,
- status,
- buffer[i].timestamp);
+ if (receiverObj.get()) {
+ env->CallVoidMethod(receiverObj.get(),
+ gBaseEventQueueClassInfo.dispatchSensorEvent,
+ buffer[i].sensor,
+ mScratch,
+ status,
+ buffer[i].timestamp);
+ }
}
if (env->ExceptionCheck()) {
mSensorQueue->sendAck(buffer, n);
@@ -269,7 +276,7 @@
}
};
-static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQ, jobject msgQ,
+static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQWeak, jobject msgQ,
jfloatArray scratch, jstring packageName) {
SensorManager& mgr(SensorManager::getInstance());
ScopedUtfChars packageUtf(env, packageName);
@@ -282,7 +289,7 @@
return 0;
}
- sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQ, scratch);
+ sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQWeak, scratch);
receiver->incStrong((void*)nativeInitSensorEventQueue);
return jlong(receiver.get());
}
@@ -325,7 +332,7 @@
static JNINativeMethod gBaseEventQueueMethods[] = {
{"nativeInitBaseEventQueue",
- "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[FLjava/lang/String;)J",
+ "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;)J",
(void*)nativeInitSensorEventQueue },
{"nativeEnableSensor",
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 0d54953..91a3c7e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -29,6 +29,8 @@
#include <gui/DisplayEventReceiver.h>
#include "android_os_MessageQueue.h"
+#include <ScopedLocalRef.h>
+
#include "core_jni_helpers.h"
namespace android {
@@ -49,7 +51,7 @@
class NativeDisplayEventReceiver : public LooperCallback {
public:
NativeDisplayEventReceiver(JNIEnv* env,
- jobject receiverObj, const sp<MessageQueue>& messageQueue);
+ jobject receiverWeak, const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
@@ -59,7 +61,7 @@
virtual ~NativeDisplayEventReceiver();
private:
- jobject mReceiverObjGlobal;
+ jobject mReceiverWeakGlobal;
sp<MessageQueue> mMessageQueue;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
@@ -72,15 +74,15 @@
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
- jobject receiverObj, const sp<MessageQueue>& messageQueue) :
- mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
+ jobject receiverWeak, const sp<MessageQueue>& messageQueue) :
+ mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mMessageQueue(messageQueue), mWaitingForVsync(false) {
ALOGV("receiver %p ~ Initializing input event receiver.", this);
}
NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mReceiverObjGlobal);
+ env->DeleteGlobalRef(mReceiverWeakGlobal);
}
status_t NativeDisplayEventReceiver::initialize() {
@@ -190,10 +192,13 @@
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- ALOGV("receiver %p ~ Invoking vsync handler.", this);
- env->CallVoidMethod(mReceiverObjGlobal,
- gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
- ALOGV("receiver %p ~ Returned from vsync handler.", this);
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking vsync handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
+ ALOGV("receiver %p ~ Returned from vsync handler.", this);
+ }
mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}
@@ -201,16 +206,19 @@
void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- ALOGV("receiver %p ~ Invoking hotplug handler.", this);
- env->CallVoidMethod(mReceiverObjGlobal,
- gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected);
- ALOGV("receiver %p ~ Returned from hotplug handler.", this);
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hotplug handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected);
+ ALOGV("receiver %p ~ Returned from hotplug handler.", this);
+ }
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
@@ -219,7 +227,7 @@
}
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
- receiverObj, messageQueue);
+ receiverWeak, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
@@ -254,7 +262,7 @@
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
- "(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)J",
+ "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
(void*)nativeInit },
{ "nativeDispose",
"(J)V",