@bits-innovate/react-native-vstarcam 1.0.31 → 1.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,6 +59,12 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
59
59
  // Counter for unique internal client IDs
60
60
  private static final java.util.concurrent.atomic.AtomicInteger nextClientPtr = new java.util.concurrent.atomic.AtomicInteger(1000);
61
61
 
62
+ // Static proxy references to prevent GC.
63
+ // If the native SDK holds these, Java must not collect them.
64
+ private static Object stateListenerProxy;
65
+ private static Object commandListenerProxy;
66
+ private static Object releaseListenerProxy;
67
+
62
68
  // Helper for conditional debug logging
63
69
  private static void logDebug(String message) {
64
70
  if (DEBUG_LOGGING) {
@@ -231,11 +237,11 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
231
237
  }
232
238
  Log.d(TAG, "======================");
233
239
 
234
- // Load listener interfaces
235
- stateListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientStateListener");
236
- commandListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientCommandListener");
237
- releaseListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientReleaseListener");
238
- Log.d(TAG, "Listener interfaces loaded");
240
+ // Load listener interfaces using the same ClassLoader as JNIApi for consistency
241
+ stateListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientStateListener", true, jniApiClass.getClassLoader());
242
+ commandListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientCommandListener", true, jniApiClass.getClassLoader());
243
+ releaseListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientReleaseListener", true, jniApiClass.getClassLoader());
244
+ Log.d(TAG, "Listener interfaces loaded from ClassLoader: " + jniApiClass.getClassLoader());
239
245
 
240
246
  // Log all methods on each listener for debugging
241
247
  Log.d(TAG, "ClientStateListener methods:");
@@ -270,15 +276,24 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
270
276
 
271
277
  try {
272
278
  // Create dynamic proxy for ClientStateListener
273
- Object stateListener = Proxy.newProxyInstance(
279
+ stateListenerProxy = Proxy.newProxyInstance(
274
280
  stateListenerClass.getClassLoader(),
275
281
  new Class<?>[] { stateListenerClass },
276
282
  new InvocationHandler() {
277
283
  @Override
278
284
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
279
285
  try {
280
- Log.d(TAG, "ClientStateListener." + method.getName() + " called with " + (args != null ? args.length : 0) + " args");
281
- if (method.getName().equals("stateListener")) {
286
+ String methodName = method.getName();
287
+
288
+ // Handle standard Object methods to avoid crashes on hashCode/toString calls via JNI
289
+ if (method.getDeclaringClass() == Object.class) {
290
+ if (methodName.equals("equals")) return proxy == args[0];
291
+ if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
292
+ if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
293
+ }
294
+
295
+ Log.d(TAG, "ClientStateListener." + methodName + " called");
296
+ if (methodName.equals("stateListener")) {
282
297
  // args: long clientPtr, int state
283
298
  final long clientPtr = (Long) args[0];
284
299
  final int state = (Integer) args[1];
@@ -297,32 +312,31 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
297
312
  } catch (Exception e) {
298
313
  Log.e(TAG, "Error in stateListener callback", e);
299
314
  }
300
-
301
- // Robust return type handling to prevent JNI signature mismatch crash
302
- Class<?> returnType = method.getReturnType();
303
- if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
304
- return 0;
305
- } else if (returnType.equals(long.class) || returnType.equals(Long.class)) {
306
- return 0L;
307
- } else if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
308
- return true; // Default to true for success indicators
309
- }
310
- return null;
315
+ return null; // void stateListener
311
316
  }
312
317
  }
313
318
  );
314
319
 
315
320
  // Create dynamic proxy for ClientCommandListener
316
- Object commandListener = Proxy.newProxyInstance(
321
+ commandListenerProxy = Proxy.newProxyInstance(
317
322
  commandListenerClass.getClassLoader(),
318
323
  new Class<?>[] { commandListenerClass },
319
324
  new InvocationHandler() {
320
325
  @Override
321
326
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
322
327
  try {
323
- Log.d(TAG, "ClientCommandListener." + method.getName() + " called with " + (args != null ? args.length : 0) + " args");
324
- if (method.getName().equals("commandListener")) {
325
- // args: long clientPtr, byte[] data, int length (from interface)
328
+ String methodName = method.getName();
329
+
330
+ // Handle standard Object methods
331
+ if (method.getDeclaringClass() == Object.class) {
332
+ if (methodName.equals("equals")) return proxy == args[0];
333
+ if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
334
+ if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
335
+ }
336
+
337
+ Log.d(TAG, "ClientCommandListener." + methodName + " called");
338
+ if (methodName.equals("commandListener")) {
339
+ // args: long clientPtr, byte[] data, int length
326
340
  final long clientPtr = (Long) args[0];
327
341
  final byte[] data = (byte[]) args[1];
328
342
  final int length = (Integer) args[2];
@@ -340,44 +354,37 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
340
354
  } catch (Exception e) {
341
355
  Log.e(TAG, "Error in commandListener callback", e);
342
356
  }
343
-
344
- // Robust return type handling to prevent JNI signature mismatch crash
345
- Class<?> returnType = method.getReturnType();
346
- if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
347
- return 0;
348
- } else if (returnType.equals(long.class) || returnType.equals(Long.class)) {
349
- return 0L;
350
- } else if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
351
- return true;
352
- }
353
- return null;
357
+ return null; // void commandListener
354
358
  }
355
359
  }
356
360
  );
357
361
 
358
362
  // Create dynamic proxy for ClientReleaseListener
359
- Object releaseListener = Proxy.newProxyInstance(
363
+ releaseListenerProxy = Proxy.newProxyInstance(
360
364
  releaseListenerClass.getClassLoader(),
361
365
  new Class<?>[] { releaseListenerClass },
362
366
  new InvocationHandler() {
363
367
  @Override
364
368
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
365
369
  try {
366
- Log.d(TAG, "ClientReleaseListener." + method.getName() + " called");
370
+ String methodName = method.getName();
371
+
372
+ // Handle standard Object methods
373
+ if (method.getDeclaringClass() == Object.class) {
374
+ if (methodName.equals("equals")) return proxy == args[0];
375
+ if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
376
+ if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
377
+ }
378
+
379
+ Log.d(TAG, "ClientReleaseListener." + methodName + " called");
380
+ if (methodName.equals("releaseListener")) {
381
+ final long clientPtr = (Long) args[0];
382
+ Log.d(TAG, "Release callback: clientPtr=" + clientPtr);
383
+ }
367
384
  } catch (Exception e) {
368
385
  Log.e(TAG, "Error in releaseListener callback", e);
369
386
  }
370
-
371
- // Robust return type handling to prevent JNI signature mismatch crash
372
- Class<?> returnType = method.getReturnType();
373
- if (returnType.equals(int.class) || returnType.equals(Integer.class)) {
374
- return 0;
375
- } else if (returnType.equals(long.class) || returnType.equals(Long.class)) {
376
- return 0L;
377
- } else if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
378
- return true;
379
- }
380
- return null;
387
+ return null; // void releaseListener
381
388
  }
382
389
  }
383
390
  );
@@ -385,7 +392,8 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
385
392
  // Call JNIApi.init(stateListener, commandListener, releaseListener)
386
393
  Method initMethod = jniApiClass.getMethod("init",
387
394
  stateListenerClass, commandListenerClass, releaseListenerClass);
388
- initMethod.invoke(null, stateListener, commandListener, releaseListener);
395
+ Log.d(TAG, "Calling JNIApi.init with proxies...");
396
+ initMethod.invoke(null, stateListenerProxy, commandListenerProxy, releaseListenerProxy);
389
397
 
390
398
  isP2PInitialized = true;
391
399
  Log.d(TAG, "P2P system initialized successfully!");
@@ -502,6 +510,15 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
502
510
  return;
503
511
  }
