一、引言
因为binder是跨进程通信,难免会遇到服务端出现异常挂死的情况,这个时候需要通知到客户端进行相应的处理,在网络通信中是非常常见的机制,当然,在binder中,Android也为我们写好了相关的框架,但通过binder源码分析,你会发现,服务端挂死能通知到客户端,可是客户端挂死了却不能通知到服务端。有没有别的解决方案呢?分析Android源码,还真发现了一个不错的处理方案—使用匿名binder,简单来说,就是把客户端挂在实名binder下面,让自己成为一个匿名binder的服务端,好像有点绕?没关系,我们挨个分析如下的问题:

1.binder框架是如何实现服务端挂死通知到客户端的?
2.什么是匿名binder?
3.Android源码中匿名binder的巧用

二、DeathRecipient的使用
IBinder类中有一个抽象类DeathRecipient,所有的客户端想要实现服务端对其的死亡通知,都需要实现一个该抽象类的子类:

class DeathRecipient : public virtual RefBase
{public:virtual void binderDied(const wp<IBinder>& who) = 0;
};

当服务端出现挂死时,会回调子类的binderDied函数,客户端可在该函数中实现需要的处理。下面看一下客户端是如何注册到服务端的,IBinder类中提供了linkToDeathunlinkToDeath两个纯虚函数用于实现双端关联:

/*** Register the @a recipient for a notification if this binder* goes away.  If this binder object unexpectedly goes away* (typically because its hosting process has been killed),* then DeathRecipient::binderDied() will be called with a reference* to this.** The @a cookie is optional -- if non-NULL, it should be a* memory address that you own (that is, you know it is unique).** @note You will only receive death notifications for remote binders,* as local binders by definition can't die without you dying as well.* Trying to use this function on a local binder will result in an* INVALID_OPERATION code being returned and nothing happening.** @note This link always holds a weak reference to its recipient.** @note You will only receive a weak reference to the dead* binder.  You should not try to promote this to a strong reference.* (Nor should you need to, as there is nothing useful you can* directly do with it now that it has passed on.)*/
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t        linkToDeath(const sp<DeathRecipient>& recipient,void* cookie = nullptr,uint32_t flags = 0) = 0;/*** Remove a previously registered death notification.* The @a recipient will no longer be called if this object* dies.  The @a cookie is optional.  If non-NULL, you can* supply a NULL @a recipient, and the recipient previously* added with that cookie will be unlinked.** If the binder is dead, this will return DEAD_OBJECT. Deleting* the object will also unlink all death recipients.*/
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t        unlinkToDeath(  const wp<DeathRecipient>& recipient,void* cookie = nullptr,uint32_t flags = 0,wp<DeathRecipient>* outRecipient = nullptr) = 0;

binder源码框架中,BBinder和BpBinder均继承自IBinder,那么是否意味着,Bp和Bn都可以实现linkToDeath和unlinkToDeath呢?很遗憾,只有BnBinder才能这么做,上面的注释已经说明了在Bn端调用linkToDeath将会返回非法操作,我们看下源码Binder.cpp:

status_t BBinder::linkToDeath(const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/,uint32_t /*flags*/)
{return INVALID_OPERATION;
}

再看下BpBinder.cpp:

// NOLINTNEXTLINE(google-default-arguments)
status_t BpBinder::linkToDeath(const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{Obituary ob;ob.recipient = recipient;ob.cookie = cookie;ob.flags = flags;LOG_ALWAYS_FATAL_IF(recipient == nullptr,"linkToDeath(): recipient must be non-NULL");{AutoMutex _l(mLock);if (!mObitsSent) {if (!mObituaries) {mObituaries = new Vector<Obituary>;if (!mObituaries) {return NO_MEMORY;}ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);getWeakRefs()->incWeak(this);IPCThreadState* self = IPCThreadState::self();self->requestDeathNotification(mHandle, this);self->flushCommands();}ssize_t res = mObituaries->add(ob);return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;}}return DEAD_OBJECT;
}

Bp端将通过requestDeathNotification函数将通知注册到底层的binder驱动,驱动里面的分析这里就不去深究了,有兴趣的可翻墙看这篇博客,讲的很详细(Android Binder 分析——死亡通知(DeathRecipient))。
通过上面两个源码的分析解释了为何服务端挂死能通知到客户端,而客户端挂死却无法通知到服务端了,当服务端出现挂死后,底层驱动会层层调用至reportOneDeath@BpBinder:

void BpBinder::reportOneDeath(const Obituary& obit)
{sp<DeathRecipient> recipient = obit.recipient.promote();ALOGV("Reporting death to recipient: %p\n", recipient.get());if (recipient == nullptr) return;recipient->binderDied(this);
}

recipient->binderDied(this)就调用到客户端子类的实现函数binderDied中了。

