android binder(二)应用层编程实例

news2025/6/4 22:12:44

一、binder驱动浅析

从上图看出,binder的通讯主要涉及三个步骤。

  • 在 Binder Server 端定义好服务,然后向 ServiceManager 注册服务
  • 在 Binder Client 中向 ServiceManager 获取到服务
  • 发起远程调用,调用 Binder Server 中定义好的服务

整个流程都是建立在 Binder 驱动提供的跨进程调用能力之上,bingde驱动的实现比较复杂,现阶段我们先以黑盒的方式去了解它:

Binder 是一个 Linux 字符驱动,对外提供了以下函数供应用程序使用:

  • open(),用于打开 binder 驱动,返回 Binder 驱动的文件描述符
  • mmap(),用于在内核中申请一块内存,并完成应用层与内核层的虚拟地址映射
  • ioctl,在应用层调用 ioctl 向内核层发送数据或者读取内核层发送到应用层的数据:
ioctl(文件描述符,ioctl命令,数据)

文件描述符是在调用 open 时的返回值,ioctl 命令和第三个参数"数据"的类型是相关联的,具体如下:

ioctl命令数据类型函数动作
BINDER_WRITE_READstruct binder_write_read应用层向内核层收发数据
BINDER_SET_MAX_THREADSsize_t设置最大线程数
BINDER_SET_CONTEXT_MGRint or flat_binder_object设置当前进程为ServiceManager
BINDER_THREAD_EXITint删除 binder 线程
BINDER_VERSIONstruct binder_version获取 binder 协议版本

二、安卓提供的封装 

servicemanager - OpenGrok cross reference for /frameworks/native/cmds/servicemanager/

frameworks/native/cmds/servicemanager 目录下的 binder.cbctest.c 针对应用编写的需求,对open mmap ioctl 等基本操作做了封装,提供了以下几个函数:

  • binder_open:用于初始化 binder 驱动
  • binder_become_context_manager:设置当前进程为 ServiceManager
  • svcmgr_lookup:用于向 ServiceManager 查找服务
  • svcmgr_publish:用于向 ServiceManager 注册服务
  • binder_call:用于发起远程过程调用
  • binder_loop:进入循环,在循环中,获取和解析收到的 binder 数据

三、 ServiceManager 源码分析

  • 打开 Binder 驱动
  • 告知驱动自身为 service manager
  • 循环处理
    • 从驱动读取数据
    • 解析数据并调用
      • 处理service端的注册服务请求:其实就是在一个链表记录服务名
      • 处理client获取服务请求:
        • 在链表查询服务
        • 返回 server 进程的 handle
service_manager.c
    binder_open //打开 Binder 驱动
    binder_become_context_manager //告知驱动自身为 service manager
    binder_loop
        binder_parse //从驱动读取数据并解析
        svcmgr_handler//根据不同的命令,进入不同的处理流程
            do_add_service //添加服务
            do_find_service//获取服务

四、编写自定义service代码

参考代码:bctest.c - OpenGrok cross reference for /frameworks/native/cmds/servicemanager/bctest.c

1、主流程:

  • 打开binder驱动
  • 注册服务
  • 进入loop,等待client请求服务

2、消息处理流程

当 client 发起远程调用时,server 端会收到数据,并将这些数据传递给服务回调函数,这个回调函数需要我们自己来定义:也就是binder_loop(bs, test_server_handler)传入的test_server_handler函数。

3、服务处理流程:hello_service_handler

我们在注册服务的时候,传入了一个func handle, hello_service_handler。当收到client请求服务的时候,会进入这个函数进行处理。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>

#include "binder.h"

#define LOG_TAG "BinderServer"
#include <log/log.h>

#define HELLO_SVR_CMD_SAYHELLO     1
#define HELLO_SVR_CMD_SAYHELLO_TO  2


void sayhello(void)
{
	static int cnt = 0;
	//fprintf(stderr, "say hello : %d\n", ++cnt);
    ALOGW("say hello : %d\n", ++cnt);
}


