@onekeyfe/react-native-jpush 6.7.0-alpha.445 → 6.7.0-alpha.464
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.
- package/android/fcm/src/test/java/com/onekey/jpush/fcm/FcmAdapterTest.java +212 -0
- package/android/src/main/cpp/cpp-adapter.cpp +41 -1
- package/android/src/main/java/cn/jiguang/core/JCoreCrypto.java +6 -9
- package/android/src/main/java/cn/jiguang/core/JCoreStorage.java +14 -2
- package/android/src/main/java/cn/jiguang/core/JCoreTcpEngine.java +476 -348
- package/android/src/main/java/cn/jiguang/core/JCoreWireCodec.java +1 -0
- package/android/src/main/java/cn/jiguang/plugins/core/JCoreModule.java +13 -2
- package/android/src/main/java/cn/jiguang/plugins/push/JPushModule.java +38 -2
- package/android/src/main/java/cn/jiguang/plugins/push/helper/JPushHelper.java +46 -15
- package/android/src/main/java/cn/jiguang/plugins/push/receiver/JPushModuleReceiver.java +0 -3
- package/android/src/main/java/cn/jpush/android/api/JPushInterface.java +0 -1
- package/android/src/main/java/cn/jpush/android/local/ActionHelper.java +223 -272
- package/android/src/main/java/cn/jpush/android/service/JNotifyActivity.java +1 -1
- package/android/src/main/java/com/margelo/nitro/reactnativejpush/ReactNativeJpush.kt +51 -16
- package/android/src/test/java/com/margelo/nitro/reactnativejpush/ReactNativeJpushProtocolTest.java +76 -32
- package/ios/bridge/RCTJPushModule.m +215 -150
- package/ios/jcore/JCOREConfiguration.h +5 -0
- package/ios/jcore/JCOREConfiguration.m +44 -4
- package/ios/jcore/JCORECrypto.m +10 -12
- package/ios/jcore/JCORETcpSession.m +197 -45
- package/ios/jcore/JCOREWireProtocol.m +20 -7
- package/ios/jpush/JPUSHNotificationManager.m +7 -1
- package/ios/jpush/JPUSHService.m +7 -9
- package/package.json +1 -1
|
@@ -52,6 +52,10 @@ public final class JCoreTcpEngine {
|
|
|
52
52
|
final long timeoutMs;
|
|
53
53
|
final int maxRetries;
|
|
54
54
|
final RequestCallback callback;
|
|
55
|
+
volatile Attempt owner;
|
|
56
|
+
final long policyGeneration;
|
|
57
|
+
final String configuration;
|
|
58
|
+
final java.util.concurrent.atomic.AtomicBoolean completed = new java.util.concurrent.atomic.AtomicBoolean();
|
|
55
59
|
|
|
56
60
|
QueuedRequest(
|
|
57
61
|
int command,
|
|
@@ -60,29 +64,64 @@ public final class JCoreTcpEngine {
|
|
|
60
64
|
byte[] body,
|
|
61
65
|
long timeoutMs,
|
|
62
66
|
int maxRetries,
|
|
63
|
-
RequestCallback callback
|
|
67
|
+
RequestCallback callback,
|
|
68
|
+
long policyGeneration,
|
|
69
|
+
String configuration
|
|
64
70
|
) {
|
|
65
71
|
this.command = command;
|
|
66
72
|
this.version = version;
|
|
67
73
|
this.rid = rid;
|
|
68
|
-
this.body = body != null ? body : new byte[0];
|
|
74
|
+
this.body = body != null ? body.clone() : new byte[0];
|
|
69
75
|
this.timeoutMs = timeoutMs > 0L ? timeoutMs : 5000L;
|
|
70
76
|
this.maxRetries = Math.max(0, maxRetries);
|
|
71
77
|
this.callback = callback;
|
|
78
|
+
this.policyGeneration = policyGeneration;
|
|
79
|
+
this.configuration = configuration;
|
|
80
|
+
}
|
|
81
|
+
void complete(JCoreWireCodec.DecodedFrame frame, int code) {
|
|
82
|
+
if (code == 0 && (owner == null || !configuration.equals(owner.configuration) || !INSTANCE.owns(owner, null)
|
|
83
|
+
|| policyGeneration != JCoreStorage.getContentGeneration() || !JCoreStorage.isCollectionAuth(null))) {
|
|
84
|
+
frame = null;
|
|
85
|
+
code = JPushInterface.ErrorCode.TIMEOUT;
|
|
86
|
+
}
|
|
87
|
+
if (completed.compareAndSet(false, true) && callback != null) callback.onResult(frame, code);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private static final class Attempt {
|
|
92
|
+
final long generation;
|
|
93
|
+
final CountDownLatch latch;
|
|
94
|
+
final String configuration;
|
|
95
|
+
volatile Socket socket;
|
|
96
|
+
volatile OutputStream output;
|
|
97
|
+
final Object writerLock = new Object();
|
|
98
|
+
boolean stopScheduled;
|
|
99
|
+
Attempt(long generation, CountDownLatch latch, String configuration) {
|
|
100
|
+
this.generation = generation; this.latch = latch; this.configuration = configuration;
|
|
72
101
|
}
|
|
73
102
|
}
|
|
74
103
|
|
|
75
104
|
private static final class ResponseSlot {
|
|
76
105
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
77
106
|
volatile JCoreWireCodec.DecodedFrame response;
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
final int command;
|
|
108
|
+
final Attempt owner;
|
|
109
|
+
final Socket socket;
|
|
110
|
+
private boolean completed;
|
|
111
|
+
ResponseSlot(Attempt owner, Socket socket, int command) { this.owner = owner; this.socket = socket; this.command = command; }
|
|
112
|
+
|
|
113
|
+
synchronized void complete(JCoreWireCodec.DecodedFrame frame) {
|
|
114
|
+
if (completed) return;
|
|
115
|
+
completed = true;
|
|
80
116
|
this.response = frame;
|
|
81
117
|
this.latch.countDown();
|
|
82
118
|
}
|
|
83
119
|
}
|
|
84
120
|
|
|
85
121
|
private static final JCoreTcpEngine INSTANCE = new JCoreTcpEngine();
|
|
122
|
+
private static final long STOP_CONTROL_GRACE_MS = 200L;
|
|
123
|
+
// Only the captured final stop control may send after local ownership is disabled.
|
|
124
|
+
private static final long STOP_CONTROL_POLICY = Long.MIN_VALUE;
|
|
86
125
|
private static final String DEFAULT_HOST = "im64.jpush.cn";
|
|
87
126
|
private static final int DEFAULT_PORT = 7000;
|
|
88
127
|
private static final String SIS_URL = "https://tsis.jpush.cn/v3/srv";
|
|
@@ -122,6 +161,9 @@ public final class JCoreTcpEngine {
|
|
|
122
161
|
new CopyOnWriteArrayList<JCoreWireCodec.CtrlCommand>();
|
|
123
162
|
private final Object socketLock = new Object();
|
|
124
163
|
|
|
164
|
+
private volatile Attempt activeAttempt;
|
|
165
|
+
private volatile String terminalConfiguration = "";
|
|
166
|
+
private Object drainingToken;
|
|
125
167
|
private volatile boolean started = false;
|
|
126
168
|
private volatile boolean loggedIn = false;
|
|
127
169
|
private volatile boolean autoReconnectEnabled = true;
|
|
@@ -191,7 +233,8 @@ public final class JCoreTcpEngine {
|
|
|
191
233
|
}
|
|
192
234
|
}
|
|
193
235
|
|
|
194
|
-
private void startHeartbeatTimer(final Context context, final Socket targetSocket) {
|
|
236
|
+
private synchronized void startHeartbeatTimer(final Context context, final Socket targetSocket, final Attempt owner) {
|
|
237
|
+
if (!owns(owner, context) || activeSocket != targetSocket) return;
|
|
195
238
|
cancelHeartbeatTimer();
|
|
196
239
|
long interval = this.heartbeatIntervalMs > 0L ? this.heartbeatIntervalMs : 290000L;
|
|
197
240
|
this.activeHeartbeatFuture = heartbeatExecutor.scheduleWithFixedDelay(
|
|
@@ -199,22 +242,24 @@ public final class JCoreTcpEngine {
|
|
|
199
242
|
@Override
|
|
200
243
|
public void run() {
|
|
201
244
|
if (!loggedIn || activeSocket != targetSocket || targetSocket == null || targetSocket.isClosed()) {
|
|
202
|
-
cancelHeartbeatTimer();
|
|
203
245
|
return;
|
|
204
246
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
JCoreWireCodec.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
247
|
+
ioExecutor.execute(() -> {
|
|
248
|
+
if (!owns(owner, context)) return;
|
|
249
|
+
try {
|
|
250
|
+
long currentUid = JCoreStorage.getUid(context);
|
|
251
|
+
byte[] hbPayload = JCoreWireCodec.buildHeartbeatPayload(JCoreStorage.isPushStopped(context));
|
|
252
|
+
sendFrameOwned(owner, targetSocket,
|
|
253
|
+
JCoreWireCodec.CMD_HEARTBEAT,
|
|
254
|
+
JCoreWireCodec.VER_HEARTBEAT,
|
|
255
|
+
JCoreStorage.nextRid(),
|
|
256
|
+
sessionId,
|
|
257
|
+
currentUid,
|
|
258
|
+
hbPayload
|
|
259
|
+
);
|
|
260
|
+
} catch (Throwable ignored) {
|
|
261
|
+
}
|
|
262
|
+
});
|
|
218
263
|
}
|
|
219
264
|
},
|
|
220
265
|
interval,
|
|
@@ -353,7 +398,13 @@ public final class JCoreTcpEngine {
|
|
|
353
398
|
}
|
|
354
399
|
|
|
355
400
|
public synchronized void closeSocket() {
|
|
401
|
+
closeSocket(null, false);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private synchronized void closeSocket(Context context, boolean preserveUnboundRequests) {
|
|
356
405
|
this.connectionGeneration++;
|
|
406
|
+
drainingToken = null;
|
|
407
|
+
activeAttempt = null;
|
|
357
408
|
cancelHeartbeatTimer();
|
|
358
409
|
synchronized (socketLock) {
|
|
359
410
|
if (activeSocket != null) {
|
|
@@ -370,75 +421,119 @@ public final class JCoreTcpEngine {
|
|
|
370
421
|
}
|
|
371
422
|
pendingRequests.clear();
|
|
372
423
|
loggedIn = false;
|
|
424
|
+
JCoreStorage.setConnected(null, false);
|
|
425
|
+
String configuration = preserveUnboundRequests ? configurationIdentity(context) : "";
|
|
426
|
+
long policyGeneration = JCoreStorage.getContentGeneration();
|
|
427
|
+
boolean mayPreserve = preserveUnboundRequests && JCoreStorage.isCollectionAuth(context)
|
|
428
|
+
&& !JCoreStorage.isPushStopped(context) && !terminalConfiguration.equals(configuration);
|
|
429
|
+
List<QueuedRequest> retained = new ArrayList<QueuedRequest>();
|
|
430
|
+
QueuedRequest queued;
|
|
431
|
+
while ((queued = preLoginQueue.poll()) != null) {
|
|
432
|
+
if (mayPreserve && queued.owner == null && !queued.completed.get()
|
|
433
|
+
&& queued.policyGeneration == policyGeneration && queued.configuration.equals(configuration)) {
|
|
434
|
+
retained.add(queued);
|
|
435
|
+
} else {
|
|
436
|
+
final QueuedRequest canceled = queued;
|
|
437
|
+
ioExecutor.execute(() -> canceled.complete(null, JPushInterface.ErrorCode.TIMEOUT));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
preLoginQueue.addAll(retained);
|
|
373
441
|
}
|
|
374
442
|
|
|
375
|
-
public
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
public void start(final Context context) {
|
|
382
|
-
JCoreStorage.setAppContext(context);
|
|
383
|
-
final long startGen;
|
|
384
|
-
final CountDownLatch latchForStart;
|
|
443
|
+
public void stop(Context context) {
|
|
444
|
+
final Attempt owner;
|
|
445
|
+
final Socket socket;
|
|
446
|
+
final long uid;
|
|
447
|
+
final int sid;
|
|
448
|
+
List<QueuedRequest> canceled;
|
|
385
449
|
synchronized (this) {
|
|
386
|
-
|
|
450
|
+
owner = activeAttempt;
|
|
451
|
+
if (owner != null && owner.stopScheduled && JCoreStorage.isPushStopped(context)) return;
|
|
452
|
+
if (owner == null || !loggedIn || activeSocket == null || !JCoreStorage.isPushStopped(context)) {
|
|
453
|
+
closeSocket();
|
|
454
|
+
started = false;
|
|
387
455
|
return;
|
|
388
456
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
457
|
+
owner.stopScheduled = true;
|
|
458
|
+
drainingToken = null;
|
|
459
|
+
socket = activeSocket;
|
|
460
|
+
uid = JCoreStorage.getUid(context);
|
|
461
|
+
sid = sessionId;
|
|
462
|
+
started = false;
|
|
463
|
+
loggedIn = false;
|
|
464
|
+
cancelHeartbeatTimer();
|
|
465
|
+
JCoreStorage.setConnected(context, false);
|
|
466
|
+
for (java.util.Map.Entry<Long, ResponseSlot> entry : pendingRequests.entrySet()) {
|
|
467
|
+
ResponseSlot slot = entry.getValue();
|
|
468
|
+
if (slot.owner == owner && pendingRequests.remove(entry.getKey(), slot)) slot.complete(null);
|
|
469
|
+
}
|
|
470
|
+
canceled = new ArrayList<QueuedRequest>(preLoginQueue);
|
|
471
|
+
preLoginQueue.clear();
|
|
397
472
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
latchForStart.countDown();
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
connectAndHandshake(app);
|
|
473
|
+
final ScheduledFuture<?> deadline = heartbeatExecutor.schedule(() -> closeOwnedSocket(owner),
|
|
474
|
+
STOP_CONTROL_GRACE_MS, TimeUnit.MILLISECONDS);
|
|
475
|
+
ioExecutor.execute(() -> {
|
|
476
|
+
try {
|
|
477
|
+
sendFrameOwned(owner, socket, JCoreWireCodec.CMD_PUSH_CTRL, JCoreWireCodec.VER_PUSH_CTRL,
|
|
478
|
+
JCoreStorage.nextRid(), sid, uid, JCoreWireCodec.buildPushControlPayload(true), STOP_CONTROL_POLICY);
|
|
479
|
+
} finally {
|
|
480
|
+
closeOwnedSocket(owner);
|
|
481
|
+
deadline.cancel(false);
|
|
411
482
|
}
|
|
412
483
|
});
|
|
484
|
+
for (QueuedRequest req : canceled) ioExecutor.execute(() -> req.complete(null, JPushInterface.ErrorCode.TIMEOUT));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private String configurationIdentity(Context context) {
|
|
488
|
+
Context app = JCoreStorage.getAppContext(context);
|
|
489
|
+
return JCoreStorage.getAppKey(context) + ":" + (app != null ? app.getPackageName() : "");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private boolean owns(Attempt owner, Context context) {
|
|
493
|
+
return owner != null && activeAttempt == owner && owner.generation == connectionGeneration
|
|
494
|
+
&& started && !JCoreStorage.isPushStopped(context) && owner.configuration.equals(configurationIdentity(context));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private synchronized Attempt beginAttempt(Context context) {
|
|
498
|
+
if (JCoreStorage.isPushStopped(context) || terminalConfiguration.equals(configurationIdentity(context))) return null;
|
|
499
|
+
if (!terminalConfiguration.isEmpty()) terminalConfiguration = "";
|
|
500
|
+
drainingToken = null;
|
|
501
|
+
if (activeAttempt != null) closeOwnedSocket(activeAttempt);
|
|
502
|
+
started = true;
|
|
503
|
+
handshakeLatch = new CountDownLatch(1);
|
|
504
|
+
Attempt owner = new Attempt(++connectionGeneration, handshakeLatch, configurationIdentity(context));
|
|
505
|
+
activeAttempt = owner;
|
|
506
|
+
return owner;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
public void start(final Context context) {
|
|
510
|
+
JCoreStorage.setAppContext(context);
|
|
511
|
+
final Attempt owner;
|
|
512
|
+
synchronized (this) {
|
|
513
|
+
if (started && owns(activeAttempt, context)) return;
|
|
514
|
+
owner = beginAttempt(context);
|
|
515
|
+
}
|
|
516
|
+
if (owner != null) ioExecutor.execute(() -> connectAndHandshake(context, owner));
|
|
413
517
|
}
|
|
414
518
|
|
|
415
519
|
public void triggerReconnectNow(final Context context) {
|
|
416
|
-
final
|
|
417
|
-
final CountDownLatch latchForReconnect;
|
|
520
|
+
final Attempt owner;
|
|
418
521
|
synchronized (this) {
|
|
419
522
|
closeSocket();
|
|
420
|
-
|
|
421
|
-
this.handshakeLatch = new CountDownLatch(1);
|
|
422
|
-
reconnectGen = this.connectionGeneration;
|
|
423
|
-
latchForReconnect = this.handshakeLatch;
|
|
523
|
+
owner = beginAttempt(context);
|
|
424
524
|
}
|
|
425
|
-
ioExecutor.execute(
|
|
426
|
-
@Override
|
|
427
|
-
public void run() {
|
|
428
|
-
if (!started || reconnectGen != connectionGeneration) {
|
|
429
|
-
latchForReconnect.countDown();
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
connectAndHandshake(JCoreStorage.getAppContext(context));
|
|
433
|
-
}
|
|
434
|
-
});
|
|
525
|
+
if (owner != null) ioExecutor.execute(() -> connectAndHandshake(context, owner));
|
|
435
526
|
}
|
|
436
527
|
|
|
437
|
-
public void onNetworkConnected(Context context) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
528
|
+
public void onNetworkConnected(final Context context) {
|
|
529
|
+
final Attempt owner;
|
|
530
|
+
synchronized (this) {
|
|
531
|
+
reconnectAttempt = 0;
|
|
532
|
+
if (loggedIn) return;
|
|
533
|
+
closeSocket(context, true);
|
|
534
|
+
owner = beginAttempt(context);
|
|
441
535
|
}
|
|
536
|
+
if (owner != null) ioExecutor.execute(() -> connectAndHandshake(context, owner));
|
|
442
537
|
}
|
|
443
538
|
|
|
444
539
|
public void executeAsync(Runnable task) {
|
|
@@ -536,80 +631,87 @@ public final class JCoreTcpEngine {
|
|
|
536
631
|
}
|
|
537
632
|
|
|
538
633
|
public void connectAndHandshake(final Context context) {
|
|
634
|
+
Attempt owner;
|
|
635
|
+
synchronized (this) { owner = beginAttempt(context); }
|
|
636
|
+
if (owner != null) connectAndHandshake(context, owner);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private void scheduleReconnect(final Context context, final Attempt previous) {
|
|
640
|
+
final long delay;
|
|
641
|
+
synchronized (this) {
|
|
642
|
+
if (!owns(previous, context) || !terminalConfiguration.isEmpty()) return;
|
|
643
|
+
if (!autoReconnectEnabled) { reconnectAttempt++; return; }
|
|
644
|
+
delay = computeReconnectDelayMs(reconnectAttempt++, reconnectBaseDelayMs);
|
|
645
|
+
}
|
|
646
|
+
heartbeatExecutor.schedule(() -> {
|
|
647
|
+
final Attempt next;
|
|
648
|
+
synchronized (JCoreTcpEngine.this) {
|
|
649
|
+
if (!owns(previous, context) || loggedIn) return;
|
|
650
|
+
next = beginAttempt(context);
|
|
651
|
+
}
|
|
652
|
+
if (next != null) ioExecutor.execute(() -> connectAndHandshake(context, next));
|
|
653
|
+
}, delay, TimeUnit.MILLISECONDS);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private void closeOwnedSocket(Attempt owner) {
|
|
657
|
+
synchronized (this) {
|
|
658
|
+
if (activeAttempt != owner) {
|
|
659
|
+
if (owner.socket != null) try { owner.socket.close(); } catch (Exception ignored) {}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
drainingToken = null;
|
|
663
|
+
synchronized (socketLock) {
|
|
664
|
+
if (activeSocket == owner.socket) {
|
|
665
|
+
if (activeSocket != null) try { activeSocket.close(); } catch (Exception ignored) {}
|
|
666
|
+
activeSocket = null;
|
|
667
|
+
activeOutput = null;
|
|
668
|
+
loggedIn = false;
|
|
669
|
+
JCoreStorage.setConnected(null, false);
|
|
670
|
+
cancelHeartbeatTimer();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
for (java.util.Map.Entry<Long, ResponseSlot> entry : pendingRequests.entrySet()) {
|
|
674
|
+
ResponseSlot slot = entry.getValue();
|
|
675
|
+
if (slot.owner == owner && pendingRequests.remove(entry.getKey(), slot)) slot.complete(null);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private void connectAndHandshake(final Context context, final Attempt owner) {
|
|
539
681
|
try {
|
|
682
|
+
synchronized (this) { if (!owns(owner, context)) return; }
|
|
540
683
|
List<String> candidates = buildOrderedCandidateList(context);
|
|
541
684
|
if (customPort <= 0 && candidateEndpoints.isEmpty()) {
|
|
542
685
|
if (!hasHealthyCandidate(context, candidates)) {
|
|
543
|
-
String[]
|
|
686
|
+
String[] resolved = resolveSisEndpoint(context, owner);
|
|
687
|
+
if (!owns(owner, context)) return;
|
|
544
688
|
candidates = buildOrderedCandidateList(context);
|
|
545
|
-
if (
|
|
546
|
-
String
|
|
547
|
-
if (!
|
|
548
|
-
candidates.add(0, resolvedEp);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (!hasHealthyCandidate(context, candidates)) {
|
|
553
|
-
String defaultEndpoint = DEFAULT_HOST + ":" + DEFAULT_PORT;
|
|
554
|
-
if (!candidates.contains(defaultEndpoint)) {
|
|
555
|
-
candidates.add(defaultEndpoint);
|
|
689
|
+
if (resolved != null && resolved.length == 2) {
|
|
690
|
+
String endpoint = resolved[0] + ":" + resolved[1];
|
|
691
|
+
if (!candidates.contains(endpoint)) candidates.add(0, endpoint);
|
|
556
692
|
}
|
|
557
693
|
}
|
|
694
|
+
if (!hasHealthyCandidate(context, candidates)) candidates.add(DEFAULT_HOST + ":" + DEFAULT_PORT);
|
|
558
695
|
}
|
|
559
|
-
if (candidates.isEmpty()) {
|
|
560
|
-
JCoreStorage.setConnected(context, false);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
boolean connectedSuccess = false;
|
|
565
696
|
for (String endpoint : candidates) {
|
|
697
|
+
if (!owns(owner, context)) return;
|
|
566
698
|
String[] hp = splitHostPort(endpoint);
|
|
567
|
-
if (hp == null)
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
int port = Integer.parseInt(hp[1]);
|
|
572
|
-
if (tryConnectAndHandshakeEndpoint(context, host, port)) {
|
|
573
|
-
JCoreStorage.setLastGoodIp(context, host + ":" + port);
|
|
574
|
-
connectedSuccess = true;
|
|
575
|
-
break;
|
|
576
|
-
} else {
|
|
577
|
-
JCoreStorage.recordFailedIp(context, host + ":" + port);
|
|
699
|
+
if (hp == null) continue;
|
|
700
|
+
if (tryConnectAndHandshakeEndpoint(context, hp[0], Integer.parseInt(hp[1]), owner)) {
|
|
701
|
+
synchronized (this) { if (owns(owner, context)) JCoreStorage.setLastGoodIp(context, endpoint); }
|
|
702
|
+
return;
|
|
578
703
|
}
|
|
704
|
+
if (!owns(owner, context) || !terminalConfiguration.isEmpty()) return;
|
|
705
|
+
synchronized (this) { if (owns(owner, context)) JCoreStorage.recordFailedIp(context, endpoint); }
|
|
579
706
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (this.started && this.autoReconnectEnabled && !JCoreStorage.isPushStopped(context)) {
|
|
584
|
-
final long delayMs = computeReconnectDelayMs(reconnectAttempt++, reconnectBaseDelayMs);
|
|
585
|
-
final long scheduledGen = this.connectionGeneration;
|
|
586
|
-
heartbeatExecutor.schedule(new Runnable() {
|
|
587
|
-
@Override
|
|
588
|
-
public void run() {
|
|
589
|
-
if (!started || scheduledGen != connectionGeneration || loggedIn || JCoreStorage.isPushStopped(context)) {
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
ioExecutor.execute(new Runnable() {
|
|
593
|
-
@Override
|
|
594
|
-
public void run() {
|
|
595
|
-
if (!started || scheduledGen != connectionGeneration || loggedIn || JCoreStorage.isPushStopped(context)) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
connectAndHandshake(context);
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
}, delayMs, TimeUnit.MILLISECONDS);
|
|
603
|
-
} else {
|
|
604
|
-
this.reconnectAttempt++;
|
|
605
|
-
}
|
|
707
|
+
if (owns(owner, context)) {
|
|
708
|
+
synchronized (this) { if (owns(owner, context)) JCoreStorage.setConnected(context, false); }
|
|
709
|
+
scheduleReconnect(context, owner);
|
|
606
710
|
}
|
|
607
|
-
} finally {
|
|
608
|
-
handshakeLatch.countDown();
|
|
609
|
-
}
|
|
711
|
+
} finally { owner.latch.countDown(); }
|
|
610
712
|
}
|
|
611
713
|
|
|
612
|
-
private boolean tryConnectAndHandshakeEndpoint(final Context context, String host, int port) {
|
|
714
|
+
private boolean tryConnectAndHandshakeEndpoint(final Context context, String host, int port, final Attempt owner) {
|
|
613
715
|
Socket socket = null;
|
|
614
716
|
try {
|
|
615
717
|
if (isPlainSocketEndpoint(host, port)) {
|
|
@@ -623,15 +725,17 @@ public final class JCoreTcpEngine {
|
|
|
623
725
|
final Socket connectedSocket = socket;
|
|
624
726
|
final InputStream in = connectedSocket.getInputStream();
|
|
625
727
|
final OutputStream out = connectedSocket.getOutputStream();
|
|
626
|
-
synchronized (
|
|
627
|
-
|
|
628
|
-
|
|
728
|
+
synchronized (this) {
|
|
729
|
+
if (!owns(owner, context)) { connectedSocket.close(); return false; }
|
|
730
|
+
owner.socket = connectedSocket;
|
|
731
|
+
owner.output = out;
|
|
732
|
+
synchronized (socketLock) { activeSocket = connectedSocket; activeOutput = out; }
|
|
629
733
|
}
|
|
630
734
|
|
|
631
735
|
Thread readerThread = new Thread(new Runnable() {
|
|
632
736
|
@Override
|
|
633
737
|
public void run() {
|
|
634
|
-
readLoop(context, connectedSocket, in);
|
|
738
|
+
readLoop(context, connectedSocket, in, owner);
|
|
635
739
|
}
|
|
636
740
|
}, "jcore-tcp-reader");
|
|
637
741
|
readerThread.setDaemon(true);
|
|
@@ -662,8 +766,8 @@ public final class JCoreTcpEngine {
|
|
|
662
766
|
&& !JCoreStorage.getRegistrationId(context).isEmpty();
|
|
663
767
|
|
|
664
768
|
if (!fastLogin) {
|
|
665
|
-
if (!performRegisterHandshake(context, appKey, channel, pkgName, deviceId)) {
|
|
666
|
-
|
|
769
|
+
if (!performRegisterHandshake(context, appKey, channel, pkgName, deviceId, owner)) {
|
|
770
|
+
closeOwnedSocket(owner);
|
|
667
771
|
return false;
|
|
668
772
|
}
|
|
669
773
|
}
|
|
@@ -671,7 +775,7 @@ public final class JCoreTcpEngine {
|
|
|
671
775
|
long uid = JCoreStorage.getUid(context);
|
|
672
776
|
String password = JCoreStorage.getPassword(context);
|
|
673
777
|
byte[] loginBody = JCoreWireCodec.buildLoginPayload(password, appKey, deviceId, countryCode);
|
|
674
|
-
JCoreWireCodec.DecodedFrame loginFrame = sendRequestOnSocket(
|
|
778
|
+
JCoreWireCodec.DecodedFrame loginFrame = sendRequestOnSocket(owner,
|
|
675
779
|
JCoreWireCodec.CMD_LOGIN,
|
|
676
780
|
JCoreWireCodec.VER_LOGIN,
|
|
677
781
|
0,
|
|
@@ -680,26 +784,29 @@ public final class JCoreTcpEngine {
|
|
|
680
784
|
defaultRequestTimeoutMs
|
|
681
785
|
);
|
|
682
786
|
if (loginFrame == null) {
|
|
683
|
-
|
|
787
|
+
closeOwnedSocket(owner);
|
|
684
788
|
return false;
|
|
685
789
|
}
|
|
686
790
|
JCoreWireCodec.LoginResponse loginResp = JCoreWireCodec.parseLoginResponse(loginFrame);
|
|
687
791
|
if (loginResp == null) {
|
|
688
|
-
|
|
792
|
+
closeOwnedSocket(owner);
|
|
689
793
|
return false;
|
|
690
794
|
}
|
|
691
795
|
|
|
692
796
|
// Handle Login statusCode == 108 (clear registration and immediately re-register + login)
|
|
693
797
|
if (loginResp.statusCode == 108) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
798
|
+
synchronized (this) {
|
|
799
|
+
if (!owns(owner, context)) return false;
|
|
800
|
+
JCoreStorage.clearRegistration(context);
|
|
801
|
+
}
|
|
802
|
+
if (!performRegisterHandshake(context, appKey, channel, pkgName, deviceId, owner)) {
|
|
803
|
+
closeOwnedSocket(owner);
|
|
697
804
|
return false;
|
|
698
805
|
}
|
|
699
806
|
uid = JCoreStorage.getUid(context);
|
|
700
807
|
password = JCoreStorage.getPassword(context);
|
|
701
808
|
loginBody = JCoreWireCodec.buildLoginPayload(password, appKey, deviceId, countryCode);
|
|
702
|
-
loginFrame = sendRequestOnSocket(
|
|
809
|
+
loginFrame = sendRequestOnSocket(owner,
|
|
703
810
|
JCoreWireCodec.CMD_LOGIN,
|
|
704
811
|
JCoreWireCodec.VER_LOGIN,
|
|
705
812
|
0,
|
|
@@ -708,29 +815,43 @@ public final class JCoreTcpEngine {
|
|
|
708
815
|
defaultRequestTimeoutMs
|
|
709
816
|
);
|
|
710
817
|
if (loginFrame == null) {
|
|
711
|
-
|
|
818
|
+
closeOwnedSocket(owner);
|
|
712
819
|
return false;
|
|
713
820
|
}
|
|
714
821
|
loginResp = JCoreWireCodec.parseLoginResponse(loginFrame);
|
|
715
822
|
} else if (loginResp.statusCode == 1012) {
|
|
716
|
-
|
|
717
|
-
|
|
823
|
+
synchronized (this) {
|
|
824
|
+
if (!owns(owner, context)) return false;
|
|
825
|
+
JCoreStorage.resetSisCache(context);
|
|
826
|
+
}
|
|
827
|
+
closeOwnedSocket(owner);
|
|
718
828
|
return false;
|
|
719
829
|
}
|
|
720
830
|
|
|
831
|
+
synchronized (this) {
|
|
832
|
+
if (loginResp != null && (loginResp.statusCode == 1005 || loginResp.statusCode == 1008 || loginResp.statusCode == 1009) && owns(owner, context)) {
|
|
833
|
+
terminalConfiguration = owner.configuration;
|
|
834
|
+
closeSocket();
|
|
835
|
+
started = false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
721
838
|
if (loginResp == null || loginResp.statusCode != 0) {
|
|
722
|
-
|
|
839
|
+
closeOwnedSocket(owner);
|
|
723
840
|
return false;
|
|
724
841
|
}
|
|
725
842
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
843
|
+
synchronized (this) {
|
|
844
|
+
if (!owns(owner, context)) return false;
|
|
845
|
+
if (loginResp.sessionId != 0) {
|
|
846
|
+
this.sessionId = loginResp.sessionId;
|
|
847
|
+
} else if (loginFrame.sessionId != 0) {
|
|
848
|
+
this.sessionId = loginFrame.sessionId;
|
|
849
|
+
}
|
|
731
850
|
|
|
732
|
-
|
|
733
|
-
|
|
851
|
+
this.loggedIn = true;
|
|
852
|
+
this.reconnectAttempt = 0;
|
|
853
|
+
JCoreStorage.setConnected(context, true);
|
|
854
|
+
}
|
|
734
855
|
int idleReadTimeoutMs = (int) Math.min(
|
|
735
856
|
(long) Integer.MAX_VALUE,
|
|
736
857
|
Math.max(320000L, this.heartbeatIntervalMs + 30000L)
|
|
@@ -749,11 +870,11 @@ public final class JCoreTcpEngine {
|
|
|
749
870
|
+ " registrationId="
|
|
750
871
|
+ JCoreStorage.getRegistrationId(context)
|
|
751
872
|
);
|
|
752
|
-
|
|
873
|
+
if (!owns(owner, context)) return false;
|
|
753
874
|
ActionHelper.getInstance().dispatchConnectedEvent(context, true);
|
|
754
875
|
|
|
755
876
|
byte[] hbBody = JCoreWireCodec.buildHeartbeatPayload(JCoreStorage.isPushStopped(context));
|
|
756
|
-
sendRequestOnSocket(
|
|
877
|
+
sendRequestOnSocket(owner,
|
|
757
878
|
JCoreWireCodec.CMD_HEARTBEAT,
|
|
758
879
|
JCoreWireCodec.VER_HEARTBEAT,
|
|
759
880
|
this.sessionId,
|
|
@@ -762,11 +883,14 @@ public final class JCoreTcpEngine {
|
|
|
762
883
|
defaultRequestTimeoutMs
|
|
763
884
|
);
|
|
764
885
|
|
|
765
|
-
|
|
766
|
-
|
|
886
|
+
if (!owns(owner, context)) return false;
|
|
887
|
+
startHeartbeatTimer(context, connectedSocket, owner);
|
|
888
|
+
flushPreLoginQueue(context, owner);
|
|
889
|
+
if (owns(owner, context)) cn.jpush.android.api.JThirdPlatFormInterface.initializeFcm(context);
|
|
767
890
|
return true;
|
|
768
891
|
} catch (Throwable ignored) {
|
|
769
|
-
|
|
892
|
+
if (socket != null && socket != owner.socket) try { socket.close(); } catch (Exception closeError) {}
|
|
893
|
+
closeOwnedSocket(owner);
|
|
770
894
|
return false;
|
|
771
895
|
}
|
|
772
896
|
}
|
|
@@ -776,12 +900,17 @@ public final class JCoreTcpEngine {
|
|
|
776
900
|
String appKey,
|
|
777
901
|
String channel,
|
|
778
902
|
String pkgName,
|
|
779
|
-
String deviceId
|
|
903
|
+
String deviceId,
|
|
904
|
+
Attempt owner
|
|
780
905
|
) {
|
|
781
|
-
int registerSeed
|
|
782
|
-
|
|
906
|
+
final int registerSeed;
|
|
907
|
+
synchronized (this) {
|
|
908
|
+
if (!owns(owner, context)) return false;
|
|
909
|
+
registerSeed = JCoreCrypto.nextRegisterSeed();
|
|
910
|
+
JCoreWireCodec.setLastRegisterSeed(registerSeed);
|
|
911
|
+
}
|
|
783
912
|
byte[] regBody = JCoreWireCodec.buildRegisterPayload(appKey, channel, pkgName, deviceId);
|
|
784
|
-
JCoreWireCodec.DecodedFrame regFrame = sendRequestOnSocket(
|
|
913
|
+
JCoreWireCodec.DecodedFrame regFrame = sendRequestOnSocket(owner,
|
|
785
914
|
JCoreWireCodec.CMD_REGISTER,
|
|
786
915
|
JCoreWireCodec.VER_REGISTER,
|
|
787
916
|
registerSeed,
|
|
@@ -794,165 +923,142 @@ public final class JCoreTcpEngine {
|
|
|
794
923
|
}
|
|
795
924
|
JCoreWireCodec.RegisterResponse regResp = JCoreWireCodec.parseRegisterResponse(regFrame);
|
|
796
925
|
if (regResp == null || regResp.statusCode != 0 || regResp.uid <= 0L) {
|
|
926
|
+
synchronized (this) {
|
|
927
|
+
if (regResp != null && owns(owner, context) && (regResp.statusCode == 1005 || regResp.statusCode == 1008 || regResp.statusCode == 1009)) {
|
|
928
|
+
terminalConfiguration = owner.configuration;
|
|
929
|
+
closeSocket();
|
|
930
|
+
started = false;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
797
933
|
return false;
|
|
798
934
|
}
|
|
799
|
-
|
|
800
|
-
context
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
935
|
+
synchronized (this) {
|
|
936
|
+
if (!owns(owner, context)) return false;
|
|
937
|
+
JCoreStorage.updateRegistration(
|
|
938
|
+
context,
|
|
939
|
+
regResp.uid,
|
|
940
|
+
regResp.password,
|
|
941
|
+
regResp.registrationId,
|
|
942
|
+
regResp.deviceId
|
|
943
|
+
);
|
|
944
|
+
JCoreStorage.setServerRegistered(context, true);
|
|
945
|
+
}
|
|
946
|
+
if (!owns(owner, context)) return false;
|
|
807
947
|
JLogger.i("TCP register success uid=" + regResp.uid + " registrationId=" + regResp.registrationId);
|
|
808
948
|
ActionHelper.getInstance().dispatchRegisterEvent(context, regResp.registrationId);
|
|
809
949
|
return true;
|
|
810
950
|
}
|
|
811
951
|
|
|
812
|
-
private void flushPreLoginQueue(final Context context) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
public void run() {
|
|
819
|
-
executeQueuedRequestWithRetry(context, req);
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
public void enqueueOrSendRequest(
|
|
826
|
-
final Context context,
|
|
827
|
-
int command,
|
|
828
|
-
int version,
|
|
829
|
-
long rid,
|
|
830
|
-
byte[] body,
|
|
831
|
-
long timeoutMs,
|
|
832
|
-
int maxRetries,
|
|
833
|
-
RequestCallback callback
|
|
834
|
-
) {
|
|
835
|
-
final long effectiveRid = rid > 0L ? rid : JCoreStorage.nextRid();
|
|
836
|
-
final QueuedRequest req = new QueuedRequest(
|
|
837
|
-
command,
|
|
838
|
-
version,
|
|
839
|
-
effectiveRid,
|
|
840
|
-
body,
|
|
841
|
-
timeoutMs > 0L ? timeoutMs : defaultRequestTimeoutMs,
|
|
842
|
-
maxRetries,
|
|
843
|
-
callback
|
|
844
|
-
);
|
|
845
|
-
if (!this.loggedIn || this.activeOutput == null) {
|
|
846
|
-
preLoginQueue.offer(req);
|
|
847
|
-
return;
|
|
952
|
+
private void flushPreLoginQueue(final Context context, final Attempt expectedOwner) {
|
|
953
|
+
final Object token;
|
|
954
|
+
synchronized (this) {
|
|
955
|
+
if (!owns(expectedOwner, context) || !loggedIn || drainingToken != null || preLoginQueue.isEmpty()) return;
|
|
956
|
+
token = new Object();
|
|
957
|
+
drainingToken = token;
|
|
848
958
|
}
|
|
849
|
-
ioExecutor.execute(
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
959
|
+
ioExecutor.execute(() -> {
|
|
960
|
+
try {
|
|
961
|
+
while (true) {
|
|
962
|
+
final QueuedRequest req;
|
|
963
|
+
synchronized (JCoreTcpEngine.this) {
|
|
964
|
+
if (drainingToken != token) return;
|
|
965
|
+
if (!owns(expectedOwner, context) || !loggedIn || (req = preLoginQueue.poll()) == null) {
|
|
966
|
+
drainingToken = null;
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
if (req.owner == null) req.owner = expectedOwner;
|
|
970
|
+
}
|
|
971
|
+
try { executeQueuedRequestWithRetry(context, req, req.owner); }
|
|
972
|
+
catch (Throwable callbackFailure) {
|
|
973
|
+
try { req.complete(null, JPushInterface.ErrorCode.TIMEOUT); } catch (Throwable ignored) {}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
} finally {
|
|
977
|
+
boolean restart = false;
|
|
978
|
+
synchronized (JCoreTcpEngine.this) {
|
|
979
|
+
if (drainingToken == token) {
|
|
980
|
+
drainingToken = null;
|
|
981
|
+
restart = owns(expectedOwner, context) && loggedIn && !preLoginQueue.isEmpty();
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (restart) flushPreLoginQueue(context, expectedOwner);
|
|
853
985
|
}
|
|
854
986
|
});
|
|
855
987
|
}
|
|
856
988
|
|
|
857
|
-
|
|
858
|
-
long
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
)
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
989
|
+
public void enqueueOrSendRequest(final Context context, int command, int version, long rid, byte[] body,
|
|
990
|
+
long timeoutMs, int maxRetries, RequestCallback callback) {
|
|
991
|
+
final QueuedRequest req;
|
|
992
|
+
final Attempt owner;
|
|
993
|
+
boolean rejected;
|
|
994
|
+
final boolean queueFull;
|
|
995
|
+
final boolean terminal;
|
|
996
|
+
synchronized (this) {
|
|
997
|
+
req = new QueuedRequest(command, version, rid > 0L ? rid : JCoreStorage.nextRid(), body,
|
|
998
|
+
timeoutMs > 0L ? timeoutMs : defaultRequestTimeoutMs, maxRetries, callback,
|
|
999
|
+
JCoreStorage.getContentGeneration(), configurationIdentity(context));
|
|
1000
|
+
queueFull = preLoginQueue.size() >= 128;
|
|
1001
|
+
terminal = terminalConfiguration.equals(configurationIdentity(context));
|
|
1002
|
+
rejected = queueFull || terminal || !JCoreStorage.isCollectionAuth(context) || JCoreStorage.isPushStopped(context);
|
|
1003
|
+
if (!rejected) {
|
|
1004
|
+
if (loggedIn && owns(activeAttempt, context)) req.owner = activeAttempt;
|
|
1005
|
+
preLoginQueue.offer(req);
|
|
1006
|
+
}
|
|
1007
|
+
owner = activeAttempt;
|
|
1008
|
+
}
|
|
1009
|
+
if (rejected) {
|
|
1010
|
+
JLogger.w(queueFull ? "Request rejected reason=queue_full" : terminal
|
|
1011
|
+
? "Request rejected reason=configuration_terminal" : "Request rejected reason=policy_disabled");
|
|
1012
|
+
req.complete(null, JPushInterface.ErrorCode.TIMEOUT);
|
|
1013
|
+
} else flushPreLoginQueue(context, owner);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private void executeQueuedRequestWithRetry(Context context, QueuedRequest req, Attempt owner) {
|
|
1017
|
+
final long uid = JCoreStorage.getUid(context);
|
|
1018
|
+
final int sid = sessionId;
|
|
1019
|
+
for (int attempt = 0; attempt <= req.maxRetries; attempt++) {
|
|
1020
|
+
if (!owns(owner, context) || !req.configuration.equals(owner.configuration)
|
|
1021
|
+
|| req.policyGeneration != JCoreStorage.getContentGeneration()
|
|
1022
|
+
|| !JCoreStorage.isCollectionAuth(context)) break;
|
|
1023
|
+
long rid = attempt == 0 ? req.rid : JCoreStorage.nextRid();
|
|
1024
|
+
JCoreWireCodec.DecodedFrame response = sendRequestWithRidOnSocket(owner, req.command, req.version, rid,
|
|
1025
|
+
sid, uid, req.body, req.timeoutMs, req.policyGeneration);
|
|
1026
|
+
if (response != null && owns(owner, context) && req.configuration.equals(owner.configuration)
|
|
1027
|
+
&& req.policyGeneration == JCoreStorage.getContentGeneration()) {
|
|
1028
|
+
int code = response.statusCode;
|
|
1029
|
+
if ((req.command == JCoreWireCodec.CMD_TAG_OP || req.command == JCoreWireCodec.CMD_ALIAS_OP
|
|
1030
|
+
|| req.command == JCoreWireCodec.CMD_PROPERTY_OP) && response.body != null && response.body.length > 0) {
|
|
1031
|
+
code = JCoreWireCodec.extractJsonInt(JCoreWireCodec.parseJsonCommandPayload(response.body), "code", code);
|
|
883
1032
|
}
|
|
1033
|
+
req.complete(response, code);
|
|
884
1034
|
return;
|
|
885
1035
|
}
|
|
886
1036
|
}
|
|
887
|
-
|
|
888
|
-
req.callback.onResult(null, JPushInterface.ErrorCode.TIMEOUT);
|
|
889
|
-
}
|
|
1037
|
+
req.complete(null, JPushInterface.ErrorCode.TIMEOUT);
|
|
890
1038
|
}
|
|
891
1039
|
|
|
892
|
-
private void readLoop(final Context context, Socket socket, InputStream in) {
|
|
1040
|
+
private void readLoop(final Context context, Socket socket, InputStream in, Attempt owner) {
|
|
893
1041
|
try {
|
|
894
|
-
while (!socket.isClosed()) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
if (
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
handleIncomingFrame(context, frame);
|
|
1042
|
+
while (!socket.isClosed() && owns(owner, context) && activeSocket == socket) {
|
|
1043
|
+
JCoreWireCodec.DecodedFrame frame = JCoreWireCodec.readFrameFromStream(in, JCoreStorage.getUid(context));
|
|
1044
|
+
if (frame == null) break;
|
|
1045
|
+
if (owns(owner, context) && activeSocket == socket) handleIncomingFrame(context, frame, owner, socket);
|
|
901
1046
|
}
|
|
902
1047
|
} catch (Throwable ignored) {
|
|
903
1048
|
} finally {
|
|
904
|
-
boolean
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
wasCurrentSocket = true;
|
|
910
|
-
wasLoggedIn = this.loggedIn;
|
|
911
|
-
this.loggedIn = false;
|
|
912
|
-
droppedUnexpectedly = !socket.isClosed();
|
|
913
|
-
try {
|
|
914
|
-
socket.close();
|
|
915
|
-
} catch (Throwable ignored) {
|
|
916
|
-
}
|
|
917
|
-
this.activeSocket = null;
|
|
918
|
-
this.activeOutput = null;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
if (wasCurrentSocket) {
|
|
922
|
-
cancelHeartbeatTimer();
|
|
923
|
-
if (wasLoggedIn) {
|
|
1049
|
+
boolean reconnect;
|
|
1050
|
+
synchronized (this) {
|
|
1051
|
+
reconnect = owns(owner, context) && activeSocket == socket && loggedIn && !socket.isClosed();
|
|
1052
|
+
if (activeAttempt == owner && activeSocket == socket) {
|
|
1053
|
+
closeOwnedSocket(owner);
|
|
924
1054
|
JCoreStorage.setConnected(context, false);
|
|
925
1055
|
}
|
|
926
|
-
for (ResponseSlot slot : pendingRequests.values()) {
|
|
927
|
-
slot.complete(null);
|
|
928
|
-
}
|
|
929
|
-
pendingRequests.clear();
|
|
930
|
-
if (wasLoggedIn && autoReconnectEnabled && droppedUnexpectedly && !JCoreStorage.isPushStopped(context)) {
|
|
931
|
-
final long delayMs = computeReconnectDelayMs(reconnectAttempt++, reconnectBaseDelayMs);
|
|
932
|
-
final long scheduledGen = this.connectionGeneration;
|
|
933
|
-
heartbeatExecutor.schedule(new Runnable() {
|
|
934
|
-
@Override
|
|
935
|
-
public void run() {
|
|
936
|
-
if (!started || scheduledGen != connectionGeneration || loggedIn || JCoreStorage.isPushStopped(context)) {
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
ioExecutor.execute(new Runnable() {
|
|
940
|
-
@Override
|
|
941
|
-
public void run() {
|
|
942
|
-
if (!started || scheduledGen != connectionGeneration || loggedIn || JCoreStorage.isPushStopped(context)) {
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
connectAndHandshake(context);
|
|
946
|
-
}
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
}, delayMs, TimeUnit.MILLISECONDS);
|
|
950
|
-
}
|
|
951
1056
|
}
|
|
1057
|
+
if (reconnect) scheduleReconnect(context, owner);
|
|
952
1058
|
}
|
|
953
1059
|
}
|
|
954
1060
|
|
|
955
|
-
private void handleIncomingFrame(Context context, JCoreWireCodec.DecodedFrame frame) {
|
|
1061
|
+
private void handleIncomingFrame(Context context, JCoreWireCodec.DecodedFrame frame, Attempt owner, Socket socket) {
|
|
956
1062
|
if (frame.command == JCoreWireCodec.CMD_PUSH_MESSAGE) {
|
|
957
1063
|
JCoreWireCodec.PushEnvelope envelope = JCoreWireCodec.parsePushMessage(frame);
|
|
958
1064
|
if (envelope != null) {
|
|
@@ -965,7 +1071,7 @@ public final class JCoreTcpEngine {
|
|
|
965
1071
|
long ackRid = frame.requestId != 0L ? frame.requestId : JCoreStorage.nextRid();
|
|
966
1072
|
long uid = frame.uid != 0L ? frame.uid : JCoreStorage.getUid(context);
|
|
967
1073
|
byte[] ackBody = JCoreWireCodec.buildMessageAckPayload(envelope.messageType, envelope.messageId);
|
|
968
|
-
|
|
1074
|
+
sendFrameOwned(owner, socket,
|
|
969
1075
|
JCoreWireCodec.CMD_MESSAGE_ACK,
|
|
970
1076
|
JCoreWireCodec.VER_MESSAGE_ACK,
|
|
971
1077
|
ackRid,
|
|
@@ -990,7 +1096,7 @@ public final class JCoreTcpEngine {
|
|
|
990
1096
|
long ackRid = frame.requestId != 0L ? frame.requestId : JCoreStorage.nextRid();
|
|
991
1097
|
long uid = frame.uid != 0L ? frame.uid : JCoreStorage.getUid(context);
|
|
992
1098
|
byte[] ackBody = JCoreWireCodec.buildCtrlAckPayload(0, ctrl.requestId);
|
|
993
|
-
|
|
1099
|
+
sendFrameOwned(owner, socket,
|
|
994
1100
|
JCoreWireCodec.CMD_CTRL,
|
|
995
1101
|
JCoreWireCodec.VER_CTRL,
|
|
996
1102
|
ackRid,
|
|
@@ -1002,29 +1108,21 @@ public final class JCoreTcpEngine {
|
|
|
1002
1108
|
return;
|
|
1003
1109
|
}
|
|
1004
1110
|
|
|
1005
|
-
ResponseSlot slot = pendingRequests.
|
|
1006
|
-
if (slot != null) {
|
|
1111
|
+
ResponseSlot slot = pendingRequests.get(frame.requestId);
|
|
1112
|
+
if (slot != null && slot.owner == owner && slot.socket == socket && slot.command == frame.command && pendingRequests.remove(frame.requestId, slot)) {
|
|
1007
1113
|
slot.complete(frame);
|
|
1008
1114
|
}
|
|
1009
1115
|
}
|
|
1010
1116
|
|
|
1117
|
+
private boolean isBusinessCommand(int command) {
|
|
1118
|
+
return command != JCoreWireCodec.CMD_REGISTER && command != JCoreWireCodec.CMD_LOGIN
|
|
1119
|
+
&& command != JCoreWireCodec.CMD_HEARTBEAT && command != JCoreWireCodec.CMD_MESSAGE_ACK
|
|
1120
|
+
&& command != JCoreWireCodec.CMD_CTRL && command != JCoreWireCodec.CMD_PUSH_CTRL;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1011
1123
|
public boolean sendFrameRaw(int command, int version, long requestId, int sid, long uid, byte[] body) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
return false;
|
|
1015
|
-
}
|
|
1016
|
-
synchronized (socketLock) {
|
|
1017
|
-
if (activeOutput == null) {
|
|
1018
|
-
return false;
|
|
1019
|
-
}
|
|
1020
|
-
try {
|
|
1021
|
-
activeOutput.write(packet);
|
|
1022
|
-
activeOutput.flush();
|
|
1023
|
-
return true;
|
|
1024
|
-
} catch (Throwable ignored) {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1124
|
+
Attempt owner = activeAttempt;
|
|
1125
|
+
return owner != null && owner.socket != null && sendFrameOwned(owner, owner.socket, command, version, requestId, sid, uid, body);
|
|
1028
1126
|
}
|
|
1029
1127
|
|
|
1030
1128
|
public JCoreWireCodec.DecodedFrame sendRequest(int command, int version, byte[] body, long timeoutMs) {
|
|
@@ -1032,44 +1130,61 @@ public final class JCoreTcpEngine {
|
|
|
1032
1130
|
return sendRequestOnSocket(command, version, this.sessionId, uid, body, timeoutMs);
|
|
1033
1131
|
}
|
|
1034
1132
|
|
|
1035
|
-
private JCoreWireCodec.DecodedFrame sendRequestOnSocket(
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
int command,
|
|
1049
|
-
int version,
|
|
1050
|
-
long rid,
|
|
1051
|
-
int sid,
|
|
1052
|
-
long uid,
|
|
1053
|
-
byte[] body,
|
|
1054
|
-
long timeoutMs
|
|
1055
|
-
) {
|
|
1056
|
-
ResponseSlot slot = new ResponseSlot();
|
|
1133
|
+
private JCoreWireCodec.DecodedFrame sendRequestOnSocket(int command, int version, int sid, long uid, byte[] body, long timeoutMs) {
|
|
1134
|
+
return sendRequestOnSocket(activeAttempt, command, version, sid, uid, body, timeoutMs);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
private JCoreWireCodec.DecodedFrame sendRequestOnSocket(Attempt owner, int command, int version, int sid, long uid, byte[] body, long timeoutMs) {
|
|
1138
|
+
return sendRequestWithRidOnSocket(owner, command, version, JCoreStorage.nextRid(), sid, uid, body, timeoutMs,
|
|
1139
|
+
isBusinessCommand(command) ? JCoreStorage.getContentGeneration() : -1L);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
private JCoreWireCodec.DecodedFrame sendRequestWithRidOnSocket(Attempt owner, int command, int version, long rid,
|
|
1143
|
+
int sid, long uid, byte[] body, long timeoutMs, long policyGeneration) {
|
|
1144
|
+
if (!owns(owner, null) || owner.socket == null) return null;
|
|
1145
|
+
ResponseSlot slot = new ResponseSlot(owner, owner.socket, command);
|
|
1057
1146
|
pendingRequests.put(rid, slot);
|
|
1058
|
-
if (!
|
|
1059
|
-
pendingRequests.remove(rid);
|
|
1147
|
+
if (!sendFrameOwned(owner, slot.socket, command, version, rid, sid, uid, body, policyGeneration)) {
|
|
1148
|
+
pendingRequests.remove(rid, slot);
|
|
1060
1149
|
return null;
|
|
1061
1150
|
}
|
|
1062
|
-
try {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1151
|
+
try { slot.latch.await(timeoutMs, TimeUnit.MILLISECONDS); }
|
|
1152
|
+
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
|
|
1153
|
+
finally { pendingRequests.remove(rid, slot); }
|
|
1154
|
+
return owns(owner, null) ? slot.response : null;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
private boolean sendFrameOwned(Attempt owner, Socket socket, int command, int version, long rid,
|
|
1158
|
+
int sid, long uid, byte[] body) {
|
|
1159
|
+
return sendFrameOwned(owner, socket, command, version, rid, sid, uid, body,
|
|
1160
|
+
isBusinessCommand(command) ? JCoreStorage.getContentGeneration() : -1L);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
private boolean sendFrameOwned(Attempt owner, Socket socket, int command, int version, long rid,
|
|
1164
|
+
int sid, long uid, byte[] body, long policyGeneration) {
|
|
1165
|
+
byte[] packet = JCoreWireCodec.encodeFrame(command, version, rid, sid, uid, body);
|
|
1166
|
+
synchronized (owner.writerLock) {
|
|
1167
|
+
final OutputStream output;
|
|
1168
|
+
synchronized (socketLock) {
|
|
1169
|
+
boolean stopControl = command == JCoreWireCodec.CMD_PUSH_CTRL
|
|
1170
|
+
&& policyGeneration == STOP_CONTROL_POLICY && owner.stopScheduled;
|
|
1171
|
+
if (activeAttempt != owner || activeSocket != socket || owner.output == null || packet.length == 0
|
|
1172
|
+
|| (!stopControl && (!owns(owner, null)
|
|
1173
|
+
|| (policyGeneration >= 0L && policyGeneration != JCoreStorage.getContentGeneration())))
|
|
1174
|
+
|| (isBusinessCommand(command) && (!JCoreStorage.isCollectionAuth(null)
|
|
1175
|
+
|| JCoreStorage.isPushStopped(null)))) return false;
|
|
1176
|
+
output = owner.output;
|
|
1177
|
+
}
|
|
1178
|
+
try { output.write(packet); output.flush(); return true; }
|
|
1179
|
+
catch (Exception ignored) { return false; }
|
|
1068
1180
|
}
|
|
1069
|
-
return slot.response;
|
|
1070
1181
|
}
|
|
1071
1182
|
|
|
1072
1183
|
public String[] resolveUdpSisEndpoint(Context context, String sisHost, int sisPort) {
|
|
1184
|
+
return resolveUdpSisEndpoint(context, sisHost, sisPort, null);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
private String[] resolveUdpSisEndpoint(Context context, String sisHost, int sisPort, Attempt owner) {
|
|
1073
1188
|
DatagramSocket udpSocket = null;
|
|
1074
1189
|
try {
|
|
1075
1190
|
String requestJson = JCoreWireCodec.buildSisRequestJson(
|
|
@@ -1097,7 +1212,10 @@ public final class JCoreTcpEngine {
|
|
|
1097
1212
|
System.arraycopy(recvBuf, 0, respBytes, 0, respBytes.length);
|
|
1098
1213
|
String decodedJson = JCoreWireCodec.decodeUdpSisPacket(respBytes);
|
|
1099
1214
|
if (!decodedJson.isEmpty()) {
|
|
1100
|
-
|
|
1215
|
+
synchronized (this) {
|
|
1216
|
+
if (owner != null && !owns(owner, context)) return null;
|
|
1217
|
+
JCoreStorage.saveSisConfig(context, decodedJson);
|
|
1218
|
+
}
|
|
1101
1219
|
}
|
|
1102
1220
|
Set<String> ips = JCoreWireCodec.extractJsonStringArray(decodedJson, "ips");
|
|
1103
1221
|
if (ips.isEmpty()) {
|
|
@@ -1120,6 +1238,10 @@ public final class JCoreTcpEngine {
|
|
|
1120
1238
|
}
|
|
1121
1239
|
|
|
1122
1240
|
public String[] resolveSisEndpoint(Context context) {
|
|
1241
|
+
return resolveSisEndpoint(context, null);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
private String[] resolveSisEndpoint(Context context, Attempt owner) {
|
|
1123
1245
|
if (customHost != null && !customHost.isEmpty() && customPort > 0) {
|
|
1124
1246
|
return new String[] { customHost, String.valueOf(customPort) };
|
|
1125
1247
|
}
|
|
@@ -1166,7 +1288,10 @@ public final class JCoreTcpEngine {
|
|
|
1166
1288
|
byte[] decrypted = JCoreCrypto.aesCbcDecrypt(buffer.toByteArray(), aesKey, SIS_AES_IV, 32);
|
|
1167
1289
|
byte[] plain = JCoreCrypto.gzipDecompress(decrypted);
|
|
1168
1290
|
String plainStr = new String(plain, StandardCharsets.UTF_8);
|
|
1169
|
-
|
|
1291
|
+
synchronized (this) {
|
|
1292
|
+
if (owner != null && !owns(owner, context)) return null;
|
|
1293
|
+
JCoreStorage.saveSisConfig(context, plainStr);
|
|
1294
|
+
}
|
|
1170
1295
|
Set<String> ips = JCoreWireCodec.extractJsonStringArray(plainStr, "ips");
|
|
1171
1296
|
if (ips.isEmpty()) {
|
|
1172
1297
|
ips = JCoreWireCodec.extractJsonStringArray(plainStr, "ssl_ips");
|
|
@@ -1201,7 +1326,7 @@ public final class JCoreTcpEngine {
|
|
|
1201
1326
|
}
|
|
1202
1327
|
}
|
|
1203
1328
|
}
|
|
1204
|
-
return resolveUdpSisEndpoint(context, defaultSisUdpHost, defaultSisUdpPort);
|
|
1329
|
+
return resolveUdpSisEndpoint(context, defaultSisUdpHost, defaultSisUdpPort, owner);
|
|
1205
1330
|
}
|
|
1206
1331
|
|
|
1207
1332
|
/**
|
|
@@ -1212,6 +1337,8 @@ public final class JCoreTcpEngine {
|
|
|
1212
1337
|
* and X-App-Key: appKey.
|
|
1213
1338
|
*/
|
|
1214
1339
|
public boolean sendHttpReport(Context context, String reportItemJson) {
|
|
1340
|
+
final long policyGeneration = JCoreStorage.getContentGeneration();
|
|
1341
|
+
if (!JCoreStorage.isCollectionAuth(context) || JCoreStorage.isPushStopped(context)) return false;
|
|
1215
1342
|
OutputStream out = null;
|
|
1216
1343
|
InputStream in = null;
|
|
1217
1344
|
try {
|
|
@@ -1272,6 +1399,7 @@ public final class JCoreTcpEngine {
|
|
|
1272
1399
|
conn.setRequestProperty("X-App-Key", appKey);
|
|
1273
1400
|
conn.setRequestProperty("Authorization", authHeader);
|
|
1274
1401
|
|
|
1402
|
+
if (policyGeneration != JCoreStorage.getContentGeneration() || !JCoreStorage.isCollectionAuth(context) || JCoreStorage.isPushStopped(context)) return false;
|
|
1275
1403
|
out = conn.getOutputStream();
|
|
1276
1404
|
out.write(encryptedBody);
|
|
1277
1405
|
out.flush();
|