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      */
setTransactListener(@ullable Binder.ProxyTransactListener listener)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          */
hash(long arg)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          */
size()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          */
unclearedSize()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          */
remove(int hash, int index)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          */
get(long key)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          */
set(long key, @NonNull BinderProxy value)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 
getSortedInterfaceCounts(int maxToReturn)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             final ArrayList<WeakReference<BinderProxy>> proxiesToQuery =
245                     new ArrayList<WeakReference<BinderProxy>>();
246             synchronized (sProxyMap) {
247                 for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
248                     if (a != null) {
249                         proxiesToQuery.addAll(a);
250                     }
251                 }
252             }
253             for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
254                 BinderProxy bp = weakRef.get();
255                 String key;
256                 if (bp == null) {
257                     key = "<cleared weak-ref>";
258                 } else {
259                     try {
260                         key = bp.getInterfaceDescriptor();
261                         if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
262                             key = "<proxy to dead node>";
263                         }
264                     } catch (Throwable t) {
265                         key = "<exception during getDescriptor>";
266                     }
267                 }
268                 Integer i = counts.get(key);
269                 if (i == null) {
270                     counts.put(key, 1);
271                 } else {
272                     counts.put(key, i + 1);
273                 }
274             }
275             Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
276                     new Map.Entry[counts.size()]);
277 
278             Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
279                     -> b.getValue().compareTo(a.getValue()));
280 
281             int returnCount = Math.min(maxToReturn, sorted.length);
282             InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
283             for (int i = 0; i < returnCount; i++) {
284                 ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
285             }
286             return ifaceCounts;
287         }
288 
289         static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
290 
291         /**
292          * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
293          */
dumpProxyInterfaceCounts()294         private void dumpProxyInterfaceCounts() {
295             final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
296 
297             Log.v(Binder.TAG, "BinderProxy descriptor histogram "
298                     + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
299             for (int i = 0; i < sorted.length; i++) {
300                 Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
301             }
302         }
303 
304         /**
305          * Dump per uid binder proxy counts to the logcat.
306          */
dumpPerUidProxyCounts()307         private void dumpPerUidProxyCounts() {
308             SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
309             if (counts.size() == 0) return;
310             Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
311             for (int i = 0; i < counts.size(); i++) {
312                 final int uid = counts.keyAt(i);
313                 final int binderCount = counts.valueAt(i);
314                 Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
315             }
316         }
317 
318         // Corresponding ArrayLists in the following two arrays always have the same size.
319         // They contain no empty entries. However WeakReferences in the values ArrayLists
320         // may have been cleared.
321 
322         // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
323         // The values ArrayList has the proper size(), the corresponding keys array
324         // is always at least the same size, but may be larger.
325         // If either a particular keys array, or the corresponding values ArrayList
326         // are null, then they both are.
327         private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
328         private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
329                 new ArrayList[MAIN_INDEX_SIZE];
330     }
331 
332     @GuardedBy("sProxyMap")
333     private static final ProxyMap sProxyMap = new ProxyMap();
334 
335     /**
336      * Simple pair-value class to store number of binder proxy interfaces live in this process.
337      */
338     public static final class InterfaceCount {
339         private final String mInterfaceName;
340         private final int mCount;
341 
InterfaceCount(String interfaceName, int count)342         InterfaceCount(String interfaceName, int count) {
343             mInterfaceName = interfaceName;
344             mCount = count;
345         }
346 
347         @Override
toString()348         public String toString() {
349             return mInterfaceName + " x" + Integer.toString(mCount);
350         }
351     }
352 
353     /**
354      * Get a sorted array with entries mapping proxy interface names to the number
355      * of live proxies with those names.
356      *
357      * @param num maximum number of proxy interface counts to return. Use
358      *            Integer.MAX_VALUE to retrieve all
359      * @hide
360      */
getSortedInterfaceCounts(int num)361     public static InterfaceCount[] getSortedInterfaceCounts(int num) {
362         return sProxyMap.getSortedInterfaceCounts(num);
363     }
364 
365     /**
366      * Returns the number of binder proxies held in this process.
367      * @return number of binder proxies in this process
368      */
getProxyCount()369     public static int getProxyCount() {
370         synchronized (sProxyMap) {
371             return sProxyMap.size();
372         }
373     }
374 
375     /**
376      * Dump proxy debug information.
377      *
378      * @hide
379      */
dumpProxyDebugInfo()380     public static void dumpProxyDebugInfo() {
381         if (Build.IS_DEBUGGABLE) {
382             sProxyMap.dumpProxyInterfaceCounts();
383             sProxyMap.dumpPerUidProxyCounts();
384         }
385     }
386 
387     /**
388      * Return a BinderProxy for IBinder.
389      * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
390      * in use, then we return the same bp.
391      *
392      * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
393      * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
394      * we exit via an exception.  If neither applies, it's the callers responsibility to
395      * recycle nativeData.
396      * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
397      */
getInstance(long nativeData, long iBinder)398     private static BinderProxy getInstance(long nativeData, long iBinder) {
399         BinderProxy result;
400         synchronized (sProxyMap) {
401             try {
402                 result = sProxyMap.get(iBinder);
403                 if (result != null) {
404                     return result;
405                 }
406                 result = new BinderProxy(nativeData);
407             } catch (Throwable e) {
408                 // We're throwing an exception (probably OOME); don't drop nativeData.
409                 NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
410                         nativeData);
411                 throw e;
412             }
413             NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
414             // The registry now owns nativeData, even if registration threw an exception.
415             sProxyMap.set(iBinder, result);
416         }
417         return result;
418     }
419 
BinderProxy(long nativeData)420     private BinderProxy(long nativeData) {
421         mNativeData = nativeData;
422     }
423 
424     /**
425      * Guestimate of native memory associated with a BinderProxy.
426      * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
427      * that points back to us. We guess high since it includes a GlobalRef, which
428      * may be in short supply.
429      */
430     private static final int NATIVE_ALLOCATION_SIZE = 1000;
431 
432     // Use a Holder to allow static initialization of BinderProxy in the boot image, and
433     // to avoid some initialization ordering issues.
434     private static class NoImagePreloadHolder {
435         public static final long sNativeFinalizer = getNativeFinalizer();
436         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
437                 BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
438     }
439 
440     /**
441      * @return false if the hosting process is gone, otherwise whatever the remote returns
442      */
pingBinder()443     public native boolean pingBinder();
444 
445     /**
446      * @return false if the hosting process is gone
447      */
isBinderAlive()448     public native boolean isBinderAlive();
449 
450     /**
451      * Retrieve a local interface - always null in case of a proxy
452      */
queryLocalInterface(String descriptor)453     public IInterface queryLocalInterface(String descriptor) {
454         return null;
455     }
456 
457     /** @hide */
458     @Override
getExtension()459     public native @Nullable IBinder getExtension() throws RemoteException;
460 
461     /**
462      * Perform a binder transaction on a proxy.
463      *
464      * @param code The action to perform.  This should
465      * be a number between {@link #FIRST_CALL_TRANSACTION} and
466      * {@link #LAST_CALL_TRANSACTION}.
467      * @param data Marshalled data to send to the target.  Must not be null.
468      * If you are not sending any data, you must create an empty Parcel
469      * that is given here.
470      * @param reply Marshalled data to be received from the target.  May be
471      * null if you are not interested in the return value.
472      * @param flags Additional operation flags.  Either 0 for a normal
473      * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
474      *
475      * @return
476      * @throws RemoteException
477      */
transact(int code, Parcel data, Parcel reply, int flags)478     public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
479         Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
480 
481         if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
482             // For now, avoid spamming the log by disabling after we've logged
483             // about this interface at least once
484             mWarnOnBlocking = false;
485             Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
486                     new Throwable());
487         }
488 
489         final boolean tracingEnabled = Binder.isTracingEnabled();
490         if (tracingEnabled) {
491             final Throwable tr = new Throwable();
492             Binder.getTransactionTracker().addTrace(tr);
493             StackTraceElement stackTraceElement = tr.getStackTrace()[1];
494             Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
495                     stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
496         }
497 
498         // Make sure the listener won't change while processing a transaction.
499         final Binder.ProxyTransactListener transactListener = sTransactListener;
500         Object session = null;
501 
502         if (transactListener != null) {
503             final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
504             session = transactListener.onTransactStarted(this, code);
505 
506             // Allow the listener to update the work source uid. We need to update the request
507             // header if the uid is updated.
508             final int updatedWorkSourceUid = Binder.getCallingWorkSourceUid();
509             if (origWorkSourceUid != updatedWorkSourceUid) {
510                 data.replaceCallingWorkSourceUid(updatedWorkSourceUid);
511             }
512         }
513 
514         try {
515             return transactNative(code, data, reply, flags);
516         } finally {
517             if (transactListener != null) {
518                 transactListener.onTransactEnded(session);
519             }
520 
521             if (tracingEnabled) {
522                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
523             }
524         }
525     }
526 
527     /* Returns the native free function */
getNativeFinalizer()528     private static native long getNativeFinalizer();
529     /**
530      *  See {@link IBinder#getInterfaceDescriptor()}
531      */
getInterfaceDescriptor()532     public native String getInterfaceDescriptor() throws RemoteException;
533 
534     /**
535      * Native implementation of transact() for proxies
536      */
transactNative(int code, Parcel data, Parcel reply, int flags)537     public native boolean transactNative(int code, Parcel data, Parcel reply,
538             int flags) throws RemoteException;
539     /**
540      * See {@link IBinder#linkToDeath(DeathRecipient, int)}
541      */
linkToDeath(DeathRecipient recipient, int flags)542     public native void linkToDeath(DeathRecipient recipient, int flags)
543             throws RemoteException;
544     /**
545      * See {@link IBinder#unlinkToDeath}
546      */
unlinkToDeath(DeathRecipient recipient, int flags)547     public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
548 
549     /**
550      * Perform a dump on the remote object
551      *
552      * @param fd The raw file descriptor that the dump is being sent to.
553      * @param args additional arguments to the dump request.
554      * @throws RemoteException
555      */
dump(FileDescriptor fd, String[] args)556     public void dump(FileDescriptor fd, String[] args) throws RemoteException {
557         Parcel data = Parcel.obtain();
558         Parcel reply = Parcel.obtain();
559         data.writeFileDescriptor(fd);
560         data.writeStringArray(args);
561         try {
562             transact(DUMP_TRANSACTION, data, reply, 0);
563             reply.readException();
564         } finally {
565             data.recycle();
566             reply.recycle();
567         }
568     }
569 
570     /**
571      * Perform an asynchronous dump on the remote object
572      *
573      * @param fd The raw file descriptor that the dump is being sent to.
574      * @param args additional arguments to the dump request.
575      * @throws RemoteException
576      */
dumpAsync(FileDescriptor fd, String[] args)577     public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
578         Parcel data = Parcel.obtain();
579         Parcel reply = Parcel.obtain();
580         data.writeFileDescriptor(fd);
581         data.writeStringArray(args);
582         try {
583             transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
584         } finally {
585             data.recycle();
586             reply.recycle();
587         }
588     }
589 
590     /**
591      * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
592      * String[], ShellCallback, ResultReceiver)}
593      *
594      * @param in The raw file descriptor that an input data stream can be read from.
595      * @param out The raw file descriptor that normal command messages should be written to.
596      * @param err The raw file descriptor that command error messages should be written to.
597      * @param args Command-line arguments.
598      * @param callback Optional callback to the caller's shell to perform operations in it.
599      * @param resultReceiver Called when the command has finished executing, with the result code.
600      * @throws RemoteException
601      */
shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)602     public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
603             String[] args, ShellCallback callback,
604             ResultReceiver resultReceiver) throws RemoteException {
605         Parcel data = Parcel.obtain();
606         Parcel reply = Parcel.obtain();
607         data.writeFileDescriptor(in);
608         data.writeFileDescriptor(out);
609         data.writeFileDescriptor(err);
610         data.writeStringArray(args);
611         ShellCallback.writeToParcel(callback, data);
612         resultReceiver.writeToParcel(data, 0);
613         try {
614             transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
615             reply.readException();
616         } finally {
617             data.recycle();
618             reply.recycle();
619         }
620     }
621 
sendDeathNotice(DeathRecipient recipient)622     private static void sendDeathNotice(DeathRecipient recipient) {
623         if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
624         try {
625             recipient.binderDied();
626         } catch (RuntimeException exc) {
627             Log.w("BinderNative", "Uncaught exception from death notification",
628                     exc);
629         }
630     }
631 
632     /**
633      * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
634      * native IBinder object, and a DeathRecipientList.
635      */
636     private final long mNativeData;
637 }
638