Android源代码中大小设置
http://androidxref.com/9.0.0_r3/xref/frameworks/native/libs/binder/ProcessState.cpp#43
默认的page size是4k(4096), 查询命令为getconf PAGE_SIZE
所以,binder的数据大小限制一般为1M-8k
43#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
44#define DEFAULT_MAX_BINDER_THREADS 15
...
425 // mmap the binder, providing a chunk of virtual address space to receive transactions.
426 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
Framework错误提示相关代码
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/jni/android_util_Binder.cpp#794
代码中可以看到:
只有binder transaction失败的情况下,才会进行错误提示。如果整个进程传输数据使用binder的总大小超过1M - 8k的情况下,会失败。
如果判断本次AIDL调用的数据量大于200k,提示TransactionTooLargeException
否则,提示Transaction failed on small parcel; remote process probably died
如果binder transaction成功的话,即使本次AIDL调用数据量超过200k,也是不会有错误的,如下面的case2
当然,从这里可以看出,进行AIDL设计时,需要尽量减小大数据通过AIDL参数传输,以及直接在AIDL中进行长时间的操作,避免binder空间耗尽
771 case FAILED_TRANSACTION: {
772 ALOGE("!!! FAILED BINDER TRANSACTION !!! (parcel size = %d)", parcelSize);
773 const char* exceptionToThrow;
774 char msg[128];
775 // TransactionTooLargeException is a checked exception, only throw from certain methods.
776 // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
777 // but it is not the only one. The Binder driver can return BR_FAILED_REPLY
778 // for other reasons also, such as if the transaction is malformed or
779 // refers to an FD that has been closed. We should change the driver
780 // to enable us to distinguish these cases in the future.
781 if (canThrowRemoteException && parcelSize > 200*1024) {
782 // bona fide large payload
783 exceptionToThrow = "android/os/TransactionTooLargeException";
784 snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
785 } else {
786 // Heuristic: a payload smaller than this threshold "shouldn't" be too
787 // big, so it's probably some other, more subtle problem. In practice
788 // it seems to always mean that the remote process died while the binder
789 // transaction was already in flight.
790 exceptionToThrow = (canThrowRemoteException)
791 ? "android/os/DeadObjectException"
792 : "java/lang/RuntimeException";
793 snprintf(msg, sizeof(msg)-1,
794 "Transaction failed on small parcel; remote process probably died");
795 }
796 jniThrowException(env, exceptionToThrow, msg);
797 } break;
传输数据大小限制的理解
1M-8k=1016k大小是所以一个进程中所有binder线程加起来的大小的限制。
比如,一个进程里面已经有3个binder线程,占用了900k。那么,后面一个binder线程,只要传输167k,就会报错。后面会有这个例子的演示。
接下来在虚拟机Android P上面进行实验。
本文代码位置
https://github.com/romulus1985/BinderTransaction
服务器端代码
服务器端的方法长时间不会退出
注意每个testCase运行后,需要重启server app。
private IBinder mMyAidlInterface = new IMyAidlInterface.Stub() {
@Override
public void hello(byte[] data) throws RemoteException {
log("hello enter.");
try {
final long time = 60 * 60 * 1000; // 1 day
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
客户端通信工具API
在多个线程中调用AIDL,发送指定byte的数据
private void sendBytes(final int size, final int count) throws RemoteException {
final byte[] bytes = new byte[size];
for (int i = 0; i < count; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
mMyAidlInterface.hello(bytes);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}
}
客户端case1
可以看到提示FAILED BINDER TRANSACTION !!! (parcel size = 1048672)。
android.os.TransactionTooLargeException:提示数据量太大了
这里客户端输出的数据是1M = 1024 * 1024 = 1,048,576, 系统提示的数据比较1M略大96byte,说明binder调用时,除了方法参数,还会占用另外部分空间。
private void testCase1() throws RemoteException {
final int size = 1024 * 1024; // 1M
sendBytes(size, 1);
}
错误提示
2020-07-19 23:53:40.183 4198-4198/com.example.binderclient E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 1048672)
2020-07-19 23:53:40.183 4198-4198/com.example.binderclient W/System.err: android.os.TransactionTooLargeException: data parcel size 1048672 bytes
2020-07-19 23:53:40.184 4198-4198/com.example.binderclient W/System.err: at android.os.BinderProxy.transactNative(Native Method)
2020-07-19 23:53:40.185 4198-4198/com.example.binderclient W/System.err: at android.os.BinderProxy.transact(Binder.java:1127)
2020-07-19 23:53:40.185 4198-4198/com.example.binderclient W/System.err: at com.example.binderserver.IMyAidlInterface$Stub$Proxy.hello(IMyAidlInterface.java:107)
2020-07-19 23:53:40.185 4198-4198/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCase1(MainActivity.java:63)
2020-07-19 23:53:40.186 4198-4198/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCases(MainActivity.java:54)
2020-07-19 23:53:40.186 4198-4198/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.access$200(MainActivity.java:16)
2020-07-19 23:53:40.186 4198-4198/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity$1.onServiceConnected(MainActivity.java:26)
2020-07-19 23:53:40.186 4198-4198/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1730)
2020-07-19 23:53:40.187 4198-4198/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1762)
2020-07-19 23:53:40.187 4198-4198/com.example.binderclient W/System.err: at android.os.Handler.handleCallback(Handler.java:873)
2020-07-19 23:53:40.187 4198-4198/com.example.binderclient W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
2020-07-19 23:53:40.187 4198-4198/com.example.binderclient W/System.err: at android.os.Looper.loop(Looper.java:193)
2020-07-19 23:53:40.188 4198-4198/com.example.binderclient W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6669)
2020-07-19 23:53:40.188 4198-4198/com.example.binderclient W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-07-19 23:53:40.188 4198-4198/com.example.binderclient W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2020-07-19 23:53:40.188 4198-4198/com.example.binderclient W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
客户端case2
300k x 3 = 900K可以正常运行
private void testCase2() throws RemoteException {
log("testCase2 enter.");
final int size = 300 * 1024; // 300k
sendBytes(size, 3); // 300k x 3
log("testCase2 exit.");
}
客户端case3
可以看到提示:FAILED BINDER TRANSACTION !!! (parcel size = 171104)
在testCase2的900K的基础上,再次传输116k=116 *1024=118,784,系统提示这次调用的binder数据大小为118880,仍然是比方法参数大96byte
private void testCase3() throws RemoteException {
log("testCase3 enter.");
testCase2();
try {
Thread.sleep(3 * 1000); // 3s
} catch (InterruptedException e) {
e.printStackTrace();
}
final int size = 116 * 1024; // 116k, crash
byte[] bytes = new byte[size];
log("send " + size + " bytes");
mMyAidlInterface.hello(bytes);
}
错误提示
2020-07-20 00:20:05.890 5064-5064/com.example.binderclient E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 118880)
2020-07-20 00:20:05.891 5064-5064/com.example.binderclient W/System.err: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
2020-07-20 00:20:05.891 5064-5064/com.example.binderclient W/System.err: at android.os.BinderProxy.transactNative(Native Method)
2020-07-20 00:20:05.892 5064-5064/com.example.binderclient W/System.err: at android.os.BinderProxy.transact(Binder.java:1127)
2020-07-20 00:20:05.892 5064-5064/com.example.binderclient W/System.err: at com.example.binderserver.IMyAidlInterface$Stub$Proxy.hello(IMyAidlInterface.java:107)
2020-07-20 00:20:05.892 5064-5064/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCase3(MainActivity.java:91)
2020-07-20 00:20:05.893 5064-5064/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCases(MainActivity.java:56)
2020-07-20 00:20:05.893 5064-5064/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.access$200(MainActivity.java:16)
2020-07-20 00:20:05.893 5064-5064/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity$1.onServiceConnected(MainActivity.java:26)
2020-07-20 00:20:05.893 5064-5064/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1730)
2020-07-20 00:20:05.894 5064-5064/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1762)
2020-07-20 00:20:05.894 5064-5064/com.example.binderclient W/System.err: at android.os.Handler.handleCallback(Handler.java:873)
2020-07-20 00:20:05.894 5064-5064/com.example.binderclient W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
2020-07-20 00:20:05.894 5064-5064/com.example.binderclient W/System.err: at android.os.Looper.loop(Looper.java:193)
2020-07-20 00:20:05.907 5064-5064/com.example.binderclient W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6669)
2020-07-20 00:20:05.907 5064-5064/com.example.binderclient W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-07-20 00:20:05.907 5064-5064/com.example.binderclient W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2020-07-20 00:20:05.908 5064-5064/com.example.binderclient W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
客户端case4
比testCase3传输数据总量,小1k,可以正常调用。
private void testCase4() throws RemoteException {
log("testCase4 enter.");
testCase2();
try {
Thread.sleep(3 * 1000); // 3s
} catch (InterruptedException e) {
e.printStackTrace();
}
final int size = 115 * 1024; // 115k, ok
byte[] bytes = new byte[size];
log("send " + size + " bytes");
mMyAidlInterface.hello(bytes);
}
客户端case5
比testCase4大632字节,正常调用
private void testCase5() throws RemoteException {
log("testCase5 enter.");
testCase2();
try {
Thread.sleep(3 * 1000); // 3s
} catch (InterruptedException e) {
e.printStackTrace();
}
final int size = 115 * 1024 + 632; // 115k + 632, ok
byte[] bytes = new byte[size];
log("send " + size + " bytes");
mMyAidlInterface.hello(bytes);
}
客户端case6
比testCase5大1字节,发送错误。
private void testCase6() throws RemoteException {
log("testCase5 enter.");
testCase2();
try {
Thread.sleep(3 * 1000); // 3s
} catch (InterruptedException e) {
e.printStackTrace();
}
final int size = 115 * 1024 + 633; // 115k + 633, crash
byte[] bytes = new byte[size];
log("send " + size + " bytes");
mMyAidlInterface.hello(bytes);
}
错误提示
2020-07-20 23:26:14.929 6128-6128/com.example.binderclient E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 118492)
2020-07-20 23:26:14.930 6128-6128/com.example.binderclient W/System.err: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
2020-07-20 23:26:14.931 6128-6128/com.example.binderclient W/System.err: at android.os.BinderProxy.transactNative(Native Method)
2020-07-20 23:26:14.931 6128-6128/com.example.binderclient W/System.err: at android.os.BinderProxy.transact(Binder.java:1127)
2020-07-20 23:26:14.932 6128-6128/com.example.binderclient W/System.err: at com.example.binderserver.IMyAidlInterface$Stub$Proxy.hello(IMyAidlInterface.java:107)
2020-07-20 23:26:14.932 6128-6128/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCase6(MainActivity.java:146)
2020-07-20 23:26:14.933 6128-6128/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.testCases(MainActivity.java:75)
2020-07-20 23:26:14.933 6128-6128/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity.access$200(MainActivity.java:16)
2020-07-20 23:26:14.933 6128-6128/com.example.binderclient W/System.err: at com.example.binderclient.MainActivity$1.onServiceConnected(MainActivity.java:26)
2020-07-20 23:26:14.936 6128-6128/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1730)
2020-07-20 23:26:14.937 6128-6128/com.example.binderclient W/System.err: at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1762)
2020-07-20 23:26:14.937 6128-6128/com.example.binderclient W/System.err: at android.os.Handler.handleCallback(Handler.java:873)
2020-07-20 23:26:14.938 6128-6128/com.example.binderclient W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
2020-07-20 23:26:14.938 6128-6128/com.example.binderclient W/System.err: at android.os.Looper.loop(Looper.java:193)
2020-07-20 23:26:14.939 6128-6128/com.example.binderclient W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6669)
2020-07-20 23:26:14.939 6128-6128/com.example.binderclient W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-07-20 23:26:14.939 6128-6128/com.example.binderclient W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2020-07-20 23:26:14.940 6128-6128/com.example.binderclient W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)