FAILED BINDER TRANSACTION (Android Binder传输数据大小限制)

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)
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值