Skip to content

About

Windows <-> Android continuity: mirrored notifications (with replies), shared clipboard, file transfer and handoff over one auditable local protocol. SAS pairing, pinned TLS, zero third-party dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

ContinuityBridge

English — a minimal, auditable implementation of Apple-style Continuity between Windows 10/11 and Android: mirrored notifications (both directions, with replies), a shared clipboard, file transfer and handoff, over a single TLS-encrypted local protocol. Pairing is a six-digit SAS plus certificate pinning; there is no cloud, no account and no third-party dependency. This document is in Chinese; docs/ mixes both.

把 iPhone ↔ Mac 的"连续互通"语义,用公开、可审计的协议搬到 Windows 10/11 ↔ Android 上。

不是模拟 Apple 的私有链路(AWDL / BLE 广告 / iCloud 身份),而是先把这些链路各自解决什么问题拆出来,再用两端都有的标准件逐一替换,并明确标出哪些东西换不掉。


1. iPhone 与 Mac 的互联是怎么实现的

Apple 把这一组能力叫 Continuity(连续互通)。它不是一条协议,而是四层拼起来的:

层 用的是什么 解决的问题
身份与信任 同一 Apple ID;设备间通过 APNs 带外完成 BLE 4.2 配对,之后每台设备生成一把 256-bit AES 密钥存进钥匙串(Apple 平台安全指南) 两台设备如何确信"对方是我的"
发现与测距 BLE 广告里的 Continuity TLV(Handoff 0x0C、Nearby Info 0x10 等),配合旋转地址防跟踪;iPhone 11+ 另用 UWB(U1/U2)做指向性选择 谁在附近、该把图标显示给谁
数据面 AWDL(Apple Wireless Direct Link):自研 802.11 ad-hoc,action frame 同步、6/44/149 社交信道、约 1 秒周期的可用窗口;AirDrop / Handoff 大载荷 / Sidecar / 隔空播放都跑在上面。载荷是 IPv6,DNS-SD 被塞进 Wi-Fi action frame 不经过路由器也能高速直连
应用语义 NSUserActivity(activityType + userInfo ≤3 KB + webpageURL)、通用剪贴板、通知/通话/SMS 中继、iPhone 镜像 传什么、由哪个 App 接

几个常被误解的点:

  • Handoff 不是"传文件"。它传的是一个 activityType 加最多几 KB 的状态,接收端按 Team ID + activityType 找到同一个开发者的 App 来续接;大载荷(比如带附件的邮件草稿)才升级到 AWDL + TLS。
  • 通用剪贴板跑在 Handoff 之上:剪贴板写入被当作一次 activity,内容经 Wi-Fi + TLS 传输,实测有效期约 2 分钟(Apple 只说"短时间内")。
  • iPhone 镜像(macOS 15)不是 AWDL。Apple 只说是"本地点对点协议 + iCloud 端到端密钥",公钥在 Secure Enclave 里;外围观察指向 LLW(低延迟 Wi-Fi)接口,但 Apple 从未文档化。
  • 通知:macOS 15 之前只中继电话和 SMS;"所有 iPhone 通知上 Mac"是 iPhone 镜像带来的,按 App 逐个开关。

细节与可信度分级见 docs/apple-continuity.md;该文把"官方文档 / 逆向研究 / 推测"分开标注。

这套东西里哪些是"不可替代"的

Apple 的做法 在 Windows/Android 上有对应物吗
iCloud 身份 + APNs 带外配对 没有。没有跨厂商的身份根,只能退化成"人对六位码"(SAS),这正是本项目采用的办法
BLE 广告做发现 部分有:Android 有 BLE 广播 API,Windows 桌面端没有可靠的广播接口。本项目改用 UDP 广播信标,语义等价("我在,端口是这个"),拿不到 RSSI 测距
AWDL 直连 没有。两端都不开放 802.11 ad-hoc。只能用普通 Wi-Fi LAN + TCP/TLS
Team ID 决定谁来续接 没有跨平台 App 身份体系。本项目改成 Windows 侧一张 activityType → 命令行模板 的路由表
Secure Enclave 保护长期密钥 Windows 有 TPM、Android 有 Keystore,但跨厂商无法互信;本项目用应用层密钥 + 证书指纹 pinning

