Android throw DeadObjectException with LOG: Transaction failed on small parcel;远程进程可能已经死了

Posted

技术标签:

【中文标题】Android throw DeadObjectException with LOG: Transaction failed on small parcel;远程进程可能已经死了【英文标题】:Android throw DeadObjectException with LOG: Transaction failed on small parcel; remote process probably died 【发布时间】:2017-08-01 08:26:13 【问题描述】:
07-22 04:38:07.933  1579  3338 E JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 352)
07-22 04:38:07.933  1579  3338 W BroadcastQueue: Can't deliver broadcast to com.android.systemui (pid 2160). Crashing it.
07-22 04:38:07.934  1579  3338 W BroadcastQueue: Failure sending broadcast Intent  act=android.intent.action.TIME_TICK flg=0x50000014 (has extras) 
07-22 04:38:07.934  1579  3338 W BroadcastQueue: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at android.os.BinderProxy.transactNative(Native Method)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at android.os.BinderProxy.transact(Binder.java:618)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at android.app.ApplicationThreadProxy.scheduleRegisteredReceiver(ApplicationThreadNative.java:1211)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.BroadcastQueue.performReceiveLocked(BroadcastQueue.java:489)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.BroadcastQueue.deliverToRegisteredReceiverLocked(BroadcastQueue.java:702)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.BroadcastQueue.processNextBroadcast(BroadcastQueue.java:1002)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.BroadcastQueue.processNextBroadcast(BroadcastQueue.java:799)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.ActivityManagerService.finishReceiver(ActivityManagerService.java:19153)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:528)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2909)
07-22 04:38:07.934  1579  3338 W BroadcastQueue:    at android.os.Binder.execTransact(Binder.java:565)
07-22 04:38:07.937  2160  2160 D AndroidRuntime: Shutting down VM
07-22 04:38:07.953  2160  2625 E JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 136)
--------- beginning of crash
07-22 04:38:07.972  2160  2160 E AndroidRuntime: FATAL EXCEPTION: main
07-22 04:38:07.972  2160  2160 E AndroidRuntime: Process: com.android.systemui, PID: 2160
07-22 04:38:07.972  2160  2160 E AndroidRuntime: android.app.RemoteServiceException: can't deliver broadcast
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1690)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:102)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:160)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:6252)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
07-22 04:38:07.972  2160  2160 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:788)

BroadcastQueue类发生错误,通过Binder调用scheduleRegisteredReceiver,抛出DeadObjectException。就像日志说的:小包裹交易失败;远程进程可能已经死了,但是如果com.android.systemui进程已经死了,为什么RuntimeException会抛出呢?

【问题讨论】:

【参考方案1】:

终于找到了根本原因,发生在binder内核中。

目前,我发现了两个可能导致在 BroadcastQueue 中引发 DeadObjectException 并在应用程序的 ActivityThread 中引发 RemoteServiceException 的原因:

    当 AMS 向 ActivityThread 发送单向绑定器调用以触发 BroadcastReceiver.onReceive 时,没有更多的异步空间来执行绑定器事务。

相关代码如下:

kernel/msm-4.4/drivers/android/binder_alloc.c
290     if (is_async &&
291            alloc->free_async_space < size + sizeof(struct binder_buffer)) 
292           binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
293                         "%d: binder_alloc_buf size %zd failed, no async space left\n",
294                           alloc->pid, size);
295            eret = ERR_PTR(-ENOSPC);
296              goto error_unlock;
297    

因此,这不会“最终导致系统不稳定”。它只会影响应用程序本身。

    用户应用程序已被强制关闭,因为 BroadcastQueue 将 scheduleCrash binder 调用发送到 ActivityThread。这个问题的根本原因是应用端没有 binder 缓冲区,因为一些 binder 线程占据了大部分。

可以通过以下步骤触发该错误:

    Process1 向 Process2 发送大数据(例如 980kB),Process2 需要休眠 30 秒,不会释放大的 binder 缓冲区。 Process1 向 Process2 发送广播,包括例如50kB 数据。这将超出 1016kB 的缓冲容量,因为 980kB + 50kB 大于缓冲容量。 BroadcastQueue 会抛出 DeadObjectException,然后将 scheduleCrash 传递给应用端的 ActivityThread。

代码如下:

kernel/msm-4.4/drivers/android/binder_alloc.c
 315     if (best_fit == NULL) 
...
341         pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
342                   alloc->pid, size);
343         pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",
344                       total_alloc_size, allocated_buffers, largest_alloc_size,
345                  total_free_size, free_buffers, largest_free_size);
346            eret = ERR_PTR(-ENOSPC);
347              goto error_unlock;
348    

总之,即使应用程序进程没有死亡,也可以抛出 DeadObjectException。

根本原因很可能是因为应用程序的绑定缓冲区已满,并且不会影响系统。

所以我认为在BroadcastQueue中捕捉到DeadObjectException后,没有必要让应用程序崩溃。

【讨论】:

您好,我也遇到了同样的问题,您有解决办法吗? @chopchop 是的,正如我的回答所示,缓冲区暂时已满,并使您的应用无法使用 Binder IPC,即每当您调用 Binder 调用方法时,它都会向您抛出 DeadObjectException。为了解决您的问题,您可以使用 Binder 传输较少的数据,这样 Binder 缓冲区就不会很容易被填满,这也可以避免这个问题。【参考方案2】:

Rick Ai 对他们自己问题的回答基本上都是正确的,但这里有一个真实的例子:

如果您的应用程序创建并注册了一大堆BroadcastReceiver 实例,它们都在监听相同的操作——可能是由于您的应用程序中的泄漏或错误——那么系统进程中的 ActivityManagerService 将调用android.app.IApplicationThread 方法scheduleRegisteredReceiver 用于每个已注册的实例。请注意,此特定方法的活页夹事务是oneway。因为它是oneway,所以每次调用都会立即返回,并且在每个事务完成之前对绑定驱动程序的调用将非常迅速地发生,因此可以有效地并行运行它们。

假设您的应用中有 100 个接收器,并且正在接收的广播包含 20 KiB 的数据。现在你有 2 MiB 试图通过 binder 驱动程序,它将失败 due to the limit of 1 MiB。

在内核日志中你会看到:

binder: 1282:1298 transaction failed 29201/-28, size 28052-8 line 3072

所以要小心泄露BroadcastReceiver 并小心oneway binder 事务。请注意,显然 AIDL 文件可能不会声明方法 oneway,但如果 AIDL 编译器认为它是可能的,它可能会以这种方式结束。

【讨论】:

以上是关于Android throw DeadObjectException with LOG: Transaction failed on small parcel;远程进程可能已经死了的主要内容,如果未能解决你的问题,请参考以下文章

Phonegap/Cordova build android node_modules/q/q.js throw e;

android throw java.lang.IllegalStateException 上的 Http Url Connection 问题:已连接

Android Studio 查看源码出现throw new RuntimeException("Stub!"); 解决办法

Android throw DeadObjectException with LOG: Transaction failed on small parcel;远程进程可能已经死了

event.js:174 throw er// 未处理的“错误”事件操作不允许

错误记录Visual Studio 中编译 NDK 报错 ( error : cannot use ‘throw‘ with exceptions disabled )