@onekeyfe/react-native-jpush 6.7.0-23 → 6.7.0-24

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 (24) hide show
  1. package/android/fcm/src/test/java/com/onekey/jpush/fcm/FcmAdapterTest.java +212 -0
  2. package/android/src/main/cpp/cpp-adapter.cpp +41 -1
  3. package/android/src/main/java/cn/jiguang/core/JCoreCrypto.java +6 -9
  4. package/android/src/main/java/cn/jiguang/core/JCoreStorage.java +14 -2
  5. package/android/src/main/java/cn/jiguang/core/JCoreTcpEngine.java +476 -348
  6. package/android/src/main/java/cn/jiguang/core/JCoreWireCodec.java +1 -0
  7. package/android/src/main/java/cn/jiguang/plugins/core/JCoreModule.java +13 -2
  8. package/android/src/main/java/cn/jiguang/plugins/push/JPushModule.java +38 -2
  9. package/android/src/main/java/cn/jiguang/plugins/push/helper/JPushHelper.java +46 -15
  10. package/android/src/main/java/cn/jiguang/plugins/push/receiver/JPushModuleReceiver.java +0 -3
  11. package/android/src/main/java/cn/jpush/android/api/JPushInterface.java +0 -1
  12. package/android/src/main/java/cn/jpush/android/local/ActionHelper.java +223 -272
  13. package/android/src/main/java/cn/jpush/android/service/JNotifyActivity.java +1 -1
  14. package/android/src/main/java/com/margelo/nitro/reactnativejpush/ReactNativeJpush.kt +51 -16
  15. package/android/src/test/java/com/margelo/nitro/reactnativejpush/ReactNativeJpushProtocolTest.java +76 -32
  16. package/ios/bridge/RCTJPushModule.m +215 -150
  17. package/ios/jcore/JCOREConfiguration.h +5 -0
  18. package/ios/jcore/JCOREConfiguration.m +44 -4
  19. package/ios/jcore/JCORECrypto.m +10 -12
  20. package/ios/jcore/JCORETcpSession.m +197 -45
  21. package/ios/jcore/JCOREWireProtocol.m +20 -7
  22. package/ios/jpush/JPUSHNotificationManager.m +7 -1
  23. package/ios/jpush/JPUSHService.m +7 -9
  24. package/package.json +2 -2
@@ -28,6 +28,7 @@ import cn.jpush.android.api.JPushInterface;
28
28
  import cn.jpush.android.api.JThirdPlatFormInterface;
29
29
  import cn.jpush.android.api.NotificationMessage;
30
30
  import cn.jpush.android.local.ActionHelper;
31
+ import cn.jpush.android.data.JPushLocalNotification;
31
32
  import cn.jpush.android.service.JNotifyActivity;
32
33
  import cn.jpush.android.service.JPushMessageReceiver;
33
34
  import com.google.android.gms.common.GoogleApiAvailabilityLight;
@@ -44,6 +45,9 @@ import java.util.ArrayList;
44
45
  import java.util.Collections;
45
46
  import java.util.List;
46
47
  import java.util.Map;
48
+ import java.util.concurrent.CountDownLatch;
49
+ import java.util.concurrent.TimeUnit;
50
+ import java.util.concurrent.atomic.AtomicReference;
47
51
  import org.junit.After;
48
52
  import org.junit.Before;
49
53
  import org.junit.Test;
@@ -104,6 +108,56 @@ public class FcmAdapterTest {
104
108
  notifications.cancelAll();
105
109
  }
106
110
 
