diff options
| -rw-r--r-- | core/java/android/os/Binder.java | 197 | ||||
| -rw-r--r-- | core/jni/android_util_Binder.cpp | 176 |
2 files changed, 278 insertions, 95 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index ceeda042f9ad..2bfb0138b7dd 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 @@ -751,6 +752,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 @@ -856,12 +1039,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 { @@ -873,14 +1050,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 1b543f604a68..dea38e81cd54 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> @@ -105,13 +106,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 @@ -152,20 +151,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); } } @@ -275,12 +299,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 @@ -297,7 +321,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); } @@ -359,7 +383,7 @@ protected: private: JavaVM* const mVM; - jobject const mObject; + jobject const mObject; // GlobalRef to Java Binder }; // ---------------------------------------------------------------------------- @@ -429,8 +453,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) @@ -510,7 +534,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); @@ -587,26 +611,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) { @@ -615,12 +632,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; @@ -630,39 +654,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; @@ -672,12 +688,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; } @@ -933,17 +951,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; } } @@ -978,8 +997,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; } static void android_os_BinderInternal_proxyLimitcallback(int uid) @@ -1261,10 +1280,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); @@ -1337,8 +1352,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) { @@ -1367,13 +1383,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;"); |