#!/usr/bin/env bash # Fetch the inference runtime the Android APK carries (docs/dev/inference.md §3). # # ./tools/fetch-android-runtime.sh [DEST] # # Two Maven artefacts, pinned to each other by ONNX Runtime's own POM: # # com.microsoft.onnxruntime:onnxruntime-android-qnn MIT # com.qualcomm.qti:qnn-runtime Qualcomm AI Engine Direct SDK licence # # The first is ONNX Runtime built with the CPU, QNN, XNNPACK, NNAPI and WebGPU # providers; the second is Qualcomm's HTP backend — the ARM-side compiler and # the per-generation Hexagon "skel" the DSP loads. Both ship as AARs whose # `jni/arm64-v8a/` is what an APK's `lib/arm64-v8a/` wants, so this script # unpacks exactly that and nothing else, plus the licence texts, which travel # with the libraries (§3.1). # # ## What is and is not taken from the Qualcomm package # # Every HTP generation has its own skel and stub, 16 MB a pair. `QNN_HTP_ARCHS` # names the ones to ship; the default is the generations in devices sold since # the 8 Gen 2 (V73 — the MagicPad 2's 8s Gen 3 is one), through the 8 Elite # (V79) and its successor (V81). V66–V69 are 2020–2021 silicon and are left # out, which is 50 MB the tablet never loads. `libQnnHtpPrepare.so`, the # on-device graph compiler, is 84 MB and cannot be left out: it is what turns # an int8 ONNX graph into something the DSP runs, once per device (§5). # # `libQnnGpu.so` and `libQnnDsp*.so` are not taken: the Adreno rung measured # slower than the Hexagon everywhere the Hexagon exists (§1.1), and the cDSP # path is the pre-HTP generation. # # ## Why a script and not a checked-in copy # # 150 MB of vendor binaries in LFS, re-fetched by every clone, for files that # Maven serves with checksums. The cache directory keeps them across builds; # CI's is the target cache it already mounts. set -euo pipefail ORT_VERSION="${ORT_VERSION:-1.29.0}" QNN_VERSION="${QNN_VERSION:-2.42.0}" QNN_HTP_ARCHS="${QNN_HTP_ARCHS:-73 75 79 81}" DEST="${1:-${PWD}/target-android/runtime}" MAVEN="https://repo1.maven.org/maven2" fetch() { # A Maven artefact and its SHA-1, verified before anything is unpacked. local group="$1" artefact="$2" version="$3" out="$4" local url="${MAVEN}/${group//.//}/${artefact}/${version}/${artefact}-${version}.aar" if [[ -f "${out}" && -f "${out}.sha1" ]] \ && [[ "$(sha1sum "${out}" | cut -d' ' -f1)" == "$(cut -c1-40 "${out}.sha1")" ]]; then return fi echo "==> fetching ${artefact} ${version}" curl -fsSL -o "${out}.sha1" "${url}.sha1" curl -fsSL -o "${out}" "${url}" [[ "$(sha1sum "${out}" | cut -d' ' -f1)" == "$(cut -c1-40 "${out}.sha1")" ]] || { echo "error: ${artefact}-${version}.aar does not match its published SHA-1" >&2 rm -f "${out}" exit 1 } } mkdir -p "${DEST}/aar" "${DEST}/lib" fetch com.microsoft.onnxruntime onnxruntime-android-qnn "${ORT_VERSION}" \ "${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar" fetch com.qualcomm.qti qnn-runtime "${QNN_VERSION}" \ "${DEST}/aar/qnn-runtime-${QNN_VERSION}.aar" # The ONNX Runtime POM names the QNN version it was built against; a pair # that disagrees loads and then fails at the first graph, which is the kind # of failure the probe would only report as "Hexagon failed". pom="${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.pom" [[ -f "${pom}" ]] || curl -fsSL -o "${pom}" \ "${MAVEN}/com/microsoft/onnxruntime/onnxruntime-android-qnn/${ORT_VERSION}/onnxruntime-android-qnn-${ORT_VERSION}.pom" wanted="$(sed -n '/qnn-runtime<\/artifactId>/{n;s/.*\(.*\)<\/version>.*/\1/p}' "${pom}")" if [[ -n "${wanted}" && "${wanted}" != "${QNN_VERSION}" ]]; then echo "error: ONNX Runtime ${ORT_VERSION} was built against QNN ${wanted}, not ${QNN_VERSION}" >&2 exit 1 fi rm -rf "${DEST}/lib" mkdir -p "${DEST}/lib" unzip -q -o -j "${DEST}/aar/onnxruntime-android-qnn-${ORT_VERSION}.aar" \ 'jni/arm64-v8a/libonnxruntime.so' -d "${DEST}/lib" members=(jni/arm64-v8a/libQnnHtp.so jni/arm64-v8a/libQnnHtpPrepare.so jni/arm64-v8a/libQnnSystem.so) for arch in ${QNN_HTP_ARCHS}; do members+=("jni/arm64-v8a/libQnnHtpV${arch}Skel.so" "jni/arm64-v8a/libQnnHtpV${arch}Stub.so") done unzip -q -o -j "${DEST}/aar/qnn-runtime-${QNN_VERSION}.aar" "${members[@]}" -d "${DEST}/lib" unzip -q -o -j "${DEST}/aar/qnn-runtime-${QNN_VERSION}.aar" 'LICENSE.pdf' 'NOTICE.txt' -d "${DEST}" 2>/dev/null || true mv -f "${DEST}/LICENSE.pdf" "${DEST}/QNN-LICENSE.pdf" 2>/dev/null || true mv -f "${DEST}/NOTICE.txt" "${DEST}/QNN-NOTICE.txt" 2>/dev/null || true echo "==> runtime in ${DEST}/lib:" du -sh "${DEST}/lib" | cut -f1 | sed 's/^/ /' ls "${DEST}/lib" | sed 's/^/ /'