当前位置: 首页 > article >正文

[Android]文件描述符的binder传送

在android中,通过传送文件描述符来实现文件数据共享是个很强大的功能,binder对文件描述符的传送进行了支持。

在 Android 中,Binder 是进程间通信(IPC)的核心机制,而文件描述符(File Descriptor, FD)的跨进程传递是一个关键功能。由于文件描述符是进程内局部资源(不同进程中相同的 FD 值可能指向不同的文件),直接传递 FD 的整数值是无效的。Binder 通过内核驱动和 Parcel 机制实现了 FD 的安全跨进程传递。


1. 基本原理

Binder 传递文件描述符的核心原理是 FD 的复制与重映射

  • 发送进程将 FD 写入 Parcel,Binder 驱动会将该 FD 转换为对内核中文件对象(struct file)的引用

  • 接收进程从 Parcel 中读取时,Binder 驱动会在接收进程中创建一个新的 FD,并指向同一个内核文件对象。

  • 内核通过引用计数管理文件对象,确保文件资源在所有进程关闭 FD 后才会释放。


2. 实现步骤

2.1 发送方(服务端)
  1. 创建 ParcelFileDescriptor:将原始 FD 包装为 Android 提供的对象。

    java

    复制

    ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(rawFd);
  2. 将 FD 写入 Parcel:通过 Parcel.writeFileDescriptor() 方法。

    java

    复制

    Parcel data = Parcel.obtain();
    data.writeFileDescriptor(pfd.getFileDescriptor());
2.2 Binder 驱动处理
  • Binder 驱动解析 Parcel 中的 FD,将其转换为对内核文件对象的引用,并记录在 binder_transaction_data 结构体中。

  • 在接收进程的内核空间,驱动为该文件对象创建新的 FD,并映射到接收进程的 FD 表中。

2.3 接收方(客户端)
  1. 从 Parcel 读取 FD:使用 Parcel.readFileDescriptor()

    java

    复制

    Parcel reply = Parcel.obtain();
    ParcelFileDescriptor pfd = reply.readFileDescriptor();
  2. 获取原始 FD:通过 ParcelFileDescriptor 对象。

    java

    复制

    int receivedFd = pfd.getFileDescriptor();

3. 关键类与 API

  • ParcelFileDescriptor:Android 提供的类,封装 FD 并支持跨进程传递。

  • Parcel.writeFileDescriptor() / Parcel.readFileDescriptor():序列化和反序列化 FD 的核心方法。

  • FileDescriptor:Java 层对原始 FD 的抽象。


4. 注意事项

  1. 资源管理

    • 必须显式关闭 FD:发送方和接收方需要各自调用 ParcelFileDescriptor.close(),避免资源泄漏。

    • 使用 try-with-resources(Java)或 use(Kotlin)确保自动关闭。

      kotlin

      复制

      pfd.use { fd ->
          // 使用 fd
      }
  2. 权限控制

    • 接收进程需有访问文件的权限(如通过 ContentProvider 或文件路径权限授予)。

    • Binder 仅传递 FD,不检查文件路径权限,需确保逻辑安全。

  3. 跨进程稳定性

    • ParcelFileDescriptor 实现了 Parcelable,可安全跨进程传递。

    • 在 AIDL 接口中直接使用 ParcelFileDescriptor 类型:

      java

      复制

      interface IMyService {
          void sendFile(in ParcelFileDescriptor pfd);
      }

5. 底层机制

  • Binder 驱动:在 binder_transaction() 函数中处理 FD 数组(fd_array),通过 dup_fd_to_user() 和 task_get_unused_fd_flags() 在接收进程创建新 FD。

  • 内核对象引用:传递的 FD 实际指向内核的 struct file,通过增加引用计数避免提前释放。


6. 示例代码

服务端发送 FD

java

复制

// 打开文件获取 FD
FileInputStream fis = new FileInputStream("test.txt");
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fis.getFD());

// 通过 Binder 传递
Parcel data = Parcel.obtain();
data.writeFileDescriptor(pfd.getFileDescriptor());
binder.transact(TRANSFER_FD, data, null, 0);
客户端接收 FD

java

复制

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
    if (code == TRANSFER_FD) {
        ParcelFileDescriptor pfd = data.readFileDescriptor();
        // 使用 pfd 操作文件
        return true;
    }
    return super.onTransact(code, data, reply, flags);
}

总结

通过 Binder 传递文件描述符,本质是借助内核将 FD 转换为对文件对象的引用,并在接收进程重新映射为新 FD。开发者需关注 ParcelFileDescriptor 的使用、资源释放和权限控制,确保跨进程文件访问的安全性与稳定性。

hal层文件描述符

数据类型  |  Android Open Source Project

hal文件中定义为handle类型,

java层使用android.os.NativeHandle

hal层使用android::hardware::hidl_handle的data[0] 即为对应的fd

句柄和内存
Android 11 引入了对 handle 和 memory 类型的 Java 支持。这两种类型分别转换为 android.os.NativeHandle 和 android.os.HidlMemory。null 句柄会被视为有效,而 null 内存则不会。

在生成的服务器代码中,接收的内存和句柄参数仅在方法调用范围内有效。如果服务器实现希望延长其生命周期,则必须使用其各自的 dup() 方法进行复制。返回的实例可在方法调用范围之外使用,且应在使用后正确关闭。

在生成的客户端代码中,作为调用的方法的输入参数发送的句柄和内存实例无需进行复制,也无需在方法返回后保持有效状态。但是,作为输出参数接收的句柄和内存实例将由自动生成的代码自动复制,且必须在复制完成后正确关闭。无论这些返回参数显示为方法的返回值(在存在单个返回值的情况下),还是使用同步回调样式(用于存在多个返回值的情况),均是如此。

如需详细了解复制与关闭,请参阅 Java 类文档


http://www.kler.cn/a/553877.html

相关文章:

  • 迅为iTOP-RK3576开发板/核心板6TOPS算力4K视频编解码
  • Redis 键对应的命令详解
  • mysql实现原理 - 字符集和排序规则
  • Python安装与环境配置全程详细教学(包含Windows版和Mac版)
  • [网络] 如何开机自动配置静态IP,并自动启动程序
  • 第六步:Python协议与模块——当字典化身数据库,import玩出花
  • python的类与对象。为什么有些东西要用到类和对象。普通的编程方式不行吗?
  • 项目管理工具Jira在营销工作管理中的应用与实践
  • BMS保护板测试仪:电池安全与性能的坚实守护者
  • ssm121基于ssm的开放式教学评价管理系统+vue(源码+包运行+LW+技术指导)
  • 关于项目证书登录流程
  • wps中的js开发
  • Vue 计算属性(computed)
  • 个人简历html网页模板,科技感炫酷html简历模板
  • 大数据的特点
  • vue单据打印 一维码、二维码实现
  • 告别卡关!XSS挑战之旅全关卡通关思路详解
  • notepad++右键菜单不见了
  • [c语言日寄]字符串的左旋与右旋
  • android studio 高版本创建项目时,修改setting跟build后,运行不了的问题解决