android binder(二)应用层编程实例
一、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_READ | struct binder_write_read | 应用层向内核层收发数据 |
BINDER_SET_MAX_THREADS | size_t | 设置最大线程数 |
BINDER_SET_CONTEXT_MGR | int or flat_binder_object | 设置当前进程为ServiceManager |
BINDER_THREAD_EXIT | int | 删除 binder 线程 |
BINDER_VERSION | struct binder_version | 获取 binder 协议版本 |
二、安卓提供的封装
servicemanager - OpenGrok cross reference for /frameworks/native/cmds/servicemanager/
在 frameworks/native/cmds/servicemanager
目录下的 binder.c
和 bctest.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.cbinder_open //打开 Binder 驱动binder_become_context_manager //告知驱动自身为 service managerbinder_loopbinder_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 2void 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); // nameif (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 2int 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 headerbio_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
相关文章:

android binder(二)应用层编程实例
一、binder驱动浅析 从上图看出,binder的通讯主要涉及三个步骤。 在 Binder Server 端定义好服务,然后向 ServiceManager 注册服务在 Binder Client 中向 ServiceManager 获取到服务发起远程调用,调用 Binder Server 中定义好的服务 整个流…...
HTML 等价字符引用:系统化记忆指南
HTML 等价字符引用:系统化记忆指南 在 HTML 中,字符引用(Character Entity References)用于表示保留字符或特殊符号。我将提供一个系统化的方法来记忆这些重要实体,并解释它们的实际应用。 什么是等价字符引用? HTML 字符引用有两种形式: 命名实体:&entity_name…...

