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