AI 手机折腾实验02 - Termux Play 版与 F-Droid 版环境混用的排错

0 阅读4分钟

上一篇文章最后,我把旧手机的 AI Agent 基础环境搭了起来。但是在测试手机硬件能力的时候,我遇到了第一个真正让我头疼的问题:Termux 的 API 全挂了。
问题最终是 Play 版 Termux 环境迁移到了另一套 Termux 构建,导致 App 与 Termux 内部软件包不匹配。

核心修复:

# 环境备份
# 更换软件源和 keyring
# 安装正确的 termux-api
# 安装正确的 termux-tools / termux-exec
# 重新打开 Termux

如果你想知道为什么会出现这个问题,再继续往下看。

命令全部超时

最开始发现问题,是因为我测试麦克风。

我想让这部手机以后能够一直听我说话,所以麦克风是必须打通的。

结果:

termux-battery-status

没反应。等到最后超时。

我又测试了几个:

termux-sensor
termux-clipboard-get
termux-notification

还是一样,全部超时。

后来我才发现,这不是某一个 API 的问题。我这台手机上,69 个 termux-* 命令基本全部不能用。

更奇怪的是,Termux 自己的日志里还出现了一句:

Invalid action: "com.termux.service_api"

这句话看起来像是说软件调用 api 接口报错,说接口不可用。按照我日常工作经验,这通常是版本不匹配导致的问题。

查看软件版本号是否匹配

于是我把 Termux、Termux API 和相关软件的版本号全部翻了一遍,看起来没问题。

我问 AI,得到的回答也是说版本没问题,官方就是这个版本,别人也在用。

于是我就顺着这个方向继续查,当时我以为是 Termux 最近有什么版本错位或者已知不匹配的缺陷,就把其中一个组件升级到了测试版本,结果还是不行。

这时候我已经开始怀疑人生了。如果版本没问题,为什么所有 API 都一起挂了?总不能 69 个命令同时坏掉吧?

后来我突然想到一个问题,我用的各种软件,特别是国产软件,Play 商店版,和国内应用商店版,以及官网版本,都会不一样,版本之间会有些许功能差异,这次会不会也是这个问题呢?

同一个版本号的两个构建

我决定不再看版本号,直接看二进制。

我把负责 Termux API 通信的程序提取出来,用 strings 看了一下里面到底写了什么。

操作很简单:

su -c 'cp -L $PREFIX/libexec/termux-api* /data/local/tmp/ && chmod 644 /data/local/tmp/termux-api*'

然后检查里面到底写了什么:

grep -aoE 'startservice|broadcast|service_api|TermuxApiReceiver' \
/data/local/tmp/termux-api-broadcast

结果很有意思。

我手机里这个版本出现的是:

startservice
com.termux.service_api

而我找到的另一个构建则是:

broadcast
com.termux.api/.TermuxApiReceiver

我当时第一反应就是这两个怎么不一样?

继续往下查,我才发现真正的问题:我这套 Termux 环境,实际上混进了两套不同构建体系的软件包。Play 商店版本和 F-Droid / GitHub 发布的 Termux,在部分组件上使用了不同的构建和版本线。它们的版本号甚至可能看起来一样,但内部实现并不一定相同。

如何修复

接下来,只需要替换软件版本,其实就可以了。

可麻烦的是,我这个环境已经安装好了 Python,node,Claude code,配置好了环境,让我从头来一次,工作量有点大,还可能又会产生新的问题。

所以我只想修坏掉的那一部分,也就是只替换软件版本,保留所有环境。

这就有点像,我重装了 Windows,可是里面的注册表,软件,个人文档,全部都要保留。有挑战性,可是做起来很有意思。

动手之前先备份,旧的二进制、apt 源文件、keyring、需要替换的 deb 包,都留一份。

再预演,任何 apt 操作,先使用下面的命令看它需要哪些包:

apt install -s

开干。

第一步:换掉旧的软件源

我首先检查 Termux 当前使用的软件源。旧环境是从 Play 版时代迁移过来的,所以这里依然指向 Play 系的软件仓库,需要把它换成对应的官方 Termux 软件源。

这里有一个很容易踩坑的地方:

软件源和签名 keyring 必须一起换。

不能只把 URL 改了就结束,因为 apt 不光要知道“东西从哪里下载”,还要确认“这个东西是谁签名的”。

所以我的修复路径是:

备份旧源
    ↓
换成官方 Termux 源
    ↓
更换对应的 termux-keyring
    ↓
