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

Commit 872a5c0c authored by Martijn Coenen's avatar Martijn Coenen Committed by android-build-merger
Browse files

Merge "Add (un)linkToDeath support to HwBinder." am: 77053631 am: 010d0be5 am: 1f8c96ca

am: c052e6c6

Change-Id: I5b515db7eaeac33a9072e6046f4c5bb948d5aa39
parents a9ceb9eb c052e6c6
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ public class HwRemoteBinder implements IHwBinder {
    public native final void transact(
            int code, HwParcel request, HwParcel reply, int flags);

    public native boolean linkToDeath(DeathRecipient recipient, long cookie);
    public native boolean unlinkToDeath(DeathRecipient recipient);

    private static native final long native_init();

    private native final void native_setup_empty();
@@ -52,5 +55,9 @@ public class HwRemoteBinder implements IHwBinder {
                128 /* size */);
    }

    private static final void sendDeathNotice(DeathRecipient recipient, long cookie) {
        recipient.serviceDied(cookie);
    }

    private long mNativeContext;
}
+12 −0
Original line number Diff line number Diff line
@@ -26,4 +26,16 @@ public interface IHwBinder {
            int code, HwParcel request, HwParcel reply, int flags);

    public IHwInterface queryLocalInterface(String descriptor);

    /**
     * Interface for receiving a callback when the process hosting a service
     * has gone away.
     */
    public interface DeathRecipient {
        public void serviceDied(long cookie);
    }

    public boolean linkToDeath(DeathRecipient recipient, long cookie);

    public boolean unlinkToDeath(DeathRecipient recipient);
}
+261 −10
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
#include <ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>

#include "core_jni_helpers.h"
@@ -38,26 +39,196 @@ using android::AndroidRuntime;
namespace android {

static struct fields_t {
    jclass proxy_class;
    jfieldID contextID;
    jmethodID constructID;
    jmethodID sendDeathNotice;
} gProxyOffsets;

static struct class_offsets_t
{
    jmethodID mGetName;
} gClassOffsets;

static JavaVM* jnienv_to_javavm(JNIEnv* env)
{
    JavaVM* vm;
    return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
}

static JNIEnv* javavm_to_jnienv(JavaVM* vm)
{
    JNIEnv* env;
    return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
}

// ----------------------------------------------------------------------------
class HwBinderDeathRecipient : public hardware::IBinder::DeathRecipient
{
public:
    HwBinderDeathRecipient(JNIEnv* env, jobject object, jlong cookie, const sp<HwBinderDeathRecipientList>& list)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
          mObjectWeak(NULL), mCookie(cookie), mList(list)
    {
        // These objects manage their own lifetimes so are responsible for final bookkeeping.
        // The list holds a strong reference to this object.
        list->add(this);
    }

    void binderDied(const wp<hardware::IBinder>& who)
    {
        if (mObject != NULL) {
            JNIEnv* env = javavm_to_jnienv(mVM);

            env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice, mObject, mCookie);
            if (env->ExceptionCheck()) {
                ALOGE("Uncaught exception returned from death notification.");
                env->ExceptionClear();
            }

            // Serialize with our containing HwBinderDeathRecipientList so that we can't
            // delete the global ref on mObject while the list is being iterated.
            sp<HwBinderDeathRecipientList> list = mList.promote();
            if (list != NULL) {
                AutoMutex _l(list->lock());

                // Demote from strong ref to weak after binderDied() has been delivered,
                // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
                mObjectWeak = env->NewWeakGlobalRef(mObject);
                env->DeleteGlobalRef(mObject);
                mObject = NULL;
            }
        }
    }

    void clearReference()
    {
        sp<HwBinderDeathRecipientList> list = mList.promote();
        if (list != NULL) {
            list->remove(this);
        } else {
            ALOGE("clearReference() on JDR %p but DRL wp purged", this);
        }
    }

    bool matches(jobject obj) {
        bool result;
        JNIEnv* env = javavm_to_jnienv(mVM);

        if (mObject != NULL) {
            result = env->IsSameObject(obj, mObject);
        } else {
            jobject me = env->NewLocalRef(mObjectWeak);
            result = env->IsSameObject(obj, me);
            env->DeleteLocalRef(me);
        }
        return result;
    }

