What: 给吉利 ECARX R621 车机(MT6735 / Android 5.1 / API 22)用的安装助手。
所做的事:
- 已安装的软件 —— 列出系统 + 用户应用,标注「系统应用 / 用户应用 / 系统·特权」。点条目看它的安装目录、APK 文件名、和 ADB 卸载命令。
- 存储里的 APK —— 扫 U 盘 / 内部存储的 APK,自动判断车机上装没装过(待安装 / 可更新 / 版本更低 / 已安装),点条目看普通安装和系统应用安装的完整教程命令。
- 文件管理 —— 自带文件管理器,浏览车机存储和 U 盘,复制 / 剪切 / 粘贴 / 重命名 / 删除 / 新建 / 看属性。支持多选批量操作:点顶栏「多选」(或长按条目选「多选」)进入多选模式,点条目逐个勾选,工具栏换成 全选 / 复制 / 剪切 / 删除 / 取消,一次处理一批。
所不做的事(有意为之):
本工具不替你安装、不替你卸载。它只把命令摆出来让你自己抄 —— 所以清单里没有 INSTALL_PACKAGES、DELETE_PACKAGES、MOUNT_UNMOUNT_FILESYSTEMS 这三条危险权限(装在 /system/priv-app 时它们会真的生效,跟「只出教程」的定位冲突)。
唯一会提权的地方: 无线 ADB 开关(会执行内置的 mtk-su —— ⚠ 在 R621 上会导致整机死机重启,见「无线 ADB」一节开头的实测警告)。
┌──────────────────────────────┐
│ 设置 重启 关于 │ ← 和原版 AwaAPK 一致的顶栏
├──────────────────────────────┤
│ 已安装的软件 │ 存储里的APK │ 文件管理 │
├──────────────────────────────┤
│ 车机 U盘 上一级 新建 粘贴 刷新 多选 │ ← 工具栏(文件管理页)
│ ... │
│ ... │
└──────────────────────────────┘
多选模式(点「多选」后工具栏整条换掉):
┌──────────────────────────────┐
│ 全选 复制 剪切 删除 取消 │
├──────────────────────────────┤
│ ■ MyApp.apk 已选 │ ← 勾上的行整行变蓝、方框填实
│ ■ Other.apk 已选 │
│ □ Download 目录 │
├──────────────────────────────┤
│ /sdcard · 3 项 · 已选 2 / 3 项 │
└──────────────────────────────┘
设置面板(照原版结构):
打开原生系统设置 ›
─────────────────────────────
本机 IP 192.168.1.5
─────────────────────────────
ADB 调试(USB) 已开启 [开关]
─────────────────────────────
无线 ADB(WiFi) 已开启 [开关]
adb connect 192.168.1.5:5555 · 27 分钟后自动关闭
⚠ 同一 WiFi 下任何人都能连上这台车机,用完记得关
─────────────────────────────
WiFi 联网 已开启 [开关]
─────────────────────────────
车机信息、提权自检、手敲命令说明、关于
无线 ADB 靠系统属性 service.adb.tcp.port 控制,而系统属性只有 root 能写。
想绕开 root 是不行的:Android 5.1 的 Settings.Global 里根本没有 adb_tcp_port 这个设置项(那是 Android 6.0 才加的),光往 Settings 里写系统压根不听。所以车机内部不存在「免 root 开无线 ADB」的接口。
USB 连着的时候在电脑上敲一条:
adb tcpip 5555这条不用 root —— 是让车机上的 adbd 自己去改属性。拔掉 USB 再 adb connect 车机IP:5555 就行,用完 adb usb 关掉。
⚠ 实测警告 —— 先读这段再往下看:
mtk-su是内核漏洞利用程序(靠触发内核内存破坏拿 root)。在这台吉利 ECARX R621(MT6735 / Android 5.1)上执行它会把内核搞挂 → 看门狗超时 → 整台车机死机重启。
- 这台车机上不要用无线 ADB 一键开关,直接用上面「免 root 的另一条路」(电脑
adb tcpip 5555),零风险;- 一键开关和下面的投放流程是给 mtk-su 真能跑起来的设备保留的,R621 上别试。
包里内置 assets/mtk-su(65,144 字节),第一次用时自动释放到应用私有目录并 chmod。候选提权程序按这个顺序试:
- 上次跑通的那个
/data/local/tmp/mtk-su—— SELinux 明确允许应用执行这里的东西,最稳(但见上方 ⚠ 实测警告:R621 上执行它会死机)- 内置的 mtk-su(释放到应用私有目录)
- 系统本来就带的 su(
/system/bin/su等) /sdcard下手动放的 —— 垫底,因为/sdcard是 FUSE + noexec,放那儿能读不能跑
每个候选先试 su -c "脚本",不行再把脚本喂给 stdin。脚本末尾追加 echo __AWA_END__,输出里 __AWA_END__ 单独占一行才算真跑通(不能只看 exit code,有的 su 失败了也返回 0)。
上面那套自动化在这台车机上会卡住,原因是 SELinux:
| 位置 | 能跑吗 |
|---|---|
/sdcard/mtk-su |
❌ FUSE + noexec,能读不能跑 |
| 应用私有目录的副本 | ❌ SELinux 不允许应用执行自己的副本 |
/data/local/tmp/mtk-su |
✅ SELinux 明确允许应用执行 |
再次提醒:这套投放 + 执行流程在 R621 上会导致整机死机重启,只适用于 mtk-su 真能跑起来的设备。
所以要借一次电脑 ADB,把 mtk-su 放进 /data/local/tmp:
adb push mtk-su /sdcard/
adb shell "cp /sdcard/mtk-su /data/local/tmp/mtk-su && chmod 755 /data/local/tmp/mtk-su"这一步只需做一次,做完永久有效。所以本工具在自动化全部失败时,会自动导一份 mtk-su 到 /sdcard/mtk-su,并弹出「需要连一次电脑」的引导,附上面这条命令;点「我已完成,检测一下」直接重试。
⚠ 这里有个连原版 AwaAPK 都踩了的坑,值得单独说: 判定「是否跑通」用的是「输出里有没有
__AWA_END__」。但ProcessBuilder.start()失败时抛的IOException,它的 message 里会带完整命令行("Errorrunning exec(). Command: [mtk-su, -c, setprop … echo __AWA_END__]…"),而命令行本身含echo __AWA_END__。于是裸用contains()判断会把「进程根本没起来」判成「跑通了」——日志里写着EXCEPTION…却返回成功,真实失败原因被彻底盖住。修法是三道保险:① 异常消息里
Command: [ … Environment:整段抹掉;② 以EXCEPTION:开头的直接判失败;③ 结束标记必须单独占一行。tests/TestHasEnd.java有 16 条断言覆盖这个场景。
30 分钟。开着的时候同一 WiFi 下谁都能连上来,所以到点自动关。倒计时时间戳存 SharedPreferences,退到后台再回来接着算。应用被杀掉、或者你在电脑上 adb usb 手动关了,它不会自作主张再去关一次。
不直接跳设置主页,弹出 14 项入口列表(照原版):开发者选项 / 应用管理 / 存储 / 无线和网络 / 蓝牙 / 安全 / 位置信息 / 日期和时间 / 设备信息 / WLAN / 流量使用 / 电源使用 / 车机设置(ECARX)/ 全部设置。
每项用显式 ComponentName 直接拉对应 Activity —— 比 Intent action 准,不同 ROM 的 action 实现不一样。带两级兜底:显式组件打不开 → 退回对应 android.settings.* action → 退回设置主页。
这台 R621 会同时挂出 /storage/usb0~usb3 和 /storage/sdcard1,空的口也是个目录,光看路径分不出来。检测流程:
- 从
/proc/mounts读vfat / exfat / texfat / ntfs / fuseblk / msdos / iso9660 / udf的挂载点(排除内部存储) - 补
/storage下非 emulated 的目录(现代 Android 拿卷 ID 当挂载点,如/storage/1A2B-3C4D) - 补 24 条固定路径兜底
- 按「路径含
usb(+1000) → 有条目(≤+300) → 路径含extsd(+400)」排序,第一个就是自动选中的那个,不再弹「选一个」
顶栏「多选」(或长按条目 → 菜单最后一项「多选」)进入多选模式:
| 状态 | 工具栏 | 列表 |
|---|---|---|
| 普通 | 车机 · U盘 · 上一级 · 新建 · 粘贴 · 刷新 · 多选 | 点进入 / 长按出操作菜单 |
| 多选 | 全选 · 复制 · 剪切 · 删除 · 取消 | 点条目勾选;勾上的行整行变蓝 + 方框填实 + 徽章「已选」 |
几个刻意的选择:
- 勾选状态存绝对路径(
Set<String> selPaths),不存FileEntry对象 ——openDir()每刷新一次就把FileEntry全重建了,存对象的话勾选状态一刷新就丢。 - 换目录 / 换标签页自动清空勾选,返回键先退出多选 —— 勾的是上一个目录里的条目,留着会指错人。
- 批量复制 / 剪切只负责装剪贴板,粘贴那一头本来就是循环的(
paste()从最早那版就支持多项),直接复用,不另写一套拷贝逻辑。 - 按列表显示顺序取勾选项(
selFiles()遍历files而不是遍历HashSet)—— 一次拷几十个文件时,落到目标目录里的先后跟眼睛看到的顺序一致,出问题好排查。 - 批量删除放后台线程,底栏走
N/M进度;确认框里写明条数、总大小、有几个文件夹(文件夹会被连带删掉)。 - 勾选方框是纯色块,不是
✓字符 —— 车机字体不一定有这个字形,画成色块最保险。 - 行是复用的,
getView()开头先把方框和行背景还原成默认态,免得文件管理页勾选过的行背景串到另外两个标签上去。
| 东西 | 干什么 | 从哪来 |
|---|---|---|
| JDK 17 | javac + 跑 d8 |
adoptium.net |
r8.jar |
含 com.android.tools.r8.D8,class → dex |
maven.google.com |
android-all-22.jar |
Android 5.1 编译期库 | Robolectric |
python3 + cryptography |
打包签名 | pip install cryptography |
export JDK=/path/to/jdk-17.0.2
export ANDROID_JAR=/path/to/android-all-22.jar
export R8_JAR=/path/to/r8.jar
export PYTHON=python3
./build.sh # javac → d8 → 打包签名
python3 verify.py # 186 项出包校验产物在 dist/,第一次构建会生成签名私钥 tools/awa_key.pem(已在 .gitignore 里,别提交)。
- javac 必须用
-classpath,不能用-bootclasspath。android-all-22.jar里只有android/*和部分java/security/*,没有java/lang/*,用-bootclasspath会直接报Unable to find package java.lang。 - d8 那句
Type java.lang.Runnable was not found是警告不是错误。--lib只给了 android jar,没有 JDK 的java.lang,desugar 用不上,不影响产物。
REBOOT(重启)和 WRITE_SECURE_SETTINGS(ADB 调试开关)都是 signature|privileged 级权限,普通应用拿不到。
Android 5.1 没有 privapp 权限白名单,所以只要把本应用装进 /system/priv-app/,权限就自动生效,不用 root。装在 /data/app 当普通应用的话,这两个开关改不了 —— 本工具会把命令显示给你,不会自己去提权。
完整教程在工具里:「存储里的 APK」→ 点本工具的 APK → 安装教程「二、安装为系统程序」。
没用系统 Switch,因为 4.x 那个 Holo 胶囊又大又丑。照原版 ToggleSwitch 实现的 android.view.View 子类:
- 尺寸 56×30 dp
- 轨道
rect(0,0,w,h),圆角半径h/2,drawRoundRect - 滑块圆心
x = on ? (w - r) : r,y = r,半径r - dp(3) - 轨道:开
#FF33B5E5/ 关#FF5A5F6B/ 不可用-开#6633B5E5/ 不可用-关#FF44474F - 滑块:开
#FFFFFFFF/ 关#FFB0B0B0/ 不可用#FF8A93A6
├── AndroidManifest.xml 二进制清单(权限已调整)
├── resources.arsc 应用名已改成「Awa的安装助手」
├── build.sh 编译流水线
├── build_apk.py 装 APK + v1 签名
├── verify.py 186 项出包校验
├── src/
│ └── com/awa/apkinstaller/
│ ├── MainActivity.java 全部 UI 和功能(约 3000 行)
│ └── AboutActivity.java
├── assets/
│ └── mtk-su 内置提权工具,65,144 字节
├── res/
│ └── ic_launcher.png
├── tests/
│ └── TestHasEnd.java 提权跑通判定的单元测试(16 条断言)
└── docs/
└── 说明.html 完整说明文档
MIT。随便用、随便改、随便商用,只要求保留版权声明和许可声明。
关于 assets/mtk-su: 这是第三方二进制(来源 akkdiy.cn),它的授权情况不明。它只是被打进包里,本身不在本项目的 MIT 授权范围内 —— 你自己用时请注意这一点。
本工具只显示教程命令,不执行安装 / 卸载 / 提权(唯一例外是无线 ADB 开关)。把它装进 /system/priv-app 会改动系统分区,操作前备份。作者不对使用本工具造成的任何后果负责。
⚠ mtk-su 风险: 内置的 assets/mtk-su 是第三方内核漏洞利用程序,靠触发内核内存破坏拿 root。在这台 R621 上执行它会把内核搞挂 → 看门狗超时 → 整台车机死机重启。无线 ADB 一键开关与 4.1 提权教程都依赖它,在 R621 上请一律改用免 root 方案(adb tcpip / 装系统应用 / 电脑 adb install)。
目标设备:吉利 ECARX R621(MT6735 / Android 5.1 / API 22 / userdebug)。别的车机没测过。