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

2024-01-09

一、引言
因为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类中提供了linkToDeath和unlinkToDeath两个纯虚函数用于实现双端关联:

/**
 * 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;
};

首先是通过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);
}

————————————————
版权声明:本文为CSDN博主「丽萨的托马斯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/achina2011jy/article/details/127070466

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 服务器中E5和I9的区别是什么,如何选择合适的配置

    随着科技的进步 服务器处理器的性能在不断攀升 其中 Intel的E5和I9系列处理器在业界具有广泛的影响力 而当我们在选择服务器的时候会有各种各样的配置让我们眼花缭乱不知道该怎么去选择 下面我跟大家分享一下E5跟I9有什么区别 方便我们在选
  • 网工内推 | 上市公司同程、科达,五险一金,年终奖,最高12k*15薪

    01 同程旅行 招聘岗位 网络工程师 职责描述 1 负责职场 门店网络规划 建设 维护 2 负责网络安全及访问控制 上网行为管理和VPN设备的日常运维 3 负责内部相关网络自动化和系统化建设 4 优化与提升网络运行质量 制定应急预案 人员培
  • 【安全-SSH】SSH安全设置

    今天发现自己的公有云服务器被攻击了 在这里插入图片描述 https img blog csdnimg cn direct cafdca04646f4b8b838400ec79ac282f png 然后查看了登录日志 如上图 ls sh va
  • 将管道/连接作为上下文参数传递给多处理 Pool.apply_async()

    我想使用管道与池中的流程实例进行通信 但出现错误 让 p 成为 Pool 的一个实例 master pipe worker pipe Pipe self p apply async worker task handler info cont
  • 如何使命名管道在 C++ 和 .NET 之间工作?

    我在让命名管道在 c 和 NET 之间工作方面经历了一段非常艰难的时期 我在创建在 2 个 C 应用程序或 2 个 NET 应用程序之间运行的命名管道时没有遇到任何问题 我对这种通信没有问题 我在某些项目中使用这种情况 C 方面 LPTST
  • 有没有一种简单的方法可以清除C中的管道

    我有一个所有子进程都使用的管道 但在子进程使用该管道与父进程通信之前 我需要清除它 以便父进程正确读取它 C 中有一个简单的函数可以做到这一点吗 清除 管道的方法是从管道中读取数据 直到缓冲区为空 这对你没有帮助 我猜测您真正的问题是父级可
  • MPI_Send() 和 MPI_Ssend() 之间的区别?

    I know MPI Send 是一个阻塞调用 它会等待直到可以安全地修改应用程序缓冲区以供重用 为了使发送调用同步 应该与接收者握手 我们需要使用MPI Ssend 我想知道两者之间的区别 假设我需要在进程之间发送固定数量的字节 哪一个应
  • 使用 setjmp / longjmp 的通信协议和本地环回

    我使用共享内存和共享互斥体编写了一些相对简单的通信协议 但后来我想扩展支持以在使用不同运行时的两个 dll 之间进行通信 很明显 如果你有一些std vector lt int64 gt 和两个 dll 一个 vs2010 一个 vs201
  • 尝试使用 POSIX 消息队列创建消息队列时权限被拒绝

    我正在使用以下代码片段创建一个消息队列Linux 编程接口 http man7 org tlpi if mq open my message queue O CREAT O RDWR NULL 1 perror mq creation fa
  • msgrcv - SA_RESTART 标志不起作用

    我的使用 IPC 队列在线程之间通信的代码有问题 我需要安全地处理 SIGINT 让程序在关闭之前出现 SIGINT 时完成所有活动线程 不过 我在寻找解决方案方面遇到了严重的问题 因为即使使用带有标志 SA RESTART 的 SIGIN
  • Windows服务之间如何通信

    我有 2 个使用 C 创建的 Windows 服务 我希望其中一个服务调用第二个 Windows 服务中的函数 我该怎么做呢 EDIT 问题是我必须运行该应用程序 我不需要它们 相反服务进程也很好 但我need这2个应用程序进行通信 这2个
  • 将文本插入另一个应用程序的文本框中

    如何使用 C 或 C 将文本插入另一个应用程序的文本框中 我很久以前就这样做过 似乎还记得一些有关使用应用程序 HWND 的事情 但由于应用程序的每个实例都发生了变化 我觉得我不记得完整的故事了 我是否可以以某种方式获取正在运行的应用程序的
  • 远程过程调用认证

    我正在使用远程过程调用 RPC 在本地计算机上通信数据 我的要求是使用 RPC 在两个处理之间通信数据 但服务器应该通过某种方式对客户端进行身份验证 我遇到了 RpcBindingSetAuthInfo 它设置身份验证和授权信息 第四个参数
  • 使用AppService的连接持续时间有限制吗?

    我有一个 UWP 应用程序托管应用服务 https learn microsoft com en us windows uwp launch resume how to create and consume an app service在同
  • 从一个 Nodejs 应用程序调用另一个 Nodejs 应用程序中的 API 的方法

    我们的应用程序将有一个网站和一个移动应用程序 两者都与同一个 API 后端进行通信 我有一个仅提供 API 服务的 Nodejs 应用程序 还有一个为网站提供 html 页面服务的 Nodejs 应用程序 我正在为这两个应用程序使用 Exp
  • 首选的跨平台 IPC Perl 模块是什么?

    我想创建一个简单的 IO 对象 它代表一个向另一个程序打开的管道 我可以在应用程序运行时定期写入另一个程序的 STDIN 我希望它是防弹的 因为它可以捕获所有错误 并且是跨平台的 我能找到的最佳选择是 open sub io read lo
  • 无法在 Linux 中阻止从命名管道 (FIFO) 读取

    很奇怪 我似乎无法完成这项工作 这是我的架构 我有一个命名管道 它将在永远运行 root读取器进程和多个应用程序编写器进程 读者进程必须是blocking当作家们在nonblocking 因此 这就是我在阅读器进程中所做的 该进程将运行ro
  • 登录方式使用GINA定制

    我知道在 GINA 中找到大师并不容易 但我的问题最接近进程间通信 IPC 我用非托管 c 编写了我的自定义 GINA 我在其中包含了一个方法来检查用户尝试的指纹的有效性为了登录 该函数将调用正在运行的系统Windows服务中用C 编写的一
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可

随机推荐

  • 毕设英文摘要怎么降重

    大家好 今天来聊聊毕设英文摘要怎么降重 希望能给大家提供一点参考 以下是针对论文重复率高的情况 提供一些修改建议和技巧 可以借助此类工具 毕设英文摘要降重策略 在毕业设计中 英文摘要的撰写是一个重要的环节 由于英文摘要的表述方式和语法与中文
  • Appium —— 初识移动APP自动化测试框架Appium

    说到移动APP自动化测试 代表性的测试框架非Appium莫属 从今天开始我们将从 APP结构解析 Appium框架学习 安卓 iOS自动化测试实战 自动遍历回归测试 自动化测试平台及持续集成 多个维度一起由浅入深的学废Appium 今天我们
  • 研发实验室装修

    研发实验室在企业 高校和研究机构中的地位日益凸显 一个优质的研发实验室装修方案不仅能提升实验室的整体形象 还能提高科研人员的工作效率 激发创新精神 本文将从设计理念 空间规划 材料选择及环保安全等方面 SICOLAB喜格 探讨如何打造高效创
  • Python selenium实现断言3种方法解析

    1 if else 判断进行断言 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  • INT201 形式语言与自动机笔记(下)

    L6 Context Free Languages 上下文无关语言 Context Free Grammar CFG 是一组用于生成字符串模式的递归规则 上下文无关的语法可以描述所有的常规语言 但它们不能描述所有可能的语言 e g 遵循这些
  • 研发实验室建设

    研发实验室是科技创新的核心场所 对于企业的技术研发和产品创新具有重要意义 为了提高研发实验室的效率和质量 建设一个符合要求的实验室是至关重要的 SICOLAB喜格 将介绍研发实验室建设的关键要素和注意事项 一 实验室规划与布局 1 空间规划
  • 软件测试开发/全日制/测试管理丨持续集成、持续交付、持续部署

    持续集成 持续交付 持续部署 是构建现代软件开发流程的三大支柱 这三个概念代表了现代软件开发中重要的实践 它们旨在通过自动化和频繁的发布流程来提高开发团队的效率和软件交付的可靠性 持续集成 CI 持续集成是指开发人员将代码频繁地集成到共享的
  • 智慧农业之新导物联RFID果园苗圃系统-新导物联

    新导物联RFID果园苗圃系统是一种利用射频识别技术 RFID 来管理果园和苗圃的系统 该系统通过在植物或容器上附加RFID标签 将每个植物或容器与唯一的识别代码相关联 使用RFID果园苗圃系统的主要目的是实现对植物和容器的自动化追踪和管理
  • 【数据压缩】基于哈夫曼编码的数据压缩算法matlab,输出哈夫曼编码树

    目录 1 算法仿真效果 2 MATLAB源码 3 算法概述 3 1 哈夫曼树构建 3 2 哈夫曼编码生成
  • 职场生存能力最强的5类人:如何成为职场中的佼佼者?

    职场生存能力最强的5类人 如何成为职场中的佼佼者 在职场中 生存能力强的人往往更容易获得成功 他们具备各种能力和特质 使他们能够在激烈的竞争中脱颖而出 本文将介绍职场生存能力最强的5类人 看看你是否具备这些特点 一 适应能力强的人 在职场中
  • Jmeter扩展函数?年薪50W+的测试大佬教你怎么玩

    很多同学 都问我 老师 我的 jmeter 里面 怎么没有 MD5 函数 base64 函数也没有 我是不是用了假的 jmeter 哈哈哈 不是的 jmeter 的函数 有自带函数和扩展函数两大块 自带函数 就是 jmeter 官方自带的
  • 计算机丢失mfc140.dll怎么办?解决mfc140.dll缺失的3种方法分享

    计算机丢失mfc140 dll怎么办 在使用微软办公软件的时候 可能会弹出一个错误提示框说 找不到mfc140 dll 无法继续执行代码 为了不影响工作效率 我们可能需要亲自动手尝试修复这一问题 以下是一些mfc140 dll缺失的3种方法
  • INT201 形式语言与自动机笔记(上)

    Lec1 Overview Alphabet and String 字母表与字符串 Alphabet 字母表 a finite nonempty set of symbols String word a finite sequence of
  • 如何处理不稳定的自动化测试?

    abluecolor 在解决这个问题之前 请停止编写更多测试 因为这将花费你较高的测试维护成本 你需要尽快行动起来对不稳定的原因进行深入研究 找到不稳定的根因 并且尝试在流程 环境和代码方面做一些优化工作解决它 MasterKindew 如
  • mybatisPlus 将List<String>字段转成json字符串,使用JacksonTypeHandler以及自定义类型处理器实现

    文章目录 场景 使用JacksonTypeHandler实现类型转换 自定义StringListTypeHandler处理器实现 场景 项目中经常需要将List转成json存储到配置文件中 mybatisPlus默认实现了JacksonTy
  • Stable Diffusion运行时自动重启的小程序

    Stable Diffusion在运行过程中 常常因为各种原因导致服务挂掉 所以希望能定时检查程序是否存活 如果dead则重启一下 思路 stable diffusion运行时需要使用GPU 如果GPU没有在用 并且GPU对应的进程也找不到
  • 多进程之Binder的意外死亡及权限校验

    Android多进程系列 Android 多进程通讯之几个基本问题 Android多进程之Binder的使用 Android多进程之手动编写Binder类 Android多进程之Binder解绑监听的问题 Android 多进程之Messe
  • 毕设开题分享 单片机智能教室系统(智能照明+人数统计)

    1 简介 Hi 大家好 今天向大家介绍一个学长做的单片机项目 单片机智能教室系统 智能照明 人数统计 大家可用于 课程设计 或 毕业设计 项目分享 https gitee com feifei1122 simulation project
  • Ubuntu下git提示:终止提交因为提交说明为空。

    这么简单的问题居然搜索了一会儿 现在的网络环境越来越差了 解决方法 gedit git config global core editor gedit s the paramater s means set the gedit mode t
  • 使用匿名binder实现client向server端的死亡通知

    一 引言 因为binder是跨进程通信 难免会遇到服务端出现异常挂死的情况 这个时候需要通知到客户端进行相应的处理 在网络通信中是非常常见的机制 当然 在binder中 Android也为我们写好了相关的框架 但通过binder源码分析 你