@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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
641
|
+
boolean loginResult = false;
|
|
625
642
|
try {
|
|
626
|
-
loginMethod.invoke(null, clientInfo.sdkClientPtr, username, password);
|
|
627
|
-
Log.d(TAG, "JNIApi.login
|
|
628
|
-
|
|
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 (
|
|
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: " +
|
|
649
|
-
promise.resolve(
|
|
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());
|