@onekeyfe/react-native-jpush 0.0.0-stage → 6.7.0-22

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.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -2
  3. package/ReactNativeJpush.podspec +66 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +144 -0
  6. package/android/fcm/build.gradle +36 -0
  7. package/android/fcm/consumer-rules.pro +2 -0
  8. package/android/fcm/src/main/AndroidManifest.xml +25 -0
  9. package/android/fcm/src/main/java/com/onekey/jpush/fcm/FcmInitProvider.java +25 -0
  10. package/android/fcm/src/main/java/com/onekey/jpush/fcm/FcmMessagingService.java +84 -0
  11. package/android/fcm/src/main/java/com/onekey/jpush/fcm/FcmPushManager.java +69 -0
  12. package/android/fcm/src/test/java/com/onekey/jpush/fcm/FcmAdapterTest.java +684 -0
  13. package/android/fcm/src/test/java/com/onekey/jpush/fcm/FcmManifestTest.java +54 -0
  14. package/android/gradle.properties +4 -0
  15. package/android/src/main/AndroidManifest.xml +143 -0
  16. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  17. package/android/src/main/java/cn/android/service/JTransitActivity.java +19 -0
  18. package/android/src/main/java/cn/jiguang/android/BuildConfig.java +10 -0
  19. package/android/src/main/java/cn/jiguang/android/IDataShare.java +190 -0
  20. package/android/src/main/java/cn/jiguang/api/BaseLogger.java +82 -0
  21. package/android/src/main/java/cn/jiguang/api/JAction.java +22 -0
  22. package/android/src/main/java/cn/jiguang/api/JActionExtra.java +21 -0
  23. package/android/src/main/java/cn/jiguang/api/JAnalyticsAction.java +11 -0
  24. package/android/src/main/java/cn/jiguang/api/JCoreInterface.java +228 -0
  25. package/android/src/main/java/cn/jiguang/api/JCoreManager.java +156 -0
  26. package/android/src/main/java/cn/jiguang/api/JDispatchAction.java +66 -0
  27. package/android/src/main/java/cn/jiguang/api/MultiSpHelper.java +114 -0
  28. package/android/src/main/java/cn/jiguang/api/ReportCallBack.java +9 -0
  29. package/android/src/main/java/cn/jiguang/api/SdkType.java +13 -0
  30. package/android/src/main/java/cn/jiguang/api/utils/JCollectionAuth.java +28 -0
  31. package/android/src/main/java/cn/jiguang/core/JCoreCrypto.java +545 -0
  32. package/android/src/main/java/cn/jiguang/core/JCoreStorage.java +737 -0
  33. package/android/src/main/java/cn/jiguang/core/JCoreTcpEngine.java +1395 -0
  34. package/android/src/main/java/cn/jiguang/core/JCoreWireCodec.java +1716 -0
  35. package/android/src/main/java/cn/jiguang/internal/ActionManager.java +63 -0
  36. package/android/src/main/java/cn/jiguang/internal/JConstants.java +61 -0
  37. package/android/src/main/java/cn/jiguang/internal/JCoreHelperAction.java +12 -0
  38. package/android/src/main/java/cn/jiguang/internal/JCoreInternalHelper.java +102 -0
  39. package/android/src/main/java/cn/jiguang/net/DefaultHostVerifier.java +40 -0
  40. package/android/src/main/java/cn/jiguang/net/HttpRequest.java +78 -0
  41. package/android/src/main/java/cn/jiguang/net/HttpResponse.java +48 -0
  42. package/android/src/main/java/cn/jiguang/net/HttpUtils.java +106 -0
  43. package/android/src/main/java/cn/jiguang/plugins/core/JCoreModule.java +91 -0
  44. package/android/src/main/java/cn/jiguang/plugins/core/JCorePackage.java +27 -0
  45. package/android/src/main/java/cn/jiguang/plugins/push/JPushModule.java +773 -0
  46. package/android/src/main/java/cn/jiguang/plugins/push/JPushPackage.java +25 -0
  47. package/android/src/main/java/cn/jiguang/plugins/push/common/JConstants.java +88 -0
  48. package/android/src/main/java/cn/jiguang/plugins/push/common/JLogger.java +122 -0
  49. package/android/src/main/java/cn/jiguang/plugins/push/helper/JPushHelper.java +710 -0
  50. package/android/src/main/java/cn/jiguang/plugins/push/receiver/JPushBroadcastReceiver.java +37 -0
  51. package/android/src/main/java/cn/jiguang/plugins/push/receiver/JPushModuleReceiver.java +264 -0
  52. package/android/src/main/java/cn/jiguang/plugins/service/JCoreModuleService.java +7 -0
  53. package/android/src/main/java/cn/jpush/android/api/BasicPushNotificationBuilder.java +83 -0
  54. package/android/src/main/java/cn/jpush/android/api/CallBackParams.java +50 -0
  55. package/android/src/main/java/cn/jpush/android/api/CmdMessage.java +33 -0
  56. package/android/src/main/java/cn/jpush/android/api/CustomMessage.java +28 -0
  57. package/android/src/main/java/cn/jpush/android/api/CustomPushNotificationBuilder.java +81 -0
  58. package/android/src/main/java/cn/jpush/android/api/DefaultPushNotificationBuilder.java +90 -0
  59. package/android/src/main/java/cn/jpush/android/api/InAppSlotParams.java +65 -0
  60. package/android/src/main/java/cn/jpush/android/api/InstrumentedActivity.java +17 -0
  61. package/android/src/main/java/cn/jpush/android/api/InstrumentedListActivity.java +17 -0
  62. package/android/src/main/java/cn/jpush/android/api/JPushInterface.java +490 -0
  63. package/android/src/main/java/cn/jpush/android/api/JPushMessage.java +121 -0
  64. package/android/src/main/java/cn/jpush/android/api/JThirdPlatFormInterface.java +247 -0
  65. package/android/src/main/java/cn/jpush/android/api/MultiActionsNotificationBuilder.java +72 -0
  66. package/android/src/main/java/cn/jpush/android/api/NotificationCustomButton.java +50 -0
  67. package/android/src/main/java/cn/jpush/android/api/NotificationMessage.java +67 -0
  68. package/android/src/main/java/cn/jpush/android/api/PushNotificationBuilder.java +13 -0
  69. package/android/src/main/java/cn/jpush/android/api/TagAliasCallback.java +8 -0
  70. package/android/src/main/java/cn/jpush/android/data/JPushCollectControl.java +96 -0
  71. package/android/src/main/java/cn/jpush/android/data/JPushConfig.java +79 -0
  72. package/android/src/main/java/cn/jpush/android/data/JPushConfigEx.java +41 -0
  73. package/android/src/main/java/cn/jpush/android/data/JPushLocalNotification.java +155 -0
  74. package/android/src/main/java/cn/jpush/android/helper/JCoreHelper.java +70 -0
  75. package/android/src/main/java/cn/jpush/android/helper/Logger.java +82 -0
  76. package/android/src/main/java/cn/jpush/android/local/ActionHelper.java +1293 -0
  77. package/android/src/main/java/cn/jpush/android/local/ActivityLifeCallBack.java +47 -0
  78. package/android/src/main/java/cn/jpush/android/local/JPushAction.java +67 -0
  79. package/android/src/main/java/cn/jpush/android/local/JPushConstants.java +34 -0
  80. package/android/src/main/java/cn/jpush/android/local/JPushResponse.java +26 -0
  81. package/android/src/main/java/cn/jpush/android/local/ProxyActivityAction.java +34 -0
  82. package/android/src/main/java/cn/jpush/android/service/AlarmReceiver.java +15 -0
  83. package/android/src/main/java/cn/jpush/android/service/BActivity.java +19 -0
  84. package/android/src/main/java/cn/jpush/android/service/DActivity.java +19 -0
  85. package/android/src/main/java/cn/jpush/android/service/DaemonService.java +17 -0
  86. package/android/src/main/java/cn/jpush/android/service/DataProvider.java +136 -0
  87. package/android/src/main/java/cn/jpush/android/service/DataShare.java +129 -0
  88. package/android/src/main/java/cn/jpush/android/service/DownloadProvider.java +139 -0
  89. package/android/src/main/java/cn/jpush/android/service/InitProvider.java +48 -0
  90. package/android/src/main/java/cn/jpush/android/service/JCommonService.java +79 -0
  91. package/android/src/main/java/cn/jpush/android/service/JNotifyActivity.java +28 -0
  92. package/android/src/main/java/cn/jpush/android/service/JPushMessageReceiver.java +94 -0
  93. package/android/src/main/java/cn/jpush/android/service/PushReceiver.java +26 -0
  94. package/android/src/main/java/cn/jpush/android/service/PushService.java +28 -0
  95. package/android/src/main/java/cn/jpush/android/service/SchedulerReceiver.java +17 -0
  96. package/android/src/main/java/cn/jpush/android/service/TagAliasReceiver.java +11 -0
  97. package/android/src/main/java/cn/jpush/android/service/WakedResultReceiver.java +24 -0
  98. package/android/src/main/java/cn/jpush/android/ui/FullScreenView.java +11 -0
  99. package/android/src/main/java/cn/jpush/android/ui/PopWinActivity.java +16 -0
  100. package/android/src/main/java/cn/jpush/android/ui/PushActivity.java +27 -0
  101. package/android/src/main/java/cn/jpush/android/ui/RoundedImageView.java +63 -0
  102. package/android/src/main/java/cn/jpush/android/ui/ShadowViewCard.java +56 -0
  103. package/android/src/main/java/cn/jpush/android/ups/ICallbackResult.java +5 -0
  104. package/android/src/main/java/cn/jpush/android/ups/JPushUPSManager.java +37 -0
  105. package/android/src/main/java/cn/jpush/android/ups/TokenResult.java +47 -0
  106. package/android/src/main/java/cn/jpush/android/ups/UPSRegisterCallBack.java +4 -0
  107. package/android/src/main/java/cn/jpush/android/ups/UPSTurnCallBack.java +4 -0
  108. package/android/src/main/java/cn/jpush/android/ups/UPSUnRegisterCallBack.java +4 -0
  109. package/android/src/main/java/com/margelo/nitro/reactnativejpush/ReactNativeJpush.kt +455 -0
  110. package/android/src/main/java/com/margelo/nitro/reactnativejpush/ReactNativeJpushPackage.kt +50 -0
  111. package/android/src/main/res/values/jpush_string.xml +7 -0
  112. package/android/src/main/res/values/jpush_style.xml +22 -0
  113. package/android/src/test/java/com/margelo/nitro/reactnativejpush/JPushCloudHandshakeTest.java +110 -0
  114. package/android/src/test/java/com/margelo/nitro/reactnativejpush/JPushHandshakeFailureTest.java +73 -0
  115. package/android/src/test/java/com/margelo/nitro/reactnativejpush/ReactNativeJpushProtocolTest.java +908 -0
  116. package/ios/ReactNativeJpush.swift +522 -0
  117. package/ios/bridge/RCTJCoreModule.h +5 -0
  118. package/ios/bridge/RCTJCoreModule.m +31 -0
  119. package/ios/bridge/RCTJPushEventQueue.h +14 -0
  120. package/ios/bridge/RCTJPushEventQueue.m +23 -0
  121. package/ios/bridge/RCTJPushModule.h +37 -0
  122. package/ios/bridge/RCTJPushModule.m +1165 -0
  123. package/ios/include/JCOREService.h +3 -0
  124. package/ios/include/JGCOREAPI.h +14 -0
  125. package/ios/include/JGInforCollectionAuth.h +17 -0
  126. package/ios/include/JGInforCollectionAuthItems.h +2 -0
  127. package/ios/include/JGPushNotificationTrigger.h +2 -0
  128. package/ios/include/JPUSHService.h +359 -0
  129. package/ios/jcore/JCOREConfiguration.h +141 -0
  130. package/ios/jcore/JCOREConfiguration.m +703 -0
  131. package/ios/jcore/JCORECrypto.h +73 -0
  132. package/ios/jcore/JCORECrypto.m +649 -0
  133. package/ios/jcore/JCORESisResolver.h +56 -0
  134. package/ios/jcore/JCORESisResolver.m +525 -0
  135. package/ios/jcore/JCORETcpSession.h +42 -0
  136. package/ios/jcore/JCORETcpSession.m +956 -0
  137. package/ios/jcore/JCOREWireProtocol.h +274 -0
  138. package/ios/jcore/JCOREWireProtocol.m +1453 -0
  139. package/ios/jpush/JPUSHModels.m +214 -0
  140. package/ios/jpush/JPUSHNotificationManager.h +34 -0
  141. package/ios/jpush/JPUSHNotificationManager.m +360 -0
  142. package/ios/jpush/JPUSHService.m +794 -0
  143. package/ios/tests/ReactNativeJpushTests.m +148 -0
  144. package/lib/module/ReactNativeJpush.nitro.js +4 -0
  145. package/lib/module/ReactNativeJpush.nitro.js.map +1 -0
  146. package/lib/module/index.js +801 -0
  147. package/lib/module/index.js.map +1 -0
  148. package/lib/module/package.json +1 -0
  149. package/lib/typescript/package.json +1 -0
  150. package/lib/typescript/src/ReactNativeJpush.nitro.d.ts +60 -0
  151. package/lib/typescript/src/ReactNativeJpush.nitro.d.ts.map +1 -0
  152. package/lib/typescript/src/index.d.ts +235 -0
  153. package/lib/typescript/src/index.d.ts.map +1 -0
  154. package/nitro.json +23 -0
  155. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
  156. package/nitrogen/generated/android/c++/JFunc_void_std__string_std__string.hpp +76 -0
  157. package/nitrogen/generated/android/c++/JHybridReactNativeJpushSpec.cpp +310 -0
  158. package/nitrogen/generated/android/c++/JHybridReactNativeJpushSpec.hpp +115 -0
  159. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativejpush/Func_void_bool.kt +78 -0
  160. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativejpush/Func_void_std__string_std__string.kt +78 -0
  161. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativejpush/HybridReactNativeJpushSpec.kt +274 -0
  162. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativejpush/reactnativejpushOnLoad.kt +35 -0
  163. package/nitrogen/generated/android/reactnativejpush+autolinking.cmake +81 -0
  164. package/nitrogen/generated/android/reactnativejpush+autolinking.gradle +27 -0
  165. package/nitrogen/generated/android/reactnativejpushOnLoad.cpp +58 -0
  166. package/nitrogen/generated/android/reactnativejpushOnLoad.hpp +34 -0
  167. package/nitrogen/generated/ios/ReactNativeJpush+autolinking.rb +62 -0
  168. package/nitrogen/generated/ios/ReactNativeJpush-Swift-Cxx-Bridge.cpp +49 -0
  169. package/nitrogen/generated/ios/ReactNativeJpush-Swift-Cxx-Bridge.hpp +147 -0
  170. package/nitrogen/generated/ios/ReactNativeJpush-Swift-Cxx-Umbrella.hpp +46 -0
  171. package/nitrogen/generated/ios/ReactNativeJpushAutolinking.mm +33 -0
  172. package/nitrogen/generated/ios/ReactNativeJpushAutolinking.swift +26 -0
  173. package/nitrogen/generated/ios/c++/HybridReactNativeJpushSpecSwift.cpp +11 -0
  174. package/nitrogen/generated/ios/c++/HybridReactNativeJpushSpecSwift.hpp +402 -0
  175. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  176. package/nitrogen/generated/ios/swift/Func_void_std__string_std__string.swift +46 -0
  177. package/nitrogen/generated/ios/swift/HybridReactNativeJpushSpec.swift +107 -0
  178. package/nitrogen/generated/ios/swift/HybridReactNativeJpushSpec_cxx.swift +723 -0
  179. package/nitrogen/generated/shared/c++/HybridReactNativeJpushSpec.cpp +73 -0
  180. package/nitrogen/generated/shared/c++/HybridReactNativeJpushSpec.hpp +116 -0
  181. package/package.json +174 -4
  182. package/src/ReactNativeJpush.nitro.ts +93 -0
  183. package/src/index.tsx +1090 -0
