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.util.Log; 20 import android.util.Slog; 21 import com.android.internal.util.FastPrintWriter; 22 import libcore.io.IoUtils; 23 24 import java.io.FileDescriptor; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.PrintWriter; 28 import java.lang.ref.WeakReference; 29 import java.lang.reflect.Modifier; 30 31 /** 32 * Base class for a remotable object, the core part of a lightweight 33 * remote procedure call mechanism defined by {@link IBinder}. 34 * This class is an implementation of IBinder that provides 35 * standard local implementation of such an object. 36 * 37 * <p>Most developers will not implement this class directly, instead using the 38 * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired 39 * interface, having it generate the appropriate Binder subclass. You can, 40 * however, derive directly from Binder to implement your own custom RPC 41 * protocol or simply instantiate a raw Binder object directly to use as a 42 * token that can be shared across processes. 43 * 44 * <p>This class is just a basic IPC primitive; it has no impact on an application's 45 * lifecycle, and is valid only as long as the process that created it continues to run. 46 * To use this correctly, you must be doing so within the context of a top-level 47 * application component (a {@link android.app.Service}, {@link android.app.Activity}, 48 * or {@link android.content.ContentProvider}) that lets the system know your process 49 * should remain running.</p> 50 * 51 * <p>You must keep in mind the situations in which your process 52 * could go away, and thus require that you later re-create a new Binder and re-attach 53 * it when the process starts again. For example, if you are using this within an 54 * {@link android.app.Activity}, your activity's process may be killed any time the 55 * activity is not started; if the activity is later re-created you will need to 56 * create a new Binder and hand it back to the correct place again; you need to be 57 * aware that your process may be started for another reason (for example to receive 58 * a broadcast) that will not involve re-creating the activity and thus run its code 59 * to create a new Binder.</p> 60 * 61 * @see IBinder 62 */ 63 public class Binder implements IBinder { 64 /* 65 * Set this flag to true to detect anonymous, local or member classes 66 * that extend this Binder class and that are not static. These kind 67 * of classes can potentially create leaks. 68 */ 69 private static final boolean FIND_POTENTIAL_LEAKS = false; 70 private static final boolean CHECK_PARCEL_SIZE = false; 71 static final String TAG = "Binder"; 72 73 /** @hide */ 74 public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE 75 76 /** 77 * Control whether dump() calls are allowed. 78 */ 79 private static String sDumpDisabled = null; 80 81 /** 82 * Global transaction tracker instance for this process. 83 */ 84 private static TransactionTracker sTransactionTracker = null; 85 86 // Transaction tracking code. 87 88 /** 89 * Flag indicating whether we should be tracing transact calls. 90 * 91 */ 92 private static boolean sTracingEnabled = false; 93 94 /** 95 * Enable Binder IPC tracing. 96 * 97 * @hide 98 */ enableTracing()99 public static void enableTracing() { 100 sTracingEnabled = true; 101 }; 102 103 /** 104 * Disable Binder IPC tracing. 105 * 106 * @hide 107 */ disableTracing()108 public static void disableTracing() { 109 sTracingEnabled = false; 110 } 111 112 /** 113 * Check if binder transaction tracing is enabled. 114 * 115 * @hide 116 */ isTracingEnabled()117 public static boolean isTracingEnabled() { 118 return sTracingEnabled; 119 } 120 121 /** 122 * Get the binder transaction tracker for this process. 123 * 124 * @hide 125 */ getTransactionTracker()126 public synchronized static TransactionTracker getTransactionTracker() { 127 if (sTransactionTracker == null) 128 sTransactionTracker = new TransactionTracker(); 129 return sTransactionTracker; 130 } 131 132 /* mObject is used by native code, do not remove or rename */ 133 private long mObject; 134 private IInterface mOwner; 135 private String mDescriptor; 136 137 /** 138 * Return the ID of the process that sent you the current transaction 139 * that is being processed. This pid can be used with higher-level 140 * system services to determine its identity and check permissions. 141 * If the current thread is not currently executing an incoming transaction, 142 * then its own pid is returned. 143 */ getCallingPid()144 public static final native int getCallingPid(); 145 146 /** 147 * Return the Linux uid assigned to the process that sent you the 148 * current transaction that is being processed. This uid can be used with 149 * higher-level system services to determine its identity and check 150 * permissions. If the current thread is not currently executing an 151 * incoming transaction, then its own uid is returned. 152 */ getCallingUid()153 public static final native int getCallingUid(); 154 155 /** 156 * Return the UserHandle assigned to the process that sent you the 157 * current transaction that is being processed. This is the user 158 * of the caller. It is distinct from {@link #getCallingUid()} in that a 159 * particular user will have multiple distinct apps running under it each 160 * with their own uid. If the current thread is not currently executing an 161 * incoming transaction, then its own UserHandle is returned. 162 */ getCallingUserHandle()163 public static final UserHandle getCallingUserHandle() { 164 return UserHandle.of(UserHandle.getUserId(getCallingUid())); 165 } 166 167 /** 168 * Reset the identity of the incoming IPC on the current thread. This can 169 * be useful if, while handling an incoming call, you will be calling 170 * on interfaces of other objects that may be local to your process and 171 * need to do permission checks on the calls coming into them (so they 172 * will check the permission of your own local process, and not whatever 173 * process originally called you). 174 * 175 * @return Returns an opaque token that can be used to restore the 176 * original calling identity by passing it to 177 * {@link #restoreCallingIdentity(long)}. 178 * 179 * @see #getCallingPid() 180 * @see #getCallingUid() 181 * @see #restoreCallingIdentity(long) 182 */ clearCallingIdentity()183 public static final native long clearCallingIdentity(); 184 185 /** 186 * Restore the identity of the incoming IPC on the current thread 187 * back to a previously identity that was returned by {@link 188 * #clearCallingIdentity}. 189 * 190 * @param token The opaque token that was previously returned by 191 * {@link #clearCallingIdentity}. 192 * 193 * @see #clearCallingIdentity 194 */ restoreCallingIdentity(long token)195 public static final native void restoreCallingIdentity(long token); 196 197 /** 198 * Sets the native thread-local StrictMode policy mask. 199 * 200 * <p>The StrictMode settings are kept in two places: a Java-level 201 * threadlocal for libcore/Dalvik, and a native threadlocal (set 202 * here) for propagation via Binder calls. This is a little 203 * unfortunate, but necessary to break otherwise more unfortunate 204 * dependencies either of Dalvik on Android, or Android 205 * native-only code on Dalvik. 206 * 207 * @see StrictMode 208 * @hide 209 */ setThreadStrictModePolicy(int policyMask)210 public static final native void setThreadStrictModePolicy(int policyMask); 211 212 /** 213 * Gets the current native thread-local StrictMode policy mask. 214 * 215 * @see #setThreadStrictModePolicy 216 * @hide 217 */ getThreadStrictModePolicy()218 public static final native int getThreadStrictModePolicy(); 219 220 /** 221 * Flush any Binder commands pending in the current thread to the kernel 222 * driver. This can be 223 * useful to call before performing an operation that may block for a long 224 * time, to ensure that any pending object references have been released 225 * in order to prevent the process from holding on to objects longer than 226 * it needs to. 227 */ flushPendingCommands()228 public static final native void flushPendingCommands(); 229 230 /** 231 * Add the calling thread to the IPC thread pool. This function does 232 * not return until the current process is exiting. 233 */ joinThreadPool()234 public static final native void joinThreadPool(); 235 236 /** 237 * Returns true if the specified interface is a proxy. 238 * @hide 239 */ isProxy(IInterface iface)240 public static final boolean isProxy(IInterface iface) { 241 return iface.asBinder() != iface; 242 } 243 244 /** 245 * Call blocks until the number of executing binder threads is less 246 * than the maximum number of binder threads allowed for this process. 247 * @hide 248 */ blockUntilThreadAvailable()249 public static final native void blockUntilThreadAvailable(); 250 251 /** 252 * Default constructor initializes the object. 253 */ Binder()254 public Binder() { 255 init(); 256 257 if (FIND_POTENTIAL_LEAKS) { 258 final Class<? extends Binder> klass = getClass(); 259 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 260 (klass.getModifiers() & Modifier.STATIC) == 0) { 261 Log.w(TAG, "The following Binder class should be static or leaks might occur: " + 262 klass.getCanonicalName()); 263 } 264 } 265 } 266 267 /** 268 * Convenience method for associating a specific interface with the Binder. 269 * After calling, queryLocalInterface() will be implemented for you 270 * to return the given owner IInterface when the corresponding 271 * descriptor is requested. 272 */ attachInterface(IInterface owner, String descriptor)273 public void attachInterface(IInterface owner, String descriptor) { 274 mOwner = owner; 275 mDescriptor = descriptor; 276 } 277 278 /** 279 * Default implementation returns an empty interface name. 280 */ getInterfaceDescriptor()281 public String getInterfaceDescriptor() { 282 return mDescriptor; 283 } 284 285 /** 286 * Default implementation always returns true -- if you got here, 287 * the object is alive. 288 */ pingBinder()289 public boolean pingBinder() { 290 return true; 291 } 292 293 /** 294 * {@inheritDoc} 295 * 296 * Note that if you're calling on a local binder, this always returns true 297 * because your process is alive if you're calling it. 298 */ isBinderAlive()299 public boolean isBinderAlive() { 300 return true; 301 } 302 303 /** 304 * Use information supplied to attachInterface() to return the 305 * associated IInterface if it matches the requested 306 * descriptor. 307 */ queryLocalInterface(String descriptor)308 public IInterface queryLocalInterface(String descriptor) { 309 if (mDescriptor.equals(descriptor)) { 310 return mOwner; 311 } 312 return null; 313 } 314 315 /** 316 * Control disabling of dump calls in this process. This is used by the system 317 * process watchdog to disable incoming dump calls while it has detecting the system 318 * is hung and is reporting that back to the activity controller. This is to 319 * prevent the controller from getting hung up on bug reports at this point. 320 * @hide 321 * 322 * @param msg The message to show instead of the dump; if null, dumps are 323 * re-enabled. 324 */ setDumpDisabled(String msg)325 public static void setDumpDisabled(String msg) { 326 synchronized (Binder.class) { 327 sDumpDisabled = msg; 328 } 329 } 330 331 /** 332 * Default implementation is a stub that returns false. You will want 333 * to override this to do the appropriate unmarshalling of transactions. 334 * 335 * <p>If you want to call this, call transact(). 336 */ onTransact(int code, Parcel data, Parcel reply, int flags)337 protected boolean onTransact(int code, Parcel data, Parcel reply, 338 int flags) throws RemoteException { 339 if (code == INTERFACE_TRANSACTION) { 340 reply.writeString(getInterfaceDescriptor()); 341 return true; 342 } else if (code == DUMP_TRANSACTION) { 343 ParcelFileDescriptor fd = data.readFileDescriptor(); 344 String[] args = data.readStringArray(); 345 if (fd != null) { 346 try { 347 dump(fd.getFileDescriptor(), args); 348 } finally { 349 IoUtils.closeQuietly(fd); 350 } 351 } 352 // Write the StrictMode header. 353 if (reply != null) { 354 reply.writeNoException(); 355 } else { 356 StrictMode.clearGatheredViolations(); 357 } 358 return true; 359 } else if (code == SHELL_COMMAND_TRANSACTION) { 360 ParcelFileDescriptor in = data.readFileDescriptor(); 361 ParcelFileDescriptor out = data.readFileDescriptor(); 362 ParcelFileDescriptor err = data.readFileDescriptor(); 363 String[] args = data.readStringArray(); 364 ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data); 365 try { 366 if (out != null) { 367 shellCommand(in != null ? in.getFileDescriptor() : null, 368 out.getFileDescriptor(), 369 err != null ? err.getFileDescriptor() : out.getFileDescriptor(), 370 args, resultReceiver); 371 } 372 } finally { 373 IoUtils.closeQuietly(in); 374 IoUtils.closeQuietly(out); 375 IoUtils.closeQuietly(err); 376 // Write the StrictMode header. 377 if (reply != null) { 378 reply.writeNoException(); 379 } else { 380 StrictMode.clearGatheredViolations(); 381 } 382 } 383 return true; 384 } 385 return false; 386 } 387 388 /** 389 * Implemented to call the more convenient version 390 * {@link #dump(FileDescriptor, PrintWriter, String[])}. 391 */ dump(FileDescriptor fd, String[] args)392 public void dump(FileDescriptor fd, String[] args) { 393 FileOutputStream fout = new FileOutputStream(fd); 394 PrintWriter pw = new FastPrintWriter(fout); 395 try { 396 doDump(fd, pw, args); 397 } finally { 398 pw.flush(); 399 } 400 } 401 doDump(FileDescriptor fd, PrintWriter pw, String[] args)402 void doDump(FileDescriptor fd, PrintWriter pw, String[] args) { 403 final String disabled; 404 synchronized (Binder.class) { 405 disabled = sDumpDisabled; 406 } 407 if (disabled == null) { 408 try { 409 dump(fd, pw, args); 410 } catch (SecurityException e) { 411 pw.println("Security exception: " + e.getMessage()); 412 throw e; 413 } catch (Throwable e) { 414 // Unlike usual calls, in this case if an exception gets thrown 415 // back to us we want to print it back in to the dump data, since 416 // that is where the caller expects all interesting information to 417 // go. 418 pw.println(); 419 pw.println("Exception occurred while dumping:"); 420 e.printStackTrace(pw); 421 } 422 } else { 423 pw.println(sDumpDisabled); 424 } 425 } 426 427 /** 428 * Like {@link #dump(FileDescriptor, String[])}, but ensures the target 429 * executes asynchronously. 430 */ dumpAsync(final FileDescriptor fd, final String[] args)431 public void dumpAsync(final FileDescriptor fd, final String[] args) { 432 final FileOutputStream fout = new FileOutputStream(fd); 433 final PrintWriter pw = new FastPrintWriter(fout); 434 Thread thr = new Thread("Binder.dumpAsync") { 435 public void run() { 436 try { 437 dump(fd, pw, args); 438 } finally { 439 pw.flush(); 440 } 441 } 442 }; 443 thr.start(); 444 } 445 446 /** 447 * Print the object's state into the given stream. 448 * 449 * @param fd The raw file descriptor that the dump is being sent to. 450 * @param fout The file to which you should dump your state. This will be 451 * closed for you after you return. 452 * @param args additional arguments to the dump request. 453 */ dump(FileDescriptor fd, PrintWriter fout, String[] args)454 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 455 } 456 457 /** 458 * @param in The raw file descriptor that an input data stream can be read from. 459 * @param out The raw file descriptor that normal command messages should be written to. 460 * @param err The raw file descriptor that command error messages should be written to. 461 * @param args Command-line arguments. 462 * @param resultReceiver Called when the command has finished executing, with the result code. 463 * @throws RemoteException 464 * @hide 465 */ shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver)466 public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 467 String[] args, ResultReceiver resultReceiver) throws RemoteException { 468 onShellCommand(in, out, err, args, resultReceiver); 469 } 470 471 /** 472 * Handle a call to {@link #shellCommand}. The default implementation simply prints 473 * an error message. Override and replace with your own. 474 * <p class="caution">Note: no permission checking is done before calling this method; you must 475 * apply any security checks as appropriate for the command being executed. 476 * Consider using {@link ShellCommand} to help in the implementation.</p> 477 * @hide 478 */ onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver)479 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 480 String[] args, ResultReceiver resultReceiver) throws RemoteException { 481 FileOutputStream fout = new FileOutputStream(err != null ? err : out); 482 PrintWriter pw = new FastPrintWriter(fout); 483 pw.println("No shell command implementation."); 484 pw.flush(); 485 resultReceiver.send(0, null); 486 } 487 488 /** 489 * Default implementation rewinds the parcels and calls onTransact. On 490 * the remote side, transact calls into the binder to do the IPC. 491 */ transact(int code, Parcel data, Parcel reply, int flags)492 public final boolean transact(int code, Parcel data, Parcel reply, 493 int flags) throws RemoteException { 494 if (false) Log.v("Binder", "Transact: " + code + " to " + this); 495 496 if (data != null) { 497 data.setDataPosition(0); 498 } 499 boolean r = onTransact(code, data, reply, flags); 500 if (reply != null) { 501 reply.setDataPosition(0); 502 } 503 return r; 504 } 505 506 /** 507 * Local implementation is a no-op. 508 */ linkToDeath(DeathRecipient recipient, int flags)509 public void linkToDeath(DeathRecipient recipient, int flags) { 510 } 511 512 /** 513 * Local implementation is a no-op. 514 */ unlinkToDeath(DeathRecipient recipient, int flags)515 public boolean unlinkToDeath(DeathRecipient recipient, int flags) { 516 return true; 517 } 518 finalize()519 protected void finalize() throws Throwable { 520 try { 521 destroy(); 522 } finally { 523 super.finalize(); 524 } 525 } 526 checkParcel(IBinder obj, int code, Parcel parcel, String msg)527 static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) { 528 if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) { 529 // Trying to send > 800k, this is way too much 530 StringBuilder sb = new StringBuilder(); 531 sb.append(msg); 532 sb.append(": on "); 533 sb.append(obj); 534 sb.append(" calling "); 535 sb.append(code); 536 sb.append(" size "); 537 sb.append(parcel.dataSize()); 538 sb.append(" (data: "); 539 parcel.setDataPosition(0); 540 sb.append(parcel.readInt()); 541 sb.append(", "); 542 sb.append(parcel.readInt()); 543 sb.append(", "); 544 sb.append(parcel.readInt()); 545 sb.append(")"); 546 Slog.wtfStack(TAG, sb.toString()); 547 } 548 } 549 init()550 private native final void init(); destroy()551 private native final void destroy(); 552 553 // Entry point from android_util_Binder.cpp's onTransact execTransact(int code, long dataObj, long replyObj, int flags)554 private boolean execTransact(int code, long dataObj, long replyObj, 555 int flags) { 556 Parcel data = Parcel.obtain(dataObj); 557 Parcel reply = Parcel.obtain(replyObj); 558 // theoretically, we should call transact, which will call onTransact, 559 // but all that does is rewind it, and we just got these from an IPC, 560 // so we'll just call it directly. 561 boolean res; 562 // Log any exceptions as warnings, don't silently suppress them. 563 // If the call was FLAG_ONEWAY then these exceptions disappear into the ether. 564 try { 565 res = onTransact(code, data, reply, flags); 566 } catch (RemoteException|RuntimeException e) { 567 if (LOG_RUNTIME_EXCEPTION) { 568 Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); 569 } 570 if ((flags & FLAG_ONEWAY) != 0) { 571 if (e instanceof RemoteException) { 572 Log.w(TAG, "Binder call failed.", e); 573 } else { 574 Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); 575 } 576 } else { 577 reply.setDataPosition(0); 578 reply.writeException(e); 579 } 580 res = true; 581 } catch (OutOfMemoryError e) { 582 // Unconditionally log this, since this is generally unrecoverable. 583 Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e); 584 RuntimeException re = new RuntimeException("Out of memory", e); 585 reply.setDataPosition(0); 586 reply.writeException(re); 587 res = true; 588 } 589 checkParcel(this, code, reply, "Unreasonably large binder reply buffer"); 590 reply.recycle(); 591 data.recycle(); 592 593 // Just in case -- we are done with the IPC, so there should be no more strict 594 // mode violations that have gathered for this thread. Either they have been 595 // parceled and are now in transport off to the caller, or we are returning back 596 // to the main transaction loop to wait for another incoming transaction. Either 597 // way, strict mode begone! 598 StrictMode.clearGatheredViolations(); 599 600 return res; 601 } 602 } 603 604 final class BinderProxy implements IBinder { pingBinder()605 public native boolean pingBinder(); isBinderAlive()606 public native boolean isBinderAlive(); 607 queryLocalInterface(String descriptor)608 public IInterface queryLocalInterface(String descriptor) { 609 return null; 610 } 611 transact(int code, Parcel data, Parcel reply, int flags)612 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { 613 Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); 614 if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); } 615 return transactNative(code, data, reply, flags); 616 } 617 getInterfaceDescriptor()618 public native String getInterfaceDescriptor() throws RemoteException; transactNative(int code, Parcel data, Parcel reply, int flags)619 public native boolean transactNative(int code, Parcel data, Parcel reply, 620 int flags) throws RemoteException; linkToDeath(DeathRecipient recipient, int flags)621 public native void linkToDeath(DeathRecipient recipient, int flags) 622 throws RemoteException; unlinkToDeath(DeathRecipient recipient, int flags)623 public native boolean unlinkToDeath(DeathRecipient recipient, int flags); 624 dump(FileDescriptor fd, String[] args)625 public void dump(FileDescriptor fd, String[] args) throws RemoteException { 626 Parcel data = Parcel.obtain(); 627 Parcel reply = Parcel.obtain(); 628 data.writeFileDescriptor(fd); 629 data.writeStringArray(args); 630 try { 631 transact(DUMP_TRANSACTION, data, reply, 0); 632 reply.readException(); 633 } finally { 634 data.recycle(); 635 reply.recycle(); 636 } 637 } 638 dumpAsync(FileDescriptor fd, String[] args)639 public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException { 640 Parcel data = Parcel.obtain(); 641 Parcel reply = Parcel.obtain(); 642 data.writeFileDescriptor(fd); 643 data.writeStringArray(args); 644 try { 645 transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY); 646 } finally { 647 data.recycle(); 648 reply.recycle(); 649 } 650 } 651 shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver)652 public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 653 String[] args, ResultReceiver resultReceiver) throws RemoteException { 654 Parcel data = Parcel.obtain(); 655 Parcel reply = Parcel.obtain(); 656 data.writeFileDescriptor(in); 657 data.writeFileDescriptor(out); 658 data.writeFileDescriptor(err); 659 data.writeStringArray(args); 660 resultReceiver.writeToParcel(data, 0); 661 try { 662 transact(SHELL_COMMAND_TRANSACTION, data, reply, 0); 663 reply.readException(); 664 } finally { 665 data.recycle(); 666 reply.recycle(); 667 } 668 } 669 BinderProxy()670 BinderProxy() { 671 mSelf = new WeakReference(this); 672 } 673 674 @Override finalize()675 protected void finalize() throws Throwable { 676 try { 677 destroy(); 678 } finally { 679 super.finalize(); 680 } 681 } 682 destroy()683 private native final void destroy(); 684 sendDeathNotice(DeathRecipient recipient)685 private static final void sendDeathNotice(DeathRecipient recipient) { 686 if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient); 687 try { 688 recipient.binderDied(); 689 } 690 catch (RuntimeException exc) { 691 Log.w("BinderNative", "Uncaught exception from death notification", 692 exc); 693 } 694 } 695 696 final private WeakReference mSelf; 697 private long mObject; 698 private long mOrgue; 699 } 700