Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 64 additions & 47 deletions .github/workflows/build-ksu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,42 @@ permissions:
contents: read

env:
ANDROID_HOME: /usr/local/lib/android/sdk
ANDROID_SDK_ROOT: /usr/local/lib/android/sdk
ANDROID_API: '36'
ANDROID_BUILD_TOOLS: '36.0.0'

jobs:
build-ksu:
runs-on: ubuntu-24.04

steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v6

- name: Set up Java 17 for Android SDK
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: '1.22.x'

- name: Install Android SDK components
shell: bash
run: |
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
yes | sdkmanager --licenses >/dev/null || true
sdkmanager "platforms;android-23" "build-tools;28.0.3"
go-version-file: vd-server-go/go.mod
cache: false

- name: Switch to Java 8 for legacy dx builds
uses: actions/setup-java@v5
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
with:
distribution: temurin
java-version: '8'
gradle-version: '9.5.0'
cache-provider: basic

- name: Prepare legacy build paths and Xposed API
- name: Install Android SDK components
shell: bash
run: |
set -euxo pipefail

sudo mkdir -p /usr/lib/android-sdk/platforms/android-23
sudo mkdir -p /usr/lib/android-sdk/build-tools/debian
sudo mkdir -p /root/xposed_lib

sudo ln -sf "$ANDROID_HOME/platforms/android-23/android.jar" /usr/lib/android-sdk/platforms/android-23/android.jar

sudo ln -sf "$ANDROID_HOME/build-tools/28.0.3/dx" /usr/lib/android-sdk/build-tools/debian/dx
sudo ln -sf "$ANDROID_HOME/build-tools/28.0.3/aapt" /usr/bin/aapt
sudo ln -sf "$ANDROID_HOME/build-tools/28.0.3/zipalign" /usr/bin/zipalign
sudo ln -sf "$ANDROID_HOME/build-tools/28.0.3/apksigner" /usr/bin/apksigner

curl -fL https://artifactory.appodeal.com/appodeal-public/de/robv/android/xposed/api/82/api-82.jar -o /tmp/xposed-api-stub.jar
sudo cp /tmp/xposed-api-stub.jar /root/xposed_lib/xposed-api-stub.jar
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
yes | sdkmanager --licenses >/dev/null || true
sdkmanager "platforms;android-$ANDROID_API" "build-tools;$ANDROID_BUILD_TOOLS"

- name: Prepare fixed APK signing key
shell: bash
Expand Down Expand Up @@ -92,38 +76,71 @@ jobs:
run: |
set -euxo pipefail
chmod +x agent-hook-apk/build.sh
sudo -E bash agent-hook-apk/build.sh
./agent-hook-apk/build.sh

- name: Build Java tools
shell: bash
run: |
set -euxo pipefail
chmod +x vd-tool-java/build.sh
sudo -E bash vd-tool-java/build.sh
./vd-tool-java/build.sh

- name: Test and build ARM64 Android vd_server
- name: Build Android ARM64 vd_server
shell: bash
run: |
set -euxo pipefail
pushd vd-server-go
go test ./...
CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build -ldflags="-s -w" -o vd_server .
popd
file vd-server-go/vd_server
chmod +x vd-server-go/build.sh
./vd-server-go/build.sh

- name: Pack complete KernelSU module
shell: bash
run: |
set -euxo pipefail
chmod +x ksu-module/pack.sh
bash ksu-module/pack.sh
unzip -l agent-mobile-use-ksu.zip
sha256sum agent-mobile-use-ksu.zip
./ksu-module/pack.sh

- name: Verify outputs and report sizes
shell: bash
run: |
set -euxo pipefail
VERSION="$(sed -n 's/^version=//p' ksu-module/module.prop)"
KSU_ZIP="release/agent-mobile-use-ksu-v${VERSION}.zip"

"$ANDROID_HOME/build-tools/$ANDROID_BUILD_TOOLS/apksigner" \
verify --verbose --print-certs agent-hook-apk/build/agent_hook.apk

APK_BADGING="$("$ANDROID_HOME/build-tools/$ANDROID_BUILD_TOOLS/aapt" dump badging agent-hook-apk/build/agent_hook.apk)"
printf '%s\n' "$APK_BADGING"
grep -Fq "package: name='com.agent.mobileuse' versionCode='800' versionName='0.8.0-alpha'" <<<"$APK_BADGING"
grep -Fq "sdkVersion:'26'" <<<"$APK_BADGING"
grep -Fq "targetSdkVersion:'28'" <<<"$APK_BADGING"