111
+ @Test
112
+ public void localSchedulingPostsAndDispatchesOnlyTheUncancelledNotification() throws Exception {
113
+ ActionHelper helper = ActionHelper.getInstance();
114
+ helper.clearActiveNotificationIds();
115
+ CountDownLatch fired = new CountDownLatch(1), cancelled = new CountDownLatch(1);
116
+ AtomicReference<NotificationMessage> delivered = new AtomicReference<>();
117
+ JPushMessageReceiver receiver = new JPushMessageReceiver() {
118
+ @Override public void onNotifyMessageArrived(Context c, NotificationMessage message) {
119
+ if (message.notificationId == 77001) cancelled.countDown();
120
+ if (message.notificationId == 77002) { delivered.set(message); fired.countDown(); }
121
+ }
122
+ };
123
+ helper.registerReceiver(receiver);
124
+ try {
125
+ JPushLocalNotification removed = new JPushLocalNotification();
126
+ removed.setNotificationId(77001L);
127
+ removed.setTitle("Cancelled local");
128
+ removed.setContent("Must not be delivered");
129
+ removed.setBroadcastTime(300L);
130
+ JPushInterface.addLocalNotification(context, removed);
131
+ JPushInterface.removeLocalNotification(context, 77001L);
132
+
133
+ JPushLocalNotification scheduled = new JPushLocalNotification();
134
+ scheduled.setNotificationId(77002L);
135
+ scheduled.setTitle("Scheduled local");
136
+ scheduled.setContent("Synthetic local scheduling verification");
137
+ scheduled.setBroadcastTime(70L);
138
+ JPushInterface.addLocalNotification(context, scheduled);
139
+ assertTrue("Scheduled local notification must arrive", fired.await(1000L, TimeUnit.MILLISECONDS));
140
+ assertEquals(1, delivered.get().notificationType);
141
+ assertEquals("77002", delivered.get().msgId);
142
+ Notification posted = Shadows.shadowOf(notifications).getNotification(77002);
143
+ assertNotNull(posted);
144
+ assertEquals("Scheduled local", posted.extras.getString(Notification.EXTRA_TITLE));
145
+ assertNotNull(posted.contentIntent);
146
+ NotificationMessage clicked = (NotificationMessage) Shadows.shadowOf(posted.contentIntent).getSavedIntent()
147
+ .getSerializableExtra("multi_notification_msg");
148
+ assertEquals(77002, clicked.notificationId);
149
+ assertEquals(1, clicked.notificationType);
150
+ assertTrue(helper.getActiveNotificationIds().contains(77002));
151
+ assertFalse("Cancelled local notification must stay cancelled", cancelled.await(360L, TimeUnit.MILLISECONDS));
152
+ assertNull(Shadows.shadowOf(notifications).getNotification(77001));
153
+ assertFalse(helper.getActiveNotificationIds().contains(77001));
154
+ assertEquals(1, arrived.size());
155
+ } finally {
156
+ helper.unregisterReceiver(receiver);
157
+ helper.clearActiveNotificationIds();
158
+ }
159
+ }
160
+
107
161
  @Test
108
162
  public void coreDiscoversSourceAdapterOnceAndRebindsTheCurrentToken() {
109
163
  FirebaseMessaging provider = mock(FirebaseMessaging.class);
@@ -417,6 +471,164 @@ public class FcmAdapterTest {
417
471
  }
418
472
  }
419
473
 