@@ -0,0 +1,908 @@
1
+ package com.margelo.nitro.reactnativejpush;
2
+
3
+ import static org.junit.Assert.assertArrayEquals;
4
+ import static org.junit.Assert.assertEquals;
5
+ import static org.junit.Assert.assertNotNull;
6
+ import static org.junit.Assert.assertTrue;
7
+
8
+ import cn.jiguang.api.JCoreInterface;
9
+ import cn.jiguang.api.JCoreManager;
10
+ import cn.jiguang.core.JCoreCrypto;
11
+ import cn.jiguang.core.JCoreStorage;
12
+ import cn.jiguang.core.JCoreTcpEngine;
13
+ import cn.jiguang.core.JCoreWireCodec;
14
+ import cn.jiguang.plugins.push.JPushModule;
15
+ import cn.jiguang.plugins.push.common.JLogger;
16
+ import cn.jiguang.plugins.push.helper.JPushHelper;
17
+ import cn.jiguang.plugins.push.receiver.JPushModuleReceiver;
18
+ import cn.jpush.android.api.CustomMessage;
19
+ import cn.jpush.android.api.JPushInterface;
20
+ import cn.jpush.android.api.JPushMessage;
21
+ import cn.jpush.android.api.NotificationMessage;
22
+ import cn.jpush.android.data.JPushLocalNotification;
23
+ import cn.jpush.android.local.ActionHelper;
24
+ import cn.jpush.android.ups.JPushUPSManager;
25
+ import java.io.File;
26
+ import java.io.InputStream;
27
+ import java.io.OutputStream;
28
+ import java.net.DatagramPacket;
29
+ import java.net.DatagramSocket;
30
+ import java.net.InetAddress;
31
+ import java.net.InetSocketAddress;
32
+ import java.net.ServerSocket;
33
+ import java.net.Socket;
34
+ import java.nio.charset.StandardCharsets;
35
+ import java.nio.file.Files;
36
+ import java.util.Arrays;
37
+ import java.util.Collections;
38
+ import java.util.LinkedHashMap;
39
+ import java.util.List;
40
+ import java.util.Map;
41
+ import java.util.Set;
42
+ import java.util.concurrent.CountDownLatch;
43
+ import java.util.concurrent.TimeUnit;
44
+ import java.util.concurrent.atomic.AtomicInteger;
45
+ import org.junit.Test;
46
+
47
+ public class ReactNativeJpushProtocolTest {
48
+ /**
49
+ * Non-production demo test AppKey used solely for automated native unit and protocol tests.
50
+ * Never used in production environments.
51
+ */
52
+ public static final String DEMO_TEST_APP_KEY = "1f2027d3cfb1b51dac664c42";
53
+
54
+ /**
55
+ * Non-production sandbox package name bound to DEMO_TEST_APP_KEY for automated Android tests.
56
+ * Intentionally avoids 'onekey' and 'jpush' identifiers to prevent production confusion or vendor filtering.
57
+ */
58
+ public static final String DEMO_TEST_PACKAGE_NAME = "com.devstudio.msgbench";
59
+
60
+ @Test
61
+ public void testCryptoPrimitivesAndSeedDerivation() throws Exception {
62
+ long seed = 123456789L;
63
+ String keyA = JCoreCrypto.generateSeedKey(seed);
64
+ String keyB = JCoreCrypto.generateSeedKey(seed);
65
+ assertEquals(32L, (long) keyA.length());
66
+ assertEquals(keyA, keyB);
67
+
68
+ byte[] plain = "OneKey JPush Clean SDK Crypto Payload 2026".getBytes(StandardCharsets.UTF_8);
69
+ byte[] aesEnc = JCoreCrypto.aes256CbcSeedEncrypt(plain, seed);
70
+ byte[] aesDec = JCoreCrypto.aes256CbcSeedDecrypt(aesEnc, seed);
71
+ assertArrayEquals(plain, aesDec);
72
+
73
+ byte[] sm4Key = JCoreCrypto.hexDecode("0123456789abcdeffedcba9876543210");
74
+ byte[] sm4Iv = new byte[16];
75
+ byte[] sm4Enc = JCoreCrypto.sm4CbcEncrypt(plain, sm4Key, sm4Iv);
76
+ byte[] sm4Dec = JCoreCrypto.sm4CbcDecrypt(sm4Enc, sm4Key, sm4Iv);
77
+ assertArrayEquals(plain, sm4Dec);
78
+
79
+ byte[] gz = JCoreCrypto.gzipCompress(plain);
80
+ byte[] ungz = JCoreCrypto.gzipDecompress(gz);
81
+ assertArrayEquals(plain, ungz);
82
+
83
+ String b64 = JCoreCrypto.base64Encode(plain);
84
+ byte[] b64Decoded = JCoreCrypto.base64Decode(b64);
85
+ assertArrayEquals(plain, b64Decoded);
86
+ }
87
+
88
+ @Test
89
+ public void testUdpSis10BytePacketWithDemoTestAppKey() {
90
+ List<String> failedIps = Collections.singletonList("198.51.100.1:7000");
91
+ String sisReqJson = JCoreWireCodec.buildSisRequestJson(
92
+ 1,
93
+ DEMO_TEST_APP_KEY,
94
+ 900100200L,
95
+ "46000",
96
+ failedIps,
97
+ 1
98
+ );
99
+ assertTrue(sisReqJson.contains(DEMO_TEST_APP_KEY));
100
+ assertTrue(sisReqJson.contains("5.1.1"));
101
+
102
+ byte[] udpPacket = JCoreWireCodec.encodeUdpSisPacket(
103
+ sisReqJson,
104
+ 0x00123456
105
+ );
106
+ assertTrue(udpPacket.length > 10);
107
+ int hdrLen = ((udpPacket[0] & 0xFF) << 8) | (udpPacket[1] & 0xFF);
108
+ assertEquals((long) udpPacket.length, (long) hdrLen);
109
+ assertEquals((long) 'U', (long) ((char) udpPacket[2]));
110
+ assertEquals((long) 'G', (long) ((char) udpPacket[3]));
111
+
112
+ String decodedJson = JCoreWireCodec.decodeUdpSisPacket(udpPacket);
113
+ assertEquals(sisReqJson, decodedJson);
114
+ }
115
+
116
+ @Test
117
+ public void testAsymmetric24ByteClientAnd20ByteServerFramesWithDemoTestAppKey() {
118
+ byte[] regBody = JCoreWireCodec.buildRegisterPayload(
119
+ DEMO_TEST_APP_KEY,
120
+ "developer-default",
121
+ DEMO_TEST_PACKAGE_NAME,
122
+ "dev_001"
123
+ );
124
+ int registerSeed = 0x13572468;
125
+ byte[] clientRegFrame = JCoreWireCodec.encodeClientFrame(
126
+ JCoreWireCodec.CMD_REGISTER,
127
+ JCoreWireCodec.VER_REGISTER,
128
+ JCoreWireCodec.ALG_AES_CBC,
129
+ 0,
130
+ 1L,
131
+ registerSeed,
132
+ 0L,
133
+ regBody
134
+ );
135
+ assertTrue(clientRegFrame.length >= 24 && ((clientRegFrame.length - 24) % 16 == 0));
136
+
137
+ JCoreWireCodec.DecodedFrame decodedReg = JCoreWireCodec.decodeClientFrame(clientRegFrame, 0L);
138
+ assertNotNull(decodedReg);
139
+ assertEquals((long) JCoreWireCodec.CMD_REGISTER, (long) decodedReg.command);
140
+ assertEquals((long) JCoreWireCodec.VER_REGISTER, (long) decodedReg.version);
141
+ assertEquals((long) registerSeed, (long) decodedReg.sessionId);
142
+
143
+ JCoreWireCodec.RegisterRequest parsedReg = JCoreWireCodec.parseRegisterRequest(decodedReg.body);
144
+ assertNotNull(parsedReg);
145
+ assertEquals(DEMO_TEST_APP_KEY, parsedReg.appKey);
146
+ assertEquals(DEMO_TEST_PACKAGE_NAME, parsedReg.packageName);
147
+
148
+ long assignedUid = 900100200L;
149
+ byte[] serverRegBody = JCoreWireCodec.buildRegisterResponsePayload(
150
+ assignedUid,
151
+ "pwd_demo_2026",
152
+ "1a0018970a900100200",
153
+ "dev_001"
154
+ );
155
+ byte[] serverRegFrame = JCoreWireCodec.encodeResponseFrame(
156
+ JCoreWireCodec.CMD_REGISTER,
157
+ JCoreWireCodec.VER_REGISTER,
158
+ JCoreWireCodec.ALG_AES_CBC,
159
+ 0,
160
+ 1L,
161
+ registerSeed,
162
+ assignedUid,
163
+ 0,
164
+ serverRegBody
165
+ );
166
+ assertTrue(serverRegFrame.length >= 20 && ((serverRegFrame.length - 20) % 16 == 0));
167
+
168
+ JCoreWireCodec.DecodedFrame decodedServerReg = JCoreWireCodec.decodeServerFrame(serverRegFrame, assignedUid);
169
+ assertNotNull(decodedServerReg);
170
+ JCoreWireCodec.RegisterResponse regResp = JCoreWireCodec.parseRegisterResponse(decodedServerReg);
171
+ assertNotNull(regResp);
172
+ assertEquals(0L, (long) regResp.statusCode);
173
+ assertEquals(assignedUid, regResp.uid);
174
+ assertEquals("1a0018970a900100200", regResp.registrationId);
175
+
176
+ byte[] tagPayload = JCoreWireCodec.buildTagOperationPayload(1, 21, Arrays.asList("btc", "eth"));
177
+ byte[] tagFrame = JCoreWireCodec.encodeClientFrame(
178
+ JCoreWireCodec.CMD_TAG_OP,
179
+ JCoreWireCodec.VER_TAG_OP,
180
+ JCoreWireCodec.ALG_SM4_CBC,
181
+ 0,
182
+ 2L,
183
+ 0x22334455,
184
+ assignedUid,
185
+ tagPayload
186
+ );
187
+ JCoreWireCodec.DecodedFrame decodedTag = JCoreWireCodec.decodeClientFrame(tagFrame, assignedUid);
188
+ assertNotNull(decodedTag);
189
+ JCoreWireCodec.OperationPayload tagOp = JCoreWireCodec.parseOperationPayload(
190
+ JCoreWireCodec.CMD_TAG_OP,
191
+ decodedTag.body
192
+ );
193
+ assertNotNull(tagOp);
194
+ assertEquals("add", tagOp.op);
195
+
196
+ Map<String, Object> props = new LinkedHashMap<String, Object>();
197
+ props.put("tier", "pro");
198
+ byte[] propPayload = JCoreWireCodec.buildPropertyOperationPayload(1, 23, props);
199
+ assertNotNull(propPayload);
200
+ assertTrue(propPayload.length > 2);
201
+ }
202
+
203
+ @Test
204
+ public void testEmptyRegistrationBeforeHandshakeMultiRuntimeEventsAndManifestExportedFalse() throws Exception {
205
+ // 1. Fresh state before TCP Register (cmd=0) must return "" for getRegistrationID (no fake 1a0018970a...)
206
+ JCoreStorage.clearRegistration(null);
207
+ assertEquals("", JPushInterface.getRegistrationID(null));
208
+ assertEquals(0L, JCoreStorage.getUid(null));
209
+ assertEquals("", JCoreStorage.getPassword(null));
210
+
211
+ // 2. Cold-start queueing and multi-runtime (main + bg) Nitro event fan-out
212
+ JPushModule.clearActiveReactContexts();
213
+ JPushHelper.clearNitroListeners();
214
+
215
+ Map<String, Object> coldStartPayload = new LinkedHashMap<String, Object>();
216
+ coldStartPayload.put("messageID", "nitro_cold_01");
217
+ coldStartPayload.put("title", "NitroCold");
218
+ JPushHelper.sendEventMap("NotificationEvent", coldStartPayload);
219
+
220
+ final List<String> mainReceivedNames = new java.util.ArrayList<String>();
221
+ final List<String> mainReceivedJsons = new java.util.ArrayList<String>();
222
+ final List<String> bgReceivedNames = new java.util.ArrayList<String>();
223
+ final List<String> bgReceivedJsons = new java.util.ArrayList<String>();
224
+
225
+ int mainListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
226
+ @Override
227
+ public void onEvent(String eventName, String payloadJson) {
228
+ mainReceivedNames.add(eventName);
229
+ mainReceivedJsons.add(payloadJson);
230
+ }
231
+ });
232
+ int bgListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
233
+ @Override
234
+ public void onEvent(String eventName, String payloadJson) {
235
+ bgReceivedNames.add(eventName);
236
+ bgReceivedJsons.add(payloadJson);
237
+ }
238
+ });
239
+ assertTrue(mainListenerId > 0);
240
+ assertTrue(bgListenerId > 0);
241
+ assertEquals(1L, (long) mainReceivedNames.size());
242
+ assertEquals("NotificationEvent", mainReceivedNames.get(0));
243
+ assertTrue(mainReceivedJsons.get(0).contains("nitro_cold_01"));
244
+
245
+ Map<String, Object> eventPayload = new LinkedHashMap<String, Object>();
246
+ eventPayload.put("messageID", "multi_rt_msg_1");
247
+ eventPayload.put("title", "MultiRuntime");
248
+ JPushHelper.sendEventMap("NotificationEvent", eventPayload);
249
+
250
+ assertEquals(2L, (long) mainReceivedNames.size());
251
+ assertEquals(1L, (long) bgReceivedNames.size());
252
+ assertEquals("NotificationEvent", mainReceivedNames.get(1));
253
+ assertEquals("NotificationEvent", bgReceivedNames.get(0));
254
+ assertTrue(mainReceivedJsons.get(1).contains("multi_rt_msg_1"));
255
+ assertTrue(bgReceivedJsons.get(0).contains("multi_rt_msg_1"));
256
+
257
+ JPushHelper.unregisterNitroListener(mainListenerId);
258
+ JPushHelper.unregisterNitroListener(bgListenerId);
259
+ JPushHelper.clearNitroListeners();
260
+
261
+ // 2b. Verify JPushModuleReceiver callbacks on plain JVM (without Arguments.createMap() or ReactApplicationContext)
262
+ JPushModuleReceiver receiver = new JPushModuleReceiver();
263
+ CustomMessage customMsg = new CustomMessage();
264
+ customMsg.messageId = "cm_101";
265
+ customMsg.title = "CustomTitle";
266
+ customMsg.message = "CustomBody";
267
+ customMsg.extra = "{\"count\":42,\"flag\":true}";
268
+ receiver.onMessage(null, customMsg);
269
+
270
+ NotificationMessage arrivedMsg = new NotificationMessage();
271
+ arrivedMsg.msgId = "nm_102";
272
+ arrivedMsg.notificationType = 0;
273
+ arrivedMsg.notificationTitle = "ArrivedTitle";
274
+ arrivedMsg.notificationContent = "ArrivedBody";
275
+ arrivedMsg.notificationExtras = "{\"amount\":100}";
276
+ receiver.onNotifyMessageArrived(null, arrivedMsg);
277
+
278
+ JPushMessage tagMsg = new JPushMessage();
279
+ tagMsg.setSequence(11);
280
+ tagMsg.setErrorCode(0);
281
+ tagMsg.setTags(new java.util.HashSet<String>(Arrays.asList("tag_a", "tag_b")));
282
+ receiver.onTagOperatorResult(null, tagMsg);
283
+
284
+ JPushMessage aliasMsg = new JPushMessage();
285
+ aliasMsg.setSequence(12);
286
+ aliasMsg.setErrorCode(0);
287
+ aliasMsg.setAlias("user_alias_01");
288
+ receiver.onAliasOperatorResult(null, aliasMsg);
289
+
290
+ JPushMessage propMsg = new JPushMessage();
291
+ propMsg.setSequence(13);
292
+ propMsg.setErrorCode(0);
293
+ Map<String, Object> propMap = new LinkedHashMap<String, Object>();
294
+ propMap.put("vip", Boolean.TRUE);
295
+ propMsg.setPros(propMap);
296
+ receiver.onPropertyOperatorResult(null, propMsg);
297
+
298
+ final List<String> receiverEvents = new java.util.ArrayList<String>();
299
+ int recvListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
300
+ @Override
301
+ public void onEvent(String eventName, String payloadJson) {
302
+ receiverEvents.add(eventName + ":" + payloadJson);
303
+ }
304
+ });
305
+ JPushHelper.unregisterNitroListener(recvListenerId);
306
+ JPushHelper.clearNitroListeners();
307
+ assertEquals(5L, (long) receiverEvents.size());
308
+ assertTrue(receiverEvents.get(0).startsWith("CustomMessageEvent:") && receiverEvents.get(0).contains("\"42\""));
309
+ assertTrue(receiverEvents.get(1).startsWith("NotificationEvent:") && receiverEvents.get(1).contains("\"100\""));
310
+ assertTrue(receiverEvents.get(2).startsWith("TagAliasEvent:") && receiverEvents.get(2).contains("tag_a"));
311
+ assertTrue(receiverEvents.get(3).startsWith("TagAliasEvent:") && receiverEvents.get(3).contains("user_alias_01"));
312
+ assertTrue(receiverEvents.get(4).startsWith("TagAliasEvent:") && receiverEvents.get(4).contains("vip"));
313
+
314
+ // 2c. Verify JCoreStorage.appendLocalLog cap and JLogger -> JCoreInterface.readNewLogs forwarding
315
+ JCoreInterface.enableSDKLocalLog(null, true, false);
316
+ JCoreInterface.readNewLogs(null);
317
+ StringBuilder longLine = new StringBuilder();
318
+ for (int i = 0; i < 600; i++) {
319
+ longLine.append("0123456789");
320
+ }
321
+ for (int i = 0; i < 10; i++) {
322
+ JCoreStorage.appendLocalLog("line_" + i + "_" + longLine.toString());
323
+ }
324
+ JLogger.i("jlogger_forwarded_marker_01");
325
+ String cappedLogs = JCoreInterface.readNewLogs(null);
326
+ assertTrue(cappedLogs.length() <= 32768);
327
+ assertTrue(cappedLogs.contains("jlogger_forwarded_marker_01"));
328
+ JCoreInterface.enableSDKLocalLog(null, false, false);
329
+
330
+ // 2d. Verify JCoreInterface.setHeartbeatInterval and JCoreManager.unRegister
331
+ JCoreInterface.setHeartbeatInterval(null, 120);
332
+ assertEquals(120000L, JCoreTcpEngine.getInstance().getHeartbeatIntervalMs());
333
+ JCoreTcpEngine.getInstance().setHeartbeatIntervalMs(290000L);
334
+ JCoreStorage.updateRegistration(null, 550011L, "unreg_pwd", "reg_550011", "dev_550011");
335
+ JCoreStorage.setServerRegistered(null, true);
336
+ JCoreStorage.setConnected(null, true);
337
+ JCoreManager.unRegister(null);
338
+ assertEquals(false, JCoreStorage.isServerRegistered(null));
339
+ assertEquals(0L, JCoreStorage.getUid(null));
340
+ assertEquals("", JCoreStorage.getRegistrationId(null));
341
+ assertEquals(false, JCoreStorage.isConnected(null));
342
+
343
+ // 2e. Verify JPushUPSManager.registerToken resolves appKey when appId is empty
344
+ JCoreStorage.setPushStopped(null, true);
345
+ JPushUPSManager.registerToken(null, "", "ups_appkey_fallback_01", "ups_secret", null);
346
+ assertEquals("ups_appkey_fallback_01", JCoreStorage.getAppKey(null));
347
+ JCoreTcpEngine.getInstance().stop(null);
348
+ JCoreStorage.setPushStopped(null, false);
349
+
350
+ // 2f. Verify delayed addLocalNotification scheduling and removeLocalNotification cancellation
351
+ ActionHelper.getInstance().clearActiveNotificationIds();
352
+ JPushLocalNotification cancelledLn = new JPushLocalNotification();
353
+ cancelledLn.setNotificationId(77001L);
354
+ cancelledLn.setTitle("CancelledTitle");
355
+ cancelledLn.setContent("CancelledContent");
356
+ cancelledLn.setBroadcastTime(300L);
357
+ JPushInterface.addLocalNotification(null, cancelledLn);
358
+ assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77001)));
359
+ JPushInterface.removeLocalNotification(null, 77001L);
360
+
361
+ JPushLocalNotification firedLn = new JPushLocalNotification();
362
+ firedLn.setNotificationId(77002L);
363
+ firedLn.setTitle("FiredTitle");
364
+ firedLn.setContent("FiredContent");
365
+ firedLn.setBroadcastTime(70L);
366
+ JPushInterface.addLocalNotification(null, firedLn);
367
+ assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77002)));
368
+ Thread.sleep(360L);
369
+ assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77001)));
370
+ assertTrue(ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77002)));
371
+ ActionHelper.getInstance().clearActiveNotificationIds();
372
+
373
+ // 2g. Verify ActionHelper server response parsing (getAlias, getAllTags, checkTagBindState)
374
+ // and rollback on non-zero statusCode for Tag/Alias/Property/MobileNumber
375
+ final ServerSocket opServer = new ServerSocket(0, 5, InetAddress.getByName("127.0.0.1"));
376
+ final int opPort = opServer.getLocalPort();
377
+ final long opUid = 660033L;
378
+ Thread opServerThread = new Thread(new Runnable() {
379
+ @Override
380
+ public void run() {
381
+ Socket client = null;
382
+ try {
383
+ opServer.setSoTimeout(5000);
384
+ client = opServer.accept();
385
+ client.setSoTimeout(5000);
386
+ InputStream in = client.getInputStream();
387
+ OutputStream out = client.getOutputStream();
388
+
389
+ JCoreWireCodec.DecodedFrame loginFrame = JCoreWireCodec.readClientFrameFromStream(in, opUid);
390
+ if (loginFrame != null && loginFrame.command == JCoreWireCodec.CMD_LOGIN) {
391
+ byte[] loginResp = JCoreWireCodec.buildLoginResponsePayload(6601, 520, "", 1700001000);
392
+ out.write(JCoreWireCodec.encodeResponseFrame(
393
+ JCoreWireCodec.CMD_LOGIN,
394
+ JCoreWireCodec.VER_LOGIN,
395
+ loginFrame.requestId,
396
+ 6601,
397
+ opUid,
398
+ 0,
399
+ loginResp
400
+ ));
401
+ out.flush();
402
+ }
403
+
404
+ while (!client.isClosed()) {
405
+ JCoreWireCodec.DecodedFrame req = JCoreWireCodec.readClientFrameFromStream(in, opUid);
406
+ if (req == null) {
407
+ break;
408
+ }
409
+ if (req.command == JCoreWireCodec.CMD_HEARTBEAT) {
410
+ out.write(JCoreWireCodec.encodeResponseFrame(
411
+ JCoreWireCodec.CMD_HEARTBEAT,
412
+ JCoreWireCodec.VER_HEARTBEAT,
413
+ req.requestId,
414
+ 6601,
415
+ opUid,
416
+ 0,
417
+ new byte[0]
418
+ ));
419
+ out.flush();
420
+ } else if (req.command == JCoreWireCodec.CMD_ALIAS_OP) {
421
+ if (req.requestId == 301L) {
422
+ byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"alias\":\"srv_alias_301\"}");
423
+ out.write(JCoreWireCodec.encodeResponseFrame(
424
+ JCoreWireCodec.CMD_ALIAS_OP,
425
+ JCoreWireCodec.VER_ALIAS_OP,
426
+ req.requestId,
427
+ 6601,
428
+ opUid,
429
+ 0,
430
+ body
431
+ ));
432
+ } else {
433
+ byte[] errBody = JCoreWireCodec.buildJsonCommandPayload("{\"code\":6002}");
434
+ out.write(JCoreWireCodec.encodeResponseFrame(
435
+ JCoreWireCodec.CMD_ALIAS_OP,
436
+ JCoreWireCodec.VER_ALIAS_OP,
437
+ req.requestId,
438
+ 6601,
439
+ opUid,
440
+ 6002,
441
+ errBody
442
+ ));
443
+ }
444
+ out.flush();
445
+ } else if (req.command == JCoreWireCodec.CMD_TAG_OP) {
446
+ if (req.requestId == 303L) {
447
+ byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"tags\":[\"srv_t1\",\"srv_t2\"]}");
448
+ out.write(JCoreWireCodec.encodeResponseFrame(
449
+ JCoreWireCodec.CMD_TAG_OP,
450
+ JCoreWireCodec.VER_TAG_OP,
451
+ req.requestId,
452
+ 6601,
453
+ opUid,
454
+ 0,
455
+ body
456
+ ));
457
+ } else if (req.requestId == 305L) {
458
+ byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"bind\":true}");
459
+ out.write(JCoreWireCodec.encodeResponseFrame(
460
+ JCoreWireCodec.CMD_TAG_OP,
461
+ JCoreWireCodec.VER_TAG_OP,
462
+ req.requestId,
463
+ 6601,
464
+ opUid,
465
+ 0,
466
+ body
467
+ ));
468
+ } else {
469
+ byte[] errBody = JCoreWireCodec.buildJsonCommandPayload("{\"code\":6002}");
470
+ out.write(JCoreWireCodec.encodeResponseFrame(
471
+ JCoreWireCodec.CMD_TAG_OP,
472
+ JCoreWireCodec.VER_TAG_OP,
473
+ req.requestId,
474
+ 6601,
475
+ opUid,
476
+ 6002,
477
+ errBody
478
+ ));
479
+ }
480
+ out.flush();
481
+ } else if (req.command == JCoreWireCodec.CMD_PROPERTY_OP) {
482
+ out.write(JCoreWireCodec.encodeResponseFrame(
483
+ JCoreWireCodec.CMD_PROPERTY_OP,
484
+ JCoreWireCodec.VER_PROPERTY_OP,
485
+ req.requestId,
486
+ 6601,
487
+ opUid,
488
+ 6002,
489
+ new byte[0]
490
+ ));
491
+ out.flush();
492
+ } else if (req.command == JCoreWireCodec.CMD_MOBILE_NUMBER) {
493
+ out.write(JCoreWireCodec.encodeResponseFrame(
494
+ JCoreWireCodec.CMD_MOBILE_NUMBER,
495
+ JCoreWireCodec.VER_MOBILE_NUMBER,
496
+ req.requestId,
497
+ 6601,
498
+ opUid,
499
+ 6002,
500
+ new byte[0]
501
+ ));
502
+ out.flush();
503
+ }
504
+ }
505
+ } catch (Throwable ignored) {
506
+ } finally {
507
+ try {
508
+ if (client != null) client.close();
509
+ } catch (Throwable ignored) {}
510
+ try {
511
+ opServer.close();
512
+ } catch (Throwable ignored) {}
513
+ }
514
+ }
515
+ }, "test-op-rollback-server");
516
+ opServerThread.setDaemon(true);
517
+ opServerThread.start();
518
+
519
+ ActionHelper.getInstance().registerReceiver(receiver);
520
+ final List<String> opEvents = new java.util.ArrayList<String>();
521
+ final CountDownLatch rollbackDoneLatch = new CountDownLatch(4);
522
+ final CountDownLatch queryDoneLatch = new CountDownLatch(3);
523
+ int opListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
524
+ @Override
525
+ public void onEvent(String eventName, String payloadJson) {
526
+ if ("TagAliasEvent".equals(eventName) || "MobileNumberEvent".equals(eventName)) {
527
+ opEvents.add(eventName + ":" + payloadJson);
528
+ if (payloadJson.contains("302") || payloadJson.contains("304") || payloadJson.contains("306") || payloadJson.contains("307")) {
529
+ rollbackDoneLatch.countDown();
530
+ }
531
+ if (payloadJson.contains("301") || payloadJson.contains("303") || payloadJson.contains("305")) {
532
+ queryDoneLatch.countDown();
533
+ }
534
+ }
535
+ }
536
+ });
537
+
538
+ JCoreStorage.updateRegistration(null, opUid, "op_pwd", "reg_op_660033", "dev_op_01");
539
+ JCoreStorage.setServerRegistered(null, true);
540
+ JCoreStorage.saveAlias(null, "old_local_alias");
541
+ JCoreStorage.saveTags(null, new java.util.HashSet<String>(Arrays.asList("old_local_tag")));
542
+ JCoreStorage.saveProperties(null, Collections.<String, Object>singletonMap("tier", "gold"));
543
+ JCoreStorage.saveMobileNumber(null, "13800000001");
544
+
545
+ JCoreTcpEngine.getInstance().setRequireServerAckForOperations(true);
546
+ JCoreTcpEngine.getInstance().setCustomEndpoint("127.0.0.1", opPort);
547
+ JCoreTcpEngine.getInstance().triggerReconnectNow(null);
548
+ assertTrue(JCoreTcpEngine.getInstance().awaitHandshake(3000L));
549
+
550
+ JPushInterface.setAlias(null, 302, "rejected_alias_302");
551
+ JPushInterface.setTags(null, 304, new java.util.HashSet<String>(Arrays.asList("rejected_tag_304")));
552
+ JPushInterface.setProperties(null, 306, Collections.<String, Object>singletonMap("tier", "rejected_tier"));
553
+ JPushInterface.setMobileNumber(null, 307, "13999999999");
554
+
555
+ assertTrue(rollbackDoneLatch.await(4000L, TimeUnit.MILLISECONDS));
556
+ assertEquals("old_local_alias", JCoreStorage.getAlias(null));
557
+ assertTrue(JCoreStorage.getTags(null).contains("old_local_tag"));
558
+ assertTrue(!JCoreStorage.getTags(null).contains("rejected_tag_304"));
559
+ assertEquals("gold", JCoreStorage.getProperties(null).get("tier"));
560
+ assertEquals("13800000001", JCoreStorage.getMobileNumber(null));
561
+
562
+ JPushInterface.getAlias(null, 301);
563
+ JPushInterface.getAllTags(null, 303);
564
+ JPushInterface.checkTagBindState(null, 305, "unbound_local_tag");
565
+
566
+ assertTrue(queryDoneLatch.await(4000L, TimeUnit.MILLISECONDS));
567
+ assertEquals("srv_alias_301", JCoreStorage.getAlias(null));
568
+ assertTrue(JCoreStorage.getTags(null).contains("srv_t1") && JCoreStorage.getTags(null).contains("srv_t2"));
569
+ boolean sawServerBindTrue = false;
570
+ for (String ev : opEvents) {
571
+ if (ev.contains("unbound_local_tag") && ev.contains("true")) {
572
+ sawServerBindTrue = true;
573
+ }
574
+ }
575
+ assertTrue(sawServerBindTrue);
576
+
577
+ JPushHelper.unregisterNitroListener(opListenerId);
578
+ JPushHelper.clearNitroListeners();
579
+ JCoreTcpEngine.getInstance().setRequireServerAckForOperations(false);
580
+ JCoreTcpEngine.getInstance().setCustomEndpoint("", 0);
581
+ JCoreTcpEngine.getInstance().stop(null);
582
+ JCoreStorage.clearRegistration(null);
583
+
584
+ // 2h. Verify JPushInterface.setProperties / deleteProperties / cleanProperties emit TagAliasEvent with pros
585
+ // via JPushModuleReceiver.onPropertyOperatorResult, and JPushBroadcastReceiver ignores intents with
586
+ // "jpush_already_dispatched" == true while JPushModuleReceiver.onNotifyMessageOpened clears stale NOTIFICATION_BUNDLE.
587
+ final List<String> propEvents = new java.util.ArrayList<String>();
588
+ int propListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
589
+ @Override
590
+ public void onEvent(String eventName, String payloadJson) {
591
+ propEvents.add(eventName + ":" + payloadJson);
592
+ }
593
+ });
594
+ ActionHelper.getInstance().registerReceiver(receiver);
595
+ Map<String, Object> setPropsInput = new LinkedHashMap<String, Object>();
596
+ setPropsInput.put("region", "apac");
597
+ setPropsInput.put("plan", "enterprise");
598
+ JPushInterface.setProperties(null, 401, setPropsInput);
599
+ assertEquals("apac", JCoreStorage.getProperties(null).get("region"));
600
+ assertEquals("enterprise", JCoreStorage.getProperties(null).get("plan"));
601
+
602
+ Map<String, Object> delPropsInput = new LinkedHashMap<String, Object>();
603
+ delPropsInput.put("region", "");
604
+ JPushInterface.deleteProperties(null, 402, delPropsInput);
605
+ assertTrue(!JCoreStorage.getProperties(null).containsKey("region"));
606
+ assertEquals("enterprise", JCoreStorage.getProperties(null).get("plan"));
607
+
608
+ JPushInterface.cleanProperties(null, 403);
609
+ assertTrue(JCoreStorage.getProperties(null).isEmpty());
610
+
611
+ assertEquals(3L, (long) propEvents.size());
612
+ assertTrue(propEvents.get(0).startsWith("TagAliasEvent:") && propEvents.get(0).contains("401") && propEvents.get(0).contains("enterprise"));
613
+ assertTrue(propEvents.get(1).startsWith("TagAliasEvent:") && propEvents.get(1).contains("402") && propEvents.get(1).contains("region"));
614
+ assertTrue(propEvents.get(2).startsWith("TagAliasEvent:") && propEvents.get(2).contains("403"));
615
+ JPushHelper.unregisterNitroListener(propListenerId);
616
+ JPushHelper.clearNitroListeners();
617
+
618
+ android.os.Bundle staleBundle = new android.os.Bundle();
619
+ cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver.NOTIFICATION_BUNDLE = staleBundle;
620
+ NotificationMessage openedMsg = new NotificationMessage();
621
+ openedMsg.msgId = "nm_open_404";
622
+ openedMsg.notificationTitle = "OpenTitle";
623
+ openedMsg.notificationContent = "OpenBody";
624
+ receiver.onNotifyMessageOpened(null, openedMsg);
625
+ assertEquals(null, cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver.NOTIFICATION_BUNDLE);
626
+
627
+ cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver broadcastReceiver =
628
+ new cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver();
629
+ android.content.Intent alreadyDispatchedIntent = new android.content.Intent(JPushInterface.ACTION_NOTIFICATION_OPENED) {
630
+ @Override
631
+ public String getAction() {
632
+ return JPushInterface.ACTION_NOTIFICATION_OPENED;
633
+ }
634
+ @Override
635
+ public boolean getBooleanExtra(String name, boolean defaultValue) {
636
+ if ("jpush_already_dispatched".equals(name)) {
637
+ return true;
638
+ }
639
+ return defaultValue;
640
+ }
641
+ @Override
642
+ public android.os.Bundle getExtras() {
643
+ return new android.os.Bundle();
644
+ }
645
+ };
646
+ broadcastReceiver.onReceive(null, alreadyDispatchedIntent);
647
+ assertEquals(null, cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver.NOTIFICATION_BUNDLE);
648
+ JPushHelper.clearNitroListeners();
649
+
650
+ // 3. Verify AndroidManifest.xml has android:exported="false" on all 5 internal activities
651
+ File manifestFile = new File("src/main/AndroidManifest.xml");
652
+ if (!manifestFile.exists()) {
653
+ manifestFile = new File("android/src/main/AndroidManifest.xml");
654
+ }
655
+ if (!manifestFile.exists()) {
656
+ manifestFile = new File(
657
+ System.getProperty("user.dir"),
658
+ "native-modules/react-native-jpush/android/src/main/AndroidManifest.xml"
659
+ );
660
+ }
661
+ assertTrue(manifestFile.exists());
662
+ String xml = new String(Files.readAllBytes(manifestFile.toPath()), StandardCharsets.UTF_8);
663
+ assertTrue(!xml.contains("android:exported=\"true\""));
664
+ for (String activityName : new String[] {
665
+ "cn.jpush.android.ui.PopWinActivity",
666
+ "cn.jpush.android.ui.PushActivity",
667
+ "cn.jpush.android.service.JNotifyActivity",
668
+ "cn.android.service.JTransitActivity",
669
+ "cn.jpush.android.service.DActivity"
670
+ }) {
671
+ int idx = xml.indexOf(activityName);
672
+ assertTrue(idx > 0);
673
+ int endTag = xml.indexOf(">", idx);
674
+ String activityHeader = xml.substring(idx, endTag);
675
+ assertTrue(activityHeader.contains("android:exported=\"false\""));
676
+ }
677
+
678
+ // 4. Verify periodic heartbeat timer sends additional CMD_HEARTBEAT frames after initial login
679
+ final ServerSocket hbServer = new ServerSocket(0, 5, InetAddress.getByName("127.0.0.1"));
680
+ final int hbPort = hbServer.getLocalPort();
681
+ final AtomicInteger heartbeatsReceived = new AtomicInteger(0);
682
+ final CountDownLatch twoHeartbeatsLatch = new CountDownLatch(1);
683
+ final long testUid = 880011L;
684
+
685
+ Thread hbThread = new Thread(new Runnable() {
686
+ @Override
687
+ public void run() {
688
+ Socket client = null;
689
+ try {
690
+ hbServer.setSoTimeout(5000);
691
+ client = hbServer.accept();
692
+ client.setSoTimeout(5000);
693
+ InputStream in = client.getInputStream();
694
+ OutputStream out = client.getOutputStream();
695
+
696
+ JCoreWireCodec.DecodedFrame loginFrame = JCoreWireCodec.readClientFrameFromStream(in, testUid);
697
+ if (loginFrame != null && loginFrame.command == JCoreWireCodec.CMD_LOGIN) {
698
+ byte[] loginResp = JCoreWireCodec.buildLoginResponsePayload(7701, 520, "", 1700000600);
699
+ out.write(JCoreWireCodec.encodeResponseFrame(
700
+ JCoreWireCodec.CMD_LOGIN,
701
+ JCoreWireCodec.VER_LOGIN,
702
+ loginFrame.requestId,
703
+ 7701,
704
+ testUid,
705
+ 0,
706
+ loginResp
707
+ ));
708
+ out.flush();
709
+ }
710
+
711
+ while (!client.isClosed()) {
712
+ JCoreWireCodec.DecodedFrame frame = JCoreWireCodec.readClientFrameFromStream(in, testUid);
713
+ if (frame == null) {
714
+ break;
715
+ }
716
+ if (frame.command == JCoreWireCodec.CMD_HEARTBEAT) {
717
+ out.write(JCoreWireCodec.encodeResponseFrame(
718
+ JCoreWireCodec.CMD_HEARTBEAT,
719
+ JCoreWireCodec.VER_HEARTBEAT,
720
+ frame.requestId,
721
+ 7701,
722
+ testUid,
723
+ 0,
724
+ new byte[0]
725
+ ));
726
+ out.flush();
727
+ if (heartbeatsReceived.incrementAndGet() >= 2) {
728
+ twoHeartbeatsLatch.countDown();
729
+ break;
730
+ }
731
+ }
732
+ }
733
+ } catch (Throwable ignored) {
734
+ } finally {
735
+ try {
736
+ if (client != null) client.close();
737
+ } catch (Throwable ignored) {}
738
+ try {
739
+ hbServer.close();
740
+ } catch (Throwable ignored) {}
741
+ }
742
+ }
743
+ }, "test-periodic-heartbeat-server");
744
+ hbThread.setDaemon(true);
745
+ hbThread.start();
746
+
747
+ JCoreStorage.updateRegistration(null, testUid, "hb_pwd", "reg_hb_880011", "dev_hb_01");
748
+ JCoreStorage.setServerRegistered(null, true);
749
+ JCoreTcpEngine.getInstance().setHeartbeatIntervalMs(60L);
750
+ JCoreTcpEngine.getInstance().setCustomEndpoint("127.0.0.1", hbPort);
751
+ JCoreTcpEngine.getInstance().triggerReconnectNow(null);
752
+ assertTrue(JCoreTcpEngine.getInstance().awaitHandshake(3000L));
753
+ assertTrue(twoHeartbeatsLatch.await(3000L, TimeUnit.MILLISECONDS));
754
+ assertTrue(heartbeatsReceived.get() >= 2);
755
+ JCoreTcpEngine.getInstance().setHeartbeatIntervalMs(290000L);
756
+ JCoreTcpEngine.getInstance().stop(null);
757
+ JCoreStorage.clearRegistration(null);
758
+
759
+ // 5. Verify connectAndHandshake automatic SIS resolution when candidateEndpoints is empty, customPort == 0,
760
+ // and JCoreStorage has no cached lastGoodIp or sisIps:
761
+ // (a) When SIS resolves to an unreachable port, connectAndHandshake must NOT set fake connected=true.
762
+ // (b) When SIS resolves to a live TCP server, connectAndHandshake connects without setCustomEndpoint.
763
+ ServerSocket deadProbe = new ServerSocket(0, 1, InetAddress.getByName("127.0.0.1"));
764
+ final int deadTcpPort = deadProbe.getLocalPort();
765
+ deadProbe.close();
766
+
767
+ final ServerSocket sisTcpServer = new ServerSocket(0, 5, InetAddress.getByName("127.0.0.1"));
768
+ final int sisTcpPort = sisTcpServer.getLocalPort();
769
+ final DatagramSocket sisUdpServer = new DatagramSocket(0, InetAddress.getByName("127.0.0.1"));
770
+ final int sisUdpPort = sisUdpServer.getLocalPort();
771
+ final AtomicInteger sisQueryCount = new AtomicInteger(0);
772
+
773
+ Thread sisUdpThread = new Thread(new Runnable() {
774
+ @Override
775
+ public void run() {
776
+ try {
777
+ sisUdpServer.setSoTimeout(15000);
778
+ for (int i = 0; i < 2; i++) {
779
+ byte[] buf = new byte[2048];
780
+ DatagramPacket req = new DatagramPacket(buf, buf.length);
781
+ sisUdpServer.receive(req);
782
+ int queryIdx = sisQueryCount.incrementAndGet();
783
+ int targetPort = (queryIdx == 1) ? deadTcpPort : sisTcpPort;
784
+ String sisJson = "{\"ips\":[\"127.0.0.1:" + targetPort + "\"]}";
785
+ byte[] encoded = JCoreWireCodec.encodeUdpSisPacket(sisJson, 0x234567);
786
+ DatagramPacket resp = new DatagramPacket(
787
+ encoded,
788
+ encoded.length,
789
+ req.getAddress(),
790
+ req.getPort()
791
+ );
792
+ sisUdpServer.send(resp);
793
+ }
794
+ } catch (Throwable ignored) {
795
+ } finally {
796
+ sisUdpServer.close();
797
+ }
798
+ }
799
+ }, "test-auto-sis-udp-server");
800
+ sisUdpThread.setDaemon(true);
801
+ sisUdpThread.start();
802
+
803
+ Thread sisTcpThread = new Thread(new Runnable() {
804
+ @Override
805
+ public void run() {
806
+ Socket client = null;
807
+ try {
808
+ sisTcpServer.setSoTimeout(15000);
809
+ client = sisTcpServer.accept();
810
+ client.setSoTimeout(10000);
811
+ InputStream in = client.getInputStream();
812
+ OutputStream out = client.getOutputStream();
813
+
814
+ JCoreWireCodec.DecodedFrame regFrame = JCoreWireCodec.readClientFrameFromStream(in, 0L);
815
+ if (regFrame != null && regFrame.command == JCoreWireCodec.CMD_REGISTER) {
816
+ byte[] regResp = JCoreWireCodec.buildRegisterResponsePayload(
817
+ 990022L,
818
+ "sis_pwd",
819
+ "reg_auto_sis_990022",
820
+ "dev_auto_sis_01"
821
+ );
822
+ out.write(JCoreWireCodec.encodeResponseFrame(
823
+ JCoreWireCodec.CMD_REGISTER,
824
+ JCoreWireCodec.VER_REGISTER,
825
+ regFrame.requestId,
826
+ regFrame.sessionId,
827
+ 990022L,
828
+ 0,
829
+ regResp
830
+ ));
831
+ out.flush();
832
+ }
833
+ JCoreWireCodec.DecodedFrame loginFrame = JCoreWireCodec.readClientFrameFromStream(in, 990022L);
834
+ if (loginFrame != null && loginFrame.command == JCoreWireCodec.CMD_LOGIN) {
835
+ byte[] loginResp = JCoreWireCodec.buildLoginResponsePayload(8802, 520, "", 1700000900);
836
+ out.write(JCoreWireCodec.encodeResponseFrame(
837
+ JCoreWireCodec.CMD_LOGIN,
838
+ JCoreWireCodec.VER_LOGIN,
839
+ loginFrame.requestId,
840
+ 8802,
841
+ 990022L,
842
+ 0,
843
+ loginResp
844
+ ));
845
+ out.flush();
846
+ }
847
+ JCoreWireCodec.DecodedFrame hbFrame = JCoreWireCodec.readClientFrameFromStream(in, 990022L);
848
+ if (hbFrame != null && hbFrame.command == JCoreWireCodec.CMD_HEARTBEAT) {
849
+ out.write(JCoreWireCodec.encodeResponseFrame(
850
+ JCoreWireCodec.CMD_HEARTBEAT,
851
+ JCoreWireCodec.VER_HEARTBEAT,
852
+ hbFrame.requestId,
853
+ 8802,
854
+ 990022L,
855
+ 0,
856
+ new byte[0]
857
+ ));
858
+ out.flush();
859
+ }
860
+ // Keep socket open until client closes it after assertions
861
+ while (!client.isClosed() && in.read() != -1) {
862
+ // wait for client close
863
+ }
864
+ } catch (Throwable ignored) {
865
+ } finally {
866
+ try {
867
+ if (client != null) client.close();
868
+ } catch (Throwable ignored) {}
869
+ try {
870
+ sisTcpServer.close();
871
+ } catch (Throwable ignored) {}
872
+ }
873
+ }
874
+ }, "test-auto-sis-tcp-server");
875
+ sisTcpThread.setDaemon(true);
876
+ sisTcpThread.start();
877
+
878
+ JCoreStorage.clearRegistration(null);
879
+ JCoreStorage.saveSisConfig(null, "{\"ips\":[],\"ssl_ips\":[]}");
880
+ JCoreStorage.setLastGoodIp(null, "");
881
+ JCoreTcpEngine.getInstance().setCustomEndpoint("", 0);
882
+ JCoreTcpEngine.getInstance().setCandidateEndpoints(Collections.<String>emptyList());
883
+ JCoreTcpEngine.getInstance().setCustomSisUrl("udp-only");
884
+ JCoreTcpEngine.getInstance().setDefaultSisUdpEndpoint("127.0.0.1", sisUdpPort);
885
+ assertEquals(0L, (long) JCoreTcpEngine.getInstance().getCustomPort());
886
+ assertTrue(JCoreStorage.getSisIps(null).isEmpty());
887
+
888
+ // (a) First SIS query returns deadTcpPort -> connectAndHandshake must NOT set fake connected=true,
889
+ // and deadTcpPort is now cached in sisIps and recorded in failedIps.
890
+ JCoreTcpEngine.getInstance().connectAndHandshake(null);
891
+ assertEquals(false, JCoreStorage.isConnected(null));
892
+ assertEquals("", JPushInterface.getRegistrationID(null));
893
+ assertTrue(JCoreStorage.getSisIps(null).contains("127.0.0.1:" + deadTcpPort));
894
+ assertTrue(JCoreStorage.getFailedIps(null).contains("127.0.0.1:" + deadTcpPort));
895
+
896
+ // (b) Without clearing sisIps, all cached SIS IPs are in failedIps (!hasHealthyCandidate) ->
897
+ // connectAndHandshake automatically re-runs SIS resolution (second SIS query returns live sisTcpPort)
898
+ // and connects cleanly!
899
+ JCoreTcpEngine.getInstance().connectAndHandshake(null);
900
+ assertEquals(true, JCoreStorage.isConnected(null));
901
+ assertEquals("reg_auto_sis_990022", JPushInterface.getRegistrationID(null));
902
+ assertTrue(JCoreStorage.getSisIps(null).contains("127.0.0.1:" + sisTcpPort));
903
+
904
+ JCoreTcpEngine.getInstance().setCustomSisUrl("");
905
+ JCoreTcpEngine.getInstance().setDefaultSisUdpEndpoint("s.jpush.cn", 19000);
906
+ JCoreTcpEngine.getInstance().stop(null);
907
+ }
908
+ }