Skip to content
 
 

Repository files navigation

旧版互传(LegacySend)

If you don't speak Chinese, please check our GitHub Wiki for the README translated into other languages!

一个独立实现的原生 Android Java 局域网文件传输应用,目标是兼容 LocalSend Protocol v2.2 的设备发现和 Upload API,专为旧版设备(Android 2.3 至 Android 6.0 / API 9 至 API 23)设计。项目不引用、导入或构建相邻的 LocalSend 源码目录。

当前版本:1.3(versionCode 5)。

下载与安装

已编译好的 APK 文件可以直接在本项目的 GitHub Releases(发布版) 页面下载安装,无需手动编译。针对 Android 2.3 至 6.0 设备,应用均支持使用 v1 签名直接安装。

源码构建(可选)

如需自行从源码构建,要求环境如下:

  • JDK 17
  • Gradle Wrapper 8.9
  • Android Gradle Plugin 8.7.3
  • Android SDK Platform 23 (Android 6.0 Marshmallow)
  • Android SDK Build Tools 23.0.3(或更高)

构建与验证命令:

./gradlew testDebugUnitTest lintDebug assembleDebug

编译输出 APK 路径:

app/build/outputs/apk/debug/app-debug.apk

项目 minSdkVersion 为 9,compileSdkVersion 为 23,targetSdkVersion 为 23 (Android 6.0)。这确保了极其轻量且统一的构建环境,完全契合经典 Android 时代的存储权限与服务语义;本项目不面向 Google Play 发布。

功能

  • UDP 组播设备发现、公告和回包
  • HTTP/HTTPS LocalSend v2 (v2.2) 注册接口
  • 支持分块传输编码(Chunked Transfer Encoding)
  • 单文件和多文件选择、发送和接收
  • 接收前接受/拒绝
  • 文件级令牌、会话 ID、来源 IP 和证书指纹检查
  • 流式上传和保存,不将完整文件载入内存
  • 总体进度、失败提示和双方取消
  • 同名文件自动添加 (1)(2) 后缀
  • 中文、空格和常见特殊字符文件名
  • 前台接收服务,Activity 重建不终止当前传输
  • 所有可见文案均为简体中文

主要目录

app/src/main/java/com/blithe/legacysend/
├── LegacySendApp.java       应用级状态、后台任务和 UI 事件
├── ReceiveService.java      前台保活接收服务
├── discovery/               UDP 组播发现
├── model/                   设备和文件模型
├── protocol/                LocalSend JSON 格式 (v2.2)
├── security/                自签名身份、BKS KeyStore、mTLS、证书指纹固定、SimpleX509Generator
├── server/                  HTTP/HTTPS 注册、准备、上传、取消接口
├── storage/                 SAF 与低版本文件系统保存目录及重名处理
├── transfer/                HTTPS/HTTP 发送客户端和进度/取消
├── ui/                      传统 Android View 中文界面
└── util/                    流式复制和进度工具

业务源码全部为 Java;没有 Kotlin、Compose、Flutter、Dart 或 React Native 代码。Gradle 使用 Groovy DSL。

Android 2.3–6.0 (API 9–23) 兼容性处理

  • minSdk 9 (Android 2.3) 至 Android 6.0 (API 23) 完整覆盖: 针对老旧硬件与固件环境进行了专项适配。
  • API 9–17 证书生成修复: 针对 API 9–17 无 AndroidKeyStore 且 OpenSSL / Conscrypt 解析 ASN.1/DER 结构极度严格的问题,实现了自定义 SimpleX509Generator
  • BKS KeyStore 格式: 在 API 9–17 上使用 BouncyCastle (BKS) 存储私钥与证书,解决 Android 2.3 上 PKCS12 序列化崩溃问题。
  • ASN.1 / DER 手动编码修复: 补全 Subject / IssuerAttributeTypeAndValueSEQUENCE 包装,并强制将 commonName (2.5.4.3) 的字符串 Type 指定为 PrintableString (0x13),彻底修复低版本 OpenSSL 的 ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG 崩溃。
  • 算法与 Y2K38 溢出处理: API 9–17 自签名证书采用 SHA1withRSA,有效期限制为 10 年,防止 32-bit 时间戳溢出。
  • SAF 与低版本文件系统: API 19–23 使用 ACTION_OPEN_DOCUMENT 和 SAF;API 9–18 使用应用内内置文件选择器直接读取外部存储。
  • Android 4.4.2 (API 19–20) TLS 接收兼容: Kindle Android 4.4.2 的 TLS 1.2 服务端仅支持 CBC 密码套件,与 LocalSend 1.17.0 的 Rust TLS 客户端无公共套件。API 19–20 接收服务使用 LocalSend 官方允许的 protocol: "http" 模式;API 9–18 及 API 21–23 接收服务与所有对外发送保持 HTTPS。
  • 文件保存路径: 支持的所有版本(API 9–23)统一保存到公共 Download/LegacySend 目录。
  • 获取 MulticastLock: 获取 MulticastLock 后监听组播;网络和文件 I/O 全部在后台线程执行。
  • 流式处理: 文件使用 32 KiB 缓冲流式复制,支持 Chunked 流式接收与落盘,并校验实际字节数与元数据大小一致。
  • 通知系统: 前台 Service 为 API 9–23 统一使用传统通知系统。