三、何为匿名binder?
binder分为实名binder和匿名binder,实名binder即注册到Service Manager中的binder,你可以通过service list查询到的服务,匿名binder则不需要注册到SM中,因此你在SM中是找不到匿名binder的。实名binder与匿名binder区别在于是否有如下注册语句:

defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());

MediaPlayerService为例,这里的defaultServiceManager即获取SM:

sp<IServiceManager> defaultServiceManager()
{std::call_once(gSmOnce, []() {sp<AidlServiceManager> sm = nullptr;while (sm == nullptr) {sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));if (sm == nullptr) {ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());sleep(1);}}gDefaultServiceManager = new ServiceManagerShim(sm);});return gDefaultServiceManager;
}

需要注意的是,匿名binder必须基于已经创建好的binder连接,不然没法通过Parcel将相关binder数据写入到驱动中并创建对应的binder node。

四、Android源码中对匿名binder的巧用
以AudioFlinger中的一段源码来看看Android是如何使用匿名binder对服务端进行死亡通知的。
AudioFlinger是音频系统中的执行者,同时也是实名binder服务IAudioFlinger的Bn端,它有一个内部类NotificationClient:

class NotificationClient : public IBinder::DeathRecipient {public:NotificationClient(const sp<AudioFlinger>& audioFlinger,const sp<IAudioFlingerClient>& client,pid_t pid,uid_t uid);virtual             ~NotificationClient();sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }pid_t getPid() const { return mPid; }uid_t getUid() const { return mUid; }// IBinder::DeathRecipientvirtual     void        binderDied(const wp<IBinder>& who);private:DISALLOW_COPY_AND_ASSIGN(NotificationClient);const sp<AudioFlinger>  mAudioFlinger;const pid_t             mPid;const uid_t             mUid;const sp<IAudioFlingerClient> mAudioFlingerClient;
};

从前面的分析已知,继承自IBinder::DeathRecipient的类一般都是Bp端去实现的,可为什么Bn端会有这个一个类呢?其实,这个类是用于注册到匿名binder服务IAudioFlingerClient中的。我们先看一下起IAudioFlingerClient的地方,在AudioSystem.cpp中,存在如下函数:

// establish binder interface to AudioFlinger service
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
{sp<IAudioFlinger> af;sp<AudioFlingerClient> afc;bool reportNoError = false;{Mutex::Autolock _l(gLock);if (gAudioFlinger == 0) {sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder;do {binder = sm->getService(String16("media.audio_flinger"));if (binder != 0)break;ALOGW("AudioFlinger not published, waiting...");usleep(500000); // 0.5 s} while (true);if (gAudioFlingerClient == NULL) {/* 创建匿名binder */gAudioFlingerClient = new AudioFlingerClient();} else {reportNoError = true;}/* 注册AudioFlinger的死亡通知 */binder->linkToDeath(gAudioFlingerClient);gAudioFlinger = interface_cast<IAudioFlinger>(binder);LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);afc = gAudioFlingerClient;// Make sure callbacks can be received by gAudioFlingerClientProcessState::self()->startThreadPool();}af = gAudioFlinger;}if (afc != 0) {int64_t token = IPCThreadState::self()->clearCallingIdentity();/* 调用AudioFlinger进行IAudioFlingerClient的死亡注册 */af->registerClient(afc);IPCThreadState::self()->restoreCallingIdentity(token);}if (reportNoError) reportError(NO_ERROR);return af;
}

首先是通过SM找到实名binder服务IAudioFlinger,然后实例化匿名binder服务AudioFlingerClient,代码中还将AudioFlingerClient的通知注册到IAudioFlinger中进行监听,以实现双向通知,我们重点跟进af->registerClient(afc),这里会调用到IAudioFlinger的接口,看下Bp端的传输:

virtual void registerClient(const sp<IAudioFlingerClient>& client)
{Parcel data, reply;data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());data.writeStrongBinder(IInterface::asBinder(client));remote()->transact(REGISTER_CLIENT, data, &reply);
}

Bn端:

case REGISTER_CLIENT: {CHECK_INTERFACE(IAudioFlinger, data, reply);sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient>(data.readStrongBinder());registerClient(client);return NO_ERROR;
} break;

拿到client的强引用,传入到registerClient中:

void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
{Mutex::Autolock _l(mLock);if (client == 0) {return;}pid_t pid = IPCThreadState::self()->getCallingPid();const uid_t uid = IPCThreadState::self()->getCallingUid();{Mutex::Autolock _cl(mClientLock);if (mNotificationClients.indexOfKey(pid) < 0) {sp<NotificationClient> notificationClient = new NotificationClient(this,client,pid,uid);ALOGV("registerClient() client %p, pid %d, uid %u",notificationClient.get(), pid, uid);mNotificationClients.add(pid, notificationClient);sp<IBinder> binder = IInterface::asBinder(client);binder->linkToDeath(notificationClient);}}...
}

实际上这段代码的意思就是将IAudioFlingerClient的强引用转成binder实体,然后将AudioFlinger中的实例化的内部类NotificationClient注册到匿名binder中,那么当匿名binder出现服务挂死时,则会回调到该内部类的binderDied函数,达到死亡通知的目的以实现需要处理的业务:

void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who __unused)
{sp<NotificationClient> keep(this);mAudioFlinger->removeNotificationClient(mPid);
}

使用匿名binder实现client向server端的死亡通知相关推荐

  1. OpenWRT(基于LEDE17.01.4)Open***的Client与Server端内网互通

    经过长达近一个星期的折腾,Open×××客户端终于可以和Server内网的客户端进行通信,其中防火墙占用了大部分时间,主要还是不太熟悉Iptables的各种设置方式以及规则(对CentOS7的Fire ...

  2. 完整mes代码(含客户端和server端_200行代码实现基于paxos的kv存储

    本文链接: https://blog.openacid.com/algo/paxoskv/ 前言 写完 paxos的直观解释 之后, 网友都说疗效甚好, 但是也会对这篇教程中一些环节提出疑问(有疑问说 ...

  3. TCP第三次握手失败的处理(Server端超时重传机制、RST包响应、SYN攻击)

    面试题: 在 TCP 建立连接的三次握手连接阶段,如果客户端发送的第三个ACK包丢了,那么客户端和服务端分别进行什么处理呢? 相信了解 tcp 协议的人,三次握手的过程肯定很了解了.第三次的 ack ...

  4. Binder对象死亡通知机制

    本文参考<Android系统源代码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder -- ...

  5. Binder系列11 死亡通知机制

    死亡通知机制概述 如果在 binder 通信已经建立的情况下,出现 binder 服务端的进程意外挂掉,这个挂掉的原因可能是因为进程本身内部发生的错误,也有可能是其它情况导致进程被系统强制结束,总之这 ...

  6. Oracle监听器Server端与Client端配置实例

    Listener.ora.tnsnames.ora这两个文件常常因为格式问题而不好用,我平时都是配置好了留个备份,以后都是拷贝过去改改就好了!嘿嘿~~~ 因为平时使用linux的时候较多,所以有时还会 ...

  7. 没写client,想先测试server端怎么办?

    没写client,想先测试server端怎么办? 办法: 1.先打开终端./server,运行起来server 2.再开一个终端, 输入nc 127.0.0.1 8888 回车(这里port号要和se ...

  8. AIdl server端监听client是否掉线

    我们知道当绑定service时 客户端可以收到服务端异常中断的消息 即onServiceConnected,那么服务器端是否可以监听到client端掉线的消息呢?下面就写个简单的demo用于监听cli ...

  9. BluetoothLE-Multi-Library 一个能够连接多台蓝牙设备的库,它可以作为client端,也可以为server端。支持主机/从机,外围设备连接。...

    github地址:https://github.com/qindachang/BluetoothLE-Multi-Library BluetoothLE-Multi-Library 一个能够连接多台蓝 ...

最新文章

  1. 2021年大数据常用语言Scala(十二):基础语法学习 方法调用方式
  2. AI初创公司都去哪了?2019年科技公司“五巨头”收购盘点
  3. Facebook的GBDT+LR模型python代码实现
  4. idea远程调试Java应用程序
  5. bat脚本实现局域网所有存活IP的精准扫描
  6. 「leetcode」654.最大二叉树(详解)
  7. 2017年最新(4月20日)手机号码归属地数据库分享
  8. CashFiesta注册网赚
  9. Threejs中文文档
  10. SCDPM2019服务器恢复数据
  11. python 可以用来炒股吗_请问利用python进行量化交易炒股投资可行吗?
  12. OGG同步字符集从US7ASCII to ZHS16GBK故障解决
  13. PDF文档如何在线进行解密操作
  14. WPF的Prism框架简介
  15. Oracle数据库之日期函数
  16. JS原型和原型链是什么?
  17. 实践是检验真理的唯一标准
  18. Fluent常见问题
  19. fastadmin框架前台常用语句
  20. iOS10网络权限数据

热门文章

  1. [Swift]DJSet
  2. alpha beta 剪枝算法
  3. 嵌入式工程师的真情吐露:孤独,却有乐趣!
  4. python保存图片的三种方法
  5. 【Vuejs】971- Vue SSR 性能优化实践
  6. WhatsApp私域流量营销,蜂巢SCRM助你触达20亿用户
  7. 计算机系统装机教程,电脑装机教程,详细教您如何给电脑装机
  8. guestbook(hackme web部分writeup)
  9. 微信公众号文章怎么添加文件下载功能?如Word,Excel,PPT,PDF等
  10. 计算机的中央处理器英文简写是,电脑的中央处理器英文简写是什么?