apt update

如果只换源,不换 keyring,后面很容易直接遇到 NO_PUBKEY。

这一步其实是在修复整个软件包环境的“信任链”。

第二步:换 termux-api

软件源恢复以后,我没有直接:

apt upgrade

甚至没有直接把所有 Termux 包全部升级。

因为我只想解决一个问题:

Termux API 为什么和 App 不匹配?

换之前先做一次预演。

apt install -s termux-api

确认它到底准备动什么。

如果发现除了目标包以外,还有一大堆 Node、Python 或其他我不想碰的东西,就先停下来。确认依赖关系没有问题以后,再进行实际安装。

最终换成官方构建的:

termux-api 0.59.1-1

而不是原来 Play 系的:

termux-api 0.59.1

看起来只差一个 -1。

实际上里面的 helper 二进制都已经不是同一个东西。

换完以后,再验证二进制的 sha256,我这次拿到的官方构建是:

27e7057e...

而之前那个 Play 系构建是:

a484a6b3...

到这里,第一层问题终于解决。

API 修好以后,我重新打开 Claude Code:

claude

结果:

bad interpreter: No such file or directory

这次问题出在 termux-exec,Claude Code 本身没坏,它的启动脚本需要:

/usr/bin/env

但 Android 系统里没有这个路径。

Termux 平时通过 termux-exec 解决这个问题,简单来说,它会在程序执行系统调用的时候,把这些 Linux 环境里的路径转换成 Termux 自己的路径。

而它需要通过:

LD_PRELOAD

把自己加载进去。

所以我先不修改任何东西,直接做一个验证:

LD_PRELOAD=$PREFIX/lib/libtermux-exec.so claude --version

结果:

2.1.112 (Claude Code)

正常。

这一刻很关键,因为我已经证明:

Claude Code 没坏,Node.js 也没坏,真正缺的是 **LD_PRELOAD** 。

接下来只需要修复 Termux 的登录环境。

第三步:换 termux-tools 和 termux-exec

我继续检查:

grep -n LD_PRELOAD $PREFIX/bin/login

结果没有。

这意味着当前的 login 并不会帮我设置 LD_PRELOAD。继续查才发现,我这里的 termux-tools 依然是 Play 商店版本。我需要使用官方的 termux-tools 和 termux-exec。

这时候又遇到了一个非常有意思的问题。

我执行:

apt install termux-tools termux-exec

apt 告诉我:

termux-tools is already the newest version (3.0.9).

而官方版本是:

1.46.0+really1.45.0-1

因为当前 apt 的软件源里仍然认为 3.0.9 是可用的最新版本,所以即使我知道自己需要另一套构建,apt 也不会主动帮我切过去。按照 apt 的版本比较规则,3.0.9 更大。

所以它认为:

已经是最新了,不需要换。

于是我直接指定官方 .deb:

apt install -y --no-install-recommends --allow-downgrades \
  /sdcard/Download/<job>/termux-tools_1.46.0+really1.45.0-1_aarch64.deb

这里的关键参数就是:

--allow-downgrades

因为从 apt 的角度看,我确实是在“降级”。

第四步:解决配置文件冲突

安装过程中,dpkg 又跳出来问我:

*** motd (Y/I/N/O/D/Z) [default=N] ?

还有另外一个 Termux 初始化脚本。

原因也很好理解:

两套 Termux 都曾经管理过这些文件。

这里我选择安装维护者版本。

因为这两个文件主要负责 Termux 的初始化和显示。

我自己真正使用的配置,比如:

~/.termux/termux.properties

并不属于这些软件包,不会因为这个操作被一起覆盖。

收尾验证

安装完成以后,我重新打开一个 Termux 会话。

这次:

claude

正常启动。

再测试:

termux-battery-status

也正常返回 JSON。

最后测试麦克风。

Recording started: /data/data/com.termux/files/home/mic-test.m4a

我把文件拉到电脑上检查。实际时长 2.936 s,单声道 AAC,8000 Hz。这次是真的录出来了,不是 API 给我返回了一个空文件。

这次终于结束了。

这次排错让我意识到,很多时候真正的问题并不在报错本身,而在于我默认了两个东西“应该是一样的”。

现在,这部手机终于可以继续往下一步走了。

下一步,我要解决的就是让她真正拥有“耳朵”,不是简单地录一段音频,我想让她能够一直听着,在合适的时候知道我在说话,再把我的声音变成她能理解的文字。

这件事情,才真正开始接近我脑子里的那部《Her》。