Merge "Make BinderProxy @hide public class."
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a81d16a6..36fe220 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -33,12 +33,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -761,397 +756,3 @@
}
}
-/**
- * Java proxy for a native IBinder object.
- * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
- * directly from Java code.
- */
-final class BinderProxy implements IBinder {
- // See android_util_Binder.cpp for the native half of this.
-
- // 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;
- // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
- private static final int CRASH_AT_SIZE = 5_000;
-
- /**
- * 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.
- */
- private int size() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- size += a.size();
- }
- }
- return size;
- }
-
- /**
- * Return the total number of pairs in the map containing values that have
- * not been cleared. More expensive than the above size function.
- */
- private int unclearedSize() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> ref : a) {
- if (ref.get() != null) {
- ++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) {
- final int totalSize = size();
- Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
- + " total = " + totalSize);
- mWarnBucketSize += WARN_INCREMENT;
- if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
- // Use the number of uncleared entries to determine whether we should
- // really report a histogram and crash. We don't want to fundamentally
- // change behavior for a debuggable process, so we GC only if we are
- // about to crash.
- final int totalUnclearedSize = unclearedSize();
- if (totalUnclearedSize >= CRASH_AT_SIZE) {
- dumpProxyInterfaceCounts();
- Runtime.getRuntime().gc();
- throw new AssertionError("Binder ProxyMap has too many entries: "
- + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
- + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
- } else if (totalSize > 3 * totalUnclearedSize / 2) {
- Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
- + (totalSize - totalUnclearedSize) + " of " + totalSize
- + " are cleared");
- }
- }
- }
- }
-
- /**
- * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
- */
- private void dumpProxyInterfaceCounts() {
- Map<String, Integer> counts = new HashMap<>();
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> weakRef : a) {
- BinderProxy bp = weakRef.get();
- String key;
- if (bp == null) {
- key = "<cleared weak-ref>";
- } else {
- try {
- key = bp.getInterfaceDescriptor();
- } catch (Throwable t) {
- key = "<exception during getDescriptor>";
- }
- }
- Integer i = counts.get(key);
- if (i == null) {
- counts.put(key, 1);
- } else {
- counts.put(key, i + 1);
- }
- }
- }
- }
- Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
- new Map.Entry[counts.size()]);
- Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
- -> b.getValue().compareTo(a.getValue()));
- Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
- int printLength = Math.min(10, sorted.length);
- for (int i = 0; i < printLength; i++) {
- Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
- + sorted[i].getValue());
- }
- }
-
- // 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
- * that points back to us. We guess high since it includes a GlobalRef, which
- * may be in short supply.
- */
- private static final int NATIVE_ALLOCATION_SIZE = 1000;
-
- // Use a Holder to allow static initialization of BinderProxy in the boot image, and
- // to avoid some initialization ordering issues.
- private static class NoImagePreloadHolder {
- public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- BinderProxy.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
- }
-
- public native boolean pingBinder();
- public native boolean isBinderAlive();
-
- public IInterface queryLocalInterface(String descriptor) {
- return null;
- }
-
- public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
- Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
-
- if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
- // For now, avoid spamming the log by disabling after we've logged
- // about this interface at least once
- mWarnOnBlocking = false;
- Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
- new Throwable());
- }
-
- final boolean tracingEnabled = Binder.isTracingEnabled();
- if (tracingEnabled) {
- final Throwable tr = new Throwable();
- Binder.getTransactionTracker().addTrace(tr);
- StackTraceElement stackTraceElement = tr.getStackTrace()[1];
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
- stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
- }
- try {
- return transactNative(code, data, reply, flags);
- } finally {
- if (tracingEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- }
- }
- }
-
- private static native long getNativeFinalizer();
- public native String getInterfaceDescriptor() throws RemoteException;
- public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
-
- public void dump(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(in);
- data.writeFileDescriptor(out);
- data.writeFileDescriptor(err);
- data.writeStringArray(args);
- ShellCallback.writeToParcel(callback, data);
- resultReceiver.writeToParcel(data, 0);
- try {
- transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- private static final void sendDeathNotice(DeathRecipient recipient) {
- if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
- try {
- recipient.binderDied();
- }
- catch (RuntimeException exc) {
- Log.w("BinderNative", "Uncaught exception from death notification",
- exc);
- }
- }
-
- /**
- * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
- * native IBinder object, and a DeathRecipientList.
- */
- private final long mNativeData;
-}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
new file mode 100644
index 0000000..629838c
--- /dev/null
+++ b/core/java/android/os/BinderProxy.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ *
+ * @hide
+ */
+public final class BinderProxy implements IBinder {
+ // See android_util_Binder.cpp for the native half of this.
+
+ // 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;
+ // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+ private static final int CRASH_AT_SIZE = 20_000;
+
+ /**
+ * 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.
+ */
+ private int size() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ size += a.size();
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Return the total number of pairs in the map containing values that have
+ * not been cleared. More expensive than the above size function.
+ */
+ private int unclearedSize() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> ref : a) {
+ if (ref.get() != null) {
+ ++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) {
+ final int totalSize = size();
+ Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
+ + " total = " + totalSize);
+ mWarnBucketSize += WARN_INCREMENT;
+ if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+ // Use the number of uncleared entries to determine whether we should
+ // really report a histogram and crash. We don't want to fundamentally
+ // change behavior for a debuggable process, so we GC only if we are
+ // about to crash.
+ final int totalUnclearedSize = unclearedSize();
+ if (totalUnclearedSize >= CRASH_AT_SIZE) {
+ dumpProxyInterfaceCounts();
+ dumpPerUidProxyCounts();
+ Runtime.getRuntime().gc();
+ throw new AssertionError("Binder ProxyMap has too many entries: "
+ + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+ } else if (totalSize > 3 * totalUnclearedSize / 2) {
+ Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+ + (totalSize - totalUnclearedSize) + " of " + totalSize
+ + " are cleared");
+ }
+ }
+ }
+ }
+
+ /**
+ * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+ */
+ private void dumpProxyInterfaceCounts() {
+ Map<String, Integer> counts = new HashMap<>();
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> weakRef : a) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
+ }
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
+ }
+ }
+ }
+ }
+ Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+ new Map.Entry[counts.size()]);
+ Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+ -> b.getValue().compareTo(a.getValue()));
+ Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
+ int printLength = Math.min(10, sorted.length);
+ for (int i = 0; i < printLength; i++) {
+ Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
+ + sorted[i].getValue());
+ }
+ }
+
+ /**
+ * Dump per uid binder proxy counts to the logcat.
+ */
+ private void dumpPerUidProxyCounts() {
+ SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+ if (counts.size() == 0) return;
+ Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+ for (int i = 0; i < counts.size(); i++) {
+ final int uid = counts.keyAt(i);
+ final int binderCount = counts.valueAt(i);
+ Log.d(Binder.TAG, "UID : " + uid + " count = " + binderCount);
+ }
+ }
+
+ // 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];
+ }
+
+ @GuardedBy("sProxyMap")
+ private static final ProxyMap sProxyMap = new ProxyMap();
+
+ /**
+ * Dump proxy debug information.
+ *
+ * @hide
+ */
+ private static void dumpProxyDebugInfo() {
+ if (Build.IS_DEBUGGABLE) {
+ synchronized (sProxyMap) {
+ sProxyMap.dumpProxyInterfaceCounts();
+ }
+ // Note that we don't call dumpPerUidProxyCounts(); this is because this
+ // method may be called as part of the uid limit being hit, and calling
+ // back into the UID tracking code would cause us to try to acquire a mutex
+ // that is held during that callback.
+ }
+ }
+
+ /**
+ * Return a BinderProxy for IBinder.
+ * 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, or if
+ * we exit via an exception. If neither applies, it's the callers responsibility to
+ * recycle nativeData.
+ * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
+ */
+ private static BinderProxy getInstance(long nativeData, long iBinder) {
+ BinderProxy result;
+ synchronized (sProxyMap) {
+ try {
+ result = sProxyMap.get(iBinder);
+ if (result != null) {
+ return result;
+ }
+ result = new BinderProxy(nativeData);
+ } catch (Throwable e) {
+ // We're throwing an exception (probably OOME); don't drop nativeData.
+ NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+ nativeData);
+ throw e;
+ }
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+ // The registry now owns nativeData, even if registration threw an exception.
+ sProxyMap.set(iBinder, result);
+ }
+ return result;
+ }
+
+ private BinderProxy(long nativeData) {
+ mNativeData = nativeData;
+ }
+
+ /**
+ * Guestimate of native memory associated with a BinderProxy.
+ * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
+ * that points back to us. We guess high since it includes a GlobalRef, which
+ * may be in short supply.
+ */
+ private static final int NATIVE_ALLOCATION_SIZE = 1000;
+
+ // Use a Holder to allow static initialization of BinderProxy in the boot image, and
+ // to avoid some initialization ordering issues.
+ private static class NoImagePreloadHolder {
+ public static final long sNativeFinalizer = getNativeFinalizer();
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
+ }
+
+ /**
+ * @return false if the hosting process is gone, otherwise whatever the remote returns
+ */
+ public native boolean pingBinder();
+
+ /**
+ * @return false if the hosting process is gone
+ */
+ public native boolean isBinderAlive();
+
+ /**
+ * Retrieve a local interface - always null in case of a proxy
+ */
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ /**
+ * Perform a binder transaction on a proxy.
+ *
+ * @param code The action to perform. This should
+ * be a number between {@link #FIRST_CALL_TRANSACTION} and
+ * {@link #LAST_CALL_TRANSACTION}.
+ * @param data Marshalled data to send to the target. Must not be null.
+ * If you are not sending any data, you must create an empty Parcel
+ * that is given here.
+ * @param reply Marshalled data to be received from the target. May be
+ * null if you are not interested in the return value.
+ * @param flags Additional operation flags. Either 0 for a normal
+ * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+ *
+ * @return
+ * @throws RemoteException
+ */
+ public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+ Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+
+ if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
+ // For now, avoid spamming the log by disabling after we've logged
+ // about this interface at least once
+ mWarnOnBlocking = false;
+ Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
+ new Throwable());
+ }
+
+ final boolean tracingEnabled = Binder.isTracingEnabled();
+ if (tracingEnabled) {
+ final Throwable tr = new Throwable();
+ Binder.getTransactionTracker().addTrace(tr);
+ StackTraceElement stackTraceElement = tr.getStackTrace()[1];
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
+ stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
+ }
+ try {
+ return transactNative(code, data, reply, flags);
+ } finally {
+ if (tracingEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ }
+ }
+ }
+
+ /* Returns the native free function */
+ private static native long getNativeFinalizer();
+ /**
+ * See {@link IBinder#getInterfaceDescriptor()}
+ */
+ public native String getInterfaceDescriptor() throws RemoteException;
+
+ /**
+ * Native implementation of transact() for proxies
+ */
+ public native boolean transactNative(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException;
+ /**
+ * See {@link IBinder#linkToDeath(DeathRecipient, int)}
+ */
+ public native void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException;
+ /**
+ * See {@link IBinder#unlinkToDeath}
+ */
+ public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+ /**
+ * Perform a dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * Perform an asynchronous dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
+ * String[], ShellCallback, ResultReceiver)}
+ *
+ * @param in The raw file descriptor that an input data stream can be read from.
+ * @param out The raw file descriptor that normal command messages should be written to.
+ * @param err The raw file descriptor that command error messages should be written to.
+ * @param args Command-line arguments.
+ * @param callback Optional callback to the caller's shell to perform operations in it.
+ * @param resultReceiver Called when the command has finished executing, with the result code.
+ * @throws RemoteException
+ */
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(in);
+ data.writeFileDescriptor(out);
+ data.writeFileDescriptor(err);
+ data.writeStringArray(args);
+ ShellCallback.writeToParcel(callback, data);
+ resultReceiver.writeToParcel(data, 0);
+ try {
+ transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ private static void sendDeathNotice(DeathRecipient recipient) {
+ if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ try {
+ recipient.binderDied();
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from death notification",
+ exc);
+ }
+ }
+
+ /**
+ * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
+ * native IBinder object, and a DeathRecipientList.
+ */
+ private final long mNativeData;
+}