欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > [Android]文件描述符的binder传送

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

2025/6/20 3:43:34 来源:https://blog.csdn.net/aaajj/article/details/145738783  浏览:    关键词:[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 类文档

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词