本文共 30075 字,大约阅读时间需要 100 分钟。
文中代码分析基于Android 10.0 (Q)
两个进程之间若是要进行Binder通信,那么发起通信的一端我们就称它为Client进程。Client进程调用每一个代理对象的方法,本质上都是一次跨进程通信。如果这个方法是同步方法(非oneway修饰),那么此调用过程将会经历如下几个阶段。
对应用工程师而言,他只会看到浮在海面的冰山一角,至于隐藏在海面下的系统调用和跨进程通信,是无需他感知的。但无需感知并不代表不存在。这些中间过程若是发生了异常,终归是需要被处理的。
本文将重点阐述两个问题:
如果Server进程中Binder实体对象的方法发生异常,该异常将会去向何处?
RemoteException的本质和类别。
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.java
106 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException107 {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.java
1000 private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,1001 int callingUid) {1002 // Make sure the observer won't 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 we'll just call it directly.1011 boolean res;1012 // Log any exceptions as warnings, don't 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 exception1037 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 access1048 // 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 strict1059 // mode violations that have gathered for this thread. Either they have been1060 // parceled and are now in transport off to the caller, or we are returning back1061 // to the main transaction loop to wait for another incoming transaction. Either1062 // way, strict mode begone!1063 StrictMode.clearGatheredViolations();1064 return res;1065 }
1021行是调用onTransact的位置。如果异常没有在此方法中被处理,将会进一步抛出给execTransact,最终进入JNI方法:JavaBBinder::onTransact。
异常的传递关系正如本节开始的那幅图所示,其最终的处理分为了两种情况。
Exception | Code |
---|---|
SecurityException | EX_SECURITY |
BadParcelableException | EX_BAD_PARCELABLE |
IllegalArgumentException | EX_ILLEGAL_ARGUMENT |
NullPointerException | EX_NULL_POINTER |
IllegalStateException | EX_ILLEGAL_STATE |
NetworkOnMainThreadException | EX_NETWORK_MAIN_THREAD |
UnsupportedOperationException | EX_UNSUPPORTED_OPERATION |
ServiceSpecificException | EX_SERVICE_SPECIFIC |
/frameworks/base/core/java/android/os/Parcel.java
1868 public final void writeException(@NonNull Exception e) {1869 int code = 0;1870 if (e instanceof Parcelable1871 && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {1872 // We only send Parcelable exceptions that are in the1873 // BootClassLoader to ensure that the receiver can unpack them1874 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进程。
/frameworks/base/core/jni/android_util_Binder.cpp
355 status_t onTransact(356 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override357 {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_cast(&data), reinterpret_cast (reply), flags);370 371 if (env->ExceptionCheck()) {372 ScopedLocalRef 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 Hangl at 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 Hangl at 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.java
1894 if (code == 0) {1895 if (e instanceof RuntimeException) {1896 throw (RuntimeException) e;1897 }1898 throw new RuntimeException(e);1899 }
回到Binder.execTransactInternal,对于RemoteException1898行会将其进行封装再次抛出,而对于非以上8种的RuntimeException,1896行也会将其再次抛出。
综合以上两种处理情况,可以推断所有Binder实体对象方法中发生的异常都会被处理。无非一种是将异常信息发送给对端进程,另一种是将异常信息在本进程输出。而这些处理都不会使Server进程退出。
仔细思考这样设计也是很合理的。作为Server进程,它在什么时候执行,该执行些什么都不由自己掌控,而是由Client进程控制。因此抛出异常本质上与Client进程相关,让一个Client进程的行为导致Server进程退出显然是不合理的。此外,Server进程可能关联着千百个Client,不能由于一个Client的错误行为而影响本可以正常获取服务的其他Client。
Client端受到的影响完全取决于Server端如何处理异常。上文中已经阐明,Server进程会分两种情况来处理异常:一种是将异常信息发送给Client进程,另一个种是将异常信息在本进程中输出。以下按照这两个情况分别讨论Client进程受到的影响。
out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java
442 @Override public int intMethod(int a) throws android.os.RemoteException443 {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.java
1983 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.java
2033 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 availalble2041 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端根据异常code,msg和stack trace重新构建出Exception对象,并将其抛出。由于readException方法并没有用throws
修饰,所以如果该异常是Checked Exception(譬如RemoteException,IOException)就不能够直接抛出,否则会产生编译错误。因此这里采用了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: mainjava.lang.NullPointerException: Attempt to get length of null array at 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
进程。否则就会犯了头疼医头,脚痛医脚的问题。
/frameworks/base/core/jni/android_util_Binder.cpp
368 jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,369 code, reinterpret_cast(&data), reinterpret_cast (reply), flags);370 371 if (env->ExceptionCheck()) {372 ScopedLocalRef 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.cpp
123 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.cpp
1228 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.cpp
821 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.cpp
1025 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; /* Don't 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_cast(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.cpp
1319 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.java
442 @Override public int intMethod(int a) throws android.os.RemoteException443 {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.java
482 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。
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 ofRuntimeException
simply because you don't want to be bothered with specifying the exceptions your methods can throw.Here's 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的子类有三个:
DeadObjectException
TransactionTooLargeException
DeadSystemException,是DeadObjectException的子类
/frameworks/base/core/java/android/os/DeadObjectException.java
20 /**21 * The object you are calling has died, because its hosting process22 * no longer exists.23 */24 public class DeadObjectException extends RemoteException {
DeadObjectException反映的是Server进程已经挂掉,而Client进程仍旧尝试通信的错误。这句话是注释的含义,但不是问题的本质。事实上DeadObjectException不仅仅有对端进程挂掉这一种情况可以触发,binder线程池耗尽也会触发该异常。
/frameworks/base/core/jni/android_util_Binder.cpp
1319 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.cpp
741 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, canThrowRemoteException781 ? "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_TRANSACTION793 // but it is not the only one. The Binder driver can return BR_FAILED_REPLY794 // for other reasons also, such as if the transaction is malformed or795 // refers to an FD that has been closed. We should change the driver796 // to enable us to distinguish these cases in the future.797 if (canThrowRemoteException && parcelSize > 200*1024) {798 // bona fide large payload799 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 "shouldn't" be too803 // big, so it's probably some other, more subtle problem. In practice804 // it seems to always mean that the remote process died while the binder805 // 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。
/frameworks/native/libs/binder/IPCThreadState.cpp
854 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 } else2987 *error = BR_DEAD_REPLY;2988 binder_node_inner_unlock(node);2989 2990 return target_node;2991 }
当node->proc为null时,表明该Binder实体所在的进程已经退出。因此2987行会返回BR_DEAD_REPLY。
/frameworks/native/libs/binder/IPCThreadState.cpp
858 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耗尽,并非表示对端进程退出了。
当Binder驱动返回BR_FAILED_REPLY且此次传输的数据大于200K,则Java层会接收到TransactionTooLargeException的错误。
需要注意的是,Binder驱动中返回BR_FAILED_REPLY的地方有很多,找不到合适的binder_buffer来传输数据只是其中的一种。
/frameworks/base/core/java/android/os/RemoteException.java
58 @UnsupportedAppUsage59 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学习资料
转载地址:http://pcepf.baihongyu.com/