diff --git a/.github/workflows/android-apk.yml b/.github/workflows/android-apk.yml index 7fbe29d4f..977d07174 100644 --- a/.github/workflows/android-apk.yml +++ b/.github/workflows/android-apk.yml @@ -299,6 +299,10 @@ jobs: working-directory: openless-all/app run: node scripts/patch-android-shizuku-deps.mjs + - name: Patch rustls-platform-verifier (Core TLS on Android) + working-directory: openless-all/app + run: node scripts/patch-android-rustls-verifier.mjs + - name: Merge updater / install manifest working-directory: openless-all/app run: node scripts/merge-android-updater-manifest.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d3f9075c..0ed67ff51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -247,6 +247,9 @@ jobs: - name: Patch Shizuku Gradle dependencies run: node scripts/patch-android-shizuku-deps.mjs + - name: Patch rustls-platform-verifier (Core TLS on Android) + run: node scripts/patch-android-rustls-verifier.mjs + - name: Run JVM credential tests and compile instrumentation tests # These tests are Kotlin-only. The Rust target is checked above; direct Gradle # rustBuild requires the live Tauri CLI RPC server used by `tauri android build`. diff --git a/openless-all/app/android/README.md b/openless-all/app/android/README.md index 68f47f08b..918093c6f 100644 --- a/openless-all/app/android/README.md +++ b/openless-all/app/android/README.md @@ -39,6 +39,7 @@ Manifest 合并脚本: - [`scripts/merge-android-overlay-manifest.mjs`](../scripts/merge-android-overlay-manifest.mjs) — 悬浮窗 / 无障碍 - [`scripts/merge-android-shizuku-manifest.mjs`](../scripts/merge-android-shizuku-manifest.mjs) — Shizuku Provider / 授权 Activity - [`scripts/patch-android-shizuku-deps.mjs`](../scripts/patch-android-shizuku-deps.mjs) — Shizuku Gradle 依赖 +- [`scripts/patch-android-rustls-verifier.mjs`](../scripts/patch-android-rustls-verifier.mjs) — rustls-platform-verifier(Core 的 HTTPS 证书校验)Gradle 依赖与 Manifest 调整 ### Rime 拼音输入(Pinyin,可选模式) @@ -88,6 +89,7 @@ node scripts/merge-android-v1-manifest.mjs node scripts/merge-android-overlay-manifest.mjs node scripts/merge-android-shizuku-manifest.mjs node scripts/patch-android-shizuku-deps.mjs +node scripts/patch-android-rustls-verifier.mjs CI=true npm run tauri:android:build ``` @@ -103,6 +105,7 @@ node scripts/merge-android-v1-manifest.mjs node scripts/merge-android-overlay-manifest.mjs node scripts/merge-android-shizuku-manifest.mjs node scripts/patch-android-shizuku-deps.mjs +node scripts/patch-android-rustls-verifier.mjs npm run tauri:android:build ``` diff --git a/openless-all/app/scripts/build-android-local.sh b/openless-all/app/scripts/build-android-local.sh index f22a52992..6d4608e37 100644 --- a/openless-all/app/scripts/build-android-local.sh +++ b/openless-all/app/scripts/build-android-local.sh @@ -26,5 +26,6 @@ node scripts/merge-android-v1-manifest.mjs node scripts/merge-android-overlay-manifest.mjs node scripts/merge-android-shizuku-manifest.mjs node scripts/patch-android-shizuku-deps.mjs +node scripts/patch-android-rustls-verifier.mjs node scripts/merge-android-updater-manifest.mjs npm run tauri:android:build:debug diff --git a/openless-all/app/scripts/patch-android-rustls-verifier.mjs b/openless-all/app/scripts/patch-android-rustls-verifier.mjs new file mode 100644 index 000000000..b55bd7042 --- /dev/null +++ b/openless-all/app/scripts/patch-android-rustls-verifier.mjs @@ -0,0 +1,108 @@ +#!/usr/bin/env node +// openless-core's reqwest 0.13 verifies TLS through rustls-platform-verifier. +// On Android its Kotlin half (org.rustls:rustls-platform-verifier) must be in +// the APK, and Rust must initialize it with the app Context +// (src-tauri/src/android/tls_verifier.rs) — otherwise every Core HTTPS request +// panics. This wires the Kotlin half into the generated Gradle project. +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import process from 'node:process'; + +const appDir = fileURLToPath(new URL('../src-tauri/gen/android/app/', import.meta.url)); +const gradlePath = `${appDir}build.gradle.kts`; +const manifestPath = `${appDir}src/main/AndroidManifest.xml`; +const proguardPath = `${appDir}proguard-rustls-platform-verifier.pro`; + +export const GRADLE_MARKER = '// openless: rustls-platform-verifier'; + +// The AAR version must equal the rustls-platform-verifier-android crate in +// src-tauri/Cargo.lock, so Gradle reads it from there at configuration time. +// A local Maven copy (OPENLESS_RUSTLS_MAVEN, or /rustls-maven) +// is preferred; the project's GitHub Maven archive is the fallback. +export const GRADLE_SNIPPET = ` +${GRADLE_MARKER} (Kotlin half of Core's TLS verifier; see scripts/patch-android-rustls-verifier.mjs) +val rustlsVerifierVersion: String = rootProject.file("../../Cargo.lock").readLines().let { lines -> + val start = lines.indexOfFirst { it.trim() == "name = \\"rustls-platform-verifier-android\\"" } + require(start >= 0) { "rustls-platform-verifier-android not found in src-tauri/Cargo.lock" } + lines.drop(start + 1).first { it.trimStart().startsWith("version = ") } + .substringAfter('"').substringBefore('"') +} +repositories { + val localMaven = file( + System.getenv("OPENLESS_RUSTLS_MAVEN") + ?: ((System.getenv("OPENLESS_CACHE_ROOT") ?: "/data-win/openless-cache") + "/rustls-maven"), + ) + if (localMaven.isDirectory) maven { url = uri(localMaven) } + maven { url = uri("https://github.com/rustls/rustls-platform-verifier/raw/maven-archive/android-release-support/maven/") } +} +dependencies { + implementation("org.rustls:rustls-platform-verifier:$rustlsVerifierVersion") +} +`; + +export const PROGUARD_RULES = `# The Rust verifier finds these classes by name through JNI. +-keep class org.rustls.platformverifier.** { *; } +`; + +const TOOLS_NS = 'xmlns:tools="http://schemas.android.com/tools"'; +export const REMOVE_ATTRIBUTE = 'tools:remove="android:networkSecurityConfig"'; + +export function patchGradle(content) { + if (content.includes(GRADLE_MARKER)) return { content, changed: false }; + return { content: `${content.trimEnd()}\n${GRADLE_SNIPPET}`, changed: true }; +} + +// The AAR's manifest sets a networkSecurityConfig that forbids cleartext +// traffic, which would silently override the app's usesCleartextTraffic. +// Its revocation checks are soft-fail, so dropping it keeps TLS working. +export function patchManifest(content) { + let updated = content; + if (!/]*xmlns:tools=/.test(updated)) { + updated = updated.replace(/\n\n' + + '\n\n\n'; +const patchedManifest = patchManifest(manifest); +assert.equal(patchedManifest.changed, true); +assert.match(patchedManifest.content, / Result<(), jni022::errors::Error> { + let context = jni022::objects::JObject::from_raw(env, raw_context.cast()); + rustls_platform_verifier::android::init_with_env(env, context) + }) + .resolve_with::(|| { + "[android-tls] rustls-platform-verifier initialization".to_string() + }); +}