int sayhello_to(char *name)
{
	static int cnt = 0;
	//fprintf(stderr, "say hello to %s : %d\n", name, ++cnt);
    ALOGW("say hello to %s : %d\n", name, ++cnt);
	return cnt;
}



int hello_service_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct binder_transaction_data *txn = &txn_secctx->transaction_data;

	/* 根据txn->code知道要调用哪一个函数
	 * 如果需要参数, 可以从msg取出
	 * 如果要返回结果, 可以把结果放入reply
	 */

	/* sayhello
	 * sayhello_to
	 */
	
    uint16_t *s;
	char name[512];
    size_t len;
    //uint32_t handle;
    uint32_t strict_policy;
	int i;


    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg);

    switch(txn->code) {
    case HELLO_SVR_CMD_SAYHELLO:
		sayhello();
		bio_put_uint32(reply, 0); /* no exception */
        return 0;

    case HELLO_SVR_CMD_SAYHELLO_TO:
		/* 从msg里取出字符串 */
		s = bio_get_string16(msg, &len);  //"IHelloService"
		s = bio_get_string16(msg, &len);  // name
		if (s == NULL) {
			return -1;
		}
		for (i = 0; i < len; i++)
			name[i] = s[i];
		name[i] = '\0';

		/* 处理 */
		i = sayhello_to(name);

		/* 把结果放入reply */
		bio_put_uint32(reply, 0); /* no exception */
		bio_put_uint32(reply, i);
		
        break;

    default:
        fprintf(stderr, "unknown code %d\n", txn->code);
        return -1;
    }

    return 0;
}

int test_server_handler(struct binder_state *bs,
                struct binder_transaction_data_secctx *txn_secctx,
                struct binder_io *msg,
                struct binder_io *reply)
{
    struct binder_transaction_data *txn = &txn_secctx->transaction_data;
	
    int (*handler)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply);

	handler = (int (*)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply))txn->target.ptr;
	
	return handler(bs, txn, msg, reply);
}


int main(int argc, char **argv)
{
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;
	int ret;

    
    //打开驱动
    bs = binder_open("/dev/binder", 128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }

	//添加服务
	ret = svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);
    if (ret) {
        fprintf(stderr, "failed to publish hello service\n");
        return -1;
    }
    
    binder_loop(bs, test_server_handler);

    return 0;
}

五、编写自定义client 代码

编写 Client 程序的主要流程如下:

  • open  binder 驱动
  • 向service manager查询服务,获取到服务的句柄 handle
  • 通过 handle 调用远程调用函数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include <stdbool.h>
#include <string.h>
#include "binder.h"

#define HELLO_SVR_CMD_SAYHELLO     1
#define HELLO_SVR_CMD_SAYHELLO_TO  2

int g_handle = 0;
struct binder_state *g_bs;

void sayhello(void)
{
    unsigned iodata[512/4];
    struct binder_io msg, reply;

	/* 构造binder_io */
    bio_init(&msg, iodata, sizeof(iodata), 4);
   

	/* 放入参数 */
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, "IHelloService");

	/* 调用binder_call */
    if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
        return ;
	
	/* 从reply中解析出返回值 */
    binder_done(g_bs, &msg, &reply);
	
}

int main(int argc, char **argv)
{
    int fd;
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
	int ret;

    bs = binder_open("/dev/binder", 128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }

    g_bs = bs;

	/* get service */
	g_handle = svcmgr_lookup(bs, svcmgr, "hello");
	if (!g_handle) {
        return -1;
	} 

    //调用服务
    sayhello();

}

六、梳理(待整理)

ref:

https://juejin.cn/post/7214342319347712057

Android系统--Binder系统具体框架分析(一) - lkq1220 - 博客园

https://juejin.cn/post/7210245482861264955

第5课第1节_Binder系统_C程序示例_框架分析_哔哩哔哩_bilibili

XRefAndroid - Support AOSP 15.0 AndroidXRef & OpenHarmony 5.0

【Android ServiceManager】从源码入手,剖析ServiceManager是如何处理客户端的请求的?_bnservicemanager 源码实现-CSDN博客

https://cs.android.com/android/platform/superproject/main

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397283.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】17. 深度生成模型:DCGAN与Wasserstein GAN公式深度推导