504
512
 
513
+ // Explicitly retain the client pointer to prevent premature release by native SDK
514
+ try {
515
+ Method retainMethod = jniApiClass.getMethod("retain", long.class);
516
+ retainMethod.invoke(null, sdkClientPtr);
517
+ Log.d(TAG, "JNIApi.retain called for: " + sdkClientPtr);
518
+ } catch (Exception re) {
519
+ Log.e(TAG, "Failed to call retain: " + re.getMessage());
520
+ }
521
+
505
522
  // Generate a unique internal client ID
506
523
  int ourClientPtr = nextClientPtr.getAndIncrement();
507
524
 
@@ -565,8 +582,8 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
565
582
  Log.d(TAG, "Calling JNIApi.connect(" + clientInfo.sdkClientPtr + ", 15, " + param + ", " + actualConnectType + ")");
566
583
 
567
584
  try {
568
- connectMethod.invoke(null, clientInfo.sdkClientPtr, 15, param, actualConnectType);
569
- Log.d(TAG, "JNIApi.connect returned normally");
585
+ Object result = connectMethod.invoke(null, clientInfo.sdkClientPtr, 15, param, actualConnectType);
586
+ Log.d(TAG, "JNIApi.connect result: " + result);
570
587
  } catch (java.lang.reflect.InvocationTargetException ite) {
571
588
  Log.e(TAG, "JNIApi.connect threw exception: " + ite.getCause(), ite.getCause());
572
589
  throw ite;
@@ -621,32 +638,35 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
621
638
 
622
639
  Log.d(TAG, "Calling JNIApi.login(" + clientInfo.sdkClientPtr + ", " + username + ", ***)");
623
640
 
624
- boolean loginSuccess = false;
641
+ boolean loginResult = false;
625
642
  try {
626
- loginMethod.invoke(null, clientInfo.sdkClientPtr, username, password);
627
- Log.d(TAG, "JNIApi.login returned normally");
628
- loginSuccess = true;
643
+ Object result = loginMethod.invoke(null, clientInfo.sdkClientPtr, username, password);
644
+ Log.d(TAG, "JNIApi.login result: " + result);
645
+ // result is likely boolean based on signature log
646
+ if (result instanceof Boolean) {
647
+ loginResult = (Boolean) result;
648
+ } else if (result != null) {
649
+ loginResult = true; // Non-null result usually means success for some SDKs
650
+ }
629
651
  } catch (java.lang.reflect.InvocationTargetException ite) {
630
652
  Throwable cause = ite.getCause();
631
653
  Log.e(TAG, "JNIApi.login threw exception: " + cause, cause);
632
- // Don't rethrow - return false instead to prevent crash
633
654
  promise.resolve(false);
634
655
  return;
635
656
  } catch (Throwable t) {
636
657
  Log.e(TAG, "JNIApi.login crashed: " + t.getMessage(), t);
637
- // Don't rethrow - return false instead to prevent crash
638
658
  promise.resolve(false);
639
659
  return;
640
660
  }
641
661
 
642
- if (loginSuccess) {
662
+ if (loginResult) {
643
663
  clientInfo.isLoggedIn = true;
644
664
  clientInfo.username = username;
645
665
  clientInfo.password = password;
646
666
  }
647
667
 
648
- Log.d(TAG, "JNIApi.login completed with result: " + loginSuccess);
649
- promise.resolve(loginSuccess);
668
+ Log.d(TAG, "JNIApi.login completed with result: " + loginResult);
669
+ promise.resolve(loginResult);
650
670
  } catch (Exception e) {
651
671
  Log.e(TAG, "clientLogin failed", e);
652
672
  promise.reject("LOGIN_ERROR", e.getMessage());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "React Native bridge for VStarCam P2P SDK",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",