一个独立实现的原生 Android Java 局域网文件传输应用,目标是兼容 LocalSend Protocol v2.1 的设备发现和 Upload API。项目不引用、导入或构建相邻的 LocalSend 源码目录。
当前版本:1.2(versionCode 3)。
要求:
- JDK 17
- Gradle Wrapper 8.9
- Android Gradle Plugin 8.7.3
- Android SDK Platform 34
- Android SDK Build Tools 34.0.0
构建与验证命令:
./gradlew testDebugUnitTest lintDebug assembleDebugAPK 输出:
app/build/outputs/apk/debug/app-debug.apk
项目 minSdkVersion 为 19,targetSdkVersion 为 28。目标 SDK 维持在 28,是为了让 API 19–28 使用同一套旧式公共下载目录权限语义;本项目不面向 Google Play 发布。
- UDP 组播设备发现、公告和回包
- HTTP/HTTPS LocalSend v2 注册接口
- 单文件和多文件选择、发送和接收
- 接收前接受/拒绝
- 文件级令牌、会话 ID、来源 IP 和证书指纹检查
- 流式上传和保存,不将完整文件载入内存
- 总体进度、失败提示和双方取消
- 同名文件自动添加
(1)、(2)后缀 - 中文、空格和常见特殊字符文件名
- 前台接收服务,Activity 重建不终止当前传输
- 所有可见文案均为简体中文
app/src/main/java/com/blithe/legacysend/
├── LegacySendApp.java 应用级状态、后台任务和 UI 事件
├── ReceiveService.java 前台保活接收服务
├── discovery/ UDP 组播发现
├── model/ 设备和文件模型
├── protocol/ LocalSend JSON 格式
├── security/ 自签名身份、mTLS、证书指纹固定、TLS 1.2
├── server/ HTTP/HTTPS 注册、准备、上传、取消接口
├── storage/ SAF 文件信息、保存目录和重名处理
├── transfer/ HTTPS 发送客户端和进度/取消
├── ui/ 传统 Android View 中文界面
└── util/ 流式复制和进度工具
业务源码全部为 Java;没有 Kotlin、Compose、Flutter、Dart 或 React Native 代码。Gradle 使用 Groovy DSL。
- 使用 API 19 可用的
ACTION_OPEN_DOCUMENT和ClipData多选文件。 - API 19–28 将接收文件保存到公共
Download/LegacySend,使用旧版存储权限。 - API 19–20 使用应用内旧版文件浏览器,绕过部分 Kindle 固件中会显示已删除下载记录的 DocumentsUI;可重复选择以添加多个文件。
- API 29+ 保存到应用专属
Android/data/com.blithe.legacysend/files/Download/LegacySend,避免 Scoped Storage 写入失败。 AndroidKeyStore使用 API 18 引入的KeyPairGeneratorSpec生成 RSA 自签名设备身份。- API 19 上显式启用 TLS 1.2,同时保留对 TLS 1.0/1.1 的协商能力。
- API 19–20 接收服务使用 LocalSend v2 官方定义的 HTTP 模式:旧版系统的 TLS 服务端仅支持 CBC 密码套件,与 LocalSend 1.17.0 的现代 Rust TLS 客户端没有共同套件。API 21+ 接收服务保持 HTTPS。
- 获取
MulticastLock后监听组播;网络和文件 I/O 全部在后台线程执行。 - 文件使用 32 KiB 缓冲流式复制,并验证实际字节数与元数据大小一致。
- 前台 Service 为 API 19 使用传统通知,为 API 26+ 创建通知频道。
- Activity 使用应用级控制器承载会话,旋转和界面重建不会直接销毁传输对象。
运行时没有第三方依赖,仅使用 Android SDK、Java 标准库和 org.json(Android 系统自带)。因此运行依赖不存在额外的 API 19 兼容风险。
测试依赖:
- JUnit 4.13.2:仅在主机 JVM 运行测试,不打入 APK。
org.json:json:20240303:仅为主机 JVM 测试提供与 Androidorg.json对应的实现,不打入 APK。
- 主机单元测试 12 项:协议序列化、多文件元数据、中文/特殊字符、接受/拒绝/取消/超时、重名、进度、流式复制、中断检测和内容哈希一致性。
- Gradle 编译、Lint 和 debug APK 打包。
- APK 清单检查确认
minSdkVersion=19、targetSdkVersion=28。 - APK v1/v2 签名校验;v1 签名可供 Android 4.4.2 安装。
- API 34 ARM64 模拟器冷启动、设备证书生成、HTTPS 53317 服务、前台 Service。
- API 34 模拟器上的
/info和/registerHTTPS 实际请求。 - API 34 模拟器上的接收确认、中文文件名上传、18 字节流式落盘和内容读取一致性。
- Kindle Android 4.4.2(API 19)真机安装、启动、应用内文件选择,以及向 Android 11 设备发送 5.2 KB 文件成功。
- 官方 LocalSend 1.17.0(Android 11)发现 Kindle,并向 Kindle Android 4.4.2 发送 6.3 KB 文件成功;发送端和接收端均显示完成,接收文件 SHA-256 与源文件一致。
Kindle 固件自带的 DocumentsUI 会保留已经被删除或移动的下载记录。旧版应用能够查询这些记录的名称和大小,但发送时重新打开对应 content:// URI 会收到 FileNotFoundException: No such file or directory。版本 1.1 在 API 19–20 改用应用内文件浏览器,直接列出实际存在且可读的外部存储文件;API 21+ 继续使用系统 SAF。对于其他无效文件来源,发送错误也会显示明确的中文提示。
- 与官方 LocalSend 的多文件传输、拒绝和取消。
- Android 真机上的 Wi-Fi 网络变化、厂商后台限制和大文件长时间传输。
- LocalSend Reverse Download API(浏览器下载);核心 Android-to-LocalSend Upload API 不依赖它。
- PIN、历史记录、文字分享、剪贴板、主题、自动更新、统计和账户等非核心功能。
- 子网逐地址扫描回退;当前使用官方默认组播发现和
/register双向确认。
- Apple Silicon 主机的 Android Emulator 36 不支持 API 19 的 ARMv7 QEMU2 镜像,启动时明确报错
CPU Architecture 'arm' is not supported by the QEMU2 emulator。
Android 4.4 的系统 TLS 服务端与 LocalSend 1.17.0 没有共同密码套件,因此 API 19–20 只能以协议规定的 HTTP 模式接收。该方向的文件内容和元数据不会被 TLS 加密;应用仍检查来源 IP、随机会话 ID 和逐文件随机 token。请只在可信局域网使用。LegacySend 向其他设备发送仍使用 HTTPS 和证书指纹固定,API 21+ 的接收服务也仍使用 HTTPS。
协议研究和端点细节见 docs/protocol.md。
欢迎提交 Issue 和 Pull Request。LegacySend 的首要目标是在 Kindle Android 4.4.2(API 19)上提供精简、稳定且可与官方 LocalSend 互通的文件传输能力;修改时请优先保护旧系统兼容性,而不是扩大功能范围。
- 保持独立实现:不要导入、复制或构建 LocalSend 的源码、资源、模块或内部库。可以依据公开协议文档、公开源码中的协议行为以及黑盒测试进行兼容实现。
- 业务代码使用 Java 和传统 Android View,不引入 Kotlin、Compose、Flutter、React Native 或 Google Play 服务。
- 保持
minSdkVersion 19。调用更高版本 API 前必须做版本判断,并为 API 19 提供可用路径。 - 运行时优先使用 Android SDK 和 Java 标准库。新增依赖必须说明用途、体积以及 API 19 兼容性。
- 核心范围是设备发现、文件发送/接收、确认/拒绝/取消、进度和错误处理;历史记录、账户、云中转、自动更新等不属于当前目标。
- 协议端口、请求路径、JSON 字段、状态码、证书指纹和版本协商必须有公开协议或实际通信行为作为依据,避免凭印象实现。
- 网络与文件 I/O 必须在后台线程运行;文件应流式处理,不能完整载入内存,并正确关闭 Socket 和流。
- 修改存储、通知、TLS、文件选择或生命周期逻辑时,应分别检查 API 19 和现代 Android 的行为。
- 保持模块职责清晰:发现、协议、安全、服务端、存储、传输和 UI 逻辑不要集中到单个 Activity。
- 测试截图、Gradle 缓存、构建目录和本机配置不要提交;Gradle Wrapper 的脚本、JAR 和配置应保留,以支持干净环境构建。
运行完整的本地检查:
./gradlew testDebugUnitTest lintDebug assembleDebug与协议或传输有关的改动应补充测试,至少覆盖相关的序列化、中文/特殊字符文件名、多文件会话、接受/拒绝/取消、超时、流式复制和内容一致性场景。提交说明或 PR 中请明确区分:
- 已通过自动化测试的内容;
- 已在 Android 真机或模拟器验证的内容;
- 已与官方 LocalSend 实际互传验证的内容;
- 尚未验证的风险或环境限制。
不要把编译成功等同于 Android 4.4.2 真机兼容,也不要把单元测试通过描述为已经完成官方 LocalSend 互传验证。