结论:能 1:1 复刻的是"语义",不是"链路"。凡是依赖 Apple 身份根或私有无线的部分,都必须换成"用户参与的一次性验证"。


2. 本项目实现的对应关系

┌─────────────────── Windows 10/11 ───────────────────┐        ┌────────────── Android 10+ ──────────────┐
│ UserNotificationListener  ─┐                        │        │  NotificationListenerService  ─┐        │
│ wpndatabase.db (历史/兜底)  ├─► WindowsNotification  │        │                                 ├─► NotifListener │
│ Win32 clipboard + 序号      │        Reader          │        │  ClipboardManager               │                 │
│ HandoffRouter(activityType) ┘                        │        │  Share sheet / intents          ┘                 │
│                     │                                │        │                     │                            │
│                 Bridge  ◄── 统一扇出/去重 ──►         │        │                 BridgeBus                        │
│                     │                                │        │                     │                            │
│  AgentServer  TLS1.2/1.3  TCP 47820  ◄───────────────┼────────┼──► BridgeClient  TLS + 证书 pinning              │
│  UDP beacon  47821 (发现)                             │  LAN   │    UDP listener 47821 (发现)                     │
│  control 127.0.0.1:47822 (仅调试/测试)               │        │    ForegroundService (dataSync)                  │
└──────────────────────────────────────────────────────┘        └──────────────────────────────────────────────────┘
iPhone ↔ Mac 本项目 状态
Handoff(按 activityType 续接) handoff 帧 + Windows 侧 activityType → 命令模板;手机通过分享菜单把链接/文本交给 PC ✅ 双向已验证
通用剪贴板 clipboard 帧,双向;Win32 序号轮询,回写抑制回声 ✅ 双向已验证(含回环一致性)
通知镜像 Windows toast → Android 通知(UserNotificationListener + toast XML);Android NotificationListener → PC ✅ 双向已验证(含真实系统 toast)
通知回复 reply:true 能力位 + notif-reply 帧;Android 侧填充 RemoteInput 并触发原通知的 action ✅ 已验证(PC 输入 → 应用回调)
BLE 广告 / 附近设备 UDP 广播信标(3 秒一次,只含端口/主机名/指纹,不含身份) ⚠️ 代码完成;模拟器 NAT 环境无法验证,真机可用
带外配对 ECDH P-256 + HKDF → 六位 SAS 人工比对 + 证书指纹 pinning ✅ 已验证(错误密钥被拒)
AirDrop 文件传输 file-* 帧:offer → accept → 256 KiB 分块 → SHA-256;手机落到系统 Downloads/ContinuityBridge ✅ 双向已验证(逐字节哈希比对)
Sidecar / 屏幕镜像 未实现 ❌
通话 / SMS 中继 未实现(Android 侧需要额外权限与运营商通道) ❌

为什么剪贴板要"人工比对六位码":TLS 用的是自签名证书,同一局域网里的主动攻击者可以插在中间换掉自己的 ECDH 公钥。两端各显示同一个六位数,人眼一比,MITM 就变成百万分之一的概率——这就是 Apple 用 APNs 带外通道做的事,本地网络里只能由用户来当这条带外通道。


3. 快速开始

3.1 Windows 端

需要 .NET 8 SDK。

cd windows/ContinuityBridge.Agent
dotnet build -c Release
# 交互模式(打印证书指纹与日志)
dotnet run -c Release
# 无人值守(服务/测试用),并开放本机控制口
dotnet run -c Release -- --headless --control-port 47822

参数:

