Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b4049e7 authored by Jeff Brown's avatar Jeff Brown
Browse files

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
parent 250dd262
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.SparseBooleanArray;
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 @@ public class SystemSensorManager extends SensorManager {
     * 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 @@ public class SystemSensorManager extends SensorManager {
        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;
+4 −2
Original line number Diff line number Diff line
@@ -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 receive display events
 * such as vertical sync.
@@ -42,7 +44,7 @@ public abstract class DisplayEventReceiver {
    // 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 @@ public abstract class DisplayEventReceiver {
        }

        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(this, mMessageQueue);
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);

        mCloseGuard.open("dispose");
    }
+23 −16
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <map>

#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>

#include <utils/Log.h>
#include <utils/Looper.h>
@@ -178,21 +179,21 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next)
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 @@ private:
    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 @@ private:
                if (buffer[i].type == SENSOR_TYPE_META_DATA) {
                    // This is a flush complete sensor event. Call dispatchFlushCompleteEvent
                    // method.
                    env->CallVoidMethod(mReceiverObject,
                    if (receiverObj.get()) {
                        env->CallVoidMethod(receiverObj.get(),
                                            gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
                                            buffer[i].meta_data.sensor);
                    }
                } else {
                    int8_t status;
                    switch (buffer[i].type) {
@@ -247,13 +252,15 @@ private:
                        status = SENSOR_STATUS_ACCURACY_HIGH;
                        break;
                    }
                    env->CallVoidMethod(mReceiverObject,
                    if (receiverObj.get()) {
                        env->CallVoidMethod(receiverObj.get(),
                                            gBaseEventQueueClassInfo.dispatchSensorEvent,
                                            buffer[i].sensor,
                                            mScratch,
                                            status,
                                            buffer[i].timestamp);
                    }
                }
                if (env->ExceptionCheck()) {
                    mSensorQueue->sendAck(buffer, n);
                    ALOGE("Exception dispatching input event.");
@@ -269,7 +276,7 @@ private:
    }
};

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 @@ static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject event
        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 gSystemSensorManagerMethods[] = {

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",
+24 −16
Original line number Diff line number Diff line
@@ -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 @@ static struct {
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 @@ protected:
    virtual ~NativeDisplayEventReceiver();

private:
    jobject mReceiverObjGlobal;
    jobject mReceiverWeakGlobal;
    sp<MessageQueue> mMessageQueue;
    DisplayEventReceiver mReceiver;
    bool mWaitingForVsync;
@@ -72,15 +74,15 @@ private:


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 @@ bool NativeDisplayEventReceiver::processPendingEvents(
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();

    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
    if (receiverObj.get()) {
        ALOGV("receiver %p ~ Invoking vsync handler.", this);
    env->CallVoidMethod(mReceiverObjGlobal,
        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::dispatchVsync(nsecs_t timestamp, int32_t id, ui
void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();

    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
    if (receiverObj.get()) {
        ALOGV("receiver %p ~ Invoking hotplug handler.", this);
    env->CallVoidMethod(mReceiverObjGlobal,
        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 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
    }

    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverObj, messageQueue);
            receiverWeak, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
@@ -254,7 +262,7 @@ static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
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",