【深度学习】17. 深度生成模型:DCGAN与Wasserstein GAN公式深度推导
深度生成模型:DCGAN与Wasserstein GAN公式深度推导 深度卷积生成对抗网络 DCGAN 在原始 GAN 框架中,生成器和判别器通常使用全连接层构建,这限制了模型处理图像的能力。为此,Radford 等人在 2016 年提出了 DCGAN(Deep Convoluti…...
Ubuntu终端性能监视工具
目录 工具1:nvidia-smi 工具2:nvtop 工具3:nvitop 工具1:nvidia-smi nvidia-smi 如果希望自动刷新这个命令,可以输入如下命令: nvidia-smi -l 工具2:nvtop nvtop 安装方法: …...

设计模式——命令设计模式(行为型)
摘要 本文介绍了命令设计模式,这是一种行为型设计模式,用于将请求封装为对象,实现请求的解耦和灵活控制。它包含命令接口、具体命令、接收者、调用者和客户端等角色,优点是解耦请求发送者与接收者,支持命令的排队、记…...
鸿蒙OSUniApp智能商品展示实战:打造高性能的动态排序系统#三方框架 #Uniapp
UniApp智能商品展示实战:打造高性能的动态排序系统 引言 在电商应用开发中,商品展示和智能排序是提升用户体验的关键因素。随着HarmonyOS生态的发展,用户对应用的性能和交互体验要求越来越高。本文将深入探讨如何在UniApp中实现一个性能优异…...

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

PABD 2025:大数据与智慧城市管理的融合之道
会议简介 2025年公共管理与大数据国际会议(ICPMBD 2025)确实在海口举办。本次会议将围绕公共管理与大数据的深度融合、数据分析在公共管理中的应用、大数据驱动的政策制定与优化等议题展开深入研讨。参会者将有机会聆听前沿学术报告,分享研究…...

Golang持续集成与自动化测试和部署
概述 Golang是一门性能优异的静态类型语言,但因其奇快的编译速度,结合DevOps, 使得它也非常适合快速开发和迭代。 本文讲述如何使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术: dep: 进行包的依赖管理gin…...
三套知识系统的实践比较:Notion、Confluence 与 Gitee Wiki
在过去几年中,我们团队先后使用过三套企业知识系统:Notion、Confluence 和 Gitee Wiki。每一套系统上线初期都带来一阵热情,但最终能真正融入研发流程、持续活跃的,只有最后一个。 我们不是要为某个平台背书,而是希望…...

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…...
OpenGL 3D 编程
OpenGL 是一个强大的跨平台图形 API,用于渲染 2D 和 3D 图形。以下是 OpenGL 3D 编程的入门基础。 一. 环境设置 安装必要的库 GLFW: 用于创建窗口和处理输入 GLEW 或 GLAD: 用于加载 OpenGL 函数 GLM: 数学库,用于 3D 变换 // 基本 OpenGL 程序结构示例 #include <GL/g…...

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

力扣刷题Day 68:搜索插入位置(35)
1.题目描述 2.思路 方法1:回溯的二分查找。 方法2:看到了一个佬很简洁的写法,代码贴在下面了。 3.代码(Python3) 方法1: class Solution:def searchInsert(self, nums: List[int], target: int) ->…...
NodeJS全栈WEB3面试题——P4Node.js后端集成 服务端设计
4.1 如何在 Node.js 中管理钱包与私钥的安全性? 私钥管理原则:不暴露,不硬编码,不明文存储。 常见做法: 加密存储: 使用 crypto 或 ethers.Wallet.encrypt() 加密私钥,存储到数据库或文件系统…...
SQL进阶之旅 Day 12:分组聚合与HAVING高效应用
【SQL进阶之旅 Day 12】分组聚合与HAVING高效应用 在SQL的世界里,分组聚合(Grouping and Aggregation)是处理大规模数据集时最常用的技术之一。它允许我们将数据按照某些列进行分类,并对每个分类进行统计计算。而 HAVING 子句则是…...
深入剖析C#构造函数执行:基类调用、初始化顺序与访问控制
导言 在面向对象编程中,理解对象构造过程至关重要。C#的构造函数执行遵循严格的顺序规则,尤其是涉及继承和成员初始化时。本文将深入解析构造函数的执行流程、初始化语句的妙用以及类访问修饰符的影响,助你写出更健壮、可维护的代码。 构造…...
Java 大数据处理:使用 Hadoop 和 Spark 进行大规模数据处理
Java 大数据处理:使用 Hadoop 和 Spark 进行大规模数据处理 在当今数字化时代,数据呈现出爆炸式增长,如何高效地处理大规模数据成为企业面临的重要挑战。Java 作为一门广泛使用的编程语言,在大数据处理领域同样发挥着关键作用。本文将深入探讨如何利用 Hadoop 和 Spark 这…...

使用Python绘制节日祝福——以端午节和儿童节为例
端午节 端午节总算是回家了,感觉时间过得真快,马上就毕业了,用Python弄了一个端午节元素的界面,虽然有点不像,祝大家端午安康。端午节粽子(python)_python画粽子-CSDN博客https://blog.csdn.net…...
探索大语言模型(LLM):参数量背后的“黄金公式”与Scaling Law的启示
引言 过去十年,人工智能领域最震撼的变革之一,是模型参数量从百万级飙升至万亿级。从GPT-3的1750亿参数到GPT-4的神秘规模,再到谷歌Gemini的“多模态巨兽”,参数量仿佛成了AI能力的代名词。但参数真的是越多越好吗?这…...
Excel to JSON 插件 2.4.0 版本更新
我们很高兴地宣布 Excel to JSON 插件已升级到 2.4.0 版本!本次更新带来了两项重要功能,旨在为您提供更大的灵活性和更强大的数据处理能力。 主要更新内容: 1. 用户可以选择从行或列中选择标题 在之前的版本中,插件通常默认从第…...
黑马点评后端笔记
1.基于Session实现登录流程 发送验证码: 先前端校验,后端再校验(防小人),合法生成验证码(RandomUtil生成),后端保存,在通过短信去发送给用户 短信验证码登录和注册: 拿到验证码和手机号后,后端通过session(spring mvc注入)拿到验证码,进行校验,如果用户…...

C#项目07-二维数组的随机创建
实现需求 创建二维数组,数组的列和宽为随机,数组内的数也是随机 知识点 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多变量单步光伏功率预测(Matlab完整源码和…...
解锁 AI 大语言模型的“知识宝藏”:知识库的奥秘与优化之道
1. 知识库在 AI 大语言模型中的作用 1.1 提供准确信息 知识库是 AI 大语言模型的重要组成部分,能够为模型提供准确的信息。在处理用户问题时,模型可以参考知识库中的数据,从而给出更准确的答案。例如,在医疗领域,知识…...

一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录——3. 服务器软件更新,以及常用软件安装
前言 前面,我们已经 安装好了 Ubuntu 服务器系统,并且 配置好了 ssh 免密登录服务器 ,现在,我们要来进一步的设置服务器。 那么,本文,就是进行服务器的系统更新,以及常用软件的安装 调整 Ubu…...
第四十天打卡
知识点回顾: 彩色和灰度图片测试和训练的规范写法:封装在函数中展平操作:除第一个维度batchsize外全部展平dropout操作:训练阶段随机丢弃神经元,测试阶段eval模式关闭dropout 作业:仔细学习下测试和训练代码…...
【请关注】ELK集群部署真实案例分享
ELK集群部署 1,准备es配置 es.yml: -------------------------------------------------------------- #集群名称 cluster.name: elasticsearch-cluster #节点名称 node.name: es-node1 #设置绑定的ip地址,可以使ipv4或者ipv6 #绑定这台机器的任何一个ip network.bind_hos…...
odoo17 windows server布署错误分析
odoo17 windows server布署错误分析 错误代码: File "C:\od172406\odoo\sql_db.py", line 681, in borrow result psycopg2.connect( ^^^^^^^^^^^^^^^^^ File "C:\od172406\venv\Lib\site-packages\psycopg2\__init__.py"…...

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