前言
前段时间调查一个死机重启问题,里面涉及到 Binder 调用的一些比较细节的地方,因此将 binder 调用的整个过程大致缕了一遍,并将所得整理下来。
问题 log
10-17 12:13:02.006 2096 5712 E JavaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
10-17 12:13:02.006 2096 5712 E JavaBinder: java.lang.RuntimeException: Could not copy bitmap to parcel blob.
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.graphics.Bitmap.nativeWriteToParcel(Native Method)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.graphics.Bitmap.writeToParcel(Bitmap.java:1553)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.widget.RemoteViews$BitmapCache.writeBitmapsToParcel(RemoteViews.java:984)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.widget.RemoteViews.writeToParcel(RemoteViews.java:2854)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.app.Notification.writeToParcel(Notification.java:1687)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.service.notification.StatusBarNotification.writeToParcel(StatusBarNotification.java:124)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.service.notification.IStatusBarNotificationHolder$Stub.onTransact(IStatusBarNotificationHolder.java:53)
10-17 12:13:02.006 2096 5712 E JavaBinder: at android.os.Binder.execTransact(Binder.java:453)
10-17 12:13:02.007 3362 15429 W Binder : Caught a RuntimeException from the binder stub implementation.
10-17 12:13:02.007 3362 15429 W Binder : java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.Notification android.service.notification.StatusBarNotification.getNotification()' on a null object reference
10-17 12:13:02.007 3362 15429 W Binder : at android.service.notification.NotificationListenerService$INotificationListenerWrapper.onNotificationPosted(NotificationListenerService.java:692)
10-17 12:13:02.007 3362 15429 W Binder : at android.service.notification.INotificationListener$Stub.onTransact(INotificationListener.java:71)
10-17 12:13:02.007 3362 15429 W Binder : at android.os.Binder.execTransact(Binder.java:453)
下面我们来看一下这段 log 究竟是如何产生的。
一、这样的调用栈是如何产生的?
1. 三个进程
从未贴出的 log 可以看出,这段 log 是在 app 更新或者添加 notification 的过程中产生的,所以我们先看一下更新或添加 notification 的调用流程:
- 不同的颜色代表不同的进程,红色运行在 app 进程,黑色在 system_server 进程,蓝色在 systemui (也可能是其他注册过的进程)进程
- 其中涉及到三次跨进程调用,依次为:app 进程调用 system_server 进程的
enqueueNotificationWithTag
方法、system_server 进程调用 systemui 进程的onNotificationPosted
方法、systemui 进程调用 system_server 进程的get
方法
第一次跨进程调用很常见,我们就不做讨论了,主要看一下第二、三次跨进程调用是如何实现的。首先,我们需要了解一下 NotificationListener 的注册流程(以 systemui 为例),如下图所示:
- 其中红色部分运行在 systemui 进程,蓝色部分运行在 system_server 进程
- StatusBar 的成员 mNotificationListener 是一个 NotificationListenerService 实例,其会调用 registerAsSystemService 方法来进行注册
下面我们来看一下 registerAsSystemService 的实现:
1.1 NotificationListenerService.registerAsSystemService
NotificationListenerService.java