深度生成模型:DCGAN与Wasserstein GAN公式深度推导 深度卷积生成对抗网络 DCGAN 在原始 GAN 框架中&#xff0c;生成器和判别器通常使用全连接层构建&#xff0c;这限制了模型处理图像的能力。为此&#xff0c;Radford 等人在 2016 年提出了 DCGAN&#xff08;Deep Convoluti…

设计模式——命令设计模式(行为型)

摘要 本文介绍了命令设计模式&#xff0c;这是一种行为型设计模式&#xff0c;用于将请求封装为对象&#xff0c;实现请求的解耦和灵活控制。它包含命令接口、具体命令、接收者、调用者和客户端等角色&#xff0c;优点是解耦请求发送者与接收者&#xff0c;支持命令的排队、记…

03 APP 自动化-定位元素工具元素定位

文章目录 一、Appium常用元素定位工具1、U IAutomator View Android SDK 自带的定位工具2、Appium Desktop Inspector3、Weditor安装&#xff1a;Weditor工具的使用 4、uiautodev通过定位工具获取app页面元素有哪些属性 二、app 元素定位方法 一、Appium常用元素定位工具 1、U…

PABD 2025:大数据与智慧城市管理的融合之道

会议简介 2025年公共管理与大数据国际会议&#xff08;ICPMBD 2025&#xff09;确实在海口举办。本次会议将围绕公共管理与大数据的深度融合、数据分析在公共管理中的应用、大数据驱动的政策制定与优化等议题展开深入研讨。参会者将有机会聆听前沿学术报告&#xff0c;分享研究…

Golang持续集成与自动化测试和部署

概述 Golang是一门性能优异的静态类型语言&#xff0c;但因其奇快的编译速度&#xff0c;结合DevOps, 使得它也非常适合快速开发和迭代。 本文讲述如何使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术&#xff1a; dep&#xff1a; 进行包的依赖管理gin…

mysql离线安装教程

1.下载地址: https://downloads.mysql.com/archives/community/ 2.上传安装包到系统目录,并解压 tar -xvf mysql-8.0.34-1.el7.x86_64.rpm-bundle.tar3.检查系统中是否存在mariadb的rpm包 rpm -qa|grep mariadb存在则删除 rpm -e xxx4.解压完后执行如下命令安装 sudo rpm -iv…

基于FPGA的VGA显示文字和动态数字基础例程,进而动态显示数据,类似温湿度等

基于FPGA的VGA显示文字和数字 前言一、VGA显示参数二、字模生成三、代码分析1.vga_char顶层2.vga_ctrl驱动文件3.vga_pic数据准备文件 总结 前言 结合正点原子以及野火的基础例程&#xff0c;理解了VGA本身基本协议&#xff0c;VGA本身显示像素为640*480&#xff0c;因此注意生…

力扣刷题Day 68:搜索插入位置(35)

1.题目描述 2.思路 方法1&#xff1a;回溯的二分查找。 方法2&#xff1a;看到了一个佬很简洁的写法&#xff0c;代码贴在下面了。 3.代码&#xff08;Python3&#xff09; 方法1&#xff1a; class Solution:def searchInsert(self, nums: List[int], target: int) ->…

使用Python绘制节日祝福——以端午节和儿童节为例

端午节 端午节总算是回家了&#xff0c;感觉时间过得真快&#xff0c;马上就毕业了&#xff0c;用Python弄了一个端午节元素的界面&#xff0c;虽然有点不像&#xff0c;祝大家端午安康。端午节粽子&#xff08;python&#xff09;_python画粽子-CSDN博客https://blog.csdn.net…

C#项目07-二维数组的随机创建

实现需求 创建二维数组&#xff0c;数组的列和宽为随机&#xff0c;数组内的数也是随机 知识点 1、Random类 Public Random rd new Random(); int Num_Int rd.Next(1, 100);2、数组上下限。 //定义数组 int[] G_Array new int[1,2,3,4];//一维数组 int[,] G_Array_T …

光伏功率预测 | LSTM多变量单步光伏功率预测(Matlab完整源码和数据)