APK_XML="$("$ANDROID_HOME/build-tools/$ANDROID_BUILD_TOOLS/aapt" dump xmltree agent-hook-apk/build/agent_hook.apk AndroidManifest.xml)"
grep -Fq 'A: android:name(0x01010003)="xposedmodule"' <<<"$APK_XML"
grep -Fq 'A: android:name(0x01010003)="xposedminversion"' <<<"$APK_XML"
grep -Fq 'A: android:name(0x01010003)="xposedscope"' <<<"$APK_XML"

file vd-server-go/vd_server | grep -q "ARM aarch64"
test -s vd-tool-java/bin/agent_tools.dex
test -s vd-tool-java/bin/agent_vd.dex
unzip -t "$KSU_ZIP"

stat -c 'agent_hook.apk bytes=%s' agent-hook-apk/build/agent_hook.apk
stat -c 'agent_tools.dex bytes=%s' vd-tool-java/bin/agent_tools.dex
stat -c 'agent_vd.dex bytes=%s' vd-tool-java/bin/agent_vd.dex
stat -c 'vd_server bytes=%s' vd-server-go/vd_server
stat -c 'ksu_zip bytes=%s' "$KSU_ZIP"

echo '--- APK entries ---'
unzip -l agent-hook-apk/build/agent_hook.apk
echo '--- KSU entries ---'
unzip -l "$KSU_ZIP"
sha256sum "$KSU_ZIP"

- name: Upload KernelSU package
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: agent-mobile-use-ksu-main
path: agent-mobile-use-ksu.zip
name: agent-mobile-use-ksu
path: release/agent-mobile-use-ksu-v0.8.0-alpha.zip
if-no-files-found: error
retention-days: 14
9 changes: 1 addition & 8 deletions agent-hook-apk/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.agent.mobileuse"
android:versionCode="800"
android:versionName="0.8.0-alpha">

<uses-sdk
android:minSdkVersion="26"
android:targetSdkVersion="28" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Expand Down
79 changes: 79 additions & 0 deletions agent-hook-apk/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
plugins {
id("com.android.application")
}

val fixedKeystorePath = System.getenv("APK_SIGNING_KEYSTORE")
val fixedSigningPassword = System.getenv("APK_SIGNING_PASSWORD")

if (System.getenv("CI") == "true" &&
(fixedKeystorePath.isNullOrBlank() || fixedSigningPassword.isNullOrBlank())) {
error("CI release builds require APK_SIGNING_KEYSTORE and APK_SIGNING_PASSWORD")
}

android {
// This module is Java-only. AGP 9 enables built-in Kotlin by default,
// which otherwise adds kotlin-stdlib to the packaged APK.
enableKotlin = false

namespace = "com.agent.mobileuse"
compileSdk = 36

defaultConfig {
applicationId = "com.agent.mobileuse"
minSdk = 26
targetSdk = 28
versionCode = 800
versionName = "0.8.0-alpha"
}

sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.directories += "src"
res.directories += "res"
assets.directories += "assets"
}
}

signingConfigs {
if (!fixedKeystorePath.isNullOrBlank() && !fixedSigningPassword.isNullOrBlank()) {
create("fixed") {
storeFile = file(fixedKeystorePath)
storePassword = fixedSigningPassword
keyAlias = "agentmobileuse"
keyPassword = fixedSigningPassword
storeType = "PKCS12"
}
}
}

buildTypes {
getByName("debug") {
isMinifyEnabled = false
}
getByName("release") {
isDebuggable = false
isMinifyEnabled = false
signingConfig = signingConfigs.findByName("fixed")
?: signingConfigs.getByName("debug")
}
}

