Add minimal QNN HTP cDSP test APK#20599
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20599
Note: Links to docs will display an error until the docs builds have been completed. ⏳ No Failures, 1 PendingAs of commit ade215d with merge base b331ebd ( This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
There was a problem hiding this comment.
Pull request overview
Adds a new standalone Android example app under examples/qualcomm/ intended to validate that an installed app in the untrusted_app SELinux domain can load the QNN HTP runtime and successfully open FastRPC to the Hexagon cDSP (via backendCreate()), using dlopen/dlsym and inlined QNN types (no QNN SDK headers).
Changes:
- Introduces a minimal Android app UI + JNI implementation that loads
libQnnHtp.so, queries providers, and callslogCreate()/backendCreate()while logging results to screen/logcat. - Adds a self-contained Gradle/NDK/CMake build setup for the APK plus packaging configuration to keep QNN
.sodebug symbols intact. - Documents how to obtain QNN runtime libraries from Maven and bundle them into the APK.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| examples/qualcomm/qnn-htp-test/settings.gradle | Declares a standalone Gradle project for the example app. |
| examples/qualcomm/qnn-htp-test/README.md | Provides setup instructions, expected outcomes, and device notes for the cDSP/FastRPC test. |
| examples/qualcomm/qnn-htp-test/gradlew | Adds the Gradle wrapper script to build the example consistently. |
| examples/qualcomm/qnn-htp-test/gradle/wrapper/gradle-wrapper.properties | Pins the Gradle distribution used by the wrapper. |
| examples/qualcomm/qnn-htp-test/gradle.properties | Enables AndroidX and sets Gradle JVM args. |
| examples/qualcomm/qnn-htp-test/build.gradle | Configures repositories and Android Gradle Plugin dependency. |
| examples/qualcomm/qnn-htp-test/app/src/main/jni/qnn_test.cpp | Implements JNI entrypoint that loads QNN HTP and attempts backendCreate() while logging diagnostic info. |
| examples/qualcomm/qnn-htp-test/app/src/main/jni/CMakeLists.txt | Builds the JNI shared library and links Android log + dl. |
| examples/qualcomm/qnn-htp-test/app/src/main/java/com/example/qnntest/MainActivity.java | Minimal UI to trigger the JNI test and display logs. |
| examples/qualcomm/qnn-htp-test/app/src/main/AndroidManifest.xml | Declares the app and includes uses-native-library for libcdsprpc.so. |
| examples/qualcomm/qnn-htp-test/app/build.gradle | Android build configuration: SDK levels, NDK/CMake setup, arm64-only, JNI libs packaging. |
| examples/qualcomm/qnn-htp-test/.gitignore | Ignores build outputs and downloaded QNN native libraries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| buildscript { | ||
| repositories { | ||
| google() | ||
| mavenCentral() | ||
| } | ||
| dependencies { | ||
| classpath 'com.android.tools.build:gradle:8.5.2' | ||
| } | ||
| } | ||
|
|
||
| allprojects { | ||
| repositories { | ||
| google() | ||
| mavenCentral() | ||
| } | ||
| } |
| if (!fmt) return; | ||
| char buf[512]; | ||
| vsnprintf(buf, sizeof(buf), fmt, argp); | ||
| LOGI("[QNN-L%u] %s", level, buf); | ||
| if (g_log_ptr) *g_log_ptr << " [QNN-L" << level << "] " << buf << "\n"; |
| // Step 3: Load QNN HTP | ||
| void* htpLib = dlopen("libQnnHtp.so", RTLD_NOW | RTLD_LOCAL); | ||
| if (!htpLib) { | ||
| log_and_append(log, "FAIL: %s", dlerror()); | ||
| goto done; | ||
| } | ||
| log_and_append(log, "[3] libQnnHtp.so: LOADED OK"); | ||
|
|
| if (err == QNN_SUCCESS) { | ||
| log_and_append(log, "========================================"); | ||
| log_and_append(log, "SUCCESS! backendCreate returned 0"); | ||
| log_and_append(log, "QNN HTP backend created successfully!"); | ||
| log_and_append(log, "cDSP FastRPC WORKS from untrusted_app."); | ||
| log_and_append(log, "========================================"); | ||
|
|
||
| // Skip cleanup — handle values like 0x1 are sentinels, not heap ptrs. | ||
| // Calling backendFree on them crashes. The process is exiting anyway. | ||
| } else { |
| cleanup: | ||
| dlclose(htpLib); | ||
| done: |
| // Read struct fields | ||
| uint32_t backendId = *(const uint32_t*)(iface + 0); | ||
| const char* providerName = *(const char**)(iface + 8); | ||
| const uint32_t* coreVer = (const uint32_t*)(iface + 16); | ||
| const uint32_t* beVer = (const uint32_t*)(iface + 28); |
| log_and_append(log, ""); | ||
| log_and_append(log, "[7] Process: pid=%d uid=%d", getpid(), getuid()); | ||
|
|
||
| std::string skelPath = std::string(nativeLibDir) + "/libQnnHtpV73Skel.so"; |
There was a problem hiding this comment.
Is this intended to be hardcoded to V73?
Standalone Android app that tests whether a regular installed app (untrusted_app SELinux domain) can initialize the QNN HTP backend and open FastRPC to the Hexagon cDSP. Verified working on Galaxy S23 (SM8550, SELinux enforcing). Supports S23/S24/S25 via skels from Maven com.qualcomm.qti:qnn-runtime. No QNN SDK headers needed — types are inlined, libs loaded via dlopen/dlsym. Authored with Claude.
| @@ -0,0 +1,7 @@ | |||
| distributionBase=GRADLE_USER_HOME | |||
| distributionPath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip | |||
| private void appendLog(String msg) { | ||
| Log.i(TAG, msg); | ||
| logView.append(msg + "\n"); | ||
| } |
| #include <stdint.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <unistd.h> | ||
|
|
| auto getProviders = | ||
| (QnnInterfaceGetProvidersFn)dlsym(htpLib, "QnnInterface_getProviders"); | ||
| const void** providers = nullptr; | ||
| uint32_t numProviders = 0; | ||
| Qnn_ErrorHandle_t err = getProviders(&providers, &numProviders); |
| log_and_append(log, "[5] logCreate (QNN_LOG_LEVEL_ERROR=1) ..."); | ||
| QnnLogCreateFn logCreate; | ||
| memcpy(&logCreate, iface + OFF_LOG_CREATE, sizeof(logCreate)); | ||
| void* logHandle = nullptr; | ||
| err = logCreate(qnnLogCallback, 1, &logHandle); | ||
| log_and_append(log, " result: err=%u handle=%p", err, logHandle); | ||
|
|
| log_and_append( | ||
| log, "[6] backendCreate(logHandle=%p, config=NULL) ...", logHandle); | ||
| log_and_append(log, " (This is the call that opens FastRPC to cDSP)"); | ||
| QnnBackendCreateFn backendCreate; | ||
| memcpy(&backendCreate, iface + OFF_BACKEND_CREATE, sizeof(backendCreate)); | ||
| void* backendHandle = nullptr; | ||
| err = backendCreate(logHandle, nullptr, &backendHandle); | ||
|
|
| runOnUiThread(() -> { | ||
| appendLog(result); | ||
| testButton.setEnabled(true); | ||
| testButton.setText("Run QNN HTP Test"); | ||
| }); |
Standalone Android app that tests whether a regular installed app (untrusted_app SELinux domain) can initialize the QNN HTP backend and open FastRPC to the Hexagon cDSP.
Verified working on Galaxy S23 (SM8550, SELinux enforcing). Supports S23/S24/S25 via skels from Maven com.qualcomm.qti:qnn-runtime.
No QNN SDK headers needed — types are inlined, libs loaded via dlopen/dlsym.