依赖

运行时没有第三方依赖,仅使用 Android SDK、Java 标准库和 org.json(Android 系统自带)。因此运行依赖不存在额外的低版本 API 兼容风险。

测试依赖:

  • JUnit 4.13.2:仅在主机 JVM 运行测试,不打入 APK。
  • org.json:json:20240303:仅为主机 JVM 测试提供与 Android org.json 对应的实现,不打入 APK。

当前验证状态

已实现并经过测试

  • 主机单元测试 12 项:协议序列化、多文件元数据、中文/特殊字符、接受/拒绝/取消/超时、重名、进度、流式复制、中断检测和内容哈希一致性。
  • Gradle 编译、Lint 和 debug APK 打包。
  • APK 清单检查确认 minSdkVersion=9compileSdkVersion=23targetSdkVersion=23
  • APK v1/v2 签名校验;v1 签名可供 Android 2.3、4.4.2 至 6.0 安装。
  • Android 2.3.6 (API 9) 真机验证: 设备自签名证书成功生成并加载(无 WRONG_TAG 异常),HTTPS 53317 服务正常启动,成功发现官方 LocalSend 客户端并完成单文件及多文件 Chunked 上传与落盘,文件 SHA-256 校验一致。
  • Kindle Android 4.4.2 (API 19) 真机验证: 启动、应用内文件选择,向 Android 11 设备发送文件成功;官方 LocalSend 1.17.0 (Android 11) 成功向 Kindle 发送文件并校验 SHA-256 一致。

Kindle 4.4.2 及低版本文件选择修复

Kindle 固件自带的 DocumentsUI 会保留已经被删除或移动的下载记录。旧版应用在打开对应 content:// URI 时会收到 FileNotFoundException。API 9–20 改用应用内文件浏览器,直接列出实际存在且可读的外部存储文件;API 21–23 继续使用系统 SAF。对于其他无效文件来源,发送错误会显示明确的中文提示。

已实现但尚未真机验证

  • Android 真机上的 Wi-Fi 频繁切换、厂商极高压后台限制和大文件长时间传输(Android 5.0–6.0 环境)。

尚未实现

  • LocalSend Reverse Download API(浏览器下载);核心 Android-to-LocalSend Upload API 不依赖它。
  • PIN、历史记录、文字分享、剪贴板、主题、自动更新、统计和账户等非核心功能。
  • 子网逐地址扫描回退;当前使用官方默认组播发现和 /register 双向确认。

因环境限制无法验证

  • Apple Silicon 主机的 Android Emulator 36 不支持 API 19 及 API 9 的 ARM 镜像,启动时会提示 CPU 架构不支持。

API 19–20 接收模式的安全边界

Android 4.4 的系统 TLS 服务端与 LocalSend 1.17.0 没有共同密码套件,因此 API 19–20 只能以协议规定的 HTTP 模式接收。该方向的文件内容和元数据不会被 TLS 加密;应用仍检查来源 IP、随机会话 ID 和逐文件随机 token。请只在可信局域网使用。LegacySend 向其他设备发送以及 API 9–18、API 21–23 的接收服务均保持 HTTPS 和证书指纹固定。

协议研究和端点细节见 docs/protocol.md

贡献

欢迎提交 Issue 和 Pull Request。LegacySend 的首要目标是在 Android 2.3 (API 9) 至 Android 6.0 (API 23) 等旧设备上提供精简、稳定且可与官方 LocalSend (v2.2) 互通的文件传输能力;修改时请优先保护旧系统兼容性,而不是扩大功能范围。

实现边界

  • 保持独立实现:不要导入、复制或构建 LocalSend 的源码、资源、模块或内部库。可以依据公开协议文档、公开源码中的协议行为以及黑盒测试进行兼容实现。
  • 业务代码使用 Java 和传统 Android View,不引入 Kotlin、Compose、Flutter、React Native 或 Google Play 服务。
  • 保持兼容范围专注于 minSdkVersion 9targetSdkVersion 23
  • 网络与文件 I/O 必须在后台线程运行;文件应流式处理,不能完整载入内存,并正确关闭 Socket 和流。
  • 修改存储、通知、TLS、文件选择或生命周期逻辑时,应分别检查 API 9、API 19 和 Android 6.0 的行为。
  • 保持模块职责清晰:发现、协议、安全、服务端、存储、传输和 UI 逻辑不要集中到单个 Activity。
  • 测试截图、Gradle 缓存、构建目录和本机配置不要提交;Gradle Wrapper 的脚本、JAR 和配置应保留,以支持干净环境构建。

提交前检查

运行完整的本地检查:

./gradlew testDebugUnitTest lintDebug assembleDebug