Merge "Add (un)linkToDeath support to HwBinder." am: 7705363166

am: 010d0be575

Change-Id: Ibabccaf8555bd0229add18a798bff685c6a1c433
This commit is contained in:
Martijn Coenen
2016-12-29 09:12:22 +00:00
committed by android-build-merger
4 changed files with 306 additions and 12 deletions

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
} 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<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);
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 {

View File

@@ -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);
};