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

一、引言
因为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::DeathRecipient
            virtual     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 gAudioFlingerClient
            ProcessState::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);
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值