summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/Binder.java197
-rw-r--r--core/jni/android_util_Binder.cpp176
2 files changed, 278 insertions, 95 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a6dd862564e7..3c1d83c44cde 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -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.
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 74031910fac6..9f8d2880d8bf 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -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;
+
+// Protected by gProxyLock. We warn if this gets too large.
+static int32_t gNumProxies = 0;
+static int32_t gProxiesWarned = 0;
-static void incRefsCreated(JNIEnv* env)
+// 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;");