474
+ private void installHostLauncher() {
475
+ ActivityInfo host = new ActivityInfo();
476
+ host.packageName = context.getPackageName();
477
+ host.name = context.getPackageName() + ".MainActivity";
478
+ host.applicationInfo = new ApplicationInfo(context.getApplicationInfo());
479
+ ResolveInfo resolvedHost = new ResolveInfo();
480
+ resolvedHost.activityInfo = host;
481
+ Intent launcher = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER).setPackage(context.getPackageName());
482
+ Shadows.shadowOf(context.getPackageManager()).addResolveInfoForIntent(launcher, resolvedHost);
483
+ }
484
+
485
+ @Test
486
+ public void tcpReceiveAndSerializedTapsRespectStopAndAuthorization() {
487
+ installHostLauncher();
488
+ ActionHelper helper = ActionHelper.getInstance();
489
+ for (boolean stopped : new boolean[]{true, false}) {
490
+ JCoreStorage.setPushStopped(context, false);
491
+ JCoreStorage.setCollectionAuth(context, true);
492
+ helper.handleDownlinkPushMessage(context, (byte) 0, 7101, "{\"n_title\":\"TCP\",\"n_content\":\"body\"}");
493
+ helper.handleDownlinkPushMessage(context, (byte) 2, 7102, "{\"message\":\"custom\"}");
494
+ assertEquals(1, arrived.size());
495
+ assertEquals(1, custom.size());
496
+ Intent tap = Shadows.shadowOf(Shadows.shadowOf(notifications).getAllNotifications().get(0).contentIntent).getSavedIntent();
497
+ notifications.cancelAll();
498
+ JCoreStorage.setPushStopped(context, stopped);
499
+ JCoreStorage.setCollectionAuth(context, stopped);
500
+ helper.handleDownlinkPushMessage(context, (byte) 0, 7103, "{\"n_title\":\"blocked\",\"n_content\":\"body\"}");
501
+ helper.handleDownlinkPushMessage(context, (byte) 2, 7104, "{\"message\":\"blocked\"}");
502
+ Robolectric.buildActivity(JNotifyActivity.class, tap).create();
503
+ assertTrue(Shadows.shadowOf(notifications).getAllNotifications().isEmpty());
504
+ assertEquals(1, arrived.size());
505
+ assertEquals(1, custom.size());
506
+ assertTrue(opened.isEmpty());
507
+ assertNull(Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity());
508
+ arrived.clear();
509
+ custom.clear();
510
+ }
511
+ }
512
+
513
+ @Test
514
+ public void bothTransportTapsLaunchTheHostOnceAndRejectForeignOrInvalidIntents() {
515
+ installHostLauncher();
516
+ ActionHelper helper = ActionHelper.getInstance();
517
+ for (int platform : new int[]{0, 8}) {
518
+ NotificationMessage message = new NotificationMessage();
519
+ message.msgId = "tap_" + platform;
520
+ message.platform = platform;
521
+ Intent tap = new Intent(context, JNotifyActivity.class)
522
+ .setAction(JPushInterface.ACTION_NOTIFICATION_OPENED)
523
+ .putExtra("multi_notification_msg", message);
524
+ JNotifyActivity activity = Robolectric.buildActivity(JNotifyActivity.class, tap).create().get();
525
+ assertTrue(activity.isFinishing());
526
+ assertEquals(platform == 0 ? 1 : 2, opened.size());
527
+ Intent launched = Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity();
528
+ assertNotNull(launched);
529
+ assertEquals(context.getPackageName() + ".MainActivity", launched.getComponent().getClassName());
530
+ assertNull(Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity());
531
+ helper.handleNotificationIntent(context, new Intent(tap).setAction("unrelated"));
532
+ helper.handleNotificationIntent(context, new Intent(tap).setPackage("foreign.package"));
533
+ helper.handleNotificationIntent(context, new Intent(tap).setComponent(new android.content.ComponentName("foreign.package", "Foreign")));
534
+ message.msgId = "";
535
+ helper.handleNotificationIntent(context, tap);
536
+ assertEquals(platform == 0 ? 1 : 2, opened.size());
537
+ assertNull(Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity());
538
+ }
539
+ }
540
+
541
+ @Test
542
+ public void notificationConstructionCannotCrossAnAuthorizationGeneration() {
543
+ NotificationManager manager = mock(NotificationManager.class);
544
+ android.content.ContextWrapper wrapped = new android.content.ContextWrapper(context) {
545
+ @Override public Object getSystemService(String name) {
546
+ return Context.NOTIFICATION_SERVICE.equals(name) ? manager : super.getSystemService(name);
547
+ }
548
+ };
549
+ ReflectionHelpers.setStaticField(JCoreStorage.class, "sAppContext", wrapped);
550
+ doAnswer(call -> {
551
+ JCoreStorage.setCollectionAuth(wrapped, false);
552
+ JCoreStorage.setCollectionAuth(wrapped, true);
553
+ return null;
554
+ }).when(manager).createNotificationChannel(any(android.app.NotificationChannel.class));
555
+ ActionHelper helper = ActionHelper.getInstance();
556
+ helper.handleDownlinkPushMessage(wrapped, (byte) 0, 7105, "{\"n_title\":\"TCP\",\"n_content\":\"body\"}");
557
+ Bundle payload = new Bundle();
558
+ payload.putString("data", "{\"msg_id\":\"fcm_generation\",\"n_title\":\"FCM\",\"n_content\":\"body\"}");
559
+ helper.doAction(wrapped, JThirdPlatFormInterface.ACTION_PLUGIN_PALTFORM_ON_MESSAGING, payload);
560
+ verify(manager, times(2)).createNotificationChannel(any(android.app.NotificationChannel.class));
561
+ verify(manager, never()).notify(anyInt(), any(Notification.class));
562
+ assertTrue(arrived.isEmpty());
563
+ }
564
+
565
+ @Test
566
+ public void revokedConstructionCannotMutateAnExistingClickIntentOrNotificationTracking() {
567
+ ActionHelper helper = ActionHelper.getInstance();
568
+ helper.clearActiveNotificationIds();
569
+ JCoreStorage.setLatestNotificationNumber(context, 2);
570
+ for (int id : new int[]{7105, 7106}) {
571
+ NotificationMessage previous = new NotificationMessage();
572
+ previous.msgId = "existing_" + id;
573
+ previous.notificationId = id;
574
+ previous.notificationTitle = "Existing notification";
575
+ helper.postSystemNotification(context, previous);
576
+ }
577
+ assertEquals(java.util.Arrays.asList(7105, 7106), helper.getActiveNotificationIds());
578
+ Notification previous = Shadows.shadowOf(notifications).getNotification(7105);
579
+ android.app.PendingIntent previousClick = previous.contentIntent;
580
+ NotificationManager manager = spy(notifications);
581
+ android.content.ContextWrapper wrapped = new android.content.ContextWrapper(context) {
582
+ @Override public Object getSystemService(String name) {
583
+ return Context.NOTIFICATION_SERVICE.equals(name) ? manager : super.getSystemService(name);
584
+ }
585
+ };
586
+ ReflectionHelpers.setStaticField(JCoreStorage.class, "sAppContext", wrapped);
587
+ doAnswer(call -> {
588
+ JCoreStorage.setCollectionAuth(wrapped, false);
589
+ return null;
590
+ }).when(manager).createNotificationChannel(any(android.app.NotificationChannel.class));
591
+ for (int id : new int[]{7105, 7107}) {
592
+ JCoreStorage.setCollectionAuth(wrapped, true);
593
+ NotificationMessage rejected = new NotificationMessage();
594
+ rejected.msgId = "rejected_" + id;
595
+ rejected.notificationId = id;
596
+ rejected.notificationTitle = "Rejected replacement";
597
+ helper.postSystemNotification(wrapped, rejected);
598
+ assertEquals(java.util.Arrays.asList(7105, 7106), helper.getActiveNotificationIds());
599
+ assertEquals(2, Shadows.shadowOf(notifications).getAllNotifications().size());
600
+ NotificationMessage saved = (NotificationMessage) Shadows.shadowOf(previousClick).getSavedIntent()
601
+ .getSerializableExtra("multi_notification_msg");
602
+ assertEquals("existing_7105", saved.msgId);
603
+ assertEquals("Existing notification", Shadows.shadowOf(notifications).getNotification(7105)
604
+ .extras.getString(Notification.EXTRA_TITLE));
605
+ }
606
+ verify(manager, never()).notify(anyInt(), any(Notification.class));
607
+ verify(manager, never()).cancel(anyInt());
608
+ }
609
+
610
+ @Test
611
+ public void openedCallbackRevocationAndRegrantCannotContinueTheOldTapLaunch() {
612
+ installHostLauncher();
613
+ ActionHelper helper = ActionHelper.getInstance();
614
+ JPushMessageReceiver revoke = new JPushMessageReceiver() {
615
+ @Override public void onNotifyMessageOpened(Context c, NotificationMessage message) {
616
+ JCoreStorage.setCollectionAuth(c, false);
617
+ JCoreStorage.setCollectionAuth(c, true);
618
+ }
619
+ };
620
+ helper.registerReceiver(revoke);
621
+ NotificationMessage message = new NotificationMessage();
622
+ message.msgId = "tap_generation";
623
+ Intent tap = new Intent(context, JNotifyActivity.class)
624
+ .setAction(JPushInterface.ACTION_NOTIFICATION_OPENED)
625
+ .putExtra("multi_notification_msg", message);
626
+ Robolectric.buildActivity(JNotifyActivity.class, tap).create();
627
+ assertEquals(1, opened.size());
628
+ assertNull(Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity());
629
+ helper.unregisterReceiver(revoke);
630
+ }
631
+
420
632
  @Test
