GnunetChatIpcBridge.cpp (21619B)
1 #include <jni.h> 2 #include <map> 3 #include <mutex> 4 #include <memory> 5 #include <atomic> 6 #include <string> 7 #include <android/log.h> 8 #include <android/asset_manager.h> 9 #include <android/asset_manager_jni.h> 10 #include "gnunet_chat_lib.h" 11 #include "gnunet_util_lib.h" 12 13 14 #define LOG_TAG "GnunetChatIpcBridge" 15 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 16 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 17 18 // ====================================================== 19 // Globale Verwaltung 20 // ====================================================== 21 22 static JavaVM* g_vm = nullptr; 23 24 // Session-Map: handle (jlong) -> ChatSession 25 static std::mutex g_mapMtx; 26 struct ChatSession; 27 static std::map<long, std::unique_ptr<ChatSession>> g_sessions; 28 static std::atomic_long g_nextHandle{1}; 29 30 static const struct GNUNET_CONFIGURATION_Handle* g_cfg = nullptr; 31 static std::atomic_bool g_cfgInited{false}; 32 33 // Hilfsfunktion: Thread-sicheres JNIEnv-Beschaffen + optionales Detach 34 struct ScopedEnv { 35 JNIEnv* env = nullptr; 36 bool didAttach = false; 37 38 ScopedEnv() { 39 if (!g_vm) return; 40 if (g_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK || !env) { 41 if (g_vm->AttachCurrentThread(&env, nullptr) == JNI_OK) { 42 didAttach = true; 43 } else { 44 env = nullptr; 45 } 46 } 47 } 48 ~ScopedEnv() { 49 if (didAttach && g_vm) { 50 g_vm->DetachCurrentThread(); 51 } 52 } 53 JNIEnv* get() const { return env; } 54 }; 55 56 // ====================================================== 57 // Session-Struktur mit GNUnet-Handle 58 // ====================================================== 59 60 struct ChatSession { 61 jlong handle = 0; 62 // Optional: Java-Callback-Objekt (IChatCallback Implementierung aus deinem Service) 63 jobject cbGlobal = nullptr; 64 65 // Beispielhafte Session-Daten 66 std::string profileName = "GNUnet"; 67 bool connected = false; 68 69 // GNUnet Chat Handle 70 struct GNUNET_CHAT_Handle* chatHandle = nullptr; 71 72 ChatSession() = default; 73 74 ~ChatSession() { 75 // GlobalRef bereinigen 76 if (cbGlobal) { 77 ScopedEnv senv; 78 if (auto* env = senv.get()) { 79 env->DeleteGlobalRef(cbGlobal); 80 } 81 cbGlobal = nullptr; 82 } 83 84 // GNUnet Chat stoppen 85 if (chatHandle) { 86 LOGD("Stopping GNUNET_CHAT handle for session %ld", (long)handle); 87 GNUNET_CHAT_disconnect(chatHandle); 88 chatHandle = nullptr; 89 } 90 } 91 }; 92 93 // Helper: Session lookup 94 static ChatSession* findSessionOrNull(long h) { 95 std::lock_guard<std::mutex> lk(g_mapMtx); 96 auto it = g_sessions.find(h); 97 return (it == g_sessions.end()) ? nullptr : it->second.get(); 98 } 99 100 // ====================================================== 101 // JNI Setup 102 // ====================================================== 103 104 extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { 105 g_vm = vm; 106 return JNI_VERSION_1_6; 107 } 108 109 // ====================================================== 110 // GNUnet Callback (eingehende Nachrichten) 111 // -> Beispiel: ruft eine Methode am Java-Callback auf (onMessageReceived(String)) 112 // ====================================================== 113 114 static JNIEnv* GetEnv() { 115 JNIEnv* env = nullptr; 116 if (g_vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { 117 if (g_vm->AttachCurrentThread(&env, nullptr) != JNI_OK) return nullptr; 118 } 119 return env; 120 } 121 122 // ---- kleine Helfer ---- 123 static void fillChatContextDto(JNIEnv* env, jobject jCtx, struct GNUNET_CHAT_Context* ctx) { 124 if (!env || !jCtx) return; 125 jclass cls = env->GetObjectClass(jCtx); 126 if (!cls) return; 127 128 jfieldID fType = env->GetFieldID(cls, "chatContextType", "I"); 129 jfieldID fIsGroup = env->GetFieldID(cls, "isGroup", "Z"); 130 jfieldID fIsPlat = env->GetFieldID(cls, "isPlatform", "Z"); 131 jfieldID fUserPtr = env->GetFieldID(cls, "userPointer", "Ljava/lang/String;"); 132 133 int type = 0; 134 bool isGroup = false; 135 bool isPlatform = false; 136 // Heuristik: Ist eine Gruppe am Context? 137 if (ctx) { 138 if (GNUNET_CHAT_context_get_group(ctx)) { isGroup = true; type = 1; } 139 // platform: falls du später einen Indikator hast → hier setzen 140 } 141 142 env->SetIntField(jCtx, fType, (jint) type); 143 env->SetBooleanField(jCtx, fIsGroup, (jboolean) isGroup); 144 env->SetBooleanField(jCtx, fIsPlat, (jboolean) isPlatform); 145 env->SetObjectField(jCtx, fUserPtr, nullptr); 146 } 147 148 static jlong getTimestampMillis(struct GNUNET_CHAT_Message* msg) { 149 if (!msg) return 0; 150 time_t ts = GNUNET_CHAT_message_get_timestamp(msg); 151 if (ts <= 0) return 0; 152 return (jlong) ts * 1000LL; 153 } 154 155 // ---- Haupt-Callback: nur die Felder, die du brauchst ---- 156 static static enum GNUNET_GenericReturnValue chat_message_cb(void* cls, 157 struct GNUNET_CHAT_Context* ctx, 158 struct GNUNET_CHAT_Message* msg) { 159 auto* sess = static_cast<ChatSession*>(cls); 160 if (!sess || !sess->cbGlobal || !msg) { 161 return GNUNET_YES; // niemals Scheduler blockieren 162 } 163 164 JNIEnv* env = GetEnv(); 165 if (!env) return GNUNET_YES; 166 167 jclass clsCb = env->GetObjectClass(sess->cbGlobal); 168 jclass clsCtx = env->FindClass("org/gnunet/gnunetmessenger/ipc/ChatContextDto"); 169 jclass clsMsg = env->FindClass("org/gnunet/gnunetmessenger/ipc/ChatMessageDto"); 170 if (!clsCb || !clsCtx || !clsMsg) return GNUNET_YES; 171 172 jmethodID ctorCtx = env->GetMethodID(clsCtx, "<init>", "()V"); 173 jmethodID ctorMsg = env->GetMethodID(clsMsg, "<init>", "()V"); 174 if (!ctorCtx || !ctorMsg) return GNUNET_YES; 175 176 jobject jCtx = env->NewObject(clsCtx, ctorCtx); 177 jobject jMsg = env->NewObject(clsMsg, ctorMsg); 178 if (!jCtx || !jMsg) return GNUNET_YES; 179 180 // -> Kontext füllen 181 fillChatContextDto(env, jCtx, ctx); // deine Helper-Funktion 182 183 // -> ChatMessageDto-Felder setzen (minimal TEXT/WARNING) 184 jfieldID fKind = env->GetFieldID(clsMsg, "kind", "I"); 185 jfieldID fTimestamp = env->GetFieldID(clsMsg, "timestamp", "J"); 186 jfieldID fText = env->GetFieldID(clsMsg, "text", "Ljava/lang/String;"); 187 if (!fKind || !fTimestamp || !fText) { 188 env->DeleteLocalRef(jCtx); 189 env->DeleteLocalRef(jMsg); 190 return GNUNET_YES; 191 } 192 193 const int kind = (int) GNUNET_CHAT_message_get_kind(msg); 194 env->SetIntField(jMsg, fKind, (jint) kind); 195 env->SetLongField(jMsg, fTimestamp, (jlong) getTimestampMillis(msg)); // dein Helper 196 197 switch (kind) { 198 case GNUNET_CHAT_KIND_WARNING: 199 case GNUNET_CHAT_KIND_TEXT: { 200 const char* t = GNUNET_CHAT_message_get_text(msg); 201 jstring jT = t ? env->NewStringUTF(t) : nullptr; 202 env->SetObjectField(jMsg, fText, jT); 203 if (jT) env->DeleteLocalRef(jT); 204 break; 205 } 206 case GNUNET_CHAT_KIND_INVITATION: 207 default: 208 env->SetObjectField(jMsg, fText, (jobject) nullptr); 209 break; 210 } 211 212 // Java-Callback: void onMessage(ChatContextDto, ChatMessageDto) 213 jmethodID mOnMessage = env->GetMethodID( 214 clsCb, 215 "onMessage", 216 "(Lorg/gnunet/gnunetmessenger/ipc/ChatContextDto;" 217 "Lorg/gnunet/gnunetmessenger/ipc/ChatMessageDto;)V" 218 ); 219 if (mOnMessage) { 220 env->CallVoidMethod(sess->cbGlobal, mOnMessage, jCtx, jMsg); 221 } 222 223 env->DeleteLocalRef(jCtx); 224 env->DeleteLocalRef(jMsg); 225 return GNUNET_YES; 226 } 227 228 229 230 extern "C" JNIEXPORT jboolean JNICALL 231 Java_org_gnunet_gnunetmessenger_ipc_NativeBridge_initCfgFromAsset( 232 JNIEnv* env, jclass, 233 jobject jAssetManager, jstring jAssetName) 234 { 235 if (g_cfgInited.load()) return JNI_TRUE; 236 237 AAssetManager* mgr = AAssetManager_fromJava(env, jAssetManager); 238 if (!mgr) return JNI_FALSE; 239 240 const char* assetName = env->GetStringUTFChars(jAssetName, nullptr); 241 AAsset* asset = AAssetManager_open(mgr, assetName, AASSET_MODE_BUFFER); 242 env->ReleaseStringUTFChars(jAssetName, assetName); 243 if (!asset) return JNI_FALSE; 244 245 off_t sz = AAsset_getLength(asset); 246 if (sz <= 0) { AAsset_close(asset); return JNI_FALSE; } 247 248 std::vector<char> buf; 249 buf.resize(static_cast<size_t>(sz)); 250 const int readBytes = AAsset_read(asset, buf.data(), sz); 251 AAsset_close(asset); 252 if (readBytes != sz) return JNI_FALSE; 253 254 // Config-Handle mit Projekt-Daten erzeugen (Android-Build hat die GNUnet-Projektinfos) 255 struct GNUNET_CONFIGURATION_Handle* cfg = 256 GNUNET_CONFIGURATION_create(GNUNET_OS_project_data_gnunet()); 257 if (!cfg) return JNI_FALSE; 258 259 // Wichtig: deserialize erwartet die exakte Datenlänge (ohne extra NUL) 260 if (GNUNET_OK != GNUNET_CONFIGURATION_deserialize(cfg, buf.data(), 261 static_cast<size_t>(sz), nullptr)) { 262 GNUNET_CONFIGURATION_destroy(cfg); 263 return JNI_FALSE; 264 } 265 266 g_cfg = cfg; 267 g_cfgInited.store(true); 268 return JNI_TRUE; 269 } 270 271 // ====================================================== 272 // JNI: startChat 273 // Signature in Kotlin/Java (Server): 274 // private external fun nativeStartChat(appName: String, callback: IChatCallback): Long 275 // Package/Cls: org.gnu.gnunet.ipc.GnunetChatIpcService 276 // ====================================================== 277 278 // Ergänzung: zum Freigeben der Config 279 extern "C" JNIEXPORT void JNICALL 280 Java_org_gnunet_gnunetmessenger_ipc_NativeBridge_destroyCfg( 281 JNIEnv*, jclass, jlong cfgPtr) { 282 auto* cfg = reinterpret_cast<GNUNET_CONFIGURATION_Handle*>(cfgPtr); 283 if (cfg) GNUNET_CONFIGURATION_destroy(cfg); 284 } 285 286 // Start mit bereits erstellter cfg 287 extern "C" JNIEXPORT jlong JNICALL 288 Java_org_gnunet_gnunetmessenger_ipc_NativeBridge_nativeStartChatUsingCfg( 289 JNIEnv* env, jclass, 290 jlong cfgPtr, jstring jAppName, jobject jCallback) { 291 292 const char* appName = env->GetStringUTFChars(jAppName, nullptr); 293 LOGD("nativeStartChat(%s)", appName ? appName : "(null)"); 294 295 // Neue Session anlegen 296 auto sess = std::make_unique<ChatSession>(); 297 sess->handle = g_nextHandle++; 298 if (jCallback) { 299 sess->cbGlobal = env->NewGlobalRef(jCallback); // global ref behalten 300 } 301 302 // --- GNUnet Chat starten --- 303 sess->chatHandle = GNUNET_CHAT_start( 304 reinterpret_cast<const GNUNET_CONFIGURATION_Handle *>(cfgPtr), &chat_message_cb, sess.get()); 305 GNUNET_CONFIGURATION_destroy(reinterpret_cast<GNUNET_CONFIGURATION_Handle *>(cfgPtr)); 306 307 if (!sess->chatHandle) { 308 LOGE("GNUNET_CHAT_start failed"); 309 if (sess->cbGlobal) { 310 env->DeleteGlobalRef(sess->cbGlobal); 311 sess->cbGlobal = nullptr; 312 } 313 env->ReleaseStringUTFChars(jAppName, appName); 314 return 0; 315 } 316 317 // Session registrieren 318 long handle = (long)sess->handle; 319 { 320 std::lock_guard<std::mutex> lk(g_mapMtx); 321 g_sessions.emplace(handle, std::move(sess)); 322 } 323 324 env->ReleaseStringUTFChars(jAppName, appName); 325 LOGD("Session created with handle=%ld", handle); 326 return static_cast<jlong>(handle); 327 } 328 329 // ====================================================== 330 // JNI: createAccount 331 // Signature in Kotlin/Java: 332 // private external fun nativeCreateAccount(handle: Long, name: String): Int 333 // ====================================================== 334 335 extern "C" JNIEXPORT jint JNICALL 336 Java_org_gnu_gnunet_ipc_GnunetChatIpcService_nativeCreateAccount( 337 JNIEnv* env, jobject /*thiz*/, jlong jHandle, jstring jName) { 338 339 const char* name = env->GetStringUTFChars(jName, nullptr); 340 long h = static_cast<long>(jHandle); 341 LOGD("nativeCreateAccount(handle=%ld, name=%s)", h, name ? name : "(null)"); 342 343 ChatSession* sess = findSessionOrNull(h); 344 if (!sess || !sess->chatHandle) { 345 env->ReleaseStringUTFChars(jName, name); 346 return -1; // GNUNET_SYSERR analog 347 } 348 349 const char* nameC = env->GetStringUTFChars(jName, nullptr); 350 if (!nameC) { 351 LOGE("nativeCreateAccount: failed to get UTF chars"); 352 return GNUNET_SYSERR; 353 } 354 355 int rc = GNUNET_CHAT_account_create(sess->chatHandle, nameC); 356 357 env->ReleaseStringUTFChars(jName, name); 358 return rc; 359 } 360 361 // ====================================================== 362 // JNI: connect 363 // Signature in Kotlin/Java: 364 // private external fun nativeConnect(handle: Long, accountKey: String, accountName: String) 365 // Du kannst die Parameterform anpassen – aktuell minimal. 366 // ====================================================== 367 368 extern "C" JNIEXPORT void JNICALL 369 Java_org_gnu_gnunet_ipc_GnunetChatIpcService_nativeConnect( 370 JNIEnv* env, jobject /*thiz*/, jlong jHandle, 371 jstring jAccountKey, jstring jAccountName) { 372 373 long h = static_cast<long>(jHandle); 374 const char* key = jAccountKey ? env->GetStringUTFChars(jAccountKey, nullptr) : ""; 375 const char* name = jAccountName ? env->GetStringUTFChars(jAccountName, nullptr) : ""; 376 struct GNUNET_CHAT_Account *account; 377 LOGD("nativeConnect(handle=%ld, key=%s, name=%s)", h, key, name); 378 379 ChatSession* sess = findSessionOrNull(h); 380 if (!sess || !sess->chatHandle) { 381 if (jAccountKey) env->ReleaseStringUTFChars(jAccountKey, key); 382 if (jAccountName) env->ReleaseStringUTFChars(jAccountName, name); 383 return; 384 } 385 386 account = GNUNET_CHAT_find_account( 387 sess->chatHandle, 388 name 389 ); 390 391 GNUNET_CHAT_connect(sess->chatHandle, account); 392 393 sess->connected = true; // Platzhalter 394 395 if (jAccountKey) env->ReleaseStringUTFChars(jAccountKey, key); 396 if (jAccountName) env->ReleaseStringUTFChars(jAccountName, name); 397 } 398 399 // ====================================================== 400 // JNI: disconnect 401 // Signature in Kotlin/Java: 402 // private external fun nativeDisconnect(handle: Long) 403 // ====================================================== 404 405 extern "C" JNIEXPORT void JNICALL 406 Java_org_gnu_gnunet_ipc_GnunetChatIpcService_nativeDisconnect( 407 JNIEnv* /*env*/, jobject /*thiz*/, jlong jHandle) { 408 409 long h = static_cast<long>(jHandle); 410 LOGD("nativeDisconnect(handle=%ld)", h); 411 412 ChatSession* sess = findSessionOrNull(h); 413 if (!sess || !sess->chatHandle) return; 414 415 if (sess->chatHandle) { 416 GNUNET_CHAT_disconnect(sess->chatHandle); // ✅ 417 sess->chatHandle = nullptr; 418 sess->connected = false; 419 } 420 sess->connected = false; // Platzhalter 421 } 422 423 // ====================================================== 424 // JNI: getProfileName 425 // Signature in Kotlin/Java: 426 // private external fun nativeGetProfileName(handle: Long): String 427 // ====================================================== 428 429 extern "C" JNIEXPORT jstring JNICALL 430 Java_org_gnu_gnunet_ipc_GnunetChatIpcService_nativeGetProfileName( 431 JNIEnv* env, jobject /*thiz*/, jlong jHandle) { 432 433 long h = static_cast<long>(jHandle); 434 ChatSession* sess = findSessionOrNull(h); 435 if (!sess) return env->NewStringUTF(""); 436 437 sess->profileName = GNUNET_CHAT_get_name (sess->chatHandle); 438 return env->NewStringUTF(sess->profileName.c_str()); 439 } 440 441 // ====================================================== 442 // JNI: setProfileName 443 // Signature in Kotlin/Java: 444 // private external fun nativeSetProfileName(handle: Long, name: String) 445 // ====================================================== 446 447 extern "C" JNIEXPORT void JNICALL 448 Java_org_gnu_gnunet_ipc_GnunetChatIpcService_nativeSetProfileName( 449 JNIEnv* env, jobject /*thiz*/, jlong jHandle, jstring jName) { 450 451 long h = static_cast<long>(jHandle); 452 const char* name = env->GetStringUTFChars(jName, nullptr); 453 454 ChatSession* sess = findSessionOrNull(h); 455 if (sess) { 456 GNUNET_CHAT_set_name (sess->chatHandle ,name); 457 sess->profileName = name ? name : ""; 458 } 459 460 env->ReleaseStringUTFChars(jName, name); 461 } 462 463 // --------- Hilfsfunktion: ChatAccountDto bauen --------- 464 static jobject buildChatAccountDto(JNIEnv* env, 465 const char* key, 466 const char* name) 467 { 468 if (!env) return nullptr; 469 470 jclass clsDto = env->FindClass("org/gnunet/gnunetmessenger/ipc/ChatAccountDto"); 471 if (!clsDto) return nullptr; 472 473 jmethodID ctor = env->GetMethodID(clsDto, "<init>", "()V"); 474 if (!ctor) return nullptr; 475 476 jobject dto = env->NewObject(clsDto, ctor); 477 if (!dto) return nullptr; 478 479 jfieldID fKey = env->GetFieldID(clsDto, "key", "Ljava/lang/String;"); 480 jfieldID fName = env->GetFieldID(clsDto, "name", "Ljava/lang/String;"); 481 if (!fKey || !fName) return dto; // Felder optional setzen 482 483 jstring jKey = key ? env->NewStringUTF(key) : nullptr; 484 jstring jName = name ? env->NewStringUTF(name) : nullptr; 485 486 env->SetObjectField(dto, fKey, jKey); 487 env->SetObjectField(dto, fName, jName); 488 489 if (jKey) env->DeleteLocalRef(jKey); 490 if (jName) env->DeleteLocalRef(jName); 491 492 return dto; 493 } 494 495 // --------- Daten für die Iteration + JNI Callback IDs cachen --------- 496 struct IterateAccountsCtx { 497 jclass cbClass = nullptr; 498 jobject cbGlobal = nullptr; 499 jmethodID onAccount = nullptr; // (LChatAccountDto;)V 500 jmethodID onDone = nullptr; // ()V 501 jmethodID onError = nullptr; // (ILjava/lang/String;)V 502 }; 503 504 // --------- GNUnet-Callback: wird je Account aufgerufen --------- 505 static enum GNUNET_GenericReturnValue 506 iterateAccountsCb(void* cls, 507 GNUNET_CHAT_Handle* /*handle*/, 508 GNUNET_CHAT_Account* account) 509 { 510 auto* ictx = static_cast<IterateAccountsCtx*>(cls); 511 if (!ictx) return GNUNET_YES; 512 513 ScopedEnv senv; 514 JNIEnv* env = senv.get(); 515 if (!env || !ictx->cbGlobal || !ictx->onAccount) return GNUNET_YES; 516 517 const char* name = GNUNET_CHAT_account_get_name(account); 518 const char* key = ""; // Platzhalter 519 //const char* name = "Account"; // Platzhalter 520 521 jobject dto = buildChatAccountDto(env, key, name); 522 if (dto) { 523 env->CallVoidMethod(ictx->cbGlobal, ictx->onAccount, dto); 524 env->DeleteLocalRef(dto); 525 } 526 // Weiter iterieren 527 return GNUNET_YES; 528 } 529 530 // --------- JNI: nativeIterateAccounts(handle, IAccountCallback) --------- 531 extern "C" JNIEXPORT void JNICALL 532 Java_org_gnunet_gnunetmessenger_ipc_NativeBridge_nativeIterateAccounts( 533 JNIEnv* env, jclass /*clazz*/, 534 jlong jHandle, 535 jobject jAccountCallback /* IAccountCallback */) 536 { 537 // 1) Session finden 538 std::unique_ptr<ChatSession>* sessPtr = nullptr; 539 { 540 std::lock_guard<std::mutex> lk(g_mapMtx); 541 auto it = g_sessions.find((long) jHandle); 542 if (it != g_sessions.end()) { 543 sessPtr = &it->second; 544 } 545 } 546 if (!sessPtr || !sessPtr->get()) { 547 // Callback.onError(...) (falls verfügbar), sonst nur loggen 548 jclass cbCls = env->GetObjectClass(jAccountCallback); 549 if (cbCls) { 550 jmethodID onError = env->GetMethodID(cbCls, "onError", "(ILjava/lang/String;)V"); 551 if (onError) { 552 jstring jMsg = env->NewStringUTF("No such session"); 553 env->CallVoidMethod(jAccountCallback, onError, (jint) -1, jMsg); 554 if (jMsg) env->DeleteLocalRef(jMsg); 555 } 556 env->DeleteLocalRef(cbCls); 557 } 558 return; 559 } 560 561 ChatSession* sess = sessPtr->get(); 562 if (!sess->chatHandle) { 563 jclass cbCls = env->GetObjectClass(jAccountCallback); 564 if (cbCls) { 565 jmethodID onError = env->GetMethodID(cbCls, "onError", "(ILjava/lang/String;)V"); 566 if (onError) { 567 jstring jMsg = env->NewStringUTF("Chat not started (chatHandle==null)"); 568 env->CallVoidMethod(jAccountCallback, onError, (jint) -2, jMsg); 569 if (jMsg) env->DeleteLocalRef(jMsg); 570 } 571 env->DeleteLocalRef(cbCls); 572 } 573 return; 574 } 575 576 // 2) Callback-Methoden auflösen & GlobalRef halten (falls GNUnet anderen Thread nutzt) 577 IterateAccountsCtx ictx; 578 579 ictx.cbGlobal = env->NewGlobalRef(jAccountCallback); 580 if (!ictx.cbGlobal) return; 581 582 ictx.cbClass = (jclass) env->NewGlobalRef(env->GetObjectClass(jAccountCallback)); 583 if (!ictx.cbClass) { 584 env->DeleteGlobalRef(ictx.cbGlobal); 585 return; 586 } 587 588 ictx.onAccount = env->GetMethodID( 589 ictx.cbClass, "onAccount", 590 "(Lorg/gnunet/gnunetmessenger/ipc/ChatAccountDto;)V"); 591 ictx.onDone = env->GetMethodID(ictx.cbClass, "onDone", "()V"); 592 ictx.onError = env->GetMethodID(ictx.cbClass, "onError", "(ILjava/lang/String;)V"); 593 594 if (!ictx.onAccount) { 595 // Minimal: ohne onAccount können wir nichts tun 596 if (ictx.onError) { 597 jstring jMsg = env->NewStringUTF("IAccountCallback.onAccount not found"); 598 env->CallVoidMethod(ictx.cbGlobal, ictx.onError, (jint) -3, jMsg); 599 if (jMsg) env->DeleteLocalRef(jMsg); 600 } 601 env->DeleteGlobalRef(ictx.cbClass); 602 env->DeleteGlobalRef(ictx.cbGlobal); 603 return; 604 } 605 606 // 3) Iteration starten (synchron) 607 int rc = GNUNET_CHAT_iterate_accounts(sess->chatHandle, &iterateAccountsCb, &ictx); 608 609 // 4) Abschluss melden 610 if (rc < 0) { 611 if (ictx.onError) { 612 jstring jMsg = env->NewStringUTF("iterate_accounts failed"); 613 env->CallVoidMethod(ictx.cbGlobal, ictx.onError, (jint) rc, jMsg); 614 if (jMsg) env->DeleteLocalRef(jMsg); 615 } 616 } else { 617 if (ictx.onDone) { 618 env->CallVoidMethod(ictx.cbGlobal, ictx.onDone); 619 } 620 } 621 622 // 5) Aufräumen 623 env->DeleteGlobalRef(ictx.cbClass); 624 env->DeleteGlobalRef(ictx.cbGlobal); 625 }