参数 含义
--port N TLS 监听端口,默认 47820
--discovery-port N UDP 信标端口,默认 47821
--data-dir PATH 证书、已配对设备、设置的存放目录(默认 %LOCALAPPDATA%\ContinuityBridge)
--poll-seconds N 剪贴板/通知轮询间隔,默认 1
--headless 无交互输出;启用本机控制口
--control-port N 控制口端口,默认 47822,只绑 127.0.0.1
--history N 直接从 wpndatabase.db 读 N 条历史通知后退出

控制口(仅调试;给端到端测试和演示用):

status
notify <app>|<title>|<body>
clip <text>
sendfile <path>
reply <notification-id> <text>

3.2 Android 端

需要 JDK 17–21(JDK 25 会让 Gradle 8.13 直接崩在 class file 69,这是实测坑)、Android SDK 35、Gradle 8.13。

cd android
JAVA_HOME=/path/to/jdk-21 ./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

# 通知访问(否则手机通知发不出去)
adb shell cmd notification allow_listener dev.continuitybridge/.NotifListener
adb shell pm grant dev.continuitybridge android.permission.POST_NOTIFICATIONS

打开 App:填入 PC 地址(模拟器用 10.0.2.2;真机填局域网 IP),点 Pair。 PC 控制台会打印 paired '...', SAS 123456,手机上弹出同样的六位数——一致才点 "It matches"。

3.3 跑协议测试

python -m pip install cryptography
python tools/e2e_test.py --agent windows/ContinuityBridge.Agent/bin/Debug/net8.0-windows10.0.19041.0/ContinuityBridge.Agent.exe

这个测试不复用任何 C# 代码,按 docs/protocol.md 独立实现客户端,覆盖:配对/ SAS 一致性 / 重连 / 错误密钥拒绝 / 剪贴板 / Handoff 路由 / 心跳 / 无版本帧拒绝。

Java 协议层也可以脱离 Android 直接跑:

javac -d /tmp/jc $(find android/app/src/main/java tools/java -name '*.java')
java -cp /tmp/jc Probe 127.0.0.1 47820 /tmp/probe-state

4. 安全模型(摘要)

  • 传输:TLS 1.2/1.3,服务端 ECDSA P-256 自签名证书,首次配对后把证书 SHA-256 pin 到手机上;指纹变化直接拒绝连接。
  • 配对:双向 ECDH(服务端临时密钥 × 客户端临时密钥),shared = SHA-256(X),HKDF-SHA256 派生 pairKey 与 SAS。双方先验 MAC(确认对方真在同一 ECDH 秘密上),再让用户比对 SAS。
  • 重连:HMAC-SHA256 挑战应答,双方各出 16 字节随机 nonce,双向出证;重放旧握手没有用。
  • 剪贴板/通知:只走已认证会话;不落盘(配对状态除外),不经过任何云。
  • 控制口:只绑回环,仅 headless 模式启用;它能注入通知,属于本地调试面,不是网络面。
  • 未做:前向保密仅由 TLS 提供;协议未经第三方审计;六位 SAS 的强度依赖用户真的去比对。

详见 docs/protocol.md。


5. 已验证 / 未验证

已验证(2026-10-23,Windows 10 19045 + Android API 35 模拟器)

项 证据
协议 17 项断言全过 python tools/e2e_test.py → 17 passed, 0 failed
Java 协议层与 C# 互通 Probe 五步全过(配对/认证/剪贴板/Handoff/重连)
真实 Windows toast → 手机通知 手机日志 mirrored: [Windows] Windows toast;截图 docs/screenshot-app.png
PC → 手机剪贴板 手机日志 clipboard received (13 chars)
手机 → PC 剪贴板 PC 日志 clipboard from ...: pc-...,Windows 剪贴板读回一致
手机通知 → PC PC 日志 PHONE_NOTIFICATION Shell Shell ...
手机 Handoff → PC PC 日志 handoff 'dev.continuitybridge.browse' -> cmd.exe,手机收到 handoff accepted
PC → 手机文件 400 KiB 文件;手机日志 received … -> Downloads/ContinuityBridge,adb pull 后 SHA-256 与源文件一致
手机 → PC 文件 256 KiB 文件;PC 日志 received e2e-262144.bin (262144 bytes)(接收端校验通过才会写这一行)
通知回复 PC reply <key> reply-from-pc-ok → 手机 reply sent → 应用回调 REPLY_RECEIVED:reply-from-pc-ok
错误密钥被拒绝 e2e 第 3 项;服务端返回 auth-failed 后主动断连
交付物静态核验(REA) 见 docs/verification.md