421
633
  public void compressedMetadataRetainsItsOwnTitleAndExtras() {
422
634
  Bundle payload = metadata(compress("{\"msg_id\":\"zipped\",\"override_msg_id\":\"\",\"n_title\":\"metadata_title\",\"n_content\":\"metadata_body\",\"n_extras\":{\"route\":\"private_route\"}}"));
@@ -1,6 +1,46 @@
1
1
  #include <jni.h>
2
+ #include <fbjni/fbjni.h>
2
3
  #include "reactnativejpushOnLoad.hpp"
4
+ #include "JHybridReactNativeJpushSpec.hpp"
5
+
6
+ namespace margelo::nitro::reactnativejpush {
7
+ class OwnedReactNativeJpush final : public JHybridReactNativeJpushSpec {
8
+ public:
9
+ explicit OwnedReactNativeJpush(const jni::local_ref<JavaPart>& javaPart)
10
+ : HybridObject(HybridReactNativeJpushSpec::TAG), JHybridObject(javaPart), JHybridReactNativeJpushSpec(javaPart) {}
11
+ ~OwnedReactNativeJpush() override {
12
+ try {
13
+ jni::ThreadScope::WithClassLoader([&] {
14
+ const auto& javaPart = getJavaPart();
15
+ const auto release = javaPart->getClass()->getMethod<void()>("releaseOwnedListeners");
16
+ release(javaPart);
17
+ });
18
+ } catch (...) {
19
+ // Runtime teardown cannot propagate Java cleanup failures.
20
+ }
21
+ }
22
+ };
23
+ class OwnedCxxPart final : public jni::HybridClass<OwnedCxxPart, JHybridReactNativeJpushSpec::CxxPart> {
24
+ public:
25
+ static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/reactnativejpush/ReactNativeJpush$OwnedCxxPart;";
26
+ using HybridBase::HybridBase;
27
+ static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> self) {
28
+ return makeCxxInstance(self);
29
+ }
30
+ static void registerNatives() {
31
+ registerHybrid({makeNativeMethod("initHybrid", OwnedCxxPart::initHybrid)});
32
+ }
33
+ protected:
34
+ std::shared_ptr<JHybridObject> createHybridObject(const jni::local_ref<JHybridObject::JavaPart>& javaPart) override {
35
+ auto typed = jni::dynamic_ref_cast<JHybridReactNativeJpushSpec::JavaPart>(javaPart);
36
+ return std::make_shared<OwnedReactNativeJpush>(typed);
37
+ }
38
+ };
39
+ }
3
40
 
4
41
  extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
- return margelo::nitro::reactnativejpush::initialize(vm);
42
+ return facebook::jni::initialize(vm, [] {
43
+ margelo::nitro::reactnativejpush::registerAllNatives();
44
+ margelo::nitro::reactnativejpush::OwnedCxxPart::registerNatives();
45
+ });
6
46
  }
@@ -478,22 +478,19 @@ public final class JCoreCrypto {
478
478
  }
479
479
 
480
480
  public static byte[] gzipDecompress(byte[] compressed) {
481
- if (compressed == null || compressed.length == 0) {
482
- return new byte[0];
483
- }
484
- try {
485
- ByteArrayInputStream in = new ByteArrayInputStream(compressed);
486
- GZIPInputStream gzip = new GZIPInputStream(in);
481
+ final int limit = 65536;
482
+ if (compressed == null || compressed.length == 0) return new byte[0];
483
+ try (GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(compressed))) {
487
484
  ByteArrayOutputStream out = new ByteArrayOutputStream();
488
485
  byte[] buffer = new byte[4096];
489
486
  int read;
490
487
  while ((read = gzip.read(buffer)) != -1) {
488
+ if (read > limit - out.size()) return new byte[0];
491
489
  out.write(buffer, 0, read);
492
490
  }
493
- gzip.close();
494
491
  return out.toByteArray();
495
- } catch (Throwable ignored) {
496
- return compressed;
492
+ } catch (Exception invalidGzip) {
493
+ return compressed.length <= limit ? compressed : new byte[0];
497
494
  }
498
495
  }
499
496
 
@@ -19,9 +19,11 @@ import java.util.Set;
19
19
  import java.util.UUID;
20
20
  import java.util.concurrent.ConcurrentHashMap;
21
21
  import java.util.concurrent.atomic.AtomicInteger;
22
+ import java.util.concurrent.atomic.AtomicLong;
22
23
  import org.json.JSONObject;
23
24
 
24
25
  public final class JCoreStorage {
26
+ private static final AtomicLong CONTENT_GENERATION = new AtomicLong();
25
27
  private static final String PREFS_NAME = "cn.jiguang.sdk.clean.prefs";
26
28
  private static final String KEY_APP_KEY = "app_key";
27
29
  private static final String KEY_CHANNEL = "channel";
@@ -230,8 +232,15 @@ public final class JCoreStorage {
230
232
  return getBooleanPref(null, KEY_DEBUG_MODE, sDebugMode);
231
233
  }
232
234
 
233
- public static void setCollectionAuth(Context context, boolean auth) {
235
+ public static synchronized void setCollectionAuth(Context context, boolean auth) {
234
236
  putBooleanPref(context, KEY_COLLECTION_AUTH, auth);
237
+ if (!auth) {
238
+ CONTENT_GENERATION.incrementAndGet();
239
+ }
240
+ }
241
+
242
+ public static long getContentGeneration() {
243
+ return CONTENT_GENERATION.get();
235
244
  }
236
245
 
237
246
  public static boolean isCollectionAuth(Context context) {
@@ -426,8 +435,11 @@ public final class JCoreStorage {
426
435
  return getBooleanPref(context, KEY_PUSH_STOPPED, false);
427
436
  }
428
437
 
429
- public static void setPushStopped(Context context, boolean stopped) {
438
+ public static synchronized void setPushStopped(Context context, boolean stopped) {
430
439
  putBooleanPref(context, KEY_PUSH_STOPPED, stopped);
440
+ if (stopped) {
441
+ CONTENT_GENERATION.incrementAndGet();
442
+ }
431
443
  }
432
444
 
433
445
  public static boolean isConnected(Context context) {