    void warnIfStillLive() {
        if (mObject != NULL) {
            // Okay, something is wrong -- we have a hard reference to a live death
            // recipient on the VM side, but the list is being torn down.
            JNIEnv* env = javavm_to_jnienv(mVM);
            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
            ScopedLocalRef<jstring> nameRef(env,
                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
            ScopedUtfChars nameUtf(env, nameRef.get());
            if (nameUtf.c_str() != NULL) {
                ALOGW("BinderProxy is being destroyed but the application did not call "
                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
                        "Releasing leaked death recipient: %s", nameUtf.c_str());
            } else {
                ALOGW("BinderProxy being destroyed; unable to get DR object name");
                env->ExceptionClear();
            }
        }
    }

protected:
    virtual ~HwBinderDeathRecipient()
    {
        JNIEnv* env = javavm_to_jnienv(mVM);
        if (mObject != NULL) {
            env->DeleteGlobalRef(mObject);
        } else {
            env->DeleteWeakGlobalRef(mObjectWeak);
        }
    }

private:
    JavaVM* const mVM;
    jobject mObject;
    jweak mObjectWeak; // will be a weak ref to the same VM-side DeathRecipient after binderDied()
    jlong mCookie;
    wp<HwBinderDeathRecipientList> mList;
};
// ----------------------------------------------------------------------------

HwBinderDeathRecipientList::HwBinderDeathRecipientList() {
}

HwBinderDeathRecipientList::~HwBinderDeathRecipientList() {
    AutoMutex _l(mLock);

    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
        deathRecipient->warnIfStillLive();
    }
}

void HwBinderDeathRecipientList::add(const sp<HwBinderDeathRecipient>& recipient) {
    AutoMutex _l(mLock);

} gFields;
    mList.push_back(recipient);
}

void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
    AutoMutex _l(mLock);

    List< sp<HwBinderDeathRecipient> >::iterator iter;
    for (iter = mList.begin(); iter != mList.end(); iter++) {
        if (*iter == recipient) {
            mList.erase(iter);
            return;
        }
    }
}

sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
    AutoMutex _l(mLock);

    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
        if (deathRecipient->matches(recipient)) {
            return deathRecipient;
        }
    }
    return NULL;
}

Mutex& HwBinderDeathRecipientList::lock() {
    return mLock;
}

// static
void JHwRemoteBinder::InitClass(JNIEnv *env) {
    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
    jclass clazz = FindClassOrDie(env, CLASS_PATH);

    gFields.contextID =
        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
    gProxyOffsets.proxy_class = MakeGlobalRefOrDie(env, clazz);
    gProxyOffsets.contextID =
        GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
    gProxyOffsets.constructID = GetMethodIDOrDie(env, clazz, "<init>", "()V");
    gProxyOffsets.sendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
            "(Landroid/os/IHwBinder$DeathRecipient;J)V");

    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
    clazz = FindClassOrDie(env, "java/lang/Class");
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
}

