东莞网站建设新闻资讯,做赌场网站犯法么,企业网络服务平台,网店分销平台文中代码分析基于Android 10.0 (Q)两个进程之间若是要进行Binder通信#xff0c;那么发起通信的一端我们就称它为Client进程。Client进程调用每一个代理对象的方法#xff0c;本质上都是一次跨进程通信。如果这个方法是同步方法#xff08;非oneway修饰#xff09;#xf… 文中代码分析基于Android 10.0 (Q)两个进程之间若是要进行Binder通信那么发起通信的一端我们就称它为Client进程。Client进程调用每一个代理对象的方法本质上都是一次跨进程通信。如果这个方法是同步方法非oneway修饰那么此调用过程将会经历如下几个阶段。对应用工程师而言他只会看到浮在海面的冰山一角至于隐藏在海面下的系统调用和跨进程通信是无需他感知的。但无需感知并不代表不存在。这些中间过程若是发生了异常终归是需要被处理的。本文将重点阐述两个问题如果Server进程中Binder实体对象的方法发生异常该异常将会去向何处RemoteException的本质和类别。1. 如果Binder实体对象的方法中发生异常该异常将会去向何处1.1 Server端有什么影响AIDL工具生成的Stub抽象类主要用于方法派发。因此实体方法中如果报出异常的话异常将首先会报给Stub类的onTransact方法。以如下intMethod方法为例如果其内部发生异常则该异常将会被onTransact方法感知。out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java106 Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
107 {
108 java.lang.String descriptor DESCRIPTOR;
109 switch (code)
110 {
111 case INTERFACE_TRANSACTION:
112 {
113 reply.writeString(descriptor);
114 return true;
115 }
116 case TRANSACTION_intMethod:
117 {
118 data.enforceInterface(descriptor);
119 int _arg0;
120 _arg0 data.readInt();
121 int _result this.intMethod(_arg0);
122 reply.writeNoException();
123 reply.writeInt(_result);
124 return true;
125 }
121行是调用intMethod的位置其内部发生的异常并没有在onTransact中被处理因此会继续上报给Binder.execTransactInternal方法。/frameworks/base/core/java/android/os/Binder.java1000 private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
1001 int callingUid) {
1002 // Make sure the observer wont change while processing a transaction.
1003 final BinderInternal.Observer observer sObserver;
1004 final CallSession callSession
1005 observer ! null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
1006 Parcel data Parcel.obtain(dataObj);
1007 Parcel reply Parcel.obtain(replyObj);
1008 // theoretically, we should call transact, which will call onTransact,
1009 // but all that does is rewind it, and we just got these from an IPC,
1010 // so well just call it directly.
1011 boolean res;
1012 // Log any exceptions as warnings, dont silently suppress them.
1013 // If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
1014 final boolean tracingEnabled Binder.isTracingEnabled();
1015 try {
1016 if (tracingEnabled) {
1017 final String transactionName getTransactionName(code);
1018 Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() :
1019 (transactionName ! null ? transactionName : code));
1020 }
1021 res onTransact(code, data, reply, flags);
1022 } catch (RemoteException|RuntimeException e) {
1023 if (observer ! null) {
1024 observer.callThrewException(callSession, e);
1025 }
1026 if (LOG_RUNTIME_EXCEPTION) {
1027 Log.w(TAG, Caught a RuntimeException from the binder stub implementation., e);
1028 }
1029 if ((flags FLAG_ONEWAY) ! 0) {
1030 if (e instanceof RemoteException) {
1031 Log.w(TAG, Binder call failed., e);
1032 } else {
1033 Log.w(TAG, Caught a RuntimeException from the binder stub implementation., e);
1034 }
1035 } else {
1036 // Clear the parcel before writing the exception
1037 reply.setDataSize(0);
1038 reply.setDataPosition(0);
1039 reply.writeException(e);
1040 }
1041 res true;
1042 } finally {
1043 if (tracingEnabled) {
1044 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
1045 }
1046 if (observer ! null) {
1047 // The parcel RPC headers have been called during onTransact so we can now access
1048 // the worksource uid from the parcel.
1049 final int workSourceUid sWorkSourceProvider.resolveWorkSourceUid(
1050 data.readCallingWorkSourceUid());
1051 observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
1052 }
1053 }
1054 checkParcel(this, code, reply, Unreasonably large binder reply buffer);
1055 reply.recycle();
1056 data.recycle();
1057
1058 // Just in case -- we are done with the IPC, so there should be no more strict
1059 // mode violations that have gathered for this thread. Either they have been
1060 // parceled and are now in transport off to the caller, or we are returning back
1061 // to the main transaction loop to wait for another incoming transaction. Either
1062 // way, strict mode begone!
1063 StrictMode.clearGatheredViolations();
1064 return res;
1065 }
1021行是调用onTransact的位置。如果异常没有在此方法中被处理将会进一步抛出给execTransact最终进入JNI方法JavaBBinder::onTransact。异常的传递关系正如本节开始的那幅图所示其最终的处理分为了两种情况。1.1.1 由Binder.execTransactInternal来处理异常ExceptionCodeSecurityExceptionEX_SECURITYBadParcelableExceptionEX_BAD_PARCELABLEIllegalArgumentExceptionEX_ILLEGAL_ARGUMENTNullPointerExceptionEX_NULL_POINTERIllegalStateExceptionEX_ILLEGAL_STATENetworkOnMainThreadExceptionEX_NETWORK_MAIN_THREADUnsupportedOperationExceptionEX_UNSUPPORTED_OPERATIONServiceSpecificExceptionEX_SERVICE_SPECIFIC/frameworks/base/core/java/android/os/Parcel.java1868 public final void writeException(NonNull Exception e) {
1869 int code 0;
1870 if (e instanceof Parcelable
1871 (e.getClass().getClassLoader() Parcelable.class.getClassLoader())) {
1872 // We only send Parcelable exceptions that are in the
1873 // BootClassLoader to ensure that the receiver can unpack them
1874 code EX_PARCELABLE;
1875 } else if (e instanceof SecurityException) {
1876 code EX_SECURITY;
1877 } else if (e instanceof BadParcelableException) {
1878 code EX_BAD_PARCELABLE;
1879 } else if (e instanceof IllegalArgumentException) {
1880 code EX_ILLEGAL_ARGUMENT;
1881 } else if (e instanceof NullPointerException) {
1882 code EX_NULL_POINTER;
1883 } else if (e instanceof IllegalStateException) {
1884 code EX_ILLEGAL_STATE;
1885 } else if (e instanceof NetworkOnMainThreadException) {
1886 code EX_NETWORK_MAIN_THREAD;
1887 } else if (e instanceof UnsupportedOperationException) {
1888 code EX_UNSUPPORTED_OPERATION;
1889 } else if (e instanceof ServiceSpecificException) {
1890 code EX_SERVICE_SPECIFIC;
1891 }
1892 writeInt(code);
1893 StrictMode.clearGatheredViolations();
1894 if (code 0) {
1895 if (e instanceof RuntimeException) {
1896 throw (RuntimeException) e;
1897 }
1898 throw new RuntimeException(e);
1899 }
1900 writeString(e.getMessage());
针对如上8种异常Server进程会将异常的信息序列化写入Parcel对象然后经由驱动发送给Client进程。1.1.2 由JavaBBinder::onTransact来处理异常/frameworks/base/core/jni/android_util_Binder.cpp355 status_t onTransact(
356 uint32_t code, const Parcel data, Parcel* reply, uint32_t flags 0) override
357 {
358 JNIEnv* env javavm_to_jnienv(mVM);
359
360 ALOGV(onTransact() on %p calling object %p in env %p vm %p\n, this, mObject, env, mVM);
361
362 IPCThreadState* thread_state IPCThreadState::self();
363 const int32_t strict_policy_before thread_state-getStrictModePolicy();
364
365 //printf(Transact from %p to Java code sending: , this);
366 //data.print();
367 //printf(\n);
368 jboolean res env-CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
369 code, reinterpret_castjlong(data), reinterpret_castjlong(reply), flags);
370
371 if (env-ExceptionCheck()) {
372 ScopedLocalRefjthrowable excep(env, env-ExceptionOccurred());
373 report_exception(env, excep.get(),
374 *** Uncaught remote exception!
375 (Exceptions are not yet supported across processes.));
376 res JNI_FALSE;
377 }
除了1.1中所述的8中异常外其余所有异常都交由JavaBBinder::onTransact来处理。368行是调用Java层execTransact方法的位置。当该方法抛出异常时371行的异常检测将为true而其后的373行会将异常打印出来。需要注意的是这些信息并不会被发送回Client进程。以下为示例这些Log是在Server进程中输出的。2020-04-15 21:54:00.454 1433-1453/com.hangl.androidemptypage:server E/JavaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.)java.lang.RuntimeException: android.os.RemoteException: Test by Hanglat android.os.Parcel.writeException(Parcel.java:1898)at android.os.Binder.execTransactInternal(Binder.java:1039)at android.os.Binder.execTransact(Binder.java:994)Caused by: android.os.RemoteException: Test by Hanglat com.hangl.androidemptypage.ServerB$ServiceB.sendMsg(ServerB.java:24)at com.hangl.androidemptypage.IServiceB$Stub.onTransact(IServiceB.java:64)at android.os.Binder.execTransactInternal(Binder.java:1021)at android.os.Binder.execTransact(Binder.java:994)
从上述示例中可以看出此次Server进程真正抛出的异常为RemoteException而RuntimeException只是对RemoteException的一层封装。/frameworks/base/core/java/android/os/Parcel.java1894 if (code 0) {
1895 if (e instanceof RuntimeException) {
1896 throw (RuntimeException) e;
1897 }
1898 throw new RuntimeException(e);
1899 }
回到Binder.execTransactInternal对于RemoteException1898行会将其进行封装再次抛出而对于非以上8种的RuntimeException1896行也会将其再次抛出。综合以上两种处理情况可以推断所有Binder实体对象方法中发生的异常都会被处理。无非一种是将异常信息发送给对端进程另一种是将异常信息在本进程输出。而这些处理都不会使Server进程退出。仔细思考这样设计也是很合理的。作为Server进程它在什么时候执行该执行些什么都不由自己掌控而是由Client进程控制。因此抛出异常本质上与Client进程相关让一个Client进程的行为导致Server进程退出显然是不合理的。此外Server进程可能关联着千百个Client不能由于一个Client的错误行为而影响本可以正常获取服务的其他Client。1.2 Client端有什么影响Client端受到的影响完全取决于Server端如何处理异常。上文中已经阐明Server进程会分两种情况来处理异常一种是将异常信息发送给Client进程另一个种是将异常信息在本进程中输出。以下按照这两个情况分别讨论Client进程受到的影响。1.2.1 从Parcel对象中读回异常信息out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java442 Override public int intMethod(int a) throws android.os.RemoteException
443 {
444 android.os.Parcel _data android.os.Parcel.obtain();
445 android.os.Parcel _reply android.os.Parcel.obtain();
446 int _result;
447 try {
448 _data.writeInterfaceToken(DESCRIPTOR);
449 _data.writeInt(a);
450 boolean _status mRemote.transact(Stub.TRANSACTION_intMethod, _data, _reply, 0);
451 if (!_status getDefaultImpl() ! null) {
452 return getDefaultImpl().intMethod(a);
453 }
454 _reply.readException();
455 _result _reply.readInt();
456 }
457 finally {
458 _reply.recycle();
459 _data.recycle();
460 }
461 return _result;
462 }
Server进程通过Parcel对象发送的异常信息最终在454行被读回。/frameworks/base/core/java/android/os/Parcel.java1983 public final void readException() {
1984 int code readExceptionCode();
1985 if (code ! 0) {
1986 String msg readString();
1987 readException(code, msg);
1988 }
1989 }
/frameworks/base/core/java/android/os/Parcel.java2033 public final void readException(int code, String msg) {
2034 String remoteStackTrace null;
2035 final int remoteStackPayloadSize readInt();
2036 if (remoteStackPayloadSize 0) {
2037 remoteStackTrace readString();
2038 }
2039 Exception e createException(code, msg);
2040 // Attach remote stack trace if availalble
2041 if (remoteStackTrace ! null) {
2042 RemoteException cause new RemoteException(
2043 Remote stack trace:\n remoteStackTrace, null, false, false);
2044 try {
2045 Throwable rootCause ExceptionUtils.getRootCause(e);
2046 if (rootCause ! null) {
2047 rootCause.initCause(cause);
2048 }
2049 } catch (RuntimeException ex) {
2050 Log.e(TAG, Cannot set cause cause for e, ex);
2051 }
2052 }
2053 SneakyThrow.sneakyThrow(e);
2054 }
Client端根据异常codemsg和stack trace重新构建出Exception对象并将其抛出。由于readException方法并没有用throws修饰所以如果该异常是Checked Exception譬如RemoteExceptionIOException就不能够直接抛出否则会产生编译错误。因此这里采用了SneakyThrow来进行规避。结合Server端异常处理的第一种情况可以知道Client端只会读到8种RuntimeException中的一种。由于RuntimeException属于Unchecked Exception因此编译过程并不会去检查对它的处理。换句话说程序员在调用代理对象的方式时虽然会用try catch代码块但通常只会去catch RemoteException而不会去catch RuntimeException对于很多程序员而言不强制等于不做。这样一来Parcel读回来的RuntimeException将会导致Client进程退出。以下为示例注意这些Log是在Client进程中输出的。Timestamp: 02-27 19:20:16.623 Process: com.google.android.dialer PID: 9782 Thread: main
java.lang.NullPointerException: Attempt to get length of null arrayat android.os.Parcel.createException(Parcel.java:2077)at android.os.Parcel.readException(Parcel.java:2039)at android.os.Parcel.readException(Parcel.java:1987)at android.app.IActivityTaskManager$Stub$Proxy.activityPaused(IActivityTaskManager.java:4489)at android.app.servertransaction.PauseActivityItem.postExecute(PauseActivityItem.java:64)at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:177)at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)at android.os.Handler.dispatchMessage(Handler.java:107)at android.os.Looper.loop(Looper.java:214)at android.app.ActivityThread.main(ActivityThread.java:7356)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: android.os.RemoteException: Remote stack trace:at android.util.ArraySet.add(ArraySet.java:422)at com.android.server.wm.AppWindowToken.setVisibility(AppWindowToken.java:579)at com.android.server.wm.ActivityRecord.setVisibility(ActivityRecord.java:1844)at com.android.server.wm.ActivityStackSupervisor.realStartActivityLocked(ActivityStackSupervisor.java:774)at com.android.server.wm.ActivityStackSupervisor.startSpecificActivityLocked(ActivityStackSupervisor.java:979)
上述Log表示进程com.google.android.dialer出现错误并退出。但如果了解Binder的异常机制就会知道问题的根源不在com.google.android.dialer进程而在system_server进程。NullPointerException并不是dialer进程发生的异常而是它从Parcel对象中读取的异常。通过IActivityTaskManager$Stub$Proxy.activityPaused可知此时dialer进程正在和system_server进程通信。因此该NullPointerException是system_server进程中抛出的异常。再结合Remote stack trace可知此异常是在调用ArraySet.Add时出现的。结合这些信息接下来调试的方向就不应该局限在dialer中而是着眼于system_server进程。否则就会犯了头疼医头脚痛医脚的问题。1.2.2 从Parcel对象中读回Error信息/frameworks/base/core/jni/android_util_Binder.cpp368 jboolean res env-CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
369 code, reinterpret_castjlong(data), reinterpret_castjlong(reply), flags);
370
371 if (env-ExceptionCheck()) {
372 ScopedLocalRefjthrowable excep(env, env-ExceptionOccurred());
373 report_exception(env, excep.get(),
374 *** Uncaught remote exception!
375 (Exceptions are not yet supported across processes.));
376 res JNI_FALSE;
377 }
......
402 return res ! JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
403 }
对于8种RuntimeException之外的其余异常Server进程会将它们交给JavaBBinder::onTransact来处理。1.1.2中只说明了Server进程会在本进程中输出异常但并未提及可能对Client进程产生的影响。376行将res赋值为JNI_FALSE因此JavaBBinder::onTransact最终返回UNKNOWN_TRANSACTION。/frameworks/native/libs/binder/Binder.cpp123 status_t BBinder::transact(
124 uint32_t code, const Parcel data, Parcel* reply, uint32_t flags)
125 {
126 data.setDataPosition(0);
127
128 status_t err NO_ERROR;
129 switch (code) {
130 case PING_TRANSACTION:
131 reply-writeInt32(pingBinder());
132 break;
133 default:
134 err onTransact(code, data, reply, flags);
135 break;
136 }
137
138 if (reply ! nullptr) {
139 reply-setDataPosition(0);
140 }
141
142 return err;
143 }
UNKNOWN_TRANSACTION最终会传入IPCThreadState::executeCommand中并赋值给如下的error。/frameworks/native/libs/binder/IPCThreadState.cpp1228 if ((tr.flags TF_ONE_WAY) 0) {
1229 LOG_ONEWAY(Sending reply to %d!, mCallingPid);
1230 if (error NO_ERROR) reply.setError(error);
1231 sendReply(reply, 0);
1232 } else {
1233 LOG_ONEWAY(NOT sending reply to %d!, mCallingPid);
1234 }
1230行会将该error写入Parcel对象中并通过驱动传回Client进程。/frameworks/native/libs/binder/IPCThreadState.cpp821 status_t IPCThreadState::sendReply(const Parcel reply, uint32_t flags)
822 {
823 status_t err;
824 status_t statusBuffer;
825 err writeTransactionData(BC_REPLY, flags, -1, 0, reply, statusBuffer);
826 if (err NO_ERROR) return err;
827
828 return waitForResponse(nullptr, nullptr);
829 }
/frameworks/native/libs/binder/IPCThreadState.cpp1025 status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
1026 int32_t handle, uint32_t code, const Parcel data, status_t* statusBuffer)
1027 {
1028 binder_transaction_data tr;
1029
1030 tr.target.ptr 0; /* Dont pass uninitialized stack data to a remote process */
1031 tr.target.handle handle;
1032 tr.code code;
1033 tr.flags binderFlags;
1034 tr.cookie 0;
1035 tr.sender_pid 0;
1036 tr.sender_euid 0;
1037
1038 const status_t err data.errorCheck();
1039 if (err NO_ERROR) {
1040 tr.data_size data.ipcDataSize();
1041 tr.data.ptr.buffer data.ipcData();
1042 tr.offsets_size data.ipcObjectsCount()*sizeof(binder_size_t);
1043 tr.data.ptr.offsets data.ipcObjects();
1044 } else if (statusBuffer) {
1045 tr.flags | TF_STATUS_CODE;
1046 *statusBuffer err;
1047 tr.data_size sizeof(status_t);
1048 tr.data.ptr.buffer reinterpret_castuintptr_t(statusBuffer);
1049 tr.offsets_size 0;
1050 tr.data.ptr.offsets 0;
1051 } else {
1052 return (mLastError err);
1053 }
1054
1055 mOut.writeInt32(cmd);
1056 mOut.write(tr, sizeof(tr));
1057
1058 return NO_ERROR;
1059 }
sendReply会调用writeTransactionData由于1038行data中读取的err是UNKNOWN_TRANSACTION所以最终写入驱动的数据并不是data本身而是data中的err值。Client端接受数据后会将该err从IPCThreadState::waitForResponse一直传回给android_os_BinderProxy_transact。/frameworks/base/core/jni/android_util_Binder.cpp1319 status_t err target-transact(code, *data, reply, flags);
1320 //if (reply) printf(Transact from Java code to %p received: , target); reply-print();
1321
1322 if (kEnableBinderSample) {
1323 if (time_binder_calls) {
1324 conditionally_log_binder_call(start_millis, target, code);
1325 }
1326 }
1327
1328 if (err NO_ERROR) {
1329 return JNI_TRUE;
1330 } else if (err UNKNOWN_TRANSACTION) {
1331 return JNI_FALSE;
1332 }
1333
1334 signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data-dataSize());
1335 return JNI_FALSE;
1336 }
1319行返回的err是UNKNOWN_TRANSACTION因此最终会在1331行直接返回而不会调用signalExceptionForError来抛出异常。out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java442 Override public int intMethod(int a) throws android.os.RemoteException
443 {
444 android.os.Parcel _data android.os.Parcel.obtain();
445 android.os.Parcel _reply android.os.Parcel.obtain();
446 int _result;
447 try {
448 _data.writeInterfaceToken(DESCRIPTOR);
449 _data.writeInt(a);
450 boolean _status mRemote.transact(Stub.TRANSACTION_intMethod, _data, _reply, 0);
451 if (!_status getDefaultImpl() ! null) {
452 return getDefaultImpl().intMethod(a);
453 }
454 _reply.readException();
455 _result _reply.readInt();
456 }
457 finally {
458 _reply.recycle();
459 _data.recycle();
460 }
461 return _result;
462 }
返回的err表现为450行的_status如果该代理有默认的实现那么最终会调用默认实现的intMethod。否则将会调用455行的readInt来获取返回值。由于此时的_reply对象中并无数据因此读回的int值为0。那如果返回的数据不是基本类型而是引用类型呢以下面方法举例。out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java482 if ((0!_reply.readInt())) {
483 _result android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
484 }
485 else {
486 _result null;
487 }
若是返回的数据为引用类型由于_reply中无数据因此返回值最终为null。总结一下对于Server进程中返回的其余异常除8种RuntimeException以外Client进程将不会感知到。最终代理对象方法的返回值将由其类型决定基本类型返回0引用类型返回null。2. RemoteException的本质和类别RemoteException不继承于RuntimeException因此它是一种Checked Exception。对于程序中抛出的Checked Exception它必须按照如下两种方式中的一种来处理否则会在编译时报错。用try catch代码块来捕获异常。将该方法用throws关键字修饰告诉调用者们此方法可能会抛出该异常。Checked Exception会对程序员的代码有强制要求。之所以这么做是因为该类异常希望程序员可以提前预知并做好准备它们本可以被处理用不着让进程退出。以下是Oracle官网的解释表示对于那些进程可以从中恢复的异常都应该把它声明为Checked Exception。像远程调用/网络请求/IO请求之类的异常它们反映的多是数据无法获取但并不意味着进程到了无法继续运行的地步因此用Checked Exception最为合适。Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you dont want to be bothered with specifying the exceptions your methods can throw.Heres the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.对于Android Q而言RemoteException的子类有三个DeadObjectExceptionTransactionTooLargeExceptionDeadSystemException是DeadObjectException的子类2.1 DeadObjectException/frameworks/base/core/java/android/os/DeadObjectException.java20 /**
21 * The object you are calling has died, because its hosting process
22 * no longer exists.
23 */
24 public class DeadObjectException extends RemoteException {
DeadObjectException反映的是Server进程已经挂掉而Client进程仍旧尝试通信的错误。这句话是注释的含义但不是问题的本质。事实上DeadObjectException不仅仅有对端进程挂掉这一种情况可以触发binder线程池耗尽也会触发该异常。/frameworks/base/core/jni/android_util_Binder.cpp1319 status_t err target-transact(code, *data, reply, flags);
1320 //if (reply) printf(Transact from Java code to %p received: , target); reply-print();
1321
1322 if (kEnableBinderSample) {
1323 if (time_binder_calls) {
1324 conditionally_log_binder_call(start_millis, target, code);
1325 }
1326 }
1327
1328 if (err NO_ERROR) {
1329 return JNI_TRUE;
1330 } else if (err UNKNOWN_TRANSACTION) {
1331 return JNI_FALSE;
1332 }
1333
1334 signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data-dataSize());
1335 return JNI_FALSE;
1336 }
Client端Java层接收到的异常有两处来源一处是根据Parcel对象中异常信息构建出的RuntimeException另一处就是如上1334行根据BpBinder::transact返回值构建出的各种异常。/frameworks/base/core/jni/android_util_Binder.cpp741 void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
742 bool canThrowRemoteException, int parcelSize)
743 {
744 switch (err) {
......
778 case DEAD_OBJECT:
779 // DeadObjectException is a checked exception, only throw from certain methods.
780 jniThrowException(env, canThrowRemoteException
781 ? android/os/DeadObjectException
782 : java/lang/RuntimeException, NULL);
783 break;
784 case UNKNOWN_TRANSACTION:
785 jniThrowException(env, java/lang/RuntimeException, Unknown transaction code);
786 break;
787 case FAILED_TRANSACTION: {
788 ALOGE(!!! FAILED BINDER TRANSACTION !!! (parcel size %d), parcelSize);
789 const char* exceptionToThrow;
790 char msg[128];
791 // TransactionTooLargeException is a checked exception, only throw from certain methods.
792 // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
793 // but it is not the only one. The Binder driver can return BR_FAILED_REPLY
794 // for other reasons also, such as if the transaction is malformed or
795 // refers to an FD that has been closed. We should change the driver
796 // to enable us to distinguish these cases in the future.
797 if (canThrowRemoteException parcelSize 200*1024) {
798 // bona fide large payload
799 exceptionToThrow android/os/TransactionTooLargeException;
800 snprintf(msg, sizeof(msg)-1, data parcel size %d bytes, parcelSize);
801 } else {
802 // Heuristic: a payload smaller than this threshold shouldnt be too
803 // big, so its probably some other, more subtle problem. In practice
804 // it seems to always mean that the remote process died while the binder
805 // transaction was already in flight.
806 exceptionToThrow (canThrowRemoteException)
807 ? android/os/DeadObjectException
808 : java/lang/RuntimeException;
809 snprintf(msg, sizeof(msg)-1,
810 Transaction failed on small parcel; remote process probably died);
811 }
812 jniThrowException(env, exceptionToThrow, msg);
813 } break;
......
861 }
862 }
由780和806行可知Java层接收到的DeadObjectException有两处来历BpBinder::transact返回值为DEAD_OBJECT。BpBinder::transact返回值为FAILED_TRANSACTION但是本次传输的Parcel小于200K。2.1.1 在什么情况下返回DEAD_OBJECT/frameworks/native/libs/binder/IPCThreadState.cpp854 case BR_DEAD_REPLY:
855 err DEAD_OBJECT;
856 goto finish;
当Binder驱动返回给用户空间的cmd为BR_DEAD_REPLY时JNI层的signalExceptionForError将会接收到DEAD_OBJECT。由此可知DEAD_OBJECT真正出生的地方是在驱动里。驱动中返回BR_DEAD_REPLY的地方有很多本文不会一一列举。这里只展示其中典型的一例。2972 static struct binder_node *binder_get_node_refs_for_txn(
2973 struct binder_node *node,
2974 struct binder_proc **procp,
2975 uint32_t *error)
2976 {
2977 struct binder_node *target_node NULL;
2978
2979 binder_node_inner_lock(node);
2980 if (node-proc) {
2981 target_node node;
2982 binder_inc_node_nilocked(node, 1, 0, NULL);
2983 binder_inc_node_tmpref_ilocked(node);
2984 node-proc-tmp_ref;
2985 *procp node-proc;
2986 } else
2987 *error BR_DEAD_REPLY;
2988 binder_node_inner_unlock(node);
2989
2990 return target_node;
2991 }
当node-proc为null时表明该Binder实体所在的进程已经退出。因此2987行会返回BR_DEAD_REPLY。2.1.2 在什么情况下返回FAILED_TRANSACTION/frameworks/native/libs/binder/IPCThreadState.cpp858 case BR_FAILED_REPLY:
859 err FAILED_TRANSACTION;
860 goto finish;
当Binder驱动返回给用户空间的cmd为BR_FAILED_REPLY时JNI层的signalExceptionForError将会接收到FAILED_TRANSACTION。由此可知FAILED_TRANSACTION真正出生的地方也是在驱动里。驱动中返回BR_FAILED_REPLY的地方有很多本文不会一一列举。这里只展示其中典型的一例。3267 t-buffer binder_alloc_new_buf(target_proc-alloc, tr-data_size,
3268 tr-offsets_size, extra_buffers_size,
3269 !reply (t-flags TF_ONE_WAY));
3270 if (IS_ERR(t-buffer)) {
3271 /*
3272 * -ESRCH indicates VMA cleared. The target is dying.
3273 */
3274 return_error_param PTR_ERR(t-buffer);
3275 return_error return_error_param -ESRCH ?
3276 BR_DEAD_REPLY : BR_FAILED_REPLY;
3277 return_error_line __LINE__;
3278 t-buffer NULL;
3279 goto err_binder_alloc_buf_failed;
3280 }
binder_alloc_new_buf用于分配此次通信所需的binder_buffer其中可能会出现诸多错误。387 if (is_async
388 alloc-free_async_space size sizeof(struct binder_buffer)) {
389 binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
390 %d: binder_alloc_buf size %zd failed, no async space left\n,
391 alloc-pid, size);
392 return ERR_PTR(-ENOSPC);
393 }
......
442 binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
443 allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n,
444 total_alloc_size, allocated_buffers,
445 largest_alloc_size, total_free_size,
446 free_buffers, largest_free_size);
447 return ERR_PTR(-ENOSPC);
上述代码展现了binder buffer空间耗尽时返回的错误-ENOSPC此错误最终会以BR_FAILED_REPLY的方式返回用户空间。当此次通信的数据量200K时最终往Java层抛出的异常为DeadObjectException。而这次异常只表示对端进程的binder buffer耗尽并非表示对端进程退出了。2.2 TransactionTooLargeException当Binder驱动返回BR_FAILED_REPLY且此次传输的数据大于200K则Java层会接收到TransactionTooLargeException的错误。需要注意的是Binder驱动中返回BR_FAILED_REPLY的地方有很多找不到合适的binder_buffer来传输数据只是其中的一种。2.3 DeadSystemException/frameworks/base/core/java/android/os/RemoteException.java58 UnsupportedAppUsage
59 public RuntimeException rethrowFromSystemServer() {
60 if (this instanceof DeadObjectException) {
61 throw new RuntimeException(new DeadSystemException());
62 } else {
63 throw new RuntimeException(this);
64 }
65 }
当Client用catch代码块捕获RemoteException时如果此次binder通信的对端为system_server该方法便可以使用rethrowFromSystemServer重新抛出异常。当原本的异常为DeadObjectException时那么新抛出的异常就是封装过的DeadSystemException。 回复「 篮球的大肚子」进入技术群聊回复「1024」获取1000G学习资料