diff --git a/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.cpp b/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.cpp index 1a637b15d1..84b5877f3b 100644 --- a/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.cpp +++ b/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.cpp @@ -35,7 +35,9 @@ void ETInstallerModule::registerNatives() { }); } -void ETInstallerModule::injectJSIBindings() { +void ETInstallerModule::injectJSIBindings( + jni::alias_ref cacheDirPath) { + auto cacheDir = cacheDirPath->toStdString(); // Grab a function for fetching images via URL from Java auto fetchDataByUrl = [](std::string url) { // Attaching Current Thread to JVM @@ -68,8 +70,8 @@ void ETInstallerModule::injectJSIBindings() { auto _isEmulator = isEmulator(); - RnExecutorchInstaller::injectJSIBindings(jsiRuntime_, jsCallInvoker_, - fetchDataByUrl, _isEmulator); + RnExecutorchInstaller::injectJSIBindings( + jsiRuntime_, jsCallInvoker_, fetchDataByUrl, cacheDir, _isEmulator); } } // namespace rnexecutorch diff --git a/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.h b/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.h index 82531ac9d5..0ecd14cbaf 100644 --- a/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.h +++ b/packages/react-native-executorch/android/src/main/cpp/ETInstallerModule.h @@ -24,7 +24,7 @@ class ETInstallerModule : public jni::HybridClass { static void registerNatives(); - void injectJSIBindings(); + void injectJSIBindings(jni::alias_ref cacheDirPath); private: friend HybridBase; diff --git a/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt b/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt index acc43c0a9e..1a9e38e00b 100644 --- a/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt +++ b/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt @@ -45,7 +45,7 @@ class ETInstaller( callInvoker: CallInvokerHolderImpl, ): HybridData - private external fun injectJSIBindings() + private external fun injectJSIBindings(cacheDirPath: String) init { try { @@ -60,7 +60,7 @@ class ETInstaller( @ReactMethod(isBlockingSynchronousMethod = true) override fun install(): Boolean { - injectJSIBindings() + injectJSIBindings(reactApplicationContext.cacheDir.absolutePath) return true } } diff --git a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp index 53ee65a904..4d458065bd 100644 --- a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp @@ -34,10 +34,19 @@ namespace rnexecutorch { // SSL intricacies manually, as it is done automagically in ObjC++/Kotlin. FetchUrlFunc_t fetchUrlFunc; +// App-private writable directory provided by the platform layer. Android +// passes reactApplicationContext.cacheDir; iOS passes NSTemporaryDirectory(). +// std::filesystem::temp_directory_path() resolves to /data/local/tmp on +// Android <13, which is not writable by app processes — so we pipe the +// platform-provided path through explicitly. +std::string cacheDir; + void RnExecutorchInstaller::injectJSIBindings( jsi::Runtime *jsiRuntime, std::shared_ptr jsCallInvoker, - FetchUrlFunc_t fetchDataFromUrl, bool isEmulator) { + FetchUrlFunc_t fetchDataFromUrl, const std::string &cacheDirPath, + bool isEmulator) { fetchUrlFunc = fetchDataFromUrl; + cacheDir = cacheDirPath; jsiRuntime->global().setProperty(*jsiRuntime, "__rne_isEmulator", jsi::Value(isEmulator)); diff --git a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.h b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.h index 887a8ac0ea..16681d2312 100644 --- a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.h +++ b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.h @@ -17,6 +17,7 @@ namespace rnexecutorch { using FetchUrlFunc_t = std::function(std::string)>; extern FetchUrlFunc_t fetchUrlFunc; +extern std::string cacheDir; using namespace facebook; class RnExecutorchInstaller { @@ -24,7 +25,8 @@ class RnExecutorchInstaller { static void injectJSIBindings(jsi::Runtime *jsiRuntime, std::shared_ptr jsCallInvoker, - FetchUrlFunc_t fetchDataFromUrl, bool isEmulator); + FetchUrlFunc_t fetchDataFromUrl, + const std::string &cacheDirPath, bool isEmulator); private: template diff --git a/packages/react-native-executorch/common/rnexecutorch/data_processing/ImageProcessing.cpp b/packages/react-native-executorch/common/rnexecutorch/data_processing/ImageProcessing.cpp index 3e73a3d8a4..78538b650e 100644 --- a/packages/react-native-executorch/common/rnexecutorch/data_processing/ImageProcessing.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/data_processing/ImageProcessing.cpp @@ -14,6 +14,11 @@ namespace rnexecutorch { // to this variable. It's done to not handle SSL intricacies manually, as it is // done automagically in ObjC++/Kotlin. extern FetchUrlFunc_t fetchUrlFunc; +// App-private writable directory bound by the platform layer (see +// RnExecutorchInstaller.cpp). Used in place of std::filesystem:: +// temp_directory_path() because the latter points to /data/local/tmp on +// Android <13, which is not writable by app processes. +extern std::string cacheDir; namespace image_processing { std::vector colorMatToVector(const cv::Mat &mat) { return colorMatToVector(mat, cv::Scalar(0.0, 0.0, 0.0), @@ -64,8 +69,7 @@ cv::Mat bufferToColorMat(const std::span &buffer, std::string saveToTempFile(const cv::Mat &image) { std::string filename = "rn_executorch_" + file_utils::getTimeID() + ".png"; - std::filesystem::path tempDir = std::filesystem::temp_directory_path(); - std::filesystem::path filePath = tempDir / filename; + std::filesystem::path filePath = std::filesystem::path(cacheDir) / filename; if (!cv::imwrite(filePath.string(), image)) { throw RnExecutorchError(RnExecutorchErrorCode::FileWriteFailed, diff --git a/packages/react-native-executorch/ios/RnExecutorch/ETInstaller.mm b/packages/react-native-executorch/ios/RnExecutorch/ETInstaller.mm index 2a8bc519ba..bad8955952 100644 --- a/packages/react-native-executorch/ios/RnExecutorch/ETInstaller.mm +++ b/packages/react-native-executorch/ios/RnExecutorch/ETInstaller.mm @@ -43,8 +43,9 @@ @implementation ETInstaller } }; bool isEmulator = TARGET_OS_SIMULATOR; + std::string cacheDir = [NSTemporaryDirectory() UTF8String]; rnexecutorch::RnExecutorchInstaller::injectJSIBindings( - jsiRuntime, jsCallInvoker, fetchUrl, isEmulator); + jsiRuntime, jsCallInvoker, fetchUrl, cacheDir, isEmulator); NSLog(@"Successfully installed JSI bindings for react-native-executorch!"); return @true;