diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java index 83866b3cceb75..e617e0a52bc6d 100644 --- a/core/java/android/os/HwRemoteBinder.java +++ b/core/java/android/os/HwRemoteBinder.java @@ -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; } diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java index 76e881eda8af4..f93bfd79f625b 100644 --- a/core/java/android/os/IHwBinder.java +++ b/core/java/android/os/IHwBinder.java @@ -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); } diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp index 1d5d6d59639a0..0a7d84d9c8c5f 100644 --- a/core/jni/android_os_HwRemoteBinder.cpp +++ b/core/jni/android_os_HwRemoteBinder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #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; -} gFields; +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& 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& 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 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 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 objClassRef(env, env->GetObjectClass(mObject)); + ScopedLocalRef 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 mList; +}; +// ---------------------------------------------------------------------------- + +HwBinderDeathRecipientList::HwBinderDeathRecipientList() { +} + +HwBinderDeathRecipientList::~HwBinderDeathRecipientList() { + AutoMutex _l(mLock); + + for (const sp& deathRecipient : mList) { + deathRecipient->warnIfStillLive(); + } +} + +void HwBinderDeathRecipientList::add(const sp& recipient) { + AutoMutex _l(mLock); + + mList.push_back(recipient); +} + +void HwBinderDeathRecipientList::remove(const sp& recipient) { + AutoMutex _l(mLock); + + List< sp >::iterator iter; + for (iter = mList.begin(); iter != mList.end(); iter++) { + if (*iter == recipient) { + mList.erase(iter); + return; + } + } +} + +sp HwBinderDeathRecipientList::find(jobject recipient) { + AutoMutex _l(mLock); + + for (const sp& deathRecipient : mList) { + if (deathRecipient->matches(recipient)) { + return deathRecipient; + } + } + return NULL; +} + +Mutex& HwBinderDeathRecipientList::lock() { + return mLock; +} // static void JHwRemoteBinder::InitClass(JNIEnv *env) { - ScopedLocalRef 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, "", "()V"); + gProxyOffsets.sendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", + "(Landroid/os/IHwBinder$DeathRecipient;J)V"); - gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "", "()V"); + clazz = FindClassOrDie(env, "java/lang/Class"); + gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;"); } // static sp JHwRemoteBinder::SetNativeContext( JNIEnv *env, jobject thiz, const sp &context) { sp 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::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::SetNativeContext( // static sp 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 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(), "", "()V"); @@ -97,6 +268,7 @@ jobject JHwRemoteBinder::NewObject( JHwRemoteBinder::JHwRemoteBinder( JNIEnv *env, jobject thiz, const sp &binder) : mBinder(binder) { + mDeathRecipientList = new HwBinderDeathRecipientList(); jclass clazz = env->GetObjectClass(thiz); CHECK(clazz != NULL); @@ -114,7 +286,7 @@ JHwRemoteBinder::~JHwRemoteBinder() { mClass = NULL; } -sp JHwRemoteBinder::getBinder() { +sp JHwRemoteBinder::getBinder() const { return mBinder; } @@ -122,6 +294,10 @@ void JHwRemoteBinder::setBinder(const sp &binder) { mBinder = binder; } +sp 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 context = JHwRemoteBinder::GetNativeContext(env, thiz); + sp binder = context->getBinder(); + + if (!binder->localBinder()) { + HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get(); + sp 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 context = JHwRemoteBinder::GetNativeContext(env, thiz); + sp 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 origJDR = list->find(recipient); + if (origJDR != NULL) { + wp dr; + err = binder->unlinkToDeath(origJDR, NULL, 0, &dr); + if (err == NO_ERROR && dr != NULL) { + sp sdr = dr.promote(); + HwBinderDeathRecipient* jdr = static_cast(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 { diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h index fd33338986a03..77a02784926db 100644 --- a/core/jni/android_os_HwRemoteBinder.h +++ b/core/jni/android_os_HwRemoteBinder.h @@ -20,10 +20,33 @@ #include #include #include +#include +#include #include 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 > mList; + Mutex mLock; + +public: + HwBinderDeathRecipientList(); + ~HwBinderDeathRecipientList(); + + void add(const sp& recipient); + void remove(const sp& recipient); + sp 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 &binder); - sp getBinder(); + sp getBinder() const; void setBinder(const sp &binder); + sp getDeathRecipientList() const; protected: virtual ~JHwRemoteBinder(); @@ -48,7 +72,7 @@ private: jobject mObject; sp mBinder; - + sp mDeathRecipientList; DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder); };