lint {
// This APK is sideloaded as part of a KernelSU/LSPosed module and is
// not published through Google Play. Keep targetSdk 28 unchanged so
// build-system modernization does not alter Android runtime behavior.
disable += "ExpiredTargetSdkVersion"
disable += "BlockedPrivateApi"
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

dependencies {
// LSPosed/Xposed provides these classes at runtime.
compileOnly("de.robv.android.xposed:api:82")
}
102 changes: 17 additions & 85 deletions agent-hook-apk/build.sh
Original file line number Diff line number Diff line change
@@ -1,95 +1,27 @@
#!/bin/bash
set -e
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

ANDROID_JAR="/usr/lib/android-sdk/platforms/android-23/android.jar"
XPOSED_JAR="/root/xposed_lib/xposed-api-stub.jar"
DX="/usr/lib/android-sdk/build-tools/debian/dx"
AAPT="/usr/bin/aapt"
APKSIGNER="/usr/bin/apksigner"

rm -rf build
mkdir -p build/gen build/classes build/apk

echo "[build] 1. Generating R.java and initial package with aapt..."
"$AAPT" package -f -m -0 arsc \
-S res \
-J build/gen \
-M AndroidManifest.xml \
-I "$ANDROID_JAR" \
-F build/apk/unaligned.apk

echo "[build] 2. Compiling Java sources..."
mkdir -p build/stubs_classes
if [ -d "stubs" ]; then
javac -proc:none -source 1.8 -target 1.8 -cp "$ANDROID_JAR" $(find stubs -name "*.java") -d build/stubs_classes
fi

javac -proc:none -source 1.8 -target 1.8 \
-cp "$ANDROID_JAR:$XPOSED_JAR:build/stubs_classes" \
$(find src build/gen -name "*.java") \
-d build/classes

echo "[build] 3. Converting classes to classes.dex..."
cd build/classes
"$DX" --dex --output=../classes.dex $(find . -name "*.class")
cd "$SCRIPT_DIR"

echo "[build] 4. Adding classes.dex and assets to APK..."
cd build
"$AAPT" add "apk/unaligned.apk" "classes.dex"
cd "$SCRIPT_DIR"
for a in $(find assets -type f); do
"$AAPT" add "build/apk/unaligned.apk" "$a"
done

echo "[build] 5. Zipaligning APK to 4-byte boundary..."
zipalign -p -f 4 "build/apk/unaligned.apk" "build/apk/aligned.apk"

echo "[build] 6. Signing APK..."
SIGNING_KEYSTORE="${APK_SIGNING_KEYSTORE:-}"
SIGNING_PASSWORD="${APK_SIGNING_PASSWORD:-}"
SIGNING_ALIAS="agentmobileuse"

if [ -n "$SIGNING_KEYSTORE" ]; then
if [ ! -f "$SIGNING_KEYSTORE" ]; then
echo "[build] ERROR: APK_SIGNING_KEYSTORE does not exist: $SIGNING_KEYSTORE" >&2
exit 1
fi
if [ -z "$SIGNING_PASSWORD" ]; then
echo "[build] ERROR: APK_SIGNING_PASSWORD is required for fixed signing" >&2
exit 1
fi
echo "[build] Using configured fixed signing keystore (alias: $SIGNING_ALIAS)"
if [ -x "$ROOT/gradlew" ]; then
GRADLE_CMD="$ROOT/gradlew"
else
if [ "${CI:-}" = "true" ]; then
echo "[build] ERROR: CI builds require APK_SIGNING_KEYSTORE and APK_SIGNING_PASSWORD" >&2
exit 1
fi
GRADLE_CMD="${GRADLE_CMD:-gradle}"
command -v "$GRADLE_CMD" >/dev/null
fi

echo "[build] No fixed keystore configured; using local debug keystore"
SIGNING_KEYSTORE="/root/debug.keystore"
SIGNING_PASSWORD="android"
SIGNING_ALIAS="androiddebugkey"
echo "[build] Building release hook APK with Gradle/AGP..."
"$GRADLE_CMD" -p "$ROOT" :agent-hook-apk:assembleRelease

if [ ! -f "$SIGNING_KEYSTORE" ]; then
keytool -genkey -v -keystore "$SIGNING_KEYSTORE" \
-storepass "$SIGNING_PASSWORD" \
-alias "$SIGNING_ALIAS" \
-keypass "$SIGNING_PASSWORD" \
-keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=Android Debug,O=Android,C=US"
fi
APK_SRC="$(find "$SCRIPT_DIR/build/outputs/apk/release" -maxdepth 1 -type f -name '*.apk' | sort | head -n 1)"
if [ -z "$APK_SRC" ] || [ ! -f "$APK_SRC" ]; then
echo "[build] ERROR: release APK was not produced" >&2
exit 1
fi

"$APKSIGNER" sign --ks "$SIGNING_KEYSTORE" \
--ks-pass "pass:$SIGNING_PASSWORD" \
--ks-key-alias "$SIGNING_ALIAS" \
--key-pass "pass:$SIGNING_PASSWORD" \
--out "build/agent_hook.apk" \
"build/apk/aligned.apk"
cp -f "$APK_SRC" "$SCRIPT_DIR/build/agent_hook.apk"

echo "[build] Build successful: build/agent_hook.apk"
ls -lh build/agent_hook.apk
ls -lh "$SCRIPT_DIR/build/agent_hook.apk"
stat -c '[build] agent_hook.apk bytes=%s' "$SCRIPT_DIR/build/agent_hook.apk"
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
id("com.android.application") version "9.3.0" apply false
}
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
20 changes: 20 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://api.xposed.info/") }
maven { url = uri("https://artifactory.appodeal.com/appodeal-public") }
}
}

rootProject.name = "agent-mobile-use-plus"
include(":agent-hook-apk")
Loading