Merge "Don't allocate GlobalRefs for BinderProxy"
This commit is contained in:
@@ -34,6 +34,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base class for a remotable object, the core part of a lightweight
|
||||
@@ -753,6 +754,188 @@ final class BinderProxy implements IBinder {
|
||||
// Assume the process-wide default value when created
|
||||
volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
|
||||
|
||||
/*
|
||||
* Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
|
||||
* We roll our own only because we need to lazily remove WeakReferences during accesses
|
||||
* to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
|
||||
* because we want weak values, not keys.
|
||||
* Our hash table is never resized, but the number of entries is unlimited;
|
||||
* performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
|
||||
* Not thread-safe. Client ensures there's a single access at a time.
|
||||
*/
|
||||
private static final class ProxyMap {
|
||||
private static final int LOG_MAIN_INDEX_SIZE = 8;
|
||||
private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
|
||||
private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
|
||||
|
||||
/**
|
||||
* We next warn when we exceed this bucket size.
|
||||
*/
|
||||
private int mWarnBucketSize = 20;
|
||||
|
||||
/**
|
||||
* Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
|
||||
*/
|
||||
private static final int WARN_INCREMENT = 10;
|
||||
|
||||
/**
|
||||
* Hash function tailored to native pointers.
|
||||
* Returns a value < MAIN_INDEX_SIZE.
|
||||
*/
|
||||
private static int hash(long arg) {
|
||||
return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of pairs in the map.
|
||||
*/
|
||||
int size() {
|
||||
int size = 0;
|
||||
for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
|
||||
if (a != null) {
|
||||
size += a.size();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ith entry from the hash bucket indicated by hash.
|
||||
*/
|
||||
private void remove(int hash, int index) {
|
||||
Long[] keyArray = mMainIndexKeys[hash];
|
||||
ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
|
||||
int size = valueArray.size(); // KeyArray may have extra elements.
|
||||
// Move last entry into empty slot, and truncate at end.
|
||||
if (index != size - 1) {
|
||||
keyArray[index] = keyArray[size - 1];
|
||||
valueArray.set(index, valueArray.get(size - 1));
|
||||
}
|
||||
valueArray.remove(size - 1);
|
||||
// Just leave key array entry; it's unused. We only trust the valueArray size.
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the supplied key. If we have a non-cleared entry for it, return it.
|
||||
*/
|
||||
BinderProxy get(long key) {
|
||||
int myHash = hash(key);
|
||||
Long[] keyArray = mMainIndexKeys[myHash];
|
||||
if (keyArray == null) {
|
||||
return null;
|
||||
}
|
||||
ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
|
||||
int bucketSize = valueArray.size();
|
||||
for (int i = 0; i < bucketSize; ++i) {
|
||||
long foundKey = keyArray[i];
|
||||
if (key == foundKey) {
|
||||
WeakReference<BinderProxy> wr = valueArray.get(i);
|
||||
BinderProxy bp = wr.get();
|
||||
if (bp != null) {
|
||||
return bp;
|
||||
} else {
|
||||
remove(myHash, i);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int mRandom; // A counter used to generate a "random" index. World's 2nd worst RNG.
|
||||
|
||||
/**
|
||||
* Add the key-value pair to the map.
|
||||
* Requires that the indicated key is not already in the map.
|
||||
*/
|
||||
void set(long key, @NonNull BinderProxy value) {
|
||||
int myHash = hash(key);
|
||||
ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
|
||||
if (valueArray == null) {
|
||||
valueArray = mMainIndexValues[myHash] = new ArrayList<>();
|
||||
mMainIndexKeys[myHash] = new Long[1];
|
||||
}
|
||||
int size = valueArray.size();
|
||||
WeakReference<BinderProxy> newWr = new WeakReference<>(value);
|
||||
// First look for a cleared reference.
|
||||
// This ensures that ArrayList size is bounded by the maximum occupancy of
|
||||
// that bucket.
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (valueArray.get(i).get() == null) {
|
||||
valueArray.set(i, newWr);
|
||||
Long[] keyArray = mMainIndexKeys[myHash];
|
||||
keyArray[i] = key;
|
||||
if (i < size - 1) {
|
||||
// "Randomly" check one of the remaining entries in [i+1, size), so that
|
||||
// needlessly long buckets are eventually pruned.
|
||||
int rnd = Math.floorMod(++mRandom, size - (i + 1));
|
||||
if (valueArray.get(i + 1 + rnd).get() == null) {
|
||||
remove(myHash, i + 1 + rnd);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
valueArray.add(size, newWr);
|
||||
Long[] keyArray = mMainIndexKeys[myHash];
|
||||
if (keyArray.length == size) {
|
||||
// size >= 1, since we initially allocated one element
|
||||
Long[] newArray = new Long[size + size / 2 + 2];
|
||||
System.arraycopy(keyArray, 0, newArray, 0, size);
|
||||
newArray[size] = key;
|
||||
mMainIndexKeys[myHash] = newArray;
|
||||
} else {
|
||||
keyArray[size] = key;
|
||||
}
|
||||
if (size >= mWarnBucketSize) {
|
||||
Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
|
||||
+ " total = " + size());
|
||||
mWarnBucketSize += WARN_INCREMENT;
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponding ArrayLists in the following two arrays always have the same size.
|
||||
// They contain no empty entries. However WeakReferences in the values ArrayLists
|
||||
// may have been cleared.
|
||||
|
||||
// mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
|
||||
// The values ArrayList has the proper size(), the corresponding keys array
|
||||
// is always at least the same size, but may be larger.
|
||||
// If either a particular keys array, or the corresponding values ArrayList
|
||||
// are null, then they both are.
|
||||
private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
|
||||
private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
|
||||
new ArrayList[MAIN_INDEX_SIZE];
|
||||
}
|
||||
|
||||
private static ProxyMap sProxyMap = new ProxyMap();
|
||||
|
||||
/**
|
||||
* Return a BinderProxy for IBinder.
|
||||
* This method is thread-hostile! The (native) caller serializes getInstance() calls using
|
||||
* gProxyLock.
|
||||
* If we previously returned a BinderProxy bp for the same iBinder, and bp is still
|
||||
* in use, then we return the same bp.
|
||||
*
|
||||
* @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
|
||||
* Takes ownership of nativeData iff <result>.mNativeData == nativeData. Caller will usually
|
||||
* delete nativeData if that's not the case.
|
||||
* @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
|
||||
*/
|
||||
private static BinderProxy getInstance(long nativeData, long iBinder) {
|
||||
BinderProxy result = sProxyMap.get(iBinder);
|
||||
if (result == null) {
|
||||
result = new BinderProxy(nativeData);
|
||||
sProxyMap.set(iBinder, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private BinderProxy(long nativeData) {
|
||||
mNativeData = nativeData;
|
||||
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guestimate of native memory associated with a BinderProxy.
|
||||
* This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
|
||||
@@ -858,12 +1041,6 @@ final class BinderProxy implements IBinder {
|
||||
}
|
||||
}
|
||||
|
||||
BinderProxy(long nativeData) {
|
||||
mNativeData = nativeData;
|
||||
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeData);
|
||||
mSelf = new WeakReference(this);
|
||||
}
|
||||
|
||||
private static final void sendDeathNotice(DeathRecipient recipient) {
|
||||
if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
|
||||
try {
|
||||
@@ -875,14 +1052,6 @@ final class BinderProxy implements IBinder {
|
||||
}
|
||||
}
|
||||
|
||||
// This WeakReference to "this" is used only by native code to "attach" to the
|
||||
// native IBinder object.
|
||||
// Using WeakGlobalRefs instead currently appears unsafe, in that they can yield a
|
||||
// non-null value after the BinderProxy is enqueued for finalization.
|
||||
// Used only once immediately after construction.
|
||||
// TODO: Consider making the extra native-to-java call to compute this on the fly.
|
||||
final private WeakReference mSelf;
|
||||
|
||||
/**
|
||||
* C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
|
||||
* native IBinder object, and a DeathRecipientList.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "android_os_Parcel.h"
|
||||
#include "android_util_Binder.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
@@ -96,13 +97,11 @@ static struct binderproxy_offsets_t
|
||||
{
|
||||
// Class state.
|
||||
jclass mClass;
|
||||
jmethodID mConstructor;
|
||||
jmethodID mGetInstance;
|
||||
jmethodID mSendDeathNotice;
|
||||
|
||||
// Object state.
|
||||
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
|
||||
jfieldID mSelf; // Field holds Java pointer to WeakReference to BinderProxy.
|
||||
|
||||
} gBinderProxyOffsets;
|
||||
|
||||
static struct class_offsets_t
|
||||
@@ -143,20 +142,45 @@ static struct thread_dispatch_offsets_t
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
static volatile int32_t gNumRefsCreated = 0;
|
||||
static volatile int32_t gNumProxyRefs = 0;
|
||||
static volatile int32_t gNumLocalRefs = 0;
|
||||
static volatile int32_t gNumDeathRefs = 0;
|
||||
static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
|
||||
static constexpr uint32_t GC_INTERVAL = 1000;
|
||||
|
||||
static void incRefsCreated(JNIEnv* env)
|
||||
// Protected by gProxyLock. We warn if this gets too large.
|
||||
static int32_t gNumProxies = 0;
|
||||
static int32_t gProxiesWarned = 0;
|
||||
|
||||
// Number of GlobalRefs held by JavaBBinders.
|
||||
static std::atomic<uint32_t> gNumLocalRefsCreated(0);
|
||||
static std::atomic<uint32_t> gNumLocalRefsDeleted(0);
|
||||
// Number of GlobalRefs held by JavaDeathRecipients.
|
||||
static std::atomic<uint32_t> gNumDeathRefsCreated(0);
|
||||
static std::atomic<uint32_t> gNumDeathRefsDeleted(0);
|
||||
|
||||
// We collected after creating this many refs.
|
||||
static std::atomic<uint32_t> gCollectedAtRefs(0);
|
||||
|
||||
// Garbage collect if we've allocated at least GC_INTERVAL refs since the last time.
|
||||
// TODO: Consider removing this completely. We should no longer be generating GlobalRefs
|
||||
// that are reclaimed as a result of GC action.
|
||||
static void gcIfManyNewRefs(JNIEnv* env)
|
||||
{
|
||||
int old = android_atomic_inc(&gNumRefsCreated);
|
||||
if (old == 200) {
|
||||
android_atomic_and(0, &gNumRefsCreated);
|
||||
env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
|
||||
gBinderInternalOffsets.mForceGc);
|
||||
uint32_t totalRefs = gNumLocalRefsCreated.load(std::memory_order_relaxed)
|
||||
+ gNumDeathRefsCreated.load(std::memory_order_relaxed);
|
||||
uint32_t collectedAtRefs = gCollectedAtRefs.load(memory_order_relaxed);
|
||||
// A bound on the number of threads that can have incremented gNum...RefsCreated before the
|
||||
// following check is executed. Effectively a bound on #threads. Almost any value will do.
|
||||
static constexpr uint32_t MAX_RACING = 100000;
|
||||
|
||||
if (totalRefs - (collectedAtRefs + GC_INTERVAL) /* modular arithmetic! */ < MAX_RACING) {
|
||||
// Recently passed next GC interval.
|
||||
if (gCollectedAtRefs.compare_exchange_strong(collectedAtRefs,
|
||||
collectedAtRefs + GC_INTERVAL, std::memory_order_relaxed)) {
|
||||
ALOGV("Binder forcing GC at %u created refs", totalRefs);
|
||||
env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
|
||||
gBinderInternalOffsets.mForceGc);
|
||||
} // otherwise somebody else beat us to it.
|
||||
} else {
|
||||
ALOGV("Now have %d binder ops", old);
|
||||
ALOGV("Now have %d binder ops", totalRefs - collectedAtRefs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,12 +290,12 @@ class JavaBBinderHolder;
|
||||
class JavaBBinder : public BBinder
|
||||
{
|
||||
public:
|
||||
JavaBBinder(JNIEnv* env, jobject object)
|
||||
JavaBBinder(JNIEnv* env, jobject /* Java Binder */ object)
|
||||
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
|
||||
{
|
||||
ALOGV("Creating JavaBBinder %p\n", this);
|
||||
android_atomic_inc(&gNumLocalRefs);
|
||||
incRefsCreated(env);
|
||||
gNumLocalRefsCreated.fetch_add(1, std::memory_order_relaxed);
|
||||
gcIfManyNewRefs(env);
|
||||
}
|
||||
|
||||
bool checkSubclass(const void* subclassID) const
|
||||
@@ -288,7 +312,7 @@ protected:
|
||||
virtual ~JavaBBinder()
|
||||
{
|
||||
ALOGV("Destroying JavaBBinder %p\n", this);
|
||||
android_atomic_dec(&gNumLocalRefs);
|
||||
gNumLocalRefsDeleted.fetch_add(1, memory_order_relaxed);
|
||||
JNIEnv* env = javavm_to_jnienv(mVM);
|
||||
env->DeleteGlobalRef(mObject);
|
||||
}
|
||||
@@ -350,7 +374,7 @@ protected:
|
||||
|
||||
private:
|
||||
JavaVM* const mVM;
|
||||
jobject const mObject;
|
||||
jobject const mObject; // GlobalRef to Java Binder
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -420,8 +444,8 @@ public:
|
||||
LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
|
||||
list->add(this);
|
||||
|
||||
android_atomic_inc(&gNumDeathRefs);
|
||||
incRefsCreated(env);
|
||||
gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
|
||||
gcIfManyNewRefs(env);
|
||||
}
|
||||
|
||||
void binderDied(const wp<IBinder>& who)
|
||||
@@ -501,7 +525,7 @@ protected:
|
||||
virtual ~JavaDeathRecipient()
|
||||
{
|
||||
//ALOGI("Removing death ref: recipient=%p\n", mObject);
|
||||
android_atomic_dec(&gNumDeathRefs);
|
||||
gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
|
||||
JNIEnv* env = javavm_to_jnienv(mVM);
|
||||
if (mObject != NULL) {
|
||||
env->DeleteGlobalRef(mObject);
|
||||
@@ -578,26 +602,19 @@ Mutex& DeathRecipientList::lock() {
|
||||
|
||||
namespace android {
|
||||
|
||||
static void proxy_cleanup(const void* id, void* obj, void* cleanupCookie)
|
||||
{
|
||||
android_atomic_dec(&gNumProxyRefs);
|
||||
JNIEnv* env = javavm_to_jnienv((JavaVM*)cleanupCookie);
|
||||
env->DeleteGlobalRef((jobject)obj);
|
||||
}
|
||||
|
||||
// We aggregate native pointer fields for BinderProxy in a single object to allow
|
||||
// management with a single NativeAllocationRegistry, and to reduce the number of JNI
|
||||
// Java field accesses. This costs us some extra indirections here.
|
||||
struct BinderProxyNativeData {
|
||||
// Both fields are constant and not null once javaObjectForIBinder returns this as
|
||||
// part of a BinderProxy.
|
||||
|
||||
// The native IBinder proxied by this BinderProxy.
|
||||
const sp<IBinder> mObject;
|
||||
sp<IBinder> mObject;
|
||||
|
||||
// Death recipients for mObject. Reference counted only because DeathRecipients
|
||||
// hold a weak reference that can be temporarily promoted.
|
||||
const sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
|
||||
|
||||
BinderProxyNativeData(const sp<IBinder> &obj, DeathRecipientList *drl)
|
||||
: mObject(obj), mOrgue(drl) {};
|
||||
sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
|
||||
};
|
||||
|
||||
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
|
||||
@@ -606,12 +623,19 @@ BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
|
||||
|
||||
static Mutex gProxyLock;
|
||||
|
||||
// We may cache a single BinderProxyNativeData node to avoid repeat allocation.
|
||||
// All fields are null. Protected by gProxyLock.
|
||||
static BinderProxyNativeData *gNativeDataCache;
|
||||
|
||||
// If the argument is a JavaBBinder, return the Java object that was used to create it.
|
||||
// Otherwise return a BinderProxy for the IBinder. If a previous call was passed the
|
||||
// same IBinder, and the original BinderProxy is still alive, return the same BinderProxy.
|
||||
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
|
||||
{
|
||||
if (val == NULL) return NULL;
|
||||
|
||||
if (val->checkSubclass(&gBinderOffsets)) {
|
||||
// One of our own!
|
||||
// It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
|
||||
jobject object = static_cast<JavaBBinder*>(val.get())->object();
|
||||
LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
|
||||
return object;
|
||||
@@ -621,39 +645,31 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
|
||||
// looking/creation/destruction of Java proxies for native Binder proxies.
|
||||
AutoMutex _l(gProxyLock);
|
||||
|
||||
// Someone else's... do we know about it?
|
||||
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
|
||||
if (object != NULL) {
|
||||
jobject res = jniGetReferent(env, object);
|
||||
if (res != NULL) {
|
||||
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
|
||||
return res;
|
||||
}
|
||||
LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
|
||||
android_atomic_dec(&gNumProxyRefs);
|
||||
val->detachObject(&gBinderProxyOffsets);
|
||||
env->DeleteGlobalRef(object);
|
||||
BinderProxyNativeData* nativeData = gNativeDataCache;
|
||||
if (nativeData == nullptr) {
|
||||
nativeData = new BinderProxyNativeData();
|
||||
}
|
||||
|
||||
DeathRecipientList* drl = new DeathRecipientList;
|
||||
BinderProxyNativeData* nativeData = new BinderProxyNativeData(val, drl);
|
||||
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor,
|
||||
(jlong)nativeData);
|
||||
if (object != NULL) {
|
||||
LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
|
||||
|
||||
// The native object needs to hold a weak reference back to the
|
||||
// proxy, so we can retrieve the same proxy if it is still active.
|
||||
// A JNI WeakGlobalRef would not currently work here, since it may be cleared
|
||||
// after the Java object has been condemned, and can thus yield a stale reference.
|
||||
jobject refObject = env->NewGlobalRef(
|
||||
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
|
||||
val->attachObject(&gBinderProxyOffsets, refObject,
|
||||
jnienv_to_javavm(env), proxy_cleanup);
|
||||
|
||||
// Note that a new object reference has been created.
|
||||
android_atomic_inc(&gNumProxyRefs);
|
||||
incRefsCreated(env);
|
||||
// gNativeDataCache is now logically empty.
|
||||
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
|
||||
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
|
||||
if (env->ExceptionCheck()) {
|
||||
gNativeDataCache = nativeData;
|
||||
return NULL;
|
||||
}
|
||||
BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
|
||||
if (actualNativeData == nativeData) {
|
||||
// New BinderProxy; we still have exclusive access.
|
||||
nativeData->mOrgue = new DeathRecipientList;
|
||||
nativeData->mObject = val;
|
||||
gNativeDataCache = nullptr;
|
||||
++gNumProxies;
|
||||
if (++gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
|
||||
ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
|
||||
gProxiesWarned = gNumProxies;
|
||||
}
|
||||
} else {
|
||||
// nativeData wasn't used. Reuse it the next time.
|
||||
gNativeDataCache = nativeData;
|
||||
}
|
||||
|
||||
return object;
|
||||
@@ -663,12 +679,14 @@ sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
|
||||
{
|
||||
if (obj == NULL) return NULL;
|
||||
|
||||
// Instance of Binder?
|
||||
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
|
||||
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
|
||||
env->GetLongField(obj, gBinderOffsets.mObject);
|
||||
return jbh->get(env, obj);
|
||||
}
|
||||
|
||||
// Instance of BinderProxy?
|
||||
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
|
||||
return getBPNativeData(env, obj)->mObject;
|
||||
}
|
||||
@@ -924,17 +942,18 @@ namespace android {
|
||||
|
||||
jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz)
|
||||
{
|
||||
return gNumLocalRefs;
|
||||
return gNumLocalRefsCreated - gNumLocalRefsDeleted;
|
||||
}
|
||||
|
||||
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
|
||||
{
|
||||
return gNumProxyRefs;
|
||||
AutoMutex _l(gProxyLock);
|
||||
return gNumProxies;
|
||||
}
|
||||
|
||||
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
|
||||
{
|
||||
return gNumDeathRefs;
|
||||
return gNumDeathRefsCreated - gNumDeathRefsDeleted;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -969,8 +988,8 @@ static void android_os_BinderInternal_setMaxThreads(JNIEnv* env,
|
||||
|
||||
static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
|
||||
{
|
||||
ALOGV("Gc has executed, clearing binder ops");
|
||||
android_atomic_and(0, &gNumRefsCreated);
|
||||
ALOGV("Gc has executed, updating Refs count at GC");
|
||||
gCollectedAtRefs = gNumLocalRefsCreated + gNumDeathRefsCreated;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -1201,10 +1220,6 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
|
||||
|
||||
BinderProxyNativeData *nd = getBPNativeData(env, obj);
|
||||
IBinder* target = nd->mObject.get();
|
||||
if (target == NULL) {
|
||||
ALOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);
|
||||
|
||||
@@ -1277,8 +1292,9 @@ static void BinderProxy_destroy(void* rawNativeData)
|
||||
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
|
||||
LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
|
||||
nativeData->mObject.get(), nativeData->mOrgue.get());
|
||||
delete (BinderProxyNativeData *) rawNativeData;
|
||||
delete nativeData;
|
||||
IPCThreadState::self()->flushCommands();
|
||||
--gNumProxies;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclass) {
|
||||
@@ -1307,13 +1323,11 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
|
||||
|
||||
clazz = FindClassOrDie(env, kBinderProxyPathName);
|
||||
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
|
||||
gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "(J)V");
|
||||
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
|
||||
"(JJ)Landroid/os/BinderProxy;");
|
||||
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
|
||||
"(Landroid/os/IBinder$DeathRecipient;)V");
|
||||
|
||||
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
|
||||
gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
|
||||
"Ljava/lang/ref/WeakReference;");
|
||||
|
||||
clazz = FindClassOrDie(env, "java/lang/Class");
|
||||
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
|
||||
|
||||
Reference in New Issue
Block a user