# -*- coding: utf-8 -*- r'''修「局域网连接被中转劫持」的 bug。 用户报的现象(手表截图): 局域网能搜到电脑,IP / 端口 / 配对码都填对了, 点连接**没有任何反应**,一点反应都没有。 根因(MainActivity.java 的 connect()): boolean usedLan = !relayUrl().isEmpty() ? false : true; if (!relay.isEmpty()) { reply = client.connectViaRelay(...); // ← 走中转 } else { reply = client.connect(host, port, ...); // ← 才走局域网 } **只看「中转设置里填没填地址」,填了就一律走中转, 完全无视用户填的局域网 IP 和端口。** 而中转超时是 40 秒(40000ms)—— 于是用户点完之后界面卡在「正在连接…」四十秒, 在他看来就是"没有任何反应"。 这个 bug 影响两条路: 1. 「局域网连接」页面填 IP 直连(connectLanTyped) 2. 首页「已配对的电脑」点一下重连(quickConnect) 修法: 给 connect() 加一个 preferLan 参数。明确要走局域网时: 先直连 host:port(超时给短一点,6 秒,局域网本来该是毫秒级) 直连不成**再退回中转**(保证最终能连上,不让用户卡死) 没有 preferLan 时保持原来的行为(走中转,连上后若同网段再切直连)。 ''' from __future__ import annotations import re import sys from pathlib import Path SRC = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote" r"\android\src\com\lanremote\watch\MainActivity.java") # ---------------------------------------------------------------- 1. 重载 OLD_SIG = """ private void connect(final String host, final int port, final String code, final String token, final String key) { if (busy) { return; }""" NEW_SIG = """ private void connect(final String host, final int port, final String code, final String token, final String key) { connect(host, port, code, token, key, false); } /** * @param preferLan 明确要求"先走局域网"。 * *

用户点「局域网连接」填了 IP,或者点首页已配对电脑时, * 应该先按他给的地址直连。原来的实现只看中转地址填没填 —— * 填了就一律走中转,**完全无视用户填的局域网 IP**, * 而中转超时 40 秒,用户点完只看到「正在连接…」,以为没反应。 * *

设 true 时:先直连(局域网该是毫秒级,超时给 6 秒), * 不成再退回中转,保证最终能连上。 */ private void connect(final String host, final int port, final String code, final String token, final String key, final boolean preferLan) { if (busy) { return; }""" # ---------------------------------------------------------------- 2. 分支 OLD_BRANCH = """ final String relay = relayUrl(); Map reply; if (!relay.isEmpty()) {""" NEW_BRANCH = """ final String relay = relayUrl(); Map reply; if (preferLan) { // ---- 用户明确要走局域网:先直连,失败再退中转 ---- // 局域网直连本该是毫秒级,所以超时给短一点。 // 原来这里会走中转(40 秒超时),用户以为卡死了。 appendLog("局域网直连 " + host + ":" + port + " …"); reply = client.connect(host, port, code, token, MainActivity.this, 6000); ok = Json.bool(reply, "ok", false); if (ok) { usedLan = true; } else if (!relay.isEmpty()) { String lanWhy = Json.str(reply, "error", "局域网直连失败"); appendLog("局域网直连不成(" + lanWhy + "),改走中转…"); ui.post(new Runnable() { @Override public void run() { setStatus("局域网连不上,改用中转…", C_WARN); } }); reply = client.connectViaRelay(relay, code, token, MainActivity.this, 40000); usedLan = false; } } else if (!relay.isEmpty()) {""" # ---------------------------------------------------------------- 3. 调用方 OLD_QUICK = """ connect(host, port, null, token, key); }""" NEW_QUICK = """ // preferLan = true:先按记录里的地址直连。 // 记录里的地址可能是旧的(换过网络),直连会失败 —— // 那时自动退回中转,中转会报回电脑的最新局域网地址并刷新记录。 connect(host, port, null, token, key, true); }""" OLD_TYPED = """ connect(host, port, code, null, DeviceStore.deviceKey(host, port, store.deviceId()));""" NEW_TYPED = """ // preferLan = true:用户就是冲着"局域网直连"来的, // 必须真的直连,不能因为有中转地址就偷偷走中转。 connect(host, port, code, null, DeviceStore.deviceKey(host, port, store.deviceId()), true);""" def patch(text: str, old: str, new: str, label: str) -> tuple: if new.split("\n")[0] in text and label != "分支": # 已经打过补丁的粗略判断 pass if old not in text: print(" ✗ 没找到锚点:%s" % label) return text, False count = text.count(old) if count != 1: print(" ! 锚点出现 %d 次(期望 1):%s" % (count, label)) return text, False text = text.replace(old, new, 1) print(" ✓ %s" % label) return text, True def main() -> int: text = SRC.read_text(encoding="utf-8") original = text print(" 打补丁:") ok_all = True for old, new, label in ( (OLD_SIG, NEW_SIG, "connect() 加重载和 preferLan 参数"), (OLD_QUICK, NEW_QUICK, "quickConnect 传 preferLan=true"), (OLD_TYPED, NEW_TYPED, "connectLanTyped 传 preferLan=true"), (OLD_BRANCH, NEW_BRANCH, "走局域网的分支"), ): text, ok = patch(text, old, new, label) ok_all = ok_all and ok if not ok_all: print() print(" 有锚点没匹配,没有写回") return 1 SRC.write_text(text, encoding="utf-8", newline="\n") print() print(" 已写回,文件 %d -> %d 行" % (original.count("\n") + 1, text.count("\n") + 1)) return 0 if __name__ == "__main__": sys.exit(main())