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",