Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.os;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.util.Log;
     22 import android.util.SparseIntArray;
     23 
     24 import com.android.internal.annotations.GuardedBy;
     25 import com.android.internal.os.BinderInternal;
     26 
     27 import libcore.util.NativeAllocationRegistry;
     28 
     29 import java.io.FileDescriptor;
     30 import java.lang.ref.WeakReference;
     31 import java.util.ArrayList;
     32 import java.util.Arrays;
     33 import java.util.HashMap;
     34 import java.util.Map;
     35 
     36 /**
     37  * Java proxy for a native IBinder object.
     38  * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
     39  * directly from Java code.
     40  *
     41  * @hide
     42  */
     43 public final class BinderProxy implements IBinder {
     44     // See android_util_Binder.cpp for the native half of this.
     45 
     46     // Assume the process-wide default value when created
     47     volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
     48 
     49     private static volatile Binder.ProxyTransactListener sTransactListener = null;
     50 
     51     /**
     52      * @see {@link Binder#setProxyTransactListener(listener)}.
     53      */
     54     public static void setTransactListener(@Nullable Binder.ProxyTransactListener listener) {
     55         sTransactListener = listener;
     56     }
     57 
     58     /*
     59      * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
     60      * We roll our own only because we need to lazily remove WeakReferences during accesses
     61      * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
     62      * because we want weak values, not keys.
     63      * Our hash table is never resized, but the number of entries is unlimited;
     64      * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
     65      * Not thread-safe. Client ensures there's a single access at a time.
     66      */
     67     private static final class ProxyMap {
     68         private static final int LOG_MAIN_INDEX_SIZE = 8;
     69         private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
     70         private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
     71         // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
     72         private static final int CRASH_AT_SIZE = 20_000;
     73 
     74         /**
     75          * We next warn when we exceed this bucket size.
     76          */
     77         private int mWarnBucketSize = 20;
     78 
     79         /**
     80          * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
     81          */
     82         private static final int WARN_INCREMENT = 10;
     83 
     84         /**
     85          * Hash function tailored to native pointers.
     86          * Returns a value < MAIN_INDEX_SIZE.
     87          */
     88         private static int hash(long arg) {
     89             return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
     90         }
     91 
     92         /**
     93          * Return the total number of pairs in the map.
     94          */
     95         private int size() {
     96             int size = 0;
     97             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
     98                 if (a != null) {
     99                     size += a.size();
    100                 }
    101             }
    102             return size;
    103         }
    104 
    105         /**
    106          * Return the total number of pairs in the map containing values that have
    107          * not been cleared. More expensive than the above size function.
    108          */
    109         private int unclearedSize() {
    110             int size = 0;
    111             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
    112                 if (a != null) {
    113                     for (WeakReference<BinderProxy> ref : a) {
    114                         if (ref.get() != null) {
    115                             ++size;
    116                         }
    117                     }
    118                 }
    119             }
    120             return size;
    121         }
    122 
    123         /**
    124          * Remove ith entry from the hash bucket indicated by hash.
    125          */
    126         private void remove(int hash, int index) {
    127             Long[] keyArray = mMainIndexKeys[hash];
    128             ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
    129             int size = valueArray.size();  // KeyArray may have extra elements.
    130             // Move last entry into empty slot, and truncate at end.
    131             if (index != size - 1) {
    132                 keyArray[index] = keyArray[size - 1];
    133                 valueArray.set(index, valueArray.get(size - 1));
    134             }
    135             valueArray.remove(size - 1);
    136             // Just leave key array entry; it's unused. We only trust the valueArray size.
    137         }
    138 
    139         /**
    140          * Look up the supplied key. If we have a non-cleared entry for it, return it.
    141          */
    142         BinderProxy get(long key) {
    143             int myHash = hash(key);
    144             Long[] keyArray = mMainIndexKeys[myHash];
    145             if (keyArray == null) {
    146                 return null;
    147             }
    148             ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
    149             int bucketSize = valueArray.size();
    150             for (int i = 0; i < bucketSize; ++i) {
    151                 long foundKey = keyArray[i];
    152                 if (key == foundKey) {
    153                     WeakReference<BinderProxy> wr = valueArray.get(i);
    154                     BinderProxy bp = wr.get();
    155                     if (bp != null) {
    156                         return bp;
    157                     } else {
    158                         remove(myHash, i);
    159                         return null;
    160                     }
    161                 }
    162             }
    163             return null;
    164         }
    165 
    166         private int mRandom;  // A counter used to generate a "random" index. World's 2nd worst RNG.
    167 
    168         /**
    169          * Add the key-value pair to the map.
    170          * Requires that the indicated key is not already in the map.
    171          */
    172         void set(long key, @NonNull BinderProxy value) {
    173             int myHash = hash(key);
    174             ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
    175             if (valueArray == null) {
    176                 valueArray = mMainIndexValues[myHash] = new ArrayList<>();
    177                 mMainIndexKeys[myHash] = new Long[1];
    178             }
    179             int size = valueArray.size();
    180             WeakReference<BinderProxy> newWr = new WeakReference<>(value);
    181             // First look for a cleared reference.
    182             // This ensures that ArrayList size is bounded by the maximum occupancy of
    183             // that bucket.
    184             for (int i = 0; i < size; ++i) {
    185                 if (valueArray.get(i).get() == null) {
    186                     valueArray.set(i, newWr);
    187                     Long[] keyArray = mMainIndexKeys[myHash];
    188                     keyArray[i] = key;
    189                     if (i < size - 1) {
    190                         // "Randomly" check one of the remaining entries in [i+1, size), so that
    191                         // needlessly long buckets are eventually pruned.
    192                         int rnd = Math.floorMod(++mRandom, size - (i + 1));
    193                         if (valueArray.get(i + 1 + rnd).get() == null) {
    194                             remove(myHash, i + 1 + rnd);
    195                         }
    196                     }
    197                     return;
    198                 }
    199             }
    200             valueArray.add(size, newWr);
    201             Long[] keyArray = mMainIndexKeys[myHash];
    202             if (keyArray.length == size) {
    203                 // size >= 1, since we initially allocated one element
    204                 Long[] newArray = new Long[size + size / 2 + 2];
    205                 System.arraycopy(keyArray, 0, newArray, 0, size);
    206                 newArray[size] = key;
    207                 mMainIndexKeys[myHash] = newArray;
    208             } else {
    209                 keyArray[size] = key;
    210             }
    211             if (size >= mWarnBucketSize) {
    212                 final int totalSize = size();
    213                 Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
    214                         + " total = " + totalSize);
    215                 mWarnBucketSize += WARN_INCREMENT;
    216                 if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
    217                     // Use the number of uncleared entries to determine whether we should
    218                     // really report a histogram and crash. We don't want to fundamentally
    219                     // change behavior for a debuggable process, so we GC only if we are
    220                     // about to crash.
    221                     final int totalUnclearedSize = unclearedSize();
    222                     if (totalUnclearedSize >= CRASH_AT_SIZE) {
    223                         dumpProxyInterfaceCounts();
    224                         dumpPerUidProxyCounts();
    225                         Runtime.getRuntime().gc();
    226                         throw new AssertionError("Binder ProxyMap has too many entries: "
    227                                 + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
    228                                 + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
    229                     } else if (totalSize > 3 * totalUnclearedSize / 2) {
    230                         Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
    231                                 + (totalSize - totalUnclearedSize) + " of " + totalSize
    232                                 + " are cleared");
    233                     }
    234                 }
    235             }
    236         }
    237 
    238         private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
    239             if (maxToReturn < 0) {
    240                 throw new IllegalArgumentException("negative interface count");
    241             }
    242 
    243             Map<String, Integer> counts = new HashMap<>();
    244             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
    245                 if (a != null) {
    246                     for (WeakReference<BinderProxy> weakRef : a) {
    247                         BinderProxy bp = weakRef.get();
    248                         String key;
    249                         if (bp == null) {
    250                             key = "<cleared weak-ref>";
    251                         } else {
    252                             try {
    253                                 key = bp.getInterfaceDescriptor();
    254                                 if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
    255                                     key = "<proxy to dead node>";
    256                                 }
    257                             } catch (Throwable t) {
    258                                 key = "<exception during getDescriptor>";
    259                             }
    260                         }
    261                         Integer i = counts.get(key);
    262                         if (i == null) {
    263                             counts.put(key, 1);
    264                         } else {
    265                             counts.put(key, i + 1);
    266                         }
    267                     }
    268                 }
    269             }
    270             Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
    271                     new Map.Entry[counts.size()]);
    272 
    273             Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
    274                     -> b.getValue().compareTo(a.getValue()));
    275 
    276             int returnCount = Math.min(maxToReturn, sorted.length);
    277             InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
    278             for (int i = 0; i < returnCount; i++) {
    279                 ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
    280             }
    281             return ifaceCounts;
    282         }
    283 
    284         static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
    285 
    286         /**
    287          * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
    288          */
    289         private void dumpProxyInterfaceCounts() {
    290             final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
    291 
    292             Log.v(Binder.TAG, "BinderProxy descriptor histogram "
    293                     + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
    294             for (int i = 0; i < sorted.length; i++) {
    295                 Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
    296             }
    297         }
    298 
    299         /**
    300          * Dump per uid binder proxy counts to the logcat.
    301          */
    302         private void dumpPerUidProxyCounts() {
    303             SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
    304             if (counts.size() == 0) return;
    305             Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
    306             for (int i = 0; i < counts.size(); i++) {
    307                 final int uid = counts.keyAt(i);
    308                 final int binderCount = counts.valueAt(i);
    309                 Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
    310             }
    311         }
    312 
    313         // Corresponding ArrayLists in the following two arrays always have the same size.
    314         // They contain no empty entries. However WeakReferences in the values ArrayLists
    315         // may have been cleared.
    316 
    317         // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
    318         // The values ArrayList has the proper size(), the corresponding keys array
    319         // is always at least the same size, but may be larger.
    320         // If either a particular keys array, or the corresponding values ArrayList
    321         // are null, then they both are.
    322         private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
    323         private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
    324                 new ArrayList[MAIN_INDEX_SIZE];
    325     }
    326 
    327     @GuardedBy("sProxyMap")
    328     private static final ProxyMap sProxyMap = new ProxyMap();
    329 
    330     /**
    331      * Simple pair-value class to store number of binder proxy interfaces live in this process.
    332      */
    333     public static final class InterfaceCount {
    334         private final String mInterfaceName;
    335         private final int mCount;
    336 
    337         InterfaceCount(String interfaceName, int count) {
    338             mInterfaceName = interfaceName;
    339             mCount = count;
    340         }
    341 
    342         @Override
    343         public String toString() {
    344             return mInterfaceName + " x" + Integer.toString(mCount);
    345         }
    346     }
    347 
    348     /**
    349      * Get a sorted array with entries mapping proxy interface names to the number
    350      * of live proxies with those names.
    351      *
    352      * @param num maximum number of proxy interface counts to return. Use
    353      *            Integer.MAX_VALUE to retrieve all
    354      * @hide
    355      */
    356     public static InterfaceCount[] getSortedInterfaceCounts(int num) {
    357         synchronized (sProxyMap) {
    358             return sProxyMap.getSortedInterfaceCounts(num);
    359         }
    360     }
    361 
    362     /**
    363      * Returns the number of binder proxies held in this process.
    364      * @return number of binder proxies in this process
    365      */
    366     public static int getProxyCount() {
    367         synchronized (sProxyMap) {
    368             return sProxyMap.size();
    369         }
    370     }
    371 
    372     /**
    373      * Dump proxy debug information.
    374      *
    375      * @hide
    376      */
    377     public static void dumpProxyDebugInfo() {
    378         if (Build.IS_DEBUGGABLE) {
    379             synchronized (sProxyMap) {
    380                 sProxyMap.dumpProxyInterfaceCounts();
    381                 sProxyMap.dumpPerUidProxyCounts();
    382             }
    383         }
    384     }
    385 
    386     /**
    387      * Return a BinderProxy for IBinder.
    388      * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
    389      * in use, then we return the same bp.
    390      *
    391      * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
    392      * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
    393      * we exit via an exception.  If neither applies, it's the callers responsibility to
    394      * recycle nativeData.
    395      * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
    396      */
    397     private static BinderProxy getInstance(long nativeData, long iBinder) {
    398         BinderProxy result;
    399         synchronized (sProxyMap) {
    400             try {
    401                 result = sProxyMap.get(iBinder);
    402                 if (result != null) {
    403                     return result;
    404                 }
    405                 result = new BinderProxy(nativeData);
    406             } catch (Throwable e) {
    407                 // We're throwing an exception (probably OOME); don't drop nativeData.
    408                 NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
    409                         nativeData);
    410                 throw e;
    411             }
    412             NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
    413             // The registry now owns nativeData, even if registration threw an exception.
    414             sProxyMap.set(iBinder, result);
    415         }
    416         return result;
    417     }
    418 
    419     private BinderProxy(long nativeData) {
    420         mNativeData = nativeData;
    421     }
    422 
    423     /**
    424      * Guestimate of native memory associated with a BinderProxy.
    425      * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
    426      * that points back to us. We guess high since it includes a GlobalRef, which
    427      * may be in short supply.
    428      */
    429     private static final int NATIVE_ALLOCATION_SIZE = 1000;
    430 
    431     // Use a Holder to allow static initialization of BinderProxy in the boot image, and
    432     // to avoid some initialization ordering issues.
    433     private static class NoImagePreloadHolder {
    434         public static final long sNativeFinalizer = getNativeFinalizer();
    435         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
    436                 BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
    437     }
    438 
    439     /**
    440      * @return false if the hosting process is gone, otherwise whatever the remote returns
    441      */
    442     public native boolean pingBinder();
    443 
    444     /**
    445      * @return false if the hosting process is gone
    446      */
    447     public native boolean isBinderAlive();
    448 
    449     /**
    450      * Retrieve a local interface - always null in case of a proxy
    451      */
    452     public IInterface queryLocalInterface(String descriptor) {
    453         return null;
    454     }
    455 
    456     /**
    457      * Perform a binder transaction on a proxy.
    458      *
    459      * @param code The action to perform.  This should
    460      * be a number between {@link #FIRST_CALL_TRANSACTION} and
    461      * {@link #LAST_CALL_TRANSACTION}.
    462      * @param data Marshalled data to send to the target.  Must not be null.
    463      * If you are not sending any data, you must create an empty Parcel
    464      * that is given here.
    465      * @param reply Marshalled data to be received from the target.  May be
    466      * null if you are not interested in the return value.
    467      * @param flags Additional operation flags.  Either 0 for a normal
    468      * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
    469      *
    470      * @return
    471      * @throws RemoteException
    472      */
    473     public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    474         Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    475 
    476         if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
    477             // For now, avoid spamming the log by disabling after we've logged
    478             // about this interface at least once
    479             mWarnOnBlocking = false;
    480             Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
    481                     new Throwable());
    482         }
    483 
    484         final boolean tracingEnabled = Binder.isTracingEnabled();
    485         if (tracingEnabled) {
    486             final Throwable tr = new Throwable();
    487             Binder.getTransactionTracker().addTrace(tr);
    488             StackTraceElement stackTraceElement = tr.getStackTrace()[1];
    489             Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
    490                     stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
    491         }
    492 
    493         // Make sure the listener won't change while processing a transaction.
    494         final Binder.ProxyTransactListener transactListener = sTransactListener;
    495         Object session = null;
    496 
    497         if (transactListener != null) {
    498             final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
    499             session = transactListener.onTransactStarted(this, code);
    500 
    501             // Allow the listener to update the work source uid. We need to update the request
    502             // header if the uid is updated.
    503             final int updatedWorkSourceUid = Binder.getCallingWorkSourceUid();
    504             if (origWorkSourceUid != updatedWorkSourceUid) {
    505                 data.replaceCallingWorkSourceUid(updatedWorkSourceUid);
    506             }
    507         }
    508 
    509         try {
    510             return transactNative(code, data, reply, flags);
    511         } finally {
    512             if (transactListener != null) {
    513                 transactListener.onTransactEnded(session);
    514             }
    515 
    516             if (tracingEnabled) {
    517                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
    518             }
    519         }
    520     }
    521 
    522     /* Returns the native free function */
    523     private static native long getNativeFinalizer();
    524     /**
    525      *  See {@link IBinder#getInterfaceDescriptor()}
    526      */
    527     public native String getInterfaceDescriptor() throws RemoteException;
    528 
    529     /**
    530      * Native implementation of transact() for proxies
    531      */
    532     public native boolean transactNative(int code, Parcel data, Parcel reply,
    533             int flags) throws RemoteException;
    534     /**
    535      * See {@link IBinder#linkToDeath(DeathRecipient, int)}
    536      */
    537     public native void linkToDeath(DeathRecipient recipient, int flags)
    538             throws RemoteException;
    539     /**
    540      * See {@link IBinder#unlinkToDeath}
    541      */
    542     public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
    543 
    544     /**
    545      * Perform a dump on the remote object
    546      *
    547      * @param fd The raw file descriptor that the dump is being sent to.
    548      * @param args additional arguments to the dump request.
    549      * @throws RemoteException
    550      */
    551     public void dump(FileDescriptor fd, String[] args) throws RemoteException {
    552         Parcel data = Parcel.obtain();
    553         Parcel reply = Parcel.obtain();
    554         data.writeFileDescriptor(fd);
    555         data.writeStringArray(args);
    556         try {
    557             transact(DUMP_TRANSACTION, data, reply, 0);
    558             reply.readException();
    559         } finally {
    560             data.recycle();
    561             reply.recycle();
    562         }
    563     }
    564 
    565     /**
    566      * Perform an asynchronous dump on the remote object
    567      *
    568      * @param fd The raw file descriptor that the dump is being sent to.
    569      * @param args additional arguments to the dump request.
    570      * @throws RemoteException
    571      */
    572     public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
    573         Parcel data = Parcel.obtain();
    574         Parcel reply = Parcel.obtain();
    575         data.writeFileDescriptor(fd);
    576         data.writeStringArray(args);
    577         try {
    578             transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
    579         } finally {
    580             data.recycle();
    581             reply.recycle();
    582         }
    583     }
    584 
    585     /**
    586      * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
    587      * String[], ShellCallback, ResultReceiver)}
    588      *
    589      * @param in The raw file descriptor that an input data stream can be read from.
    590      * @param out The raw file descriptor that normal command messages should be written to.
    591      * @param err The raw file descriptor that command error messages should be written to.
    592      * @param args Command-line arguments.
    593      * @param callback Optional callback to the caller's shell to perform operations in it.
    594      * @param resultReceiver Called when the command has finished executing, with the result code.
    595      * @throws RemoteException
    596      */
    597     public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
    598             String[] args, ShellCallback callback,
    599             ResultReceiver resultReceiver) throws RemoteException {
    600         Parcel data = Parcel.obtain();
    601         Parcel reply = Parcel.obtain();
    602         data.writeFileDescriptor(in);
    603         data.writeFileDescriptor(out);
    604         data.writeFileDescriptor(err);
    605         data.writeStringArray(args);
    606         ShellCallback.writeToParcel(callback, data);
    607         resultReceiver.writeToParcel(data, 0);
    608         try {
    609             transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
    610             reply.readException();
    611         } finally {
    612             data.recycle();
    613             reply.recycle();
    614         }
    615     }
    616 
    617     private static void sendDeathNotice(DeathRecipient recipient) {
    618         if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
    619         try {
    620             recipient.binderDied();
    621         } catch (RuntimeException exc) {
    622             Log.w("BinderNative", "Uncaught exception from death notification",
    623                     exc);
    624         }
    625     }
    626 
    627     /**
    628      * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
    629      * native IBinder object, and a DeathRecipientList.
    630      */
    631     private final long mNativeData;
    632 }
    633