mirror of
git://f0xx.org/android_cast
synced 2026-07-29 02:59:00 +03:00
vpn service sync
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# CMake in ndk/ autolinks when those archives exist (ANDROIDCAST_HAVE_* = 1).
|
||||
#
|
||||
# Prerequisites:
|
||||
# git submodule update --init third-party/libvpx
|
||||
# bash scripts/init-third-party-submodules.sh
|
||||
# Android NDK (auto-detected via scripts/android-ndk.sh)
|
||||
#
|
||||
# Usage:
|
||||
@@ -22,7 +22,7 @@ BUILD_DIR="$OUT/libvpx-build"
|
||||
|
||||
if [[ ! -d "$VPX_SRC_REAL" ]]; then
|
||||
echo "ERROR: $VPX_SRC_REAL missing. Run:"
|
||||
echo " git submodule update --init third-party/libvpx"
|
||||
echo " bash scripts/init-third-party-submodules.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -253,7 +253,7 @@ androidcast_ndk_configure_host() {
|
||||
build_opus_for_abi() {
|
||||
local opus_src="$ROOT/third-party/opus"
|
||||
if [[ ! -f "$opus_src/CMakeLists.txt" ]]; then
|
||||
echo "Skip libopus: $opus_src missing (git submodule update --init third-party/opus)"
|
||||
echo "Skip libopus: $opus_src missing (bash scripts/init-third-party-submodules.sh)"
|
||||
return 0
|
||||
fi
|
||||
local opus_build="$BUILD_DIR/opus-cmake"
|
||||
@@ -284,7 +284,7 @@ build_opus_for_abi() {
|
||||
build_speex_for_abi() {
|
||||
local speex_src_real="$ROOT/third-party/speex"
|
||||
if [[ ! -d "$speex_src_real" ]]; then
|
||||
echo "Skip libspeex: $speex_src_real missing (git submodule update --init third-party/speex)"
|
||||
echo "Skip libspeex: $speex_src_real missing (bash scripts/init-third-party-submodules.sh)"
|
||||
return 0
|
||||
fi
|
||||
local speex_src="$speex_src_real"
|
||||
|
||||
@@ -61,6 +61,9 @@ if [[ -n "${ANDROID_SDK_ROOT:-}" ]]; then
|
||||
printf 'sdk.dir=%s\n' "${ANDROID_SDK_ROOT//\\/\\\\}" > "$LOCAL_PROPS"
|
||||
fi
|
||||
|
||||
echo "==> Phase: third-party submodules (libvpx, opus, speex, wireguard-android)"
|
||||
bash "$ROOT/scripts/init-third-party-submodules.sh"
|
||||
|
||||
if [[ "$RUN_NATIVE" == "1" && "$RUN_APK" == "1" ]]; then
|
||||
echo "==> Phase: native codecs"
|
||||
ABIS="${ANDROIDCAST_CI_ABIS:-armeabi-v7a arm64-v8a x86_64}"
|
||||
@@ -75,17 +78,21 @@ fi
|
||||
|
||||
# Avoid host-specific CMake cache paths when running in Docker.
|
||||
rm -rf "$ROOT/app/.cxx" "$ROOT/app/build/.cxx" "$ROOT/app/build/intermediates/cxx"
|
||||
rm -rf "$ROOT/gradle/wireguard-tunnel/.cxx" "$ROOT/gradle/wireguard-tunnel/build" 2>/dev/null || true
|
||||
|
||||
WG_HEAD="$(git -C "$ROOT/third-party/wireguard-android" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||
echo "==> Phase: wireguard-android (:tunnel) third-party/wireguard-android @ ${WG_HEAD}"
|
||||
|
||||
if [[ "$RUN_APK" == "1" ]]; then
|
||||
echo "==> Phase: gradle ${GRADLE_TASK}"
|
||||
echo "==> Phase: gradle :tunnel + ${GRADLE_TASK}"
|
||||
if [[ "$RUN_UNIT_TESTS" == "1" ]]; then
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean "${GRADLE_TASK}" testDebugUnitTest
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean :tunnel:assembleDebug "${GRADLE_TASK}" testDebugUnitTest
|
||||
else
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean "${GRADLE_TASK}"
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" clean :tunnel:assembleDebug "${GRADLE_TASK}"
|
||||
fi
|
||||
else
|
||||
echo "==> Phase: gradle tests-only"
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" testDebugUnitTest
|
||||
echo "==> Phase: gradle tests-only (:tunnel + testDebugUnitTest)"
|
||||
./gradlew --no-daemon "${GRADLE_JAVA_OPT[@]}" :tunnel:assembleDebug testDebugUnitTest
|
||||
fi
|
||||
|
||||
APK=""
|
||||
|
||||
@@ -14,9 +14,8 @@ source "$ROOT/scripts/android-ndk.sh"
|
||||
export ANDROID_NDK_HOME="$(androidcast_find_ndk_root "$ROOT")"
|
||||
export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME"
|
||||
|
||||
if [[ -f .gitmodules ]]; then
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
echo "==> Initializing third-party git submodules"
|
||||
bash "$ROOT/scripts/init-third-party-submodules.sh"
|
||||
|
||||
ABIS="${ANDROIDCAST_CI_ABIS:-armeabi-v7a arm64-v8a x86_64}"
|
||||
echo "==> Building libvpx for: $ABIS"
|
||||
@@ -26,7 +25,7 @@ for abi in $ABIS; do
|
||||
./scripts/build-native-codecs.sh "$abi"
|
||||
done
|
||||
|
||||
echo "==> Gradle assembleDebug + unit tests"
|
||||
./gradlew --no-daemon clean assembleDebug testDebugUnitTest
|
||||
echo "==> Gradle :tunnel + assembleDebug + unit tests"
|
||||
./gradlew --no-daemon clean :tunnel:assembleDebug assembleDebug testDebugUnitTest
|
||||
|
||||
echo "==> Done: $ROOT/app/build/outputs/apk/debug/app-debug.apk"
|
||||
|
||||
110
scripts/init-third-party-submodules.sh
Executable file
110
scripts/init-third-party-submodules.sh
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync and initialize all third-party/ git submodules from .gitmodules (recursive nested).
|
||||
# Used by rebuild.sh, ci-build.sh, and deploy paths — no network except submodule fetch/update.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
ONLY_PATH=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--only)
|
||||
ONLY_PATH="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--only third-party/wireguard-android]"
|
||||
echo " Initializes every path in .gitmodules under third-party/ (recursive)."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown argument: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f .gitmodules ]]; then
|
||||
echo "error: .gitmodules not found in $ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mapfile -t SUB_PATHS < <(git config -f .gitmodules --get-regexp '^submodule\..*\.path$' \
|
||||
| awk '{ print $2 }' | grep '^third-party/' | sort -u)
|
||||
|
||||
if [[ ${#SUB_PATHS[@]} -eq 0 ]]; then
|
||||
echo "error: no third-party paths in .gitmodules" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$ONLY_PATH" ]]; then
|
||||
SUB_PATHS=("$ONLY_PATH")
|
||||
fi
|
||||
|
||||
ensure_gitlink() {
|
||||
local path="$1"
|
||||
local name="$path"
|
||||
if git cat-file -e "HEAD:${path}" 2>/dev/null \
|
||||
|| git ls-files --stage "$path" | grep -q '^160000'; then
|
||||
return 0
|
||||
fi
|
||||
local url
|
||||
url="$(git config -f .gitmodules --get "submodule.${name}.url" || true)"
|
||||
if [[ -z "$url" ]]; then
|
||||
echo "error: $path listed in .gitmodules but no gitlink in tree and no url" >&2
|
||||
return 1
|
||||
fi
|
||||
local sha
|
||||
sha="$(git ls-remote "$url" HEAD | awk 'NR==1 {print $1}')"
|
||||
if [[ -z "$sha" ]]; then
|
||||
echo "error: could not resolve HEAD for $url (needed to register missing gitlink)" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "WARN: registering missing gitlink for $path at $sha (commit this gitlink)" >&2
|
||||
git update-index --add --cacheinfo 160000,"$sha","$path"
|
||||
}
|
||||
|
||||
validate_path() {
|
||||
local path="$1"
|
||||
case "$path" in
|
||||
third-party/wireguard-android)
|
||||
[[ -d "$path/tunnel/src/main/java" ]] \
|
||||
&& [[ -f "$path/tunnel/tools/wireguard-tools/src/config.c" ]] \
|
||||
&& [[ -f "$path/tunnel/tools/elf-cleaner/elf-cleaner.cpp" || -f "$path/tunnel/tools/elf-cleaner/main.cpp" ]]
|
||||
;;
|
||||
third-party/libvpx)
|
||||
[[ -d "$path" ]]
|
||||
;;
|
||||
third-party/opus|third-party/speex)
|
||||
[[ -f "$path/configure" || -f "$path/CMakeLists.txt" || -d "$path/include" ]]
|
||||
;;
|
||||
*)
|
||||
[[ -d "$path" ]]
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo "==> third-party submodules: sync + update --init --recursive"
|
||||
for path in "${SUB_PATHS[@]}"; do
|
||||
if ! grep -q "$path" .gitmodules 2>/dev/null; then
|
||||
echo "error: $path not in .gitmodules" >&2
|
||||
exit 1
|
||||
fi
|
||||
ensure_gitlink "$path"
|
||||
git submodule sync "$path"
|
||||
git submodule update --init --recursive "$path"
|
||||
if ! validate_path "$path"; then
|
||||
echo "error: $path incomplete after submodule update (nested submodules missing?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
short="$(git -C "$path" rev-parse --short HEAD 2>/dev/null || echo '?')"
|
||||
echo " $path @ $short"
|
||||
done
|
||||
|
||||
if [[ -d third-party/wireguard-android/tunnel ]]; then
|
||||
wg_tools="$(git -C third-party/wireguard-android/tunnel/tools/wireguard-tools rev-parse --short HEAD 2>/dev/null || echo '?')"
|
||||
echo " third-party/wireguard-android/tunnel/tools/wireguard-tools @ $wg_tools"
|
||||
fi
|
||||
|
||||
echo "==> third-party submodules ready"
|
||||
@@ -1,36 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Initialize wireguard-android submodule (third-party/wireguard-android in .gitmodules).
|
||||
# Back-compat wrapper — wireguard-android is one of the third-party submodules.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
SUB_PATH="third-party/wireguard-android"
|
||||
SUB_NAME="third-party/wireguard-android"
|
||||
|
||||
if ! grep -q "$SUB_PATH" .gitmodules 2>/dev/null; then
|
||||
echo "error: .gitmodules missing $SUB_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# .gitmodules without a gitlink in the tree breaks plain `git submodule update`.
|
||||
if ! git cat-file -e "HEAD:$SUB_PATH" 2>/dev/null \
|
||||
&& ! git ls-files --stage "$SUB_PATH" | grep -q '^160000'; then
|
||||
url="$(git config -f .gitmodules --get "submodule.$SUB_NAME.url" || true)"
|
||||
if [ -z "$url" ]; then
|
||||
echo "error: no url for submodule.$SUB_NAME in .gitmodules" >&2
|
||||
exit 1
|
||||
fi
|
||||
sha="$(git ls-remote "$url" HEAD | awk 'NR==1 {print $1}')"
|
||||
if [ -z "$sha" ]; then
|
||||
echo "error: could not resolve HEAD for $url" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "registering missing gitlink for $SUB_PATH at $sha"
|
||||
git update-index --add --cacheinfo 160000,"$sha","$SUB_PATH"
|
||||
echo "warning: gitlink was missing from the tree — commit third-party/wireguard-android after init" >&2
|
||||
fi
|
||||
|
||||
git submodule sync "$SUB_PATH"
|
||||
git submodule update --init --recursive "$SUB_PATH"
|
||||
echo "wireguard-android ready under $SUB_PATH"
|
||||
echo "Gradle includes :tunnel when $SUB_PATH/tunnel exists."
|
||||
exec "$ROOT/scripts/init-third-party-submodules.sh" --only third-party/wireguard-android
|
||||
|
||||
196
scripts/simulate-vpn-crashes.sh
Executable file
196
scripts/simulate-vpn-crashes.sh
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env bash
|
||||
# VPN crash simulation lab — device (adb) and/or BE upload verification.
|
||||
#
|
||||
# Modes:
|
||||
# MODE=device — trigger VpnCrashSimulator in :vpn via adb (debug APK)
|
||||
# MODE=upload — POST synthetic vpn-process crash JSON to BE upload API
|
||||
# MODE=both — device then verify BE (default when adb device present)
|
||||
#
|
||||
# Env:
|
||||
# COUNT=70
|
||||
# BASE=https://apps.f0xx.org/app/androidcast_project/crashes
|
||||
# RUN_ID=YYYYMMDD_HHMMSS (batch tag for Issues search)
|
||||
# PACKAGE=com.foxx.androidcast
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
COUNT="${COUNT:-70}"
|
||||
BASE="${BASE:-https://apps.f0xx.org/app/androidcast_project/crashes}"
|
||||
UPLOAD_URL="$BASE/api/upload.php"
|
||||
PACKAGE="${PACKAGE:-com.foxx.androidcast}"
|
||||
RUN_ID="${RUN_ID:-$(date -u +%Y%m%d_%H%M%S)}"
|
||||
MODE="${MODE:-auto}"
|
||||
JAR="${JAR:-/tmp/ac_vpn_crash_cookies.txt}"
|
||||
DELAY_MS="${DELAY_MS:-80}"
|
||||
VERIFY_LOGIN="${VERIFY_LOGIN:-1}"
|
||||
|
||||
log() { printf '[vpn-crash-sim] %s\n' "$*"; }
|
||||
|
||||
has_device() {
|
||||
adb devices 2>/dev/null | awk 'NR>1 && $2=="device" { found=1 } END { exit !found }'
|
||||
}
|
||||
|
||||
simulate_device() {
|
||||
local n="$1"
|
||||
local i seq
|
||||
log "device: triggering $n VPN-process crash recordings via $PACKAGE"
|
||||
for ((i = 1; i <= n; i++)); do
|
||||
seq="$i"
|
||||
adb shell am start-service \
|
||||
-n "$PACKAGE/com.foxx.androidcast.vpn.AndroidCastVpnService" \
|
||||
-a com.foxx.androidcast.vpn.SIMULATE_CRASH \
|
||||
--ei sim_seq "$seq" >/dev/null 2>&1 \
|
||||
|| adb shell am startservice \
|
||||
-n "$PACKAGE/com.foxx.androidcast.vpn.AndroidCastVpnService" \
|
||||
-a com.foxx.androidcast.vpn.SIMULATE_CRASH \
|
||||
--ei sim_seq "$seq" >/dev/null 2>&1 \
|
||||
|| true
|
||||
if [[ "$DELAY_MS" -gt 0 ]]; then
|
||||
sleep "$(awk "BEGIN { print $DELAY_MS / 1000 }")"
|
||||
fi
|
||||
if (( i % 10 == 0 )); then
|
||||
log " device progress $i/$n"
|
||||
fi
|
||||
done
|
||||
log "device: done — waiting for crash watcher upload sweep"
|
||||
sleep 15
|
||||
}
|
||||
|
||||
upload_one() {
|
||||
local seq="$1"
|
||||
local report_id="vpn_sim_${RUN_ID}_$(printf '%03d' "$seq")"
|
||||
local ms
|
||||
ms="$(date +%s)000"
|
||||
local fp
|
||||
fp="$(printf 'vpn_sim_%s_%03d' "$RUN_ID" "$seq" | sha256sum | awk '{print $1}')"
|
||||
local body
|
||||
body="$(cat <<EOF
|
||||
{
|
||||
"schema_version": 1,
|
||||
"report_id": "$report_id",
|
||||
"generated_at_epoch_ms": $ms,
|
||||
"crash_type": "java",
|
||||
"scenario": "vpn_service",
|
||||
"process": "com.foxx.androidcast:vpn",
|
||||
"fingerprint": "$fp",
|
||||
"tags": ["vpn_sim", "$RUN_ID"],
|
||||
"device": {
|
||||
"manufacturer": "SimLab",
|
||||
"brand": "AndroidCast",
|
||||
"model": "VpnCrashSim",
|
||||
"device": "vpn_sim",
|
||||
"product": "vpn_sim",
|
||||
"sdk_int": 34,
|
||||
"release": "14",
|
||||
"abis": ["arm64-v8a"]
|
||||
},
|
||||
"app": {
|
||||
"package": "$PACKAGE",
|
||||
"version_name": "0.1.0",
|
||||
"version_code": 100
|
||||
},
|
||||
"build": { "debug": true, "git_commit": "vpn_sim" },
|
||||
"runtime_context": { "vpn_sim_seq": $seq, "run_id": "$RUN_ID" },
|
||||
"java": {
|
||||
"thread": "androidcast-vpn-worker",
|
||||
"exception": "java.lang.RuntimeException",
|
||||
"message": "VpnCrashSimulation seq=$seq proc=com.foxx.androidcast:vpn",
|
||||
"stack_frames": [
|
||||
"com.foxx.androidcast.vpn.WireGuardVpnEngine.apply(WireGuardVpnEngine.java:40)",
|
||||
"com.foxx.androidcast.vpn.AndroidCastVpnService\$1.applyWireGuardConfig(AndroidCastVpnService.java:60)",
|
||||
"com.foxx.androidcast.vpn.AndroidCastVpnService.onStartCommand(AndroidCastVpnService.java:95)",
|
||||
"android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5001)"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
curl -sk -w '\n%{http_code}' -H 'Content-Type: application/json' --data-binary "$body" "$UPLOAD_URL"
|
||||
}
|
||||
|
||||
simulate_upload() {
|
||||
local n="$1"
|
||||
local ok=0 fail=0 dup=0
|
||||
log "upload: posting $n vpn-process crash reports to $UPLOAD_URL (run_id=$RUN_ID)"
|
||||
local i resp code
|
||||
for ((i = 1; i <= n; i++)); do
|
||||
resp="$(upload_one "$i")"
|
||||
code="$(echo "$resp" | tail -n1)"
|
||||
case "$code" in
|
||||
200) ((ok++)) || true ;;
|
||||
409) ((dup++)) || true ;;
|
||||
*) ((fail++)) || true; log " FAIL seq=$i HTTP $code" ;;
|
||||
esac
|
||||
if (( i % 10 == 0 )); then
|
||||
log " upload progress $i/$n (ok=$ok dup=$dup fail=$fail)"
|
||||
fi
|
||||
if [[ "$DELAY_MS" -gt 0 ]]; then
|
||||
sleep "$(awk "BEGIN { print $DELAY_MS / 1000 }")"
|
||||
fi
|
||||
done
|
||||
log "upload: ok=$ok duplicate=$dup failed=$fail / $n"
|
||||
[[ "$fail" -eq 0 ]]
|
||||
}
|
||||
|
||||
verify_be() {
|
||||
if [[ "$VERIFY_LOGIN" != "1" ]]; then
|
||||
log "verify: skipped (VERIFY_LOGIN=$VERIFY_LOGIN)"
|
||||
return 0
|
||||
fi
|
||||
log "verify: login + search Issues for run_id=$RUN_ID"
|
||||
curl -sk -c "$JAR" -b "$JAR" -L -X POST "$BASE/login" \
|
||||
-d 'username=admin&password=admin' -o /dev/null || true
|
||||
local json
|
||||
local q="${QUERY:-$RUN_ID}"
|
||||
json="$(curl -sk -b "$JAR" "$BASE/api/reports.php?q=${q}&per_page=200")"
|
||||
local cnt
|
||||
cnt="$(echo "$json" | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
items=d.get('items') or []
|
||||
print(len(items))
|
||||
" 2>/dev/null || echo 0)"
|
||||
log "verify: BE reports matching q=$q → $cnt (expected up to $COUNT)"
|
||||
if [[ "$cnt" -lt 1 ]]; then
|
||||
log "verify: WARN — Issues API returned 0 (login cookie may be invalid in automation)"
|
||||
log "verify: open $BASE/?view=reports&q=$q in browser when logged in"
|
||||
return 1
|
||||
fi
|
||||
echo "$json" | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
for it in (d.get('items') or [])[:3]:
|
||||
p=it.get('payload') or {}
|
||||
j=p.get('java') or {}
|
||||
print(' sample:', it.get('report_id'), 'process=', p.get('process'), 'frames=', len(j.get('stack_frames') or []))
|
||||
" 2>/dev/null || true
|
||||
}
|
||||
|
||||
if [[ "$MODE" == "auto" ]]; then
|
||||
if has_device; then
|
||||
MODE=both
|
||||
else
|
||||
MODE=upload
|
||||
log "no adb device — using upload-only mode"
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
device)
|
||||
simulate_device "$COUNT"
|
||||
;;
|
||||
upload)
|
||||
simulate_upload "$COUNT"
|
||||
verify_be || true
|
||||
;;
|
||||
both)
|
||||
simulate_device "$COUNT"
|
||||
QUERY="${QUERY:-VpnCrashSimulation}" verify_be || true
|
||||
;;
|
||||
*)
|
||||
echo "unknown MODE=$MODE (device|upload|both)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log "complete run_id=$RUN_ID — Issues: $BASE/?view=reports&q=$RUN_ID"
|
||||
Reference in New Issue
Block a user