// static
sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
        JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) {
    sp<JHwRemoteBinder> old =
        (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
        (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);

    if (context != NULL) {
        context->incStrong(NULL /* id */);
@@ -67,7 +238,7 @@ sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
        old->decStrong(NULL /* id */);
    }

    env->SetLongField(thiz, gFields.contextID, (long)context.get());
    env->SetLongField(thiz, gProxyOffsets.contextID, (long)context.get());

    return old;
}
@@ -75,7 +246,7 @@ sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
// static
sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext(
        JNIEnv *env, jobject thiz) {
    return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
    return (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
}

// static
@@ -84,7 +255,7 @@ jobject JHwRemoteBinder::NewObject(
    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));

    // XXX Have to look up the constructor here because otherwise that static
    // class initializer isn't called and gFields.constructID is undefined :(
    // class initializer isn't called and gProxyOffsets.constructID is undefined :(

    jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");

@@ -97,6 +268,7 @@ jobject JHwRemoteBinder::NewObject(
JHwRemoteBinder::JHwRemoteBinder(
        JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
    : mBinder(binder) {
    mDeathRecipientList = new HwBinderDeathRecipientList();
    jclass clazz = env->GetObjectClass(thiz);
    CHECK(clazz != NULL);

@@ -114,7 +286,7 @@ JHwRemoteBinder::~JHwRemoteBinder() {
    mClass = NULL;
}

sp<hardware::IBinder> JHwRemoteBinder::getBinder() {
sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
    return mBinder;
}

@@ -122,6 +294,10 @@ void JHwRemoteBinder::setBinder(const sp<hardware::IBinder> &binder) {
    mBinder = binder;
}

sp<HwBinderDeathRecipientList> JHwRemoteBinder::getDeathRecipientList() const {
    return mDeathRecipientList;
}

}  // namespace android

////////////////////////////////////////////////////////////////////////////////
@@ -174,6 +350,73 @@ static void JHwRemoteBinder_native_transact(
    signalExceptionForError(env, err);
}

static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
        jobject recipient, jlong cookie)
{
    if (recipient == NULL) {
        jniThrowNullPointerException(env, NULL);
        return JNI_FALSE;
    }

    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
    sp<hardware::IBinder> binder = context->getBinder();

    if (!binder->localBinder()) {
        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
        sp<HwBinderDeathRecipient> jdr = new HwBinderDeathRecipient(env, recipient, cookie, list);
        status_t err = binder->linkToDeath(jdr, NULL, 0);
        if (err != NO_ERROR) {
            // Failure adding the death recipient, so clear its reference
            // now.
            jdr->clearReference();
            return JNI_FALSE;
        }
    }

    return JNI_TRUE;
}

static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
                                                 jobject recipient)
{
    jboolean res = JNI_FALSE;
    if (recipient == NULL) {
        jniThrowNullPointerException(env, NULL);
        return res;
    }

    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
    sp<hardware::IBinder> binder = context->getBinder();

    if (!binder->localBinder()) {
        status_t err = NAME_NOT_FOUND;

        // If we find the matching recipient, proceed to unlink using that
        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
        sp<HwBinderDeathRecipient> origJDR = list->find(recipient);
        if (origJDR != NULL) {
            wp<hardware::IBinder::DeathRecipient> dr;
            err = binder->unlinkToDeath(origJDR, NULL, 0, &dr);
            if (err == NO_ERROR && dr != NULL) {
                sp<hardware::IBinder::DeathRecipient> sdr = dr.promote();
                HwBinderDeathRecipient* jdr = static_cast<HwBinderDeathRecipient*>(sdr.get());
                if (jdr != NULL) {
                    jdr->clearReference();
                }
            }
        }

        if (err == NO_ERROR || err == DEAD_OBJECT) {
            res = JNI_TRUE;
        } else {
            jniThrowException(env, "java/util/NoSuchElementException",
                              "Death link does not exist");
        }
    }

    return res;
}

static JNINativeMethod gMethods[] = {
    { "native_init", "()J", (void *)JHwRemoteBinder_native_init },

@@ -183,6 +426,14 @@ static JNINativeMethod gMethods[] = {
    { "transact",
        "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
        (void *)JHwRemoteBinder_native_transact },

    {"linkToDeath",
        "(Landroid/os/IHwBinder$DeathRecipient;J)Z",
        (void*)JHwRemoteBinder_linkToDeath},

    {"unlinkToDeath",
        "(Landroid/os/IHwBinder$DeathRecipient;)Z",
        (void*)JHwRemoteBinder_unlinkToDeath},
};

namespace android {
+26 −2
Original line number Diff line number Diff line
@@ -20,10 +20,33 @@
#include <android-base/macros.h>
#include <hwbinder/Binder.h>
#include <jni.h>
#include <utils/List.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>

namespace android {

// Per-IBinder death recipient bookkeeping.  This is how we reconcile local jobject
// death recipient references passed in through JNI with the permanent corresponding
// HwBinderDeathRecipient objects.

class HwBinderDeathRecipient;

class HwBinderDeathRecipientList : public RefBase {
    List< sp<HwBinderDeathRecipient> > mList;
    Mutex mLock;

public:
    HwBinderDeathRecipientList();
    ~HwBinderDeathRecipientList();

    void add(const sp<HwBinderDeathRecipient>& recipient);
    void remove(const sp<HwBinderDeathRecipient>& recipient);
    sp<HwBinderDeathRecipient> find(jobject recipient);

    Mutex& lock();  // Use with care; specifically for mutual exclusion during binder death
};

struct JHwRemoteBinder : public RefBase {
    static void InitClass(JNIEnv *env);

@@ -37,8 +60,9 @@ struct JHwRemoteBinder : public RefBase {
    JHwRemoteBinder(
            JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder);

    sp<hardware::IBinder> getBinder();
    sp<hardware::IBinder> getBinder() const;
    void setBinder(const sp<hardware::IBinder> &binder);
    sp<HwBinderDeathRecipientList> getDeathRecipientList() const;

protected:
    virtual ~JHwRemoteBinder();
@@ -48,7 +72,7 @@ private:
    jobject mObject;

    sp<hardware::IBinder> mBinder;

    sp<HwBinderDeathRecipientList> mDeathRecipientList;
    DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
};