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
13 changes: 13 additions & 0 deletions examples/qualcomm/qnn-htp-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Build artifacts
.gradle/
build/
app/build/
app/.cxx/

# QNN native libraries — download from Maven, don't commit
app/src/main/jniLibs/arm64-v8a/*.so

# IDE
.idea/
*.iml
local.properties
74 changes: 74 additions & 0 deletions examples/qualcomm/qnn-htp-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# QNN HTP cDSP Test APK

Minimal Android app that tests whether a regular installed app (`untrusted_app` SELinux
domain) can initialize the QNN HTP backend and open a FastRPC channel to the Hexagon
cDSP on Qualcomm Snapdragon devices.

## What This Tests

Tap a button, call `backendCreate()` via the HTP backend. This opens FastRPC to the
cDSP. If the device blocks it, you get an error. If it succeeds, unsigned PD access
works from a normal installed app.

**Tested and confirmed working on Galaxy S23 (SM8550, SELinux enforcing, untrusted_app).**

## Setup

### 1. Get the QNN Libraries

Download the QNN runtime AAR from Maven Central and extract the native libs:

```bash
curl -L -o /tmp/qnn-runtime.aar \
"https://repo1.maven.org/maven2/com/qualcomm/qti/qnn-runtime/2.44.0/qnn-runtime-2.44.0.aar"

mkdir -p app/src/main/jniLibs/arm64-v8a
cd app/src/main/jniLibs/arm64-v8a
unzip /tmp/qnn-runtime.aar 'jni/arm64-v8a/*.so'
mv jni/arm64-v8a/*.so .
rm -rf jni
```

This includes skels for V68–V81, covering S23 through S25.

### 2. Build

```bash
./gradlew assembleDebug
```

### 3. Install & Run

```bash
adb install -r app/build/outputs/apk/debug/app-debug.apk
```

Launch the app, tap **"Run QNN HTP Test"**. Results appear on screen.

## Interpreting Results

- **`SUCCESS! backendCreate returned 0`** → cDSP works from `untrusted_app`
- **Error 4000+** → Transport/SELinux block
- **Error 2000** → Check skel version matches device Hexagon arch

## Supported Devices

| Chipset | Hexagon | Devices |
|---------|---------|---------|
| SM8550 | V73 | Galaxy S23 family |
| SM8650 | V75 | Galaxy S24 family |
| SM8750 | V79/V81 | Galaxy S25 family |

## How It Works

QNN libraries are bundled inside the APK via `jniLibs`. On install, Android extracts
them (`extractNativeLibs="true"`). The JNI code:

1. Sets `ADSP_LIBRARY_PATH` to the app's `nativeLibraryDir`
2. `dlopen("libQnnHtp.so")` → `dlsym("QnnInterface_getProviders")`
3. Calls `logCreate()` and `backendCreate()` via the interface function pointer table
4. Reports success or failure

No QNN SDK headers needed — types are inlined. The manifest entry
`<uses-native-library android:name="libcdsprpc.so"/>` makes the vendor FastRPC client
accessible on Android 12+.
60 changes: 60 additions & 0 deletions examples/qualcomm/qnn-htp-test/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
plugins {
id 'com.android.application'
}

android {
namespace 'com.example.qnntest'
compileSdk 34
ndkVersion '29.0.13599879'

defaultConfig {
applicationId "com.example.qnntest"
minSdk 30
targetSdk 34
versionCode 1
versionName "1.0"

ndk {
abiFilters 'arm64-v8a'
}

externalNativeBuild {
cmake {
cppFlags ""
}
}
}

externalNativeBuild {
cmake {
path "src/main/jni/CMakeLists.txt"
}
}

buildTypes {
release {
minifyEnabled false
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}

packaging {
jniLibs {
// Don't strip QNN libs — keep skel intact
keepDebugSymbols.add('**/*.so')
}
}
}

dependencies {
}
24 changes: 24 additions & 0 deletions examples/qualcomm/qnn-htp-test/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:label="QNN HTP Test"
android:allowBackup="false"
android:extractNativeLibs="true"
android:theme="@android:style/Theme.DeviceDefault">

<!-- Required for cDSP FastRPC access on Android 12+ -->
<uses-native-library
android:name="libcdsprpc.so"
android:required="false"/>

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.example.qnntest;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

private static final String TAG = "QnnHtpTest";
private TextView logView;
private Button testButton;

static {
System.loadLibrary("qnn_test_jni");
}

private native String runQnnHtpTest(String nativeLibDir);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(32, 32, 32, 32);

testButton = new Button(this);
testButton.setText("Run QNN HTP Test");
testButton.setTextSize(18);
layout.addView(testButton);

ScrollView scrollView = new ScrollView(this);
logView = new TextView(this);
logView.setPadding(0, 24, 0, 0);
logView.setTextSize(13);
logView.setTextIsSelectable(true);
scrollView.addView(logView);
layout.addView(scrollView);

setContentView(layout);

String nativeLibDir = getApplicationInfo().nativeLibraryDir;

testButton.setOnClickListener(v -> {
testButton.setEnabled(false);
testButton.setText("Running...");
logView.setText("");
appendLog("nativeLibraryDir: " + nativeLibDir + "\n");

new Thread(() -> {
String result = runQnnHtpTest(nativeLibDir);
Log.i(TAG, result);
runOnUiThread(() -> {
appendLog(result);
testButton.setEnabled(true);
testButton.setText("Run QNN HTP Test");
});
Comment on lines +57 to +61
}).start();
});
}

private void appendLog(String msg) {
Log.i(TAG, msg);
logView.append(msg + "\n");
}
Comment on lines +66 to +69
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.22.1)
project("qnn_test_jni")

add_library(qnn_test_jni SHARED qnn_test.cpp)

find_library(log-lib log)
target_link_libraries(qnn_test_jni ${log-lib} dl)
Loading
Loading