未验证 / 明确不做

  • UDP 发现:模拟器 NAT 收不到宿主机广播;真机需要同一 LAN 且允许广播。
  • Android 10+ 限制:后台读剪贴板被系统禁止,所以"手机→PC 剪贴板"只在 App 位于前台时可靠(本项目用按钮触发);写剪贴板在后台也可能被静默丢弃,失败时值会留在 App 内供手动复制。
  • 屏幕镜像、通话/SMS 中继、BLE/UWB 测距、剪贴板图片均未实现。
  • 文件传输未做断点续传(协议里明确写了原因);也没有和 LocalSend 客户端互通——cb1 自带信任,LocalSend 有另一套会话模型,混用会多开一个未审计面。
  • 只在 Windows 10 19045 与 API 35 模拟器上跑过;未在真机、Windows 11、Android 14 以下验证。

6. 目录结构

continuity-bridge/
├─ windows/ContinuityBridge.Agent/     Windows 端(.NET 8,无第三方 NuGet)
│  ├─ Core/        证书、ECDH/HKDF/SAS、帧、TLS 会话、配对与重连
│  ├─ Platform/    Win32 剪贴板、通知读取(API + wpndatabase.db)、Handoff 路由
│  └─ Services/    Bridge 扇出、控制口
├─ android/                             Android 端(纯 Java,零 AndroidX/Compose 依赖)
│  └─ app/src/main/java/dev/continuitybridge/
│     ├─ Crypto.java / Json.java / Wire.java      协议层(可在 JVM 上单独跑)
│     ├─ BridgeClient.java                         配对/认证/连接状态机
│     ├─ BridgeService.java / NotifListener.java  前台服务、通知中继
│     └─ MainActivity.java / BridgeBus.java       单页 UI 与状态总线
├─ tools/
│  ├─ e2e_test.py                      独立实现的协议测试(Python)
│  └─ java/Probe.java                  JVM 协议探针
└─ docs/
   ├─ apple-continuity.md              Apple 侧机制与可信度分级
   ├─ protocol.md                      线协议规范
   ├─ verification.md                  测试与 REA 证据
   └─ rea-evidence.json                REA 导出的证据包(6 条记录)

7. 路线图

  1. 文件投放 已完成(无断点续传);可选增强:与 LocalSend v2.2 互通,复用其 prepare-upload/upload 端点。
  2. 发现增强:mDNS/DNS-SD(_continuitybridge._tcp)+ 真机 BLE 信标;Windows 侧改用 Wi-Fi Direct 或保持 LAN。
  3. 可信设备管理 UI:列出已配对设备、按能力授权(通知/剪贴板/Handoff 分开)、一键吊销。
  4. 通知回复的桌面 UI:现在只能通过控制口回复;下一步在 Windows 侧加一个跟随通知的输入框。
  5. 剪贴板补全:图片(DIB ↔ PNG)、Android 输入法插件以绕开后台限制。
  6. 屏幕镜像:封装 scrcpy(或 App-Scrcpy 的无 ADB 路线),不自研编码。

License

MIT — see LICENSE.

About

Windows <-> Android continuity: mirrored notifications (with replies), shared clipboard, file transfer and handoff over one auditable local protocol. SAS pairing, pinned TLS, zero third-party dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages