@@ -711,92 +711,294 @@ static jstring emptyString(JNIEnv* env) {
711711 return env->NewStringUTF (" " );
712712}
713713
714- JNIEXPORT jstring JNICALL
715- Java_com_m2049r_xmrwallet_model_Wallet_getSeed (
716- JNIEnv *env,
717- jobject instance,
718- jstring seedOffset) {
719-
720- LOGD (" getSeed: ENTER - thread: %ld" , (long )gettid ());
721-
722- // ============================================================
723- // PHASE 1: ALL JNI OPERATIONS BEFORE MUTEX
724- // ============================================================
714+ static jstring getSeedInternal (JNIEnv *env, jobject instance, const std::string &offset) {
715+ LOGD (" getSeedInternal: ENTER - thread: %ld, offset: '%s'" ,
716+ (long )gettid (), offset.c_str ());
725717
726718 if (env == nullptr ) {
727- LOGE (" getSeed : ERROR - JNIEnv is null" );
719+ LOGE (" getSeedInternal : ERROR - JNIEnv is null" );
728720 return nullptr ;
729721 }
730722
731- // Get wallet handle
732- Monero::Wallet *wallet = getHandle<Monero::Wallet>(env, instance);
733- if (!wallet) {
734- LOGE (" getSeed: ERROR - Wallet handle is null" );
735- return env->NewStringUTF (" " );
723+ // Clear any pending exceptions at entry
724+ if (env->ExceptionCheck ()) {
725+ LOGW (" getSeedInternal: Clearing pre-existing exception at entry" );
726+ env->ExceptionClear ();
736727 }
737728
738- // CRITICAL: Extract string content BEFORE mutex
739- std::string offset;
740- if (seedOffset != nullptr ) {
741- const char *off = env->GetStringUTFChars (seedOffset, nullptr );
742- if (off == nullptr || env->ExceptionCheck ()) {
729+ // Get wallet handle with safety check
730+ Monero::Wallet *wallet = nullptr ;
731+ try {
732+ wallet = getHandle<Monero::Wallet>(env, instance);
733+ } catch (...) {
734+ LOGE (" getSeedInternal: Exception in getHandle" );
735+ wallet = nullptr ;
736+ }
737+
738+ if (!wallet) {
739+ LOGE (" getSeedInternal: ERROR - Wallet handle is null" );
740+ // Create empty string to return
741+ jstring emptyResult = env->NewStringUTF (" " );
742+ if (!emptyResult || env->ExceptionCheck ()) {
743743 if (env->ExceptionCheck ()) {
744744 env->ExceptionClear ();
745745 }
746- LOGE (" getSeed: Failed to get UTF chars from seedOffset" );
747- return env->NewStringUTF (" " );
746+ return nullptr ;
748747 }
749-
750- offset.assign (off);
751- env->ReleaseStringUTFChars (seedOffset, off);
752- // seedOffset jstring is now "dead" to us - never touch it again
748+ return emptyResult;
753749 }
754750
755751 // ============================================================
756- // PHASE 2: CRITICAL SECTION - NO JNI CALLS WHILE LOCKED
752+ // CRITICAL SECTION - Get seed
757753 // ============================================================
758754
759755 std::string seed;
756+ bool success = false ;
757+
760758 {
761759 std::lock_guard<std::mutex> lock (g_walletMutex);
762- LOGD (" getSeed : acquired wallet mutex" );
760+ LOGD (" getSeedInternal : acquired wallet mutex" );
763761
764- // Wallet status check
765- if (wallet->status () != Monero::Wallet::Status_Ok) {
766- std::string err;
767- int status;
768- wallet->statusWithErrorString (status, err);
769- LOGE (" getSeed: Wallet status not OK (%d): %s" , status, err.c_str ());
770- // Lock releases here when scope ends
771- return env->NewStringUTF (" " );
762+ // Clear exceptions before native code
763+ if (env->ExceptionCheck ()) {
764+ env->ExceptionClear ();
772765 }
773766
774- // Get seed using C++ string (safe - no JNI involved)
767+ // Wallet status check
775768 try {
776- seed = wallet->seed (offset);
769+ if (wallet->status () != Monero::Wallet::Status_Ok) {
770+ std::string err;
771+ int status;
772+ wallet->statusWithErrorString (status, err);
773+ LOGE (" getSeedInternal: Wallet status not OK (%d): %s" , status, err.c_str ());
774+ // Don't return here - let cleanup handle it
775+ } else {
776+ // Get seed with the provided offset
777+ seed = wallet->seed (offset);
778+ LOGD (" getSeedInternal: Successfully retrieved seed (length: %zu)" , seed.length ());
779+ success = true ;
780+ }
777781 } catch (const std::exception& e) {
778- LOGE (" getSeed: EXCEPTION in wallet->seed: %s" , e.what ());
779- return env->NewStringUTF (" " );
782+ LOGE (" getSeedInternal: EXCEPTION in wallet->seed: %s" , e.what ());
780783 } catch (...) {
781- LOGE (" getSeed: UNKNOWN EXCEPTION in wallet->seed" );
782- return env->NewStringUTF (" " );
784+ LOGE (" getSeedInternal: UNKNOWN EXCEPTION in wallet->seed" );
783785 }
784786 } // Lock automatically released here
785787
786788 // ============================================================
787- // PHASE 3: CREATE RETURN VALUE - MUTEX RELEASED
789+ // CREATE RETURN VALUE WITH IMPROVED ERROR HANDLING
788790 // ============================================================
789791
790- jstring result = env->NewStringUTF (seed.c_str ());
791- if (result == nullptr || env->ExceptionCheck ()) {
792- if (env->ExceptionCheck ()) {
793- env->ExceptionClear ();
792+ if (!success) {
793+ // Return empty string on failure
794+ jstring emptyResult = env->NewStringUTF (" " );
795+ if (!emptyResult || env->ExceptionCheck ()) {
796+ if (env->ExceptionCheck ()) {
797+ env->ExceptionClear ();
798+ }
799+ // Last resort - return null
800+ return nullptr ;
794801 }
795- LOGE (" getSeed: Failed to create return jstring" );
796- return env->NewStringUTF (" " );
802+ return emptyResult;
803+ }
804+
805+ // Clear exceptions before creating return value
806+ if (env->ExceptionCheck ()) {
807+ LOGW (" getSeedInternal: Clearing exception before NewStringUTF" );
808+ env->ExceptionClear ();
809+ }
810+
811+ jstring result = nullptr ;
812+ try {
813+ result = env->NewStringUTF (seed.c_str ());
814+ } catch (...) {
815+ LOGE (" getSeedInternal: Exception in NewStringUTF" );
816+ result = nullptr ;
817+ }
818+
819+ if (result == nullptr ) {
820+ LOGE (" getSeedInternal: NewStringUTF returned null" );
821+
822+ // Try to return empty string as fallback
823+ jstring fallback = env->NewStringUTF (" " );
824+ if (!fallback || env->ExceptionCheck ()) {
825+ if (env->ExceptionCheck ()) {
826+ env->ExceptionClear ();
827+ }
828+ return nullptr ;
829+ }
830+ return fallback;
831+ }
832+
833+ // Check for exception after NewStringUTF
834+ if (env->ExceptionCheck ()) {
835+ LOGE (" getSeedInternal: Exception after NewStringUTF" );
836+ env->ExceptionClear ();
837+
838+ // Clean up and return fallback
839+ env->DeleteLocalRef (result);
840+ jstring fallback = env->NewStringUTF (" " );
841+ if (!fallback || env->ExceptionCheck ()) {
842+ if (env->ExceptionCheck ()) {
843+ env->ExceptionClear ();
844+ }
845+ return nullptr ;
846+ }
847+ return fallback;
848+ }
849+
850+ LOGD (" getSeedInternal: EXIT - success" );
851+ return result;
852+ }
853+
854+ // ============================================================
855+ // SAFE STRING EXTRACTION HELPER
856+ // ============================================================
857+
858+ static bool extractJString (JNIEnv *env, jstring jstr, std::string &result) {
859+ if (env == nullptr || jstr == nullptr ) {
860+ return false ;
797861 }
798862
799- LOGD (" getSeed: EXIT - success" );
863+ // Clear any pending exceptions
864+ if (env->ExceptionCheck ()) {
865+ env->ExceptionClear ();
866+ }
867+
868+ const char *cstr = nullptr ;
869+ jboolean isCopy = JNI_FALSE;
870+
871+ try {
872+ cstr = env->GetStringUTFChars (jstr, &isCopy);
873+ } catch (...) {
874+ LOGE (" extractJString: Exception in GetStringUTFChars" );
875+ return false ;
876+ }
877+
878+ if (cstr == nullptr ) {
879+ LOGE (" extractJString: GetStringUTFChars returned null" );
880+ return false ;
881+ }
882+
883+ if (env->ExceptionCheck ()) {
884+ LOGE (" extractJString: Exception after GetStringUTFChars" );
885+ env->ExceptionClear ();
886+
887+ // Still need to release the string if we got it
888+ try {
889+ env->ReleaseStringUTFChars (jstr, cstr);
890+ } catch (...) {
891+ // Ignore release errors
892+ }
893+ return false ;
894+ }
895+
896+ // Copy the string
897+ try {
898+ result.assign (cstr);
899+ } catch (const std::exception& e) {
900+ LOGE (" extractJString: Exception copying string: %s" , e.what ());
901+ try {
902+ env->ReleaseStringUTFChars (jstr, cstr);
903+ } catch (...) {
904+ // Ignore release errors
905+ }
906+ return false ;
907+ } catch (...) {
908+ LOGE (" extractJString: Unknown exception copying string" );
909+ try {
910+ env->ReleaseStringUTFChars (jstr, cstr);
911+ } catch (...) {
912+ // Ignore release errors
913+ }
914+ return false ;
915+ }
916+
917+ // Release the string
918+ try {
919+ env->ReleaseStringUTFChars (jstr, cstr);
920+ } catch (const std::exception& e) {
921+ LOGE (" extractJString: Exception releasing string: %s" , e.what ());
922+ // Still return success since we got the string
923+ } catch (...) {
924+ LOGE (" extractJString: Unknown exception releasing string" );
925+ // Still return success since we got the string
926+ }
927+
928+ return true ;
929+ }
930+
931+ // ============================================================
932+ // JNI FUNCTION 1: getSeed() - NO PARAMETERS
933+ // ============================================================
934+ // Maps to: public native String getSeed();
935+ // JNI name mangling: Double underscore (__) for overloaded method with no params
936+
937+ JNIEXPORT jstring JNICALL
938+ Java_com_m2049r_xmrwallet_model_Wallet_getSeed__ (
939+ JNIEnv *env,
940+ jobject instance) {
941+
942+ LOGD (" Java_Wallet_getSeed__: ENTER (no parameters)" );
943+
944+ // Clear any pending exceptions
945+ if (env->ExceptionCheck ()) {
946+ LOGW (" Java_Wallet_getSeed__: Clearing pre-existing exception" );
947+ env->ExceptionClear ();
948+ }
949+
950+ // Call internal implementation with empty offset (standard seed)
951+ jstring result = getSeedInternal (env, instance, " " );
952+
953+ LOGD (" Java_Wallet_getSeed__: EXIT %s" ,
954+ result != nullptr ? " success" : " failed" );
955+ return result;
956+ }
957+
958+ // ============================================================
959+ // JNI FUNCTION 2: getSeed(String) - WITH OFFSET PARAMETER
960+ // ============================================================
961+ // Maps to: public native String getSeed(String seedOffset);
962+ // JNI name mangling: __Ljava_lang_String_2 for (String) parameter
963+
964+ JNIEXPORT jstring JNICALL
965+ Java_com_m2049r_xmrwallet_model_Wallet_getSeed__Ljava_lang_String_2 (
966+ JNIEnv *env,
967+ jobject instance,
968+ jstring seedOffset) {
969+
970+ LOGD (" Java_Wallet_getSeed__Ljava_lang_String_2: ENTER (with offset parameter)" );
971+
972+ // Clear any pre-existing exceptions
973+ if (env->ExceptionCheck ()) {
974+ LOGW (" Java_Wallet_getSeed__Ljava_lang_String_2: Clearing pre-existing exception" );
975+ env->ExceptionClear ();
976+ }
977+
978+ // ============================================================
979+ // SAFELY EXTRACT OFFSET STRING
980+ // ============================================================
981+
982+ std::string offset;
983+
984+ if (seedOffset != nullptr ) {
985+ // Use the safe extraction helper
986+ if (!extractJString (env, seedOffset, offset)) {
987+ LOGW (" Java_Wallet_getSeed__Ljava_lang_String_2: Failed to extract offset, using empty" );
988+ offset.clear ();
989+ } else {
990+ LOGD (" Java_Wallet_getSeed__Ljava_lang_String_2: Extracted offset '%s' (length: %zu)" ,
991+ offset.c_str (), offset.length ());
992+ }
993+ } else {
994+ LOGD (" Java_Wallet_getSeed__Ljava_lang_String_2: seedOffset is null, using empty offset" );
995+ }
996+
997+ // Call internal implementation with extracted offset
998+ jstring result = getSeedInternal (env, instance, offset);
999+
1000+ LOGD (" Java_Wallet_getSeed__Ljava_lang_String_2: EXIT %s" ,
1001+ result != nullptr ? " success" : " failed" );
8001002 return result;
8011003}
8021004
0 commit comments