Skip to content

Horizon Platform SDK: React Native Compatibility Issue #82

@hyochan

Description

@hyochan

Description

The Horizon Platform SDK works perfectly in pure Android and Flutter applications, but is incompatible with React Native due to wrapped C++ symbol requirements (__wrap__ prefix). This blocks all React Native developers from integrating Horizon Billing and targeting the Quest Store.

Also posted facebook/react-native#54220

The Issue

When React Native apps try to use Horizon Platform SDK, they crash with:

java.lang.UnsatisfiedLinkError: dlopen failed:
cannot locate symbol "__wrap__ZTVSt12length_error"
referenced by "/data/app/.../lib/arm64-v8a/libpsdk_jni.so"

Platform Compatibility

Platform Status Repository
Pure Android (Kotlin) Works perfectly Example
Flutter Works perfectly Example
React Native Runtime crash Tested on Expo SDK 52, 53, 54 (RN 0.76.x-0.81.x)

Environment Tested

  • React Native: 0.76.9 (Expo SDK 52), 0.79.x (Expo SDK 53), 0.81.x (Expo SDK 54)
  • Platform: Android
  • Device: Meta Quest 3
  • SDK: Meta Horizon Platform SDK (com.meta.horizon.platform.ovr:android-platform-sdk:72)
  • Horizon Billing: com.meta.horizon.billingclient.api:horizon-billing-compatibility:1.1.1

Why React Native Is Different

React Native has a specific native library loading architecture:

  1. Early Loading: React Native loads libc++_shared.so during static initialization before any third-party code runs
  2. Standard Symbols: The loaded libc++ contains standard C++ symbols (no __wrap__ prefix)
  3. Fixed Symbol Table: Once loaded, the symbol table is fixed and cannot be modified
  4. Ecosystem Standard: All React Native modules use ANDROID_STL=c++_shared with standard symbols

Result: When libpsdk_jni.so tries to load and searches for wrapped symbols, they don't exist in the already-loaded libc++, causing an immediate crash.

Technical Details

Symbol Mismatch:

APK's libc++_shared.so (from React Native):

$ strings libc++_shared.so | grep "length_error"
_ZTVSt12length_error          # Standard symbol (no __wrap__ prefix)

What libpsdk_jni.so expects:

__wrap__ZTVSt12length_error   # Wrapped symbol

React Native's Loading Sequence:

  1. App starts → React Native initializes → SoLoader.init()
  2. ReactNativeJNISoLoader.staticInit() loads native libs → libc++_shared.so loaded (standard symbols)
  3. Symbol table fixed in memory
  4. Later: HorizonBillingClient.startConnection() → tries to load libpsdk_jni.so → searches for __wrap__ symbols → not found → crash

Steps to Reproduce

Full reproducer available: hyochan/expo-iap#105

1. Create React Native Project

npx create-expo-app@latest HorizonTest
cd HorizonTest

2. Add Horizon Platform SDK

android/build.gradle:

allprojects {
  repositories {
    maven { url "https://maven.horizon.meta.com/" }
  }
}

android/app/build.gradle:

dependencies {
  implementation 'com.meta.horizon.platform.ovr:android-platform-sdk:72'
  implementation 'com.meta.horizon.billingclient.api:horizon-billing-compatibility:1.1.1'
}

3. Add App ID to AndroidManifest.xml

<meta-data
  android:name="com.oculus.application_id"
  android:value="YOUR_APP_ID" />

4. Initialize Horizon Billing

val billingClient = HorizonBillingClient.newBuilder(context)
    .setListener { billingResult, purchases -> }
    .build()

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) { }
    override fun onBillingServiceDisconnected() { }
})

5. Run on Quest

cd android && ./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk

6. Observe Crash

App crashes immediately when startConnection() is called.

Stacktrace

FATAL EXCEPTION: main
Process: dev.hyo.martie, PID: 398
java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__wrap__ZTVSt12length_error" referenced by "/data/app/~~XXX/dev.hyo.martie-YYY/base.apk!/lib/arm64-v8a/libpsdk_jni.so"...
    at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
    at java.lang.System.loadLibrary(System.java:1893)
    at com.meta.horizon.platform.PlatformSDK.<clinit>(PlatformSDK.java)
    at com.meta.horizon.billingclient.api.HorizonBillingClient.startConnection(HorizonBillingClient.java)

Screenshots and Videos

Expo App (Failed 😞)

expo.mp4

Shows React Native/Expo app crashing when attempting to connect to Horizon Billing.

Android App (Success ✅)

android.mp4

Same Horizon SDK works perfectly in pure Android (Kotlin) implementation.

Flutter App (Success ✅)

flutter.mp4

Same Horizon SDK works perfectly in Flutter implementation.

All three apps tested on the same device (Meta Quest 3) with the same SDK version.

Questions

1. Why does the SDK use wrapped symbols?

  • Is the __wrap__ prefix intentional or a build artifact?
  • What is the purpose of symbol wrapping?
  • How does it work in pure Android/Flutter if they also use standard libc++?

2. Is React Native officially supported?

  • Are you aware of this compatibility issue?
  • Is there a React Native-compatible version available?
  • Are there plans to support React Native?

3. Is there a wrapped libc++ we're missing?

We searched these SDK artifacts but didn't find a wrapped libc++_shared.so:

  • android-platform-sdk-72.aar
  • horizon-billing-compatibility-1.1.1.aar

If it exists:

  • Where is it located in the SDK?
  • How should it be configured?
  • Why does Flutter find it but React Native doesn't?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions