blob: a26bd39eed9d3392c63e20946d9aad62f39782bd [file] [log] [blame]
Martijn Coenen03e6d922018-07-09 20:22:10 +02001/*
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
17package android.os;
18
19import android.annotation.NonNull;
Olivier Gaillard510cdfc2018-09-17 09:35:32 +010020import android.annotation.Nullable;
Martijn Coenen03e6d922018-07-09 20:22:10 +020021import android.util.Log;
22import android.util.SparseIntArray;
23
24import com.android.internal.annotations.GuardedBy;
25import com.android.internal.os.BinderInternal;
26
27import libcore.util.NativeAllocationRegistry;
28
29import java.io.FileDescriptor;
30import java.lang.ref.WeakReference;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.HashMap;
34import 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 */
43public 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
Olivier Gaillard510cdfc2018-09-17 09:35:32 +010049 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
Martijn Coenen03e6d922018-07-09 20:22:10 +020058 /*
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
Martijn Coenenba75cf12018-07-06 11:26:26 +0200238 private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
239 if (maxToReturn < 0) {
240 throw new IllegalArgumentException("negative interface count");
241 }
242
Martijn Coenen03e6d922018-07-09 20:22:10 +0200243 Map<String, Integer> counts = new HashMap<>();
Martijn Coenene48baa52019-10-09 11:10:01 +0200244 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);
Martijn Coenen03e6d922018-07-09 20:22:10 +0200250 }
251 }
252 }
Martijn Coenene48baa52019-10-09 11:10:01 +0200253 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 }
Martijn Coenen03e6d922018-07-09 20:22:10 +0200275 Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
276 new Map.Entry[counts.size()]);
Martijn Coenenba75cf12018-07-06 11:26:26 +0200277
Martijn Coenen03e6d922018-07-09 20:22:10 +0200278 Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
279 -> b.getValue().compareTo(a.getValue()));
Martijn Coenenba75cf12018-07-06 11:26:26 +0200280
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 */
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]);
Martijn Coenen03e6d922018-07-09 20:22:10 +0200301 }
302 }
303
304 /**
305 * Dump per uid binder proxy counts to the logcat.
306 */
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 /**
Martijn Coenenba75cf12018-07-06 11:26:26 +0200336 * 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
342 InterfaceCount(String interfaceName, int count) {
343 mInterfaceName = interfaceName;
344 mCount = count;
345 }
346
347 @Override
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 */
361 public static InterfaceCount[] getSortedInterfaceCounts(int num) {
Martijn Coenene48baa52019-10-09 11:10:01 +0200362 return sProxyMap.getSortedInterfaceCounts(num);
Martijn Coenenba75cf12018-07-06 11:26:26 +0200363 }
364
365 /**
Martijn Coenen66213de2018-11-20 13:18:33 +0100366 * Returns the number of binder proxies held in this process.
367 * @return number of binder proxies in this process
368 */
369 public static int getProxyCount() {
370 synchronized (sProxyMap) {
371 return sProxyMap.size();
372 }
373 }
374
375 /**
Martijn Coenenba75cf12018-07-06 11:26:26 +0200376 * Dump proxy debug information.
377 *
378 * @hide
379 */
Martijn Coenen331d8dc2018-07-06 12:04:48 +0200380 public static void dumpProxyDebugInfo() {
Martijn Coenen03e6d922018-07-09 20:22:10 +0200381 if (Build.IS_DEBUGGABLE) {
Martijn Coenene48baa52019-10-09 11:10:01 +0200382 sProxyMap.dumpProxyInterfaceCounts();
383 sProxyMap.dumpPerUidProxyCounts();
Martijn Coenen03e6d922018-07-09 20:22:10 +0200384 }
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 */
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
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 */
443 public native boolean pingBinder();
444
445 /**
446 * @return false if the hosting process is gone
447 */
448 public native boolean isBinderAlive();
449
450 /**
451 * Retrieve a local interface - always null in case of a proxy
452 */
453 public IInterface queryLocalInterface(String descriptor) {
454 return null;
455 }
456
Yo Chiangb0316852020-01-21 16:10:20 +0800457 /** @hide */
458 @Override
459 public native @Nullable IBinder getExtension() throws RemoteException;
460
Martijn Coenen03e6d922018-07-09 20:22:10 +0200461 /**
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 */
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 }
Olivier Gaillard510cdfc2018-09-17 09:35:32 +0100497
498 // Make sure the listener won't change while processing a transaction.
499 final Binder.ProxyTransactListener transactListener = sTransactListener;
500 Object session = null;
Olivier Gaillardbab444a2019-01-30 17:11:40 +0000501
Olivier Gaillard510cdfc2018-09-17 09:35:32 +0100502 if (transactListener != null) {
Olivier Gaillardbab444a2019-01-30 17:11:40 +0000503 final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
Olivier Gaillard510cdfc2018-09-17 09:35:32 +0100504 session = transactListener.onTransactStarted(this, code);
Olivier Gaillardbab444a2019-01-30 17:11:40 +0000505
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 }
Olivier Gaillard510cdfc2018-09-17 09:35:32 +0100512 }
513
Martijn Coenen03e6d922018-07-09 20:22:10 +0200514 try {
515 return transactNative(code, data, reply, flags);
516 } finally {
Olivier Gaillard510cdfc2018-09-17 09:35:32 +0100517 if (transactListener != null) {
518 transactListener.onTransactEnded(session);
519 }
520
Martijn Coenen03e6d922018-07-09 20:22:10 +0200521 if (tracingEnabled) {
522 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
523 }
524 }
525 }
526
527 /* Returns the native free function */
528 private static native long getNativeFinalizer();
529 /**
530 * See {@link IBinder#getInterfaceDescriptor()}
531 */
532 public native String getInterfaceDescriptor() throws RemoteException;
533
534 /**
535 * Native implementation of transact() for proxies
536 */
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 */
542 public native void linkToDeath(DeathRecipient recipient, int flags)
543 throws RemoteException;
544 /**
545 * See {@link IBinder#unlinkToDeath}
546 */
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 */
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 */
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 */
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
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}