光伏功率预测 | MATLAB实现基于LSTM长短期记忆神经网络的光伏功率预测 目录 光伏功率预测 | MATLAB实现基于LSTM长短期记忆神经网络的光伏功率预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 光伏功率预测 | LSTM多变量单步光伏功率预测&#xff08;Matlab完整源码和…

一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录——3. 服务器软件更新,以及常用软件安装

前言 前面&#xff0c;我们已经 安装好了 Ubuntu 服务器系统&#xff0c;并且 配置好了 ssh 免密登录服务器 &#xff0c;现在&#xff0c;我们要来进一步的设置服务器。 那么&#xff0c;本文&#xff0c;就是进行服务器的系统更新&#xff0c;以及常用软件的安装 调整 Ubu…

PyTorch 入门学习笔记

一、简介 PyTorch 是由 Meta&#xff08;原 Facebook&#xff09; 开源的深度学习框架。其前身 Torch 是一个基于 LuaJIT 的科学计算框架&#xff0c;核心功能是提供高效的张量&#xff08;Tensor&#xff09;操作和神经网络支持。由于 Lua 语言的生态限制&#xff0c;Torch 逐…

pycharm生成图片

文章目录 图片例子生成图片并储存&#xff0c;设置中文字体支持两条线绘制散点图和直方图绘制条形图&#xff08;bar&#xff09;绘制条形图&#xff08;横着的&#xff09;&#xff08;plt.barh&#xff09;分组的条形图 颜色和线条风格1. **颜色字符 (color)**其他颜色指定方…

Android 云手机横屏模式下真机键盘遮挡输入框问题处理

一、背景 打开横屏应用,点击云机EditText输入框,输入框被键盘遮挡,如下图&#xff1a; 未打开键盘状态: 点击第二个输入框,键盘遮挡了输入框&#xff1a; 二、解决方案&#xff08;推荐第三中方案,博主采用的也是第三种方案&#xff09; 博主这里整理了三种方案&#xff1a;…

Axure设计案例——科技感对比柱状图

想让数据对比展示摆脱平淡无奇&#xff0c;瞬间抓住观众的眼球吗&#xff1f;那就来看看这个Axure设计的科技感对比柱状图案例&#xff01;科技感设计风格运用独特元素打破传统对比柱状图的常规&#xff0c;营造出一种极具冲击力的视觉氛围。每一组柱状体都仿佛是科技战场上的士…

FPGA仿真中阻塞赋值(=)和非阻塞赋值(<=)区别

FPGA仿真中阻塞赋值和非阻塞赋值的区别 单独仿真小模块对但将小模块加入整个工程仿真不对就有可能是没有注意到仿真中阻塞赋值和非阻塞赋值的区别 目录 前言 一、简介 二、设计实例 三、仿真实例 1、仿真用非阻塞赋值 2、仿真用阻塞赋值 总结 前言 网上很多人介绍verilo…

LabVIEW轴角编码器自动检测

LabVIEW 开发轴角编码器自动检测系统&#xff0c;针对指控系统中高故障率的轴角编码器性能检测需求&#xff0c;通过模块化硬件架构与软件设计&#xff0c;实现编码器运转状态模拟、扭矩 / 转速实时监测、19 位并行编码采集译码、数据自动分析及报告生成等功能&#xff0c;解决…

MySQL数据库从0到1

目录 数据库概述 基本命令 查询命令 函数 表的操作 增删改数据和表结构 约束 事务 索引 视图 触发器 存储过程和函数 三范式 数据库概述 SQL语句的分类&#xff1a; DQL&#xff1a;查询语句&#xff0c;凡是select语句都是DQL。 DML&#xff1a;insert,delete,up…

WiFi万能钥匙鲲鹏服务器部署 TiDB 集群实战指南

作者&#xff1a; TiDBer_yangxi 原文来源&#xff1a; https://tidb.net/blog/15a234d0 一、环境准备 1. 硬件要求 服务器架构 &#xff1a;鲲鹏服务器&#xff08;ARM架构&#xff09;&#xff0c;TiDB 官方明确支持 ARM 架构服务器部署 推荐配置 &#xff08;生产环…