react-native-update 10.59.0 → 10.59.2
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/build.gradle +46 -14
- package/android/jni/Android.mk +2 -1
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +20 -16
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/{CrashRescue.java → CrashHold.java} +12 -12
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +28 -24
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +2 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateFlow.java → FlowBridge.java} +6 -6
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +6 -3
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateCore.java → NativeCore.java} +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +10 -5
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +7 -7
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeCheckOrchestrator.java → SyncCoordinator.java} +79 -75
- package/android/src/main/java/cn/reactnative/modules/update/Texts.java +28 -0
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +28 -28
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +12 -9
- package/cpp/patch_core/archive_limits.h +5 -0
- package/cpp/patch_core/archive_patch_core.cpp +26 -19
- package/cpp/patch_core/archive_patch_core.h +17 -8
- package/cpp/patch_core/digest.h +6 -1
- package/cpp/patch_core/error_codes.h +8 -1
- package/cpp/patch_core/hbc_transform.h +5 -0
- package/cpp/patch_core/hbc_transform_wire.h +5 -0
- package/cpp/patch_core/install_record.h +5 -0
- package/cpp/patch_core/jni_natives.h +27 -0
- package/cpp/patch_core/jni_registration.cpp +103 -0
- package/cpp/patch_core/obscured_text.h +44 -0
- package/cpp/patch_core/patch_core.cpp +13 -13
- package/cpp/patch_core/patch_core.h +11 -6
- package/cpp/patch_core/patch_core_android.cpp +10 -10
- package/cpp/patch_core/state_core.h +5 -0
- package/cpp/patch_core/state_ops.h +5 -0
- package/cpp/patch_core/update_core_android.cpp +23 -22
- package/cpp/update_flow_core/flow_json.h +6 -1
- package/cpp/update_flow_core/update_flow_core.cpp +35 -18
- package/cpp/update_flow_core/update_flow_core.h +13 -8
- package/cpp/update_flow_core/update_flow_jni.cpp +11 -13
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +10 -8
- package/harmony/pushy/src/main/cpp/pushy.cpp +33 -33
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +15 -15
- package/harmony/pushy/src/main/ets/DownloadTask.ts +60 -60
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +13 -11
- package/harmony/pushy/src/main/ets/InstallRecord.ts +4 -4
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +9 -9
- package/harmony/pushy/src/main/ets/PathUtils.ts +2 -2
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -6
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +42 -41
- package/harmony/pushy/src/main/ets/{NativeCheckOrchestrator.ts → SyncCoordinator.ts} +33 -32
- package/harmony/pushy/src/main/ets/Texts.ts +28 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +31 -30
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +154 -140
- package/ios/RCTPushy/RCTPushyConfiguration.h +8 -0
- package/ios/RCTPushy/RCTPushyConfiguration.mm +32 -5
- package/package.json +1 -1
- package/react-native-update.podspec +16 -4
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
package/ios/RCTPushy/RCTPushy.mm
CHANGED
|
@@ -31,6 +31,12 @@
|
|
|
31
31
|
|
|
32
32
|
#import <React/RCTConvert.h>
|
|
33
33
|
#import <React/RCTLog.h>
|
|
34
|
+
// RN's log macros pass __FILE__, which would embed this file's absolute build
|
|
35
|
+
// path in the binary; log with the bare file name instead.
|
|
36
|
+
#if RCTLOG_ENABLED
|
|
37
|
+
#undef _RCTLog
|
|
38
|
+
#define _RCTLog(lvl, ...) _RCTLogNativeInternal(lvl, __FILE_NAME__, __LINE__, __VA_ARGS__)
|
|
39
|
+
#endif
|
|
34
40
|
#import <objc/runtime.h>
|
|
35
41
|
#import <os/lock.h>
|
|
36
42
|
|
|
@@ -64,7 +70,7 @@ static NSString *const keyUuid = @"REACTNATIVECN_PUSHY_UUID";
|
|
|
64
70
|
static NSString *const keyHashInfo = @"REACTNATIVECN_PUSHY_HASH_";
|
|
65
71
|
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
|
|
66
72
|
static NSString *const keyRolledBackMarked = @"REACTNATIVECN_PUSHY_ROLLEDBACKMARKED_KEY";
|
|
67
|
-
static NSString *const KeyPackageUpdatedMarked =
|
|
73
|
+
static NSString *const KeyPackageUpdatedMarked = RCTPushyRevealText("0832d5f29aa549710b0939daf88ca058790f3dded7e888b451640d2ec3f69099bb43711523cafa8ea7bb4372132a");
|
|
68
74
|
// bundleHash cache: "<cacheKey>|<sha256hex>" where cacheKey identifies the
|
|
69
75
|
// installed binary (packageVersion + embedded bundle size + mtime). Recomputed
|
|
70
76
|
// only when the key changes, i.e. once per install.
|
|
@@ -84,7 +90,15 @@ static NSString *const PushyErrorDomain = @"cn.reactnative.pushy";
|
|
|
84
90
|
// file def
|
|
85
91
|
static NSString * const BUNDLE_FILE_NAME = @"index.bundlejs";
|
|
86
92
|
static NSString * const SOURCE_PATCH_NAME = @"__diff.json";
|
|
87
|
-
static NSString * const BUNDLE_PATCH_NAME =
|
|
93
|
+
static NSString * const BUNDLE_PATCH_NAME = RCTPushyRevealText("3319f0d4b6c56a502c3b10fcdca0de7d4b3307e9");
|
|
94
|
+
// Protocol strings and version-info flags read by JS, stored encoded
|
|
95
|
+
// (RCTPushyRevealText) so they do not appear as plain binary strings.
|
|
96
|
+
static NSString * const PushyOptionUrlKey = RCTPushyRevealText("2f07f0d0ba8e5d572e");
|
|
97
|
+
static NSString * const PushyIpaDeltaSuffix = RCTPushyRevealText("741ee4d0e09b69512137");
|
|
98
|
+
static NSString * const PushyPpkDeltaSuffix = RCTPushyRevealText("7407e4dae09b69512137");
|
|
99
|
+
static NSString * const PushyInfoCrashHold = RCTPushyRevealText("3905f5c2a6b96d56212a19");
|
|
100
|
+
static NSString * const PushyInfoForceBoot = RCTPushyRevealText("3c18e6d2aba9674a360d19ead5a695");
|
|
101
|
+
static NSString * const PushyStatusNone = RCTPushyRevealText("3418c1c1aa8a7c40");
|
|
88
102
|
#define VERSION_COMPLETE_FILE_NAME_LITERAL ".pushy-complete"
|
|
89
103
|
static NSString * const VERSION_COMPLETE_FILE_NAME = @VERSION_COMPLETE_FILE_NAME_LITERAL;
|
|
90
104
|
|
|
@@ -211,7 +225,7 @@ static NSError *PushyEnsureFreeSpace(NSString *path, long long bytesToWrite) {
|
|
|
211
225
|
if (shortfall == nil) {
|
|
212
226
|
return nil;
|
|
213
227
|
}
|
|
214
|
-
return PushyErrorWithCode(pushy::error_codes::
|
|
228
|
+
return PushyErrorWithCode(pushy::error_codes::kDeltaFailed, shortfall);
|
|
215
229
|
}
|
|
216
230
|
|
|
217
231
|
// SSZipArchive delegate enforcing cpp/patch_core/archive_limits.h while the
|
|
@@ -477,12 +491,12 @@ static std::string PushyToStdString(NSString *value) {
|
|
|
477
491
|
return std::string([value UTF8String]);
|
|
478
492
|
}
|
|
479
493
|
|
|
480
|
-
static NSError *PushyNSErrorFromStatus(const pushy::
|
|
494
|
+
static NSError *PushyNSErrorFromStatus(const pushy::delta::Status &status) {
|
|
481
495
|
return [NSError errorWithDomain:PushyErrorDomain
|
|
482
496
|
code:-1
|
|
483
497
|
userInfo:@{
|
|
484
498
|
NSLocalizedDescriptionKey: [NSString stringWithUTF8String:status.message.c_str()],
|
|
485
|
-
PushyErrorCodeKey: PushyCode(pushy::error_codes::
|
|
499
|
+
PushyErrorCodeKey: PushyCode(pushy::error_codes::kDeltaFailed),
|
|
486
500
|
}];
|
|
487
501
|
}
|
|
488
502
|
|
|
@@ -568,12 +582,12 @@ static BOOL PushyJsonIsAbsent(id value) {
|
|
|
568
582
|
// raise takes the whole app down (and trips the crash rescue) instead of
|
|
569
583
|
// failing the patch. NO with `reason` set for a malformed manifest.
|
|
570
584
|
static BOOL PushyPatchManifestFromJson(NSDictionary *json,
|
|
571
|
-
pushy::
|
|
585
|
+
pushy::delta::PatchManifest *manifest,
|
|
572
586
|
NSString **reason) {
|
|
573
587
|
id copies = json[@"copies"];
|
|
574
588
|
if (!PushyJsonIsAbsent(copies)) {
|
|
575
589
|
if (![copies isKindOfClass:[NSDictionary class]]) {
|
|
576
|
-
*reason = @"
|
|
590
|
+
*reason = @"delta manifest: copies is not an object";
|
|
577
591
|
return NO;
|
|
578
592
|
}
|
|
579
593
|
// Content checksum per copy target ("copiesCrc", pdiff manifests
|
|
@@ -588,13 +602,13 @@ static BOOL PushyPatchManifestFromJson(NSDictionary *json,
|
|
|
588
602
|
for (id to in (NSDictionary *)copies) {
|
|
589
603
|
id from = ((NSDictionary *)copies)[to];
|
|
590
604
|
if (![to isKindOfClass:[NSString class]] || ![from isKindOfClass:[NSString class]]) {
|
|
591
|
-
*reason = @"
|
|
605
|
+
*reason = @"delta manifest: copies entry is not a string";
|
|
592
606
|
return NO;
|
|
593
607
|
}
|
|
594
608
|
if ([from length] == 0) {
|
|
595
609
|
from = to;
|
|
596
610
|
}
|
|
597
|
-
pushy::
|
|
611
|
+
pushy::delta::CopyOperation operation;
|
|
598
612
|
operation.from = PushyToStdString(from);
|
|
599
613
|
operation.to = PushyToStdString(to);
|
|
600
614
|
NSNumber *expectedCrc = copiesCrc[to];
|
|
@@ -611,12 +625,12 @@ static BOOL PushyPatchManifestFromJson(NSDictionary *json,
|
|
|
611
625
|
id deletes = json[@"deletes"];
|
|
612
626
|
if (!PushyJsonIsAbsent(deletes)) {
|
|
613
627
|
if (![deletes isKindOfClass:[NSDictionary class]] && ![deletes isKindOfClass:[NSArray class]]) {
|
|
614
|
-
*reason = @"
|
|
628
|
+
*reason = @"delta manifest: deletes is not an object or array";
|
|
615
629
|
return NO;
|
|
616
630
|
}
|
|
617
631
|
for (id path in deletes) {
|
|
618
632
|
if (![path isKindOfClass:[NSString class]]) {
|
|
619
|
-
*reason = @"
|
|
633
|
+
*reason = @"delta manifest: deletes entry is not a string";
|
|
620
634
|
return NO;
|
|
621
635
|
}
|
|
622
636
|
manifest->deletes.push_back(PushyToStdString(path));
|
|
@@ -697,14 +711,14 @@ static void PushySwitchVersionLocked(NSString *hash) {
|
|
|
697
711
|
}
|
|
698
712
|
|
|
699
713
|
@interface RCTPushy ()
|
|
700
|
-
- (void)
|
|
714
|
+
- (void)fetchPackage:(PushyType)type
|
|
701
715
|
options:(NSDictionary *)options
|
|
702
716
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
703
717
|
rejecter:(RCTPromiseRejectBlock)reject;
|
|
704
|
-
- (void)
|
|
718
|
+
- (void)performFetch:(PushyType)type
|
|
705
719
|
options:(NSDictionary *)options
|
|
706
720
|
callback:(void (^)(NSError *error))callback;
|
|
707
|
-
- (NSError *)
|
|
721
|
+
- (NSError *)restartBridgeWithReason:(NSString *)reason;
|
|
708
722
|
- (void)unzipDownloadedPackage:(NSString *)zipFilePath
|
|
709
723
|
hash:(NSString *)hash
|
|
710
724
|
type:(PushyType)type
|
|
@@ -714,7 +728,7 @@ static void PushySwitchVersionLocked(NSString *hash) {
|
|
|
714
728
|
type:(PushyType)type
|
|
715
729
|
originHash:(NSString *)originHash
|
|
716
730
|
callback:(void (^)(NSError *error))callback;
|
|
717
|
-
- (void)
|
|
731
|
+
- (void)applyDeltaForHash:(NSString *)hash
|
|
718
732
|
type:(PushyType)type
|
|
719
733
|
fromBundle:(NSString *)bundleOrigin
|
|
720
734
|
source:(NSString *)sourceOrigin
|
|
@@ -744,10 +758,10 @@ static void PushySwitchVersionLocked(NSString *hash) {
|
|
|
744
758
|
+ (BOOL)restorePurgedLaunch:(NSString *)purgedVersion
|
|
745
759
|
rolledBack:(NSString *)launchRolledBackVersion;
|
|
746
760
|
#endif
|
|
747
|
-
+ (void)
|
|
761
|
+
+ (void)recordJsRound:(NSString *)config;
|
|
748
762
|
+ (void)startRoundWithDeadline:(NSTimeInterval)deadlineUptime;
|
|
749
763
|
+ (void)runOnce:(NSString *)launchRolledBackVersion deadline:(NSTimeInterval)deadlineUptime;
|
|
750
|
-
+ (void)
|
|
764
|
+
+ (void)runHoldRoundWithDeadline:(NSTimeInterval)deadlineUptime;
|
|
751
765
|
+ (BOOL)commitRoundWithGeneration:(uint64_t)generation
|
|
752
766
|
hashInfo:(NSDictionary *)hashInfoEntry
|
|
753
767
|
activate:(NSString *)hashToActivate
|
|
@@ -766,8 +780,8 @@ static std::atomic<bool> pushyRoundCompleted{false};
|
|
|
766
780
|
// Flipped the moment a crash is being held. JS is dead from that point on,
|
|
767
781
|
// so there is no second decision maker: the round force-activates whatever
|
|
768
782
|
// it downloads (§11.3).
|
|
769
|
-
static std::atomic<bool>
|
|
770
|
-
static std::atomic<bool>
|
|
783
|
+
static std::atomic<bool> pushyCrashHoldActive{false};
|
|
784
|
+
static std::atomic<bool> pushyHoldAttempted{false};
|
|
771
785
|
// Set when this process's round is the tvOS purged-version restore.
|
|
772
786
|
static std::atomic<bool> pushyPurgeRestoreActive{false};
|
|
773
787
|
// True while +bundleURL waits for that restore. Only then may the round
|
|
@@ -778,7 +792,7 @@ static std::atomic<bool> pushyPurgeRestoreActive{false};
|
|
|
778
792
|
// (and is left to JS) — never selected behind the packaged bundle's back.
|
|
779
793
|
static bool pushyPurgeRestoreWindowOpen = false;
|
|
780
794
|
static dispatch_semaphore_t pushyRoundDone;
|
|
781
|
-
static NSString *
|
|
795
|
+
static NSString *pushyLaunchRolledBackForHold = nil;
|
|
782
796
|
// A version this process downloaded but left for JS to activate. If the
|
|
783
797
|
// process then crashes, JS will never activate it — the crash handler
|
|
784
798
|
// activates it directly (bounded local work, no network). The generation is
|
|
@@ -796,27 +810,27 @@ static NSTimeInterval pushyProcessAnchorUptime = 0;
|
|
|
796
810
|
static NSString *pushyJsCompletedConfig = nil;
|
|
797
811
|
// The group supplements (rather than consumes) the crash-rescue semaphore.
|
|
798
812
|
static dispatch_group_t pushyHostRoundGroup;
|
|
799
|
-
static std::atomic<bool>
|
|
813
|
+
static std::atomic<bool> pushySyncReady{false};
|
|
800
814
|
static NSDictionary *pushyHostRoundResult = nil;
|
|
801
815
|
static NSString *pushyHostRoundConfig = nil;
|
|
802
816
|
static uint64_t pushyHostRoundGeneration = 0;
|
|
803
817
|
static std::atomic<uint64_t> pushyNativeConfigGeneration{0};
|
|
804
818
|
static uint64_t pushyHostRoundConfigGeneration = 0;
|
|
805
819
|
|
|
806
|
-
static const NSTimeInterval
|
|
807
|
-
static const NSTimeInterval
|
|
820
|
+
static const NSTimeInterval kPushyHoldTriggerUptime = 60;
|
|
821
|
+
static const NSTimeInterval kPushyHoldBudgetBackgroundThread = 10;
|
|
808
822
|
// A held main thread freezes UI teardown; stay well under the watchdog.
|
|
809
|
-
static const NSTimeInterval
|
|
823
|
+
static const NSTimeInterval kPushyHoldBudgetMainThread = 3.5;
|
|
810
824
|
// The purged-version restore blocks +bundleURL, usually inside
|
|
811
825
|
// application:didFinishLaunching: — stay clear of the launch watchdog.
|
|
812
826
|
static const NSTimeInterval kPushyPurgeRestoreBudget = 12;
|
|
813
827
|
|
|
814
|
-
static void
|
|
828
|
+
static void PushyMaybeHoldProcess(void) {
|
|
815
829
|
// Once per process; a second crashing thread passes straight through
|
|
816
830
|
// instead of waiting behind the first (§11.3: prefer under-rescuing over
|
|
817
831
|
// wedging the teardown).
|
|
818
832
|
bool expected = false;
|
|
819
|
-
if (!
|
|
833
|
+
if (!pushyHoldAttempted.compare_exchange_strong(expected, true)) {
|
|
820
834
|
return;
|
|
821
835
|
}
|
|
822
836
|
NSTimeInterval uptime = PushyMonotonicNow() - pushyProcessAnchorUptime;
|
|
@@ -824,14 +838,14 @@ static void PushyMaybeHoldForRescue(void) {
|
|
|
824
838
|
// Early crashes are the brick signature; a crash with an in-flight round
|
|
825
839
|
// is worth finishing regardless of uptime. Everything else is an ordinary
|
|
826
840
|
// crash whose UX must not be delayed.
|
|
827
|
-
if (uptime >=
|
|
841
|
+
if (uptime >= kPushyHoldTriggerUptime && !roundInFlight) {
|
|
828
842
|
return;
|
|
829
843
|
}
|
|
830
844
|
NSTimeInterval budget = [NSThread isMainThread]
|
|
831
|
-
?
|
|
832
|
-
:
|
|
845
|
+
? kPushyHoldBudgetMainThread
|
|
846
|
+
: kPushyHoldBudgetBackgroundThread;
|
|
833
847
|
NSTimeInterval deadline = PushyMonotonicNow() + budget;
|
|
834
|
-
NSLog(@"RCTPushy -- crash
|
|
848
|
+
NSLog(@"RCTPushy -- crash hold: holding process for up to %.1fs "
|
|
835
849
|
@"(uptime %.1fs)", budget, uptime);
|
|
836
850
|
|
|
837
851
|
// The rescue runs on its own queue and the dying thread only waits with a
|
|
@@ -841,15 +855,15 @@ static void PushyMaybeHoldForRescue(void) {
|
|
|
841
855
|
dispatch_semaphore_t done = dispatch_semaphore_create(0);
|
|
842
856
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
843
857
|
@try {
|
|
844
|
-
[RCTPushyOrchestrator
|
|
858
|
+
[RCTPushyOrchestrator runHoldRoundWithDeadline:deadline];
|
|
845
859
|
} @catch (NSException *exception) {
|
|
846
|
-
NSLog(@"RCTPushy -- crash
|
|
860
|
+
NSLog(@"RCTPushy -- crash hold failed: %@", exception.reason);
|
|
847
861
|
}
|
|
848
862
|
dispatch_semaphore_signal(done);
|
|
849
863
|
});
|
|
850
864
|
if (dispatch_semaphore_wait(done, dispatch_time(DISPATCH_TIME_NOW,
|
|
851
865
|
(int64_t)(budget * NSEC_PER_SEC))) != 0) {
|
|
852
|
-
NSLog(@"RCTPushy -- crash
|
|
866
|
+
NSLog(@"RCTPushy -- crash hold: budget exhausted, letting go");
|
|
853
867
|
}
|
|
854
868
|
}
|
|
855
869
|
|
|
@@ -858,9 +872,9 @@ static void PushyMaybeHoldForRescue(void) {
|
|
|
858
872
|
// process is still alive and JS will never run again — a natural,
|
|
859
873
|
// false-positive-free window to finish the cold-start check. The previous
|
|
860
874
|
// handler (crash reporters chain the same way) always runs afterwards.
|
|
861
|
-
static void
|
|
875
|
+
static void PushyCrashHoldExceptionHandler(NSException *exception) {
|
|
862
876
|
@try {
|
|
863
|
-
|
|
877
|
+
PushyMaybeHoldProcess();
|
|
864
878
|
} @catch (NSException *inner) {
|
|
865
879
|
// The dying process owes the previous handler its turn no matter
|
|
866
880
|
// what the rescue did.
|
|
@@ -870,11 +884,11 @@ static void PushyCrashRescueExceptionHandler(NSException *exception) {
|
|
|
870
884
|
}
|
|
871
885
|
}
|
|
872
886
|
|
|
873
|
-
static void
|
|
887
|
+
static void PushyInstallCrashHoldHandler(void) {
|
|
874
888
|
static dispatch_once_t onceToken;
|
|
875
889
|
dispatch_once(&onceToken, ^{
|
|
876
890
|
pushyPreviousExceptionHandler = NSGetUncaughtExceptionHandler();
|
|
877
|
-
NSSetUncaughtExceptionHandler(&
|
|
891
|
+
NSSetUncaughtExceptionHandler(&PushyCrashHoldExceptionHandler);
|
|
878
892
|
});
|
|
879
893
|
}
|
|
880
894
|
|
|
@@ -1137,14 +1151,14 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|
|
1137
1151
|
static dispatch_queue_t hostQueue;
|
|
1138
1152
|
static dispatch_once_t once;
|
|
1139
1153
|
dispatch_once(&once, ^{
|
|
1140
|
-
hostQueue = dispatch_queue_create("cn.reactnative.pushy.host-
|
|
1154
|
+
hostQueue = dispatch_queue_create("cn.reactnative.pushy.host-sync", DISPATCH_QUEUE_SERIAL);
|
|
1141
1155
|
});
|
|
1142
1156
|
dispatch_async(hostQueue, ^{
|
|
1143
1157
|
NSDictionary *result;
|
|
1144
1158
|
@try {
|
|
1145
1159
|
result = [RCTPushyOrchestrator prepareBundle];
|
|
1146
1160
|
} @catch (NSException *exception) {
|
|
1147
|
-
RCTLogWarn(@"RCTPushy -- native host
|
|
1161
|
+
RCTLogWarn(@"RCTPushy -- native host sync failed: %@", exception.reason);
|
|
1148
1162
|
result = PushyHostResult(@"failed", @"internal_error", nil, NO);
|
|
1149
1163
|
}
|
|
1150
1164
|
if (completion != nil) {
|
|
@@ -1286,7 +1300,7 @@ RCT_EXPORT_METHOD(markJsCheckCompleted:(NSString *)config
|
|
|
1286
1300
|
PushyRejectError(reject, PushyErrorWithCode(pushy::error_codes::kInvalidOptions, ERROR_OPTIONS));
|
|
1287
1301
|
return;
|
|
1288
1302
|
}
|
|
1289
|
-
[RCTPushyOrchestrator
|
|
1303
|
+
[RCTPushyOrchestrator recordJsRound:config];
|
|
1290
1304
|
resolve(@true);
|
|
1291
1305
|
}
|
|
1292
1306
|
|
|
@@ -1328,21 +1342,21 @@ RCT_EXPORT_METHOD(downloadFullUpdate:(NSDictionary *)options
|
|
|
1328
1342
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
1329
1343
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
1330
1344
|
{
|
|
1331
|
-
[self
|
|
1345
|
+
[self fetchPackage:PushyTypeFullDownload options:options resolver:resolve rejecter:reject];
|
|
1332
1346
|
}
|
|
1333
1347
|
|
|
1334
1348
|
RCT_EXPORT_METHOD(downloadPatchFromPackage:(NSDictionary *)options
|
|
1335
1349
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
1336
1350
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
1337
1351
|
{
|
|
1338
|
-
[self
|
|
1352
|
+
[self fetchPackage:PushyTypePatchFromPackage options:options resolver:resolve rejecter:reject];
|
|
1339
1353
|
}
|
|
1340
1354
|
|
|
1341
1355
|
RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options
|
|
1342
1356
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
1343
1357
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
1344
1358
|
{
|
|
1345
|
-
[self
|
|
1359
|
+
[self fetchPackage:PushyTypePatchFromPpk options:options resolver:resolve rejecter:reject];
|
|
1346
1360
|
}
|
|
1347
1361
|
|
|
1348
1362
|
RCT_EXPORT_METHOD(downloadAndInstallApk:(NSDictionary *)options
|
|
@@ -1377,7 +1391,7 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
|
|
|
1377
1391
|
return;
|
|
1378
1392
|
}
|
|
1379
1393
|
|
|
1380
|
-
NSError *reloadError = [self
|
|
1394
|
+
NSError *reloadError = [self restartBridgeWithReason:@"pushy restart"];
|
|
1381
1395
|
if (reloadError != nil) {
|
|
1382
1396
|
PushyRejectError(reject, reloadError);
|
|
1383
1397
|
return;
|
|
@@ -1388,7 +1402,7 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
|
|
|
1388
1402
|
RCT_EXPORT_METHOD(restartApp:(RCTPromiseResolveBlock)resolve
|
|
1389
1403
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
1390
1404
|
{
|
|
1391
|
-
NSError *reloadError = [self
|
|
1405
|
+
NSError *reloadError = [self restartBridgeWithReason:@"pushy restartApp"];
|
|
1392
1406
|
if (reloadError != nil) {
|
|
1393
1407
|
PushyRejectError(reject, reloadError);
|
|
1394
1408
|
return;
|
|
@@ -1499,7 +1513,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1499
1513
|
// promise settles here, after the cleanup, so a failed wipe reaches
|
|
1500
1514
|
// JS as RESET_FAILED (Android parity) instead of a warning nobody
|
|
1501
1515
|
// aggregates.
|
|
1502
|
-
pushy::
|
|
1516
|
+
pushy::delta::Status status = pushy::delta::CleanupOldEntries(
|
|
1503
1517
|
PushyToStdString([RCTPushy downloadDir]),
|
|
1504
1518
|
std::vector<std::string>{PushyToStdString(keepVersion)},
|
|
1505
1519
|
0
|
|
@@ -1533,12 +1547,12 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1533
1547
|
hasListeners = NO;
|
|
1534
1548
|
}
|
|
1535
1549
|
|
|
1536
|
-
- (void)
|
|
1550
|
+
- (void)fetchPackage:(PushyType)type
|
|
1537
1551
|
options:(NSDictionary *)options
|
|
1538
1552
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
1539
1553
|
rejecter:(RCTPromiseRejectBlock)reject
|
|
1540
1554
|
{
|
|
1541
|
-
[self
|
|
1555
|
+
[self performFetch:type options:options callback:^(NSError *error) {
|
|
1542
1556
|
if (error != nil) {
|
|
1543
1557
|
if (error.userInfo[PushyErrorCodeKey] == nil) {
|
|
1544
1558
|
// Unclassified (system/network) errors from the download
|
|
@@ -1560,7 +1574,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1560
1574
|
// here: it consumes one-shot launch state (first_time, the first-load mark,
|
|
1561
1575
|
// ignoreRollback), and consuming it for a reload that never happens would
|
|
1562
1576
|
// make the next cold start roll the freshly switched version back.
|
|
1563
|
-
- (NSError *)
|
|
1577
|
+
- (NSError *)restartBridgeWithReason:(NSString *)reason
|
|
1564
1578
|
{
|
|
1565
1579
|
#if PUSHY_HAS_RELOAD_COMMAND
|
|
1566
1580
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -1575,7 +1589,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1575
1589
|
RCTBridge *bridge = self.bridge;
|
|
1576
1590
|
if (bridge == nil || object_getClass(bridge) == NSClassFromString(@"RCTBridgeProxy")) {
|
|
1577
1591
|
return PushyErrorWithCode(pushy::error_codes::kRestartFailed,
|
|
1578
|
-
@"no bridge available to
|
|
1592
|
+
@"no bridge available to restart (bridgeless host without the RN restart command)");
|
|
1579
1593
|
}
|
|
1580
1594
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1581
1595
|
[bridge reload];
|
|
@@ -1584,9 +1598,9 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1584
1598
|
#endif
|
|
1585
1599
|
}
|
|
1586
1600
|
|
|
1587
|
-
- (void)
|
|
1601
|
+
- (void)performFetch:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
|
|
1588
1602
|
{
|
|
1589
|
-
NSString *updateUrl = PushyOptionString(options,
|
|
1603
|
+
NSString *updateUrl = PushyOptionString(options, PushyOptionUrlKey);
|
|
1590
1604
|
NSString *hash = PushyOptionString(options, @"hash");
|
|
1591
1605
|
|
|
1592
1606
|
if (PushyStringIsBlank(updateUrl) || !PushyIsSafePathComponent(hash)) {
|
|
@@ -1632,7 +1646,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1632
1646
|
}
|
|
1633
1647
|
};
|
|
1634
1648
|
void (^deferredStart)(void) = ^{
|
|
1635
|
-
[self
|
|
1649
|
+
[self performFetch:type options:options callback:callback];
|
|
1636
1650
|
};
|
|
1637
1651
|
PushyDownloadRegistration registration = PushyRegisterDownload(
|
|
1638
1652
|
hash, type, deadlineUptime, callback, progress, deferredStart);
|
|
@@ -1669,7 +1683,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1669
1683
|
// version directory in one atomic step.
|
|
1670
1684
|
NSString *bundlePath = [staging stringByAppendingPathComponent:BUNDLE_FILE_NAME];
|
|
1671
1685
|
if (![fileManager fileExistsAtPath:bundlePath]) {
|
|
1672
|
-
finalError = PushyErrorWithCode(pushy::error_codes::
|
|
1686
|
+
finalError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
|
|
1673
1687
|
@"bundle missing after install");
|
|
1674
1688
|
} else {
|
|
1675
1689
|
std::string bundleSha256 = pushy::digest::Sha256File(PushyToStdString(bundlePath));
|
|
@@ -1768,8 +1782,8 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1768
1782
|
} @catch (NSException *exception) {
|
|
1769
1783
|
// An uncaught exception in a GCD block is fatal (and would
|
|
1770
1784
|
// trip the crash rescue); a bad package is a PATCH_FAILED.
|
|
1771
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1772
|
-
exception.reason ?: @"
|
|
1785
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
|
|
1786
|
+
exception.reason ?: @"delta failed"));
|
|
1773
1787
|
}
|
|
1774
1788
|
});
|
|
1775
1789
|
}];
|
|
@@ -1782,7 +1796,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1782
1796
|
{
|
|
1783
1797
|
switch (type) {
|
|
1784
1798
|
case PushyTypePatchFromPackage:
|
|
1785
|
-
[self
|
|
1799
|
+
[self applyDeltaForHash:hash
|
|
1786
1800
|
type:type
|
|
1787
1801
|
fromBundle:[[RCTPushy binaryBundleURL] path]
|
|
1788
1802
|
source:[[NSBundle mainBundle] resourcePath]
|
|
@@ -1790,7 +1804,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1790
1804
|
return;
|
|
1791
1805
|
case PushyTypePatchFromPpk: {
|
|
1792
1806
|
NSString *lastVersionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:originHash];
|
|
1793
|
-
[self
|
|
1807
|
+
[self applyDeltaForHash:hash
|
|
1794
1808
|
type:type
|
|
1795
1809
|
fromBundle:[lastVersionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]
|
|
1796
1810
|
source:lastVersionDir
|
|
@@ -1803,7 +1817,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1803
1817
|
}
|
|
1804
1818
|
}
|
|
1805
1819
|
|
|
1806
|
-
- (void)
|
|
1820
|
+
- (void)applyDeltaForHash:(NSString *)hash
|
|
1807
1821
|
type:(PushyType)type
|
|
1808
1822
|
fromBundle:(NSString *)bundleOrigin
|
|
1809
1823
|
source:(NSString *)sourceOrigin
|
|
@@ -1818,13 +1832,13 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1818
1832
|
NSString *destination = [unzipDir stringByAppendingPathComponent:BUNDLE_FILE_NAME];
|
|
1819
1833
|
long long manifestBytes = RCTPushyFileSize(sourcePatch);
|
|
1820
1834
|
if (manifestBytes > pushy::archive_limits::kMaxManifestBytes) {
|
|
1821
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1822
|
-
[NSString stringWithFormat:@"
|
|
1835
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
|
|
1836
|
+
[NSString stringWithFormat:@"delta manifest too large: %lld bytes", manifestBytes]));
|
|
1823
1837
|
return;
|
|
1824
1838
|
}
|
|
1825
1839
|
NSData *data = [NSData dataWithContentsOfFile:sourcePatch];
|
|
1826
1840
|
if (data == nil) {
|
|
1827
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1841
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"missing delta manifest"));
|
|
1828
1842
|
return;
|
|
1829
1843
|
}
|
|
1830
1844
|
|
|
@@ -1833,19 +1847,19 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1833
1847
|
if (error != nil) {
|
|
1834
1848
|
// Classify as a patch failure like the sibling manifest branches;
|
|
1835
1849
|
// unclassified errors would otherwise be tagged DOWNLOAD_FAILED by the
|
|
1836
|
-
//
|
|
1837
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1850
|
+
// fetchPackage fallback even though the download itself succeeded.
|
|
1851
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, error.localizedDescription));
|
|
1838
1852
|
return;
|
|
1839
1853
|
}
|
|
1840
1854
|
if (![jsonObject isKindOfClass:[NSDictionary class]]) {
|
|
1841
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1855
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"invalid delta manifest"));
|
|
1842
1856
|
return;
|
|
1843
1857
|
}
|
|
1844
1858
|
NSDictionary *json = (NSDictionary *)jsonObject;
|
|
1845
|
-
pushy::
|
|
1859
|
+
pushy::delta::PatchManifest manifest;
|
|
1846
1860
|
NSString *manifestReason = nil;
|
|
1847
1861
|
if (!PushyPatchManifestFromJson(json, &manifest, &manifestReason)) {
|
|
1848
|
-
callback(PushyErrorWithCode(pushy::error_codes::
|
|
1862
|
+
callback(PushyErrorWithCode(pushy::error_codes::kDeltaFailed, manifestReason));
|
|
1849
1863
|
return;
|
|
1850
1864
|
}
|
|
1851
1865
|
|
|
@@ -1858,7 +1872,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1858
1872
|
}
|
|
1859
1873
|
|
|
1860
1874
|
pushy::archive_patch::ArchivePatchPlan plan;
|
|
1861
|
-
pushy::
|
|
1875
|
+
pushy::delta::Status planStatus = pushy::archive_patch::BuildArchivePatchPlan(
|
|
1862
1876
|
type == PushyTypePatchFromPackage
|
|
1863
1877
|
? pushy::archive_patch::ArchivePatchType::kPatchFromPackage
|
|
1864
1878
|
: pushy::archive_patch::ArchivePatchType::kPatchFromPpk,
|
|
@@ -1871,8 +1885,8 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1871
1885
|
return;
|
|
1872
1886
|
}
|
|
1873
1887
|
|
|
1874
|
-
pushy::
|
|
1875
|
-
pushy::
|
|
1888
|
+
pushy::delta::FileSourcePatchOptions options;
|
|
1889
|
+
pushy::delta::Status optionStatus = pushy::archive_patch::BuildFileSourcePatchOptions(
|
|
1876
1890
|
plan,
|
|
1877
1891
|
PushyToStdString(sourceOrigin),
|
|
1878
1892
|
PushyToStdString(unzipDir),
|
|
@@ -1901,7 +1915,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
1901
1915
|
}
|
|
1902
1916
|
}
|
|
1903
1917
|
|
|
1904
|
-
pushy::
|
|
1918
|
+
pushy::delta::Status status = pushy::delta::ApplyPatchFromFileSource(options);
|
|
1905
1919
|
if (!status.ok) {
|
|
1906
1920
|
callback(PushyNSErrorFromStatus(status));
|
|
1907
1921
|
return;
|
|
@@ -2025,7 +2039,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
2025
2039
|
long long archiveBytes = RCTPushyFileSize(path);
|
|
2026
2040
|
NSError *preflight = nil;
|
|
2027
2041
|
if (archiveBytes > pushy::archive_limits::kMaxArchiveBytes) {
|
|
2028
|
-
preflight = PushyErrorWithCode(pushy::error_codes::
|
|
2042
|
+
preflight = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
|
|
2029
2043
|
[NSString stringWithFormat:@"archive too large: %lld bytes", archiveBytes]);
|
|
2030
2044
|
} else {
|
|
2031
2045
|
preflight = PushyEnsureFreeSpace(destination, MAX(0LL, archiveBytes) * 2);
|
|
@@ -2063,15 +2077,15 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
2063
2077
|
|
|
2064
2078
|
NSError *unzipError = error;
|
|
2065
2079
|
if (guard.violation != nil) {
|
|
2066
|
-
unzipError = PushyErrorWithCode(pushy::error_codes::
|
|
2080
|
+
unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed, guard.violation);
|
|
2067
2081
|
} else if (!succeeded && unzipError == nil) {
|
|
2068
|
-
unzipError = PushyErrorWithCode(pushy::error_codes::
|
|
2082
|
+
unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed, @"unzip failed");
|
|
2069
2083
|
} else if (unzipError != nil && unzipError.userInfo[PushyErrorCodeKey] == nil) {
|
|
2070
2084
|
// SSZipArchive's own NSError (corrupt zip, bad magic, ...) has
|
|
2071
|
-
// no stable code; without one,
|
|
2085
|
+
// no stable code; without one, fetchPackage's fallback would
|
|
2072
2086
|
// classify it as DOWNLOAD_FAILED even though the download
|
|
2073
2087
|
// succeeded — keep the classification deterministic.
|
|
2074
|
-
unzipError = PushyErrorWithCode(pushy::error_codes::
|
|
2088
|
+
unzipError = PushyErrorWithCode(pushy::error_codes::kDeltaFailed,
|
|
2075
2089
|
unzipError.localizedDescription ?: @"unzip failed");
|
|
2076
2090
|
}
|
|
2077
2091
|
completionHandler(unzipError);
|
|
@@ -2095,7 +2109,7 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
2095
2109
|
// process booted from: two switches without a restart would otherwise
|
|
2096
2110
|
// evict the running bundle while its on-demand assets are still
|
|
2097
2111
|
// being served.
|
|
2098
|
-
pushy::
|
|
2112
|
+
pushy::delta::Status status = pushy::delta::CleanupOldEntries(
|
|
2099
2113
|
PushyToStdString(downloadDir),
|
|
2100
2114
|
std::vector<std::string>{
|
|
2101
2115
|
state.current_version,
|
|
@@ -2114,9 +2128,9 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
2114
2128
|
{
|
|
2115
2129
|
switch (type) {
|
|
2116
2130
|
case PushyTypePatchFromPackage:
|
|
2117
|
-
return
|
|
2131
|
+
return PushyIpaDeltaSuffix;
|
|
2118
2132
|
case PushyTypePatchFromPpk:
|
|
2119
|
-
return
|
|
2133
|
+
return PushyPpkDeltaSuffix;
|
|
2120
2134
|
case PushyTypeFullDownload:
|
|
2121
2135
|
break;
|
|
2122
2136
|
}
|
|
@@ -2210,10 +2224,10 @@ RCT_EXPORT_METHOD(resetToPackagedBundle:(RCTPromiseResolveBlock)resolve
|
|
|
2210
2224
|
// NSAllowsArbitraryLoads. Same rule as RCTPushyDownloader. Returning nil
|
|
2211
2225
|
// delivers the 3xx itself as the final response, which the status check in
|
|
2212
2226
|
// PushyHttpRequest then treats as a failed endpoint.
|
|
2213
|
-
@interface
|
|
2227
|
+
@interface PushyRequestRedirectGuard : NSObject <NSURLSessionTaskDelegate>
|
|
2214
2228
|
@end
|
|
2215
2229
|
|
|
2216
|
-
@implementation
|
|
2230
|
+
@implementation PushyRequestRedirectGuard
|
|
2217
2231
|
|
|
2218
2232
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
|
|
2219
2233
|
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
|
@@ -2255,7 +2269,7 @@ static NSString *PushyHttpRequest(NSString *urlString, NSString *method,
|
|
|
2255
2269
|
// final response.
|
|
2256
2270
|
NSURLSession *session = [NSURLSession
|
|
2257
2271
|
sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
|
|
2258
|
-
delegate:[
|
|
2272
|
+
delegate:[PushyRequestRedirectGuard new]
|
|
2259
2273
|
delegateQueue:nil];
|
|
2260
2274
|
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
|
2261
2275
|
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
@@ -2290,13 +2304,13 @@ static NSString *PushyNormalizeEndpointBase(NSString *base) {
|
|
|
2290
2304
|
return base;
|
|
2291
2305
|
}
|
|
2292
2306
|
|
|
2293
|
-
static BOOL
|
|
2307
|
+
static BOOL PushyIsValidResponse(NSString *responseText) {
|
|
2294
2308
|
if (responseText == nil) {
|
|
2295
2309
|
return NO;
|
|
2296
2310
|
}
|
|
2297
|
-
// Shared schema rule (update_flow_core::
|
|
2311
|
+
// Shared schema rule (update_flow_core::IsValidResponse): a 200 with
|
|
2298
2312
|
// `{"error": ...}` is a failed endpoint, not a verdict.
|
|
2299
|
-
return
|
|
2313
|
+
return flowcore::IsValidResponse(PushyToStdString(responseText)) ? YES : NO;
|
|
2300
2314
|
}
|
|
2301
2315
|
|
|
2302
2316
|
@implementation RCTPushyOrchestrator
|
|
@@ -2316,10 +2330,10 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2316
2330
|
pushyNativeConfigGeneration.fetch_add(1);
|
|
2317
2331
|
[defaults setObject:config forKey:keyNativeConfig];
|
|
2318
2332
|
[defaults removeObjectForKey:keyNativeCheckCache];
|
|
2319
|
-
[self
|
|
2333
|
+
[self recordJsRound:nil];
|
|
2320
2334
|
});
|
|
2321
|
-
if (
|
|
2322
|
-
|
|
2335
|
+
if (pushySyncReady.load() && [self hasRunnableConfig]) {
|
|
2336
|
+
PushyInstallCrashHoldHandler();
|
|
2323
2337
|
}
|
|
2324
2338
|
}
|
|
2325
2339
|
|
|
@@ -2343,12 +2357,12 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2343
2357
|
pushyHostRoundGroup = dispatch_group_create();
|
|
2344
2358
|
dispatch_group_enter(pushyHostRoundGroup);
|
|
2345
2359
|
pushyProcessAnchorUptime = PushyMonotonicNow();
|
|
2346
|
-
|
|
2347
|
-
|
|
2360
|
+
pushyLaunchRolledBackForHold = [launchRolledBackVersion copy];
|
|
2361
|
+
pushySyncReady.store(true);
|
|
2348
2362
|
// The crash-hold rescue shares the orchestrator's rollout gate: no
|
|
2349
2363
|
// persisted config, no handler (§11.3).
|
|
2350
2364
|
if ([PushyDefaults() stringForKey:keyNativeConfig].length > 0) {
|
|
2351
|
-
|
|
2365
|
+
PushyInstallCrashHoldHandler();
|
|
2352
2366
|
}
|
|
2353
2367
|
});
|
|
2354
2368
|
}
|
|
@@ -2367,10 +2381,10 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2367
2381
|
[defaults objectForKey:keyNativeCheckIncomplete] != nil ? 0 : 5;
|
|
2368
2382
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delaySeconds * NSEC_PER_SEC),
|
|
2369
2383
|
dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
|
2370
|
-
if ([self
|
|
2384
|
+
if ([self hasJsRound]) {
|
|
2371
2385
|
// Not consuming the round: a later crash rescue may still
|
|
2372
2386
|
// need it.
|
|
2373
|
-
NSLog(@"RCTPushy -- native
|
|
2387
|
+
NSLog(@"RCTPushy -- native sync skipped: JS already queried in this process");
|
|
2374
2388
|
return;
|
|
2375
2389
|
}
|
|
2376
2390
|
[self startRoundWithDeadline:0];
|
|
@@ -2392,7 +2406,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2392
2406
|
rolledBack:(NSString *)launchRolledBackVersion {
|
|
2393
2407
|
[self prepareProcess:launchRolledBackVersion];
|
|
2394
2408
|
if (![self hasRunnableConfig]) {
|
|
2395
|
-
RCTLogWarn(@"RCTPushy -- version %@ was purged and no native
|
|
2409
|
+
RCTLogWarn(@"RCTPushy -- version %@ was purged and no native sync is "
|
|
2396
2410
|
@"configured; launching the packaged bundle", purgedVersion);
|
|
2397
2411
|
return NO;
|
|
2398
2412
|
}
|
|
@@ -2429,7 +2443,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2429
2443
|
#if DEBUG
|
|
2430
2444
|
return PushyHostResult(@"skipped", @"debug", nil, NO);
|
|
2431
2445
|
#else
|
|
2432
|
-
if (!
|
|
2446
|
+
if (!pushySyncReady.load()) {
|
|
2433
2447
|
return PushyHostResult(@"skipped", @"not_initialized", nil, NO);
|
|
2434
2448
|
}
|
|
2435
2449
|
NSString *configJson = [PushyDefaults() stringForKey:keyNativeConfig];
|
|
@@ -2468,7 +2482,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2468
2482
|
#endif
|
|
2469
2483
|
}
|
|
2470
2484
|
|
|
2471
|
-
+ (void)
|
|
2485
|
+
+ (void)recordJsRound:(NSString *)config {
|
|
2472
2486
|
@synchronized (RCTPushyOrchestrator.class) {
|
|
2473
2487
|
pushyJsCompletedConfig = [config copy];
|
|
2474
2488
|
}
|
|
@@ -2478,7 +2492,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2478
2492
|
// exact config the native round would use (§10.3): the delayed round is
|
|
2479
2493
|
// then a duplicate request. Only the scheduled round asks — the crash-rescue
|
|
2480
2494
|
// path still runs, JS is dead by then.
|
|
2481
|
-
+ (BOOL)
|
|
2495
|
+
+ (BOOL)hasJsRound {
|
|
2482
2496
|
NSString *jsConfig;
|
|
2483
2497
|
@synchronized (RCTPushyOrchestrator.class) {
|
|
2484
2498
|
jsConfig = pushyJsCompletedConfig;
|
|
@@ -2503,10 +2517,10 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2503
2517
|
return;
|
|
2504
2518
|
}
|
|
2505
2519
|
@try {
|
|
2506
|
-
[self runOnce:
|
|
2520
|
+
[self runOnce:pushyLaunchRolledBackForHold deadline:deadlineUptime];
|
|
2507
2521
|
} @catch (NSException *exception) {
|
|
2508
2522
|
// The rescue path must never take the app down with it.
|
|
2509
|
-
RCTLogWarn(@"RCTPushy -- native
|
|
2523
|
+
RCTLogWarn(@"RCTPushy -- native sync crashed: %@", exception.reason);
|
|
2510
2524
|
pushyHostRoundResult = PushyHostResult(@"failed", @"internal_error", nil, NO);
|
|
2511
2525
|
} @finally {
|
|
2512
2526
|
pushyRoundCompleted.store(true);
|
|
@@ -2520,8 +2534,8 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2520
2534
|
// process's round runs to completion within the budget, then activates a
|
|
2521
2535
|
// downloaded-but-unactivated version if one exists — the last chance before
|
|
2522
2536
|
// the process is gone.
|
|
2523
|
-
+ (void)
|
|
2524
|
-
|
|
2537
|
+
+ (void)runHoldRoundWithDeadline:(NSTimeInterval)deadlineUptime {
|
|
2538
|
+
pushyCrashHoldActive.store(true);
|
|
2525
2539
|
[self startRoundWithDeadline:deadlineUptime];
|
|
2526
2540
|
if (pushyRoundStarted.load() && !pushyRoundCompleted.load()) {
|
|
2527
2541
|
NSTimeInterval remaining = deadlineUptime - PushyMonotonicNow();
|
|
@@ -2552,7 +2566,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2552
2566
|
// catch below anyway, or by cleanup).
|
|
2553
2567
|
NSString *versionDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash];
|
|
2554
2568
|
if (!PushyHasCompletedVersionAtPath(versionDir, hash)) {
|
|
2555
|
-
NSLog(@"RCTPushy -- crash
|
|
2569
|
+
NSLog(@"RCTPushy -- crash hold: version %@ no longer on disk, dropping activation", hash);
|
|
2556
2570
|
return;
|
|
2557
2571
|
}
|
|
2558
2572
|
NSDictionary *hashInfoEntry = nil;
|
|
@@ -2562,7 +2576,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2562
2576
|
[NSJSONSerialization JSONObjectWithData:existingData options:0 error:nil];
|
|
2563
2577
|
if ([parsed isKindOfClass:[NSDictionary class]]) {
|
|
2564
2578
|
NSMutableDictionary *merged = [parsed mutableCopy];
|
|
2565
|
-
merged[
|
|
2579
|
+
merged[PushyInfoCrashHold] = @YES;
|
|
2566
2580
|
hashInfoEntry = @{@"hash": hash, @"info": merged};
|
|
2567
2581
|
}
|
|
2568
2582
|
BOOL committed = [self commitRoundWithGeneration:generation
|
|
@@ -2577,9 +2591,9 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2577
2591
|
@synchronized (self) {
|
|
2578
2592
|
pushyUnactivatedHash = nil;
|
|
2579
2593
|
}
|
|
2580
|
-
NSLog(@"RCTPushy -- crash
|
|
2594
|
+
NSLog(@"RCTPushy -- crash hold: activated downloaded version %@", hash);
|
|
2581
2595
|
} else {
|
|
2582
|
-
NSLog(@"RCTPushy -- crash
|
|
2596
|
+
NSLog(@"RCTPushy -- crash hold: reset since download, dropping activation");
|
|
2583
2597
|
}
|
|
2584
2598
|
}
|
|
2585
2599
|
|
|
@@ -2691,28 +2705,28 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2691
2705
|
// shared with JS getBundleHash) on the ordinary delayed round.
|
|
2692
2706
|
input.Set("bundleHash",
|
|
2693
2707
|
flowjson::Value::String(PushyToStdString(
|
|
2694
|
-
PushyBundleHashSync(!
|
|
2708
|
+
PushyBundleHashSync(!pushyCrashHoldActive.load()))));
|
|
2695
2709
|
|
|
2696
2710
|
std::string bodyJson =
|
|
2697
|
-
flowjson::Stringify(
|
|
2711
|
+
flowjson::Stringify(flowcore::BuildRequestBody(input));
|
|
2698
2712
|
NSString *body = [NSString stringWithUTF8String:bodyJson.c_str()];
|
|
2699
2713
|
if (body == nil) {
|
|
2700
|
-
RCTLogWarn(@"RCTPushy -- native
|
|
2714
|
+
RCTLogWarn(@"RCTPushy -- native sync: request body is not valid UTF-8");
|
|
2701
2715
|
pushyHostRoundResult = PushyHostResult(@"failed", @"invalid_request", nil, NO);
|
|
2702
2716
|
return;
|
|
2703
2717
|
}
|
|
2704
2718
|
|
|
2705
|
-
NSString *responseText = [self
|
|
2719
|
+
NSString *responseText = [self runQueryRequest:config
|
|
2706
2720
|
appKey:appKey
|
|
2707
2721
|
body:body
|
|
2708
2722
|
deadline:deadlineUptime];
|
|
2709
2723
|
if (responseText == nil) {
|
|
2710
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
2724
|
+
RCTLogInfo(@"RCTPushy -- native sync: no endpoint reachable, giving up until next launch");
|
|
2711
2725
|
return;
|
|
2712
2726
|
}
|
|
2713
2727
|
long long responseAtSeconds = (long long)[[NSDate date] timeIntervalSince1970];
|
|
2714
2728
|
|
|
2715
|
-
flowjson::Value decision =
|
|
2729
|
+
flowjson::Value decision = flowcore::HandleResponse(
|
|
2716
2730
|
PushyToStdString(responseText), identity, false,
|
|
2717
2731
|
config.Get("afterDownload").AsString());
|
|
2718
2732
|
if (decision.Get("action").AsString() != "download") {
|
|
@@ -2725,9 +2739,9 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2725
2739
|
responseAt:responseAtSeconds
|
|
2726
2740
|
activated:NULL];
|
|
2727
2741
|
pushyHostRoundResult = committed
|
|
2728
|
-
? PushyHostResult(
|
|
2742
|
+
? PushyHostResult(PushyStatusNone, PushyFromStdString(decision.Get("reason").AsString()), nil, NO)
|
|
2729
2743
|
: PushyHostResult(@"cancelled", @"reset", nil, NO);
|
|
2730
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
2744
|
+
RCTLogInfo(@"RCTPushy -- native sync: nothing to do (%s)",
|
|
2731
2745
|
decision.Get("reason").AsString().c_str());
|
|
2732
2746
|
return;
|
|
2733
2747
|
}
|
|
@@ -2774,10 +2788,10 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2774
2788
|
// survives to markSuccess. Only the server-sent directive counts — a
|
|
2775
2789
|
// silent-strategy activation is ordinary delivery.
|
|
2776
2790
|
if (info.Get("config").Get("forceBoot").Truthy()) {
|
|
2777
|
-
versionInfo[
|
|
2791
|
+
versionInfo[PushyInfoForceBoot] = @YES;
|
|
2778
2792
|
}
|
|
2779
|
-
if (
|
|
2780
|
-
versionInfo[
|
|
2793
|
+
if (pushyCrashHoldActive.load()) {
|
|
2794
|
+
versionInfo[PushyInfoCrashHold] = @YES;
|
|
2781
2795
|
}
|
|
2782
2796
|
// Silent strategies or a server-marked forceBoot version (per-version
|
|
2783
2797
|
// remote override — the brick rescue) activate for the next launch;
|
|
@@ -2786,7 +2800,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2786
2800
|
// forever (§11.3). Or the launch is blocked on reinstalling a version
|
|
2787
2801
|
// tvOS purged: JS has not started, and the whole point is to boot it —
|
|
2788
2802
|
// commitRoundWithGeneration drops that one once the restore window closed.
|
|
2789
|
-
BOOL activate = decision.Get("activate").Truthy() ||
|
|
2803
|
+
BOOL activate = decision.Get("activate").Truthy() || pushyCrashHoldActive.load()
|
|
2790
2804
|
|| pushyPurgeRestoreActive.load();
|
|
2791
2805
|
BOOL activated = NO;
|
|
2792
2806
|
BOOL committed = [self commitRoundWithGeneration:resetGeneration
|
|
@@ -2799,12 +2813,12 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2799
2813
|
activated:&activated];
|
|
2800
2814
|
activate = activated;
|
|
2801
2815
|
if (!committed) {
|
|
2802
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
2816
|
+
RCTLogInfo(@"RCTPushy -- native sync: reset during round, dropping result");
|
|
2803
2817
|
} else if (activate) {
|
|
2804
2818
|
@synchronized (self) {
|
|
2805
2819
|
pushyUnactivatedHash = nil;
|
|
2806
2820
|
}
|
|
2807
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
2821
|
+
RCTLogInfo(@"RCTPushy -- native sync: downloaded %@ and set for next launch", hash);
|
|
2808
2822
|
} else {
|
|
2809
2823
|
// Remembered so a crash later in this process can still activate it
|
|
2810
2824
|
// (activatePendingVersion) — JS never will.
|
|
@@ -2812,7 +2826,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2812
2826
|
pushyUnactivatedHash = [hash copy];
|
|
2813
2827
|
pushyUnactivatedGeneration = resetGeneration;
|
|
2814
2828
|
}
|
|
2815
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
2829
|
+
RCTLogInfo(@"RCTPushy -- native sync: downloaded %@, activation left to JS", hash);
|
|
2816
2830
|
}
|
|
2817
2831
|
pushyHostRoundResult = committed
|
|
2818
2832
|
? PushyHostResult(@"downloaded", @"", hash, activate)
|
|
@@ -2863,7 +2877,7 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2863
2877
|
marked[@"purgeRestore"] = @YES;
|
|
2864
2878
|
info = marked;
|
|
2865
2879
|
}
|
|
2866
|
-
} else if (!
|
|
2880
|
+
} else if (!pushyCrashHoldActive.load()) {
|
|
2867
2881
|
activation = nil;
|
|
2868
2882
|
}
|
|
2869
2883
|
}
|
|
@@ -2899,20 +2913,20 @@ static BOOL PushyIsValidCheckResponse(NSString *responseText) {
|
|
|
2899
2913
|
// more round. No hedged race on purpose — this path is latency-insensitive.
|
|
2900
2914
|
// Per-request timeout, capped to the crash-rescue budget when one is active.
|
|
2901
2915
|
// <= 0 means the budget is gone and the round must stop issuing requests.
|
|
2902
|
-
static NSTimeInterval
|
|
2916
|
+
static NSTimeInterval PushyQueryRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
2903
2917
|
if (deadlineUptime <= 0) {
|
|
2904
2918
|
return 10;
|
|
2905
2919
|
}
|
|
2906
2920
|
return MIN(10, deadlineUptime - PushyMonotonicNow());
|
|
2907
2921
|
}
|
|
2908
2922
|
|
|
2909
|
-
+ (NSString *)
|
|
2923
|
+
+ (NSString *)runQueryRequest:(const flowjson::Value &)config
|
|
2910
2924
|
appKey:(NSString *)appKey
|
|
2911
2925
|
body:(NSString *)body
|
|
2912
2926
|
deadline:(NSTimeInterval)deadlineUptime {
|
|
2913
2927
|
double sample = arc4random() / 4294967296.0;
|
|
2914
2928
|
flowjson::Value ordered =
|
|
2915
|
-
|
|
2929
|
+
flowcore::OrderEndpointCandidates(config.Get("endpoints"), sample);
|
|
2916
2930
|
NSMutableSet<NSString *> *tried = [NSMutableSet set];
|
|
2917
2931
|
const NSUInteger maxHttpAttempts = 8;
|
|
2918
2932
|
NSUInteger httpAttempts = 0;
|
|
@@ -2926,14 +2940,14 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
2926
2940
|
return nil;
|
|
2927
2941
|
}
|
|
2928
2942
|
[tried addObject:base];
|
|
2929
|
-
NSTimeInterval timeout =
|
|
2943
|
+
NSTimeInterval timeout = PushyQueryRequestTimeout(deadlineUptime);
|
|
2930
2944
|
if (timeout <= 0) {
|
|
2931
2945
|
return nil;
|
|
2932
2946
|
}
|
|
2933
2947
|
NSString *response = PushyHttpRequest(
|
|
2934
|
-
[NSString stringWithFormat:@"
|
|
2948
|
+
[NSString stringWithFormat:@"%@%@%@", base, RCTPushyQueryPath(), appKey],
|
|
2935
2949
|
@"POST", body, timeout);
|
|
2936
|
-
if (
|
|
2950
|
+
if (PushyIsValidResponse(response)) {
|
|
2937
2951
|
return response;
|
|
2938
2952
|
}
|
|
2939
2953
|
}
|
|
@@ -2945,7 +2959,7 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
2945
2959
|
if (httpAttempts++ >= maxHttpAttempts) {
|
|
2946
2960
|
return nil;
|
|
2947
2961
|
}
|
|
2948
|
-
NSTimeInterval listTimeout =
|
|
2962
|
+
NSTimeInterval listTimeout = PushyQueryRequestTimeout(deadlineUptime);
|
|
2949
2963
|
if (listTimeout <= 0) {
|
|
2950
2964
|
return nil;
|
|
2951
2965
|
}
|
|
@@ -2968,14 +2982,14 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
2968
2982
|
return nil;
|
|
2969
2983
|
}
|
|
2970
2984
|
[tried addObject:base];
|
|
2971
|
-
NSTimeInterval timeout =
|
|
2985
|
+
NSTimeInterval timeout = PushyQueryRequestTimeout(deadlineUptime);
|
|
2972
2986
|
if (timeout <= 0) {
|
|
2973
2987
|
return nil;
|
|
2974
2988
|
}
|
|
2975
2989
|
NSString *response = PushyHttpRequest(
|
|
2976
|
-
[NSString stringWithFormat:@"
|
|
2990
|
+
[NSString stringWithFormat:@"%@%@%@", base, RCTPushyQueryPath(), appKey],
|
|
2977
2991
|
@"POST", body, timeout);
|
|
2978
|
-
if (
|
|
2992
|
+
if (PushyIsValidResponse(response)) {
|
|
2979
2993
|
return response;
|
|
2980
2994
|
}
|
|
2981
2995
|
}
|
|
@@ -2987,12 +3001,12 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
2987
3001
|
+ (BOOL)performAttempts:(const flowjson::Value &)attempts
|
|
2988
3002
|
hash:(NSString *)hash
|
|
2989
3003
|
originHash:(NSString *)originHash
|
|
2990
|
-
deadline:(NSTimeInterval)
|
|
3004
|
+
deadline:(NSTimeInterval)holdDeadline {
|
|
2991
3005
|
RCTPushy *engine = [self engine];
|
|
2992
3006
|
// Crash-rescue budget caps every phase; 0 keeps the normal 10min windows.
|
|
2993
3007
|
NSTimeInterval incrementalDeadline = PushyMonotonicNow() + 600;
|
|
2994
|
-
if (
|
|
2995
|
-
incrementalDeadline = MIN(incrementalDeadline,
|
|
3008
|
+
if (holdDeadline > 0) {
|
|
3009
|
+
incrementalDeadline = MIN(incrementalDeadline, holdDeadline);
|
|
2996
3010
|
}
|
|
2997
3011
|
NSTimeInterval fullDeadline = 0;
|
|
2998
3012
|
for (const auto &attempt : attempts.elements()) {
|
|
@@ -3007,8 +3021,8 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
3007
3021
|
if (isFullAttempt && fullDeadline == 0) {
|
|
3008
3022
|
// diff/pdiff cannot starve the last-resort full download.
|
|
3009
3023
|
fullDeadline = PushyMonotonicNow() + 600;
|
|
3010
|
-
if (
|
|
3011
|
-
fullDeadline = MIN(fullDeadline,
|
|
3024
|
+
if (holdDeadline > 0) {
|
|
3025
|
+
fullDeadline = MIN(fullDeadline, holdDeadline);
|
|
3012
3026
|
}
|
|
3013
3027
|
}
|
|
3014
3028
|
NSTimeInterval deadline = isFullAttempt ? fullDeadline : incrementalDeadline;
|
|
@@ -3026,7 +3040,7 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
3026
3040
|
}
|
|
3027
3041
|
NSMutableDictionary *options =
|
|
3028
3042
|
[@{
|
|
3029
|
-
|
|
3043
|
+
PushyOptionUrlKey: url,
|
|
3030
3044
|
@"hash": hash,
|
|
3031
3045
|
@"deadlineUptime": @(deadline),
|
|
3032
3046
|
} mutableCopy];
|
|
@@ -3035,13 +3049,13 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
3035
3049
|
}
|
|
3036
3050
|
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
|
3037
3051
|
__block NSError *resultError = nil;
|
|
3038
|
-
[engine
|
|
3052
|
+
[engine performFetch:pushyType options:options callback:^(NSError *error) {
|
|
3039
3053
|
resultError = error;
|
|
3040
3054
|
dispatch_semaphore_signal(sem);
|
|
3041
3055
|
}];
|
|
3042
3056
|
if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW,
|
|
3043
3057
|
(int64_t)(remaining * NSEC_PER_SEC))) != 0) {
|
|
3044
|
-
RCTLogWarn(@"RCTPushy -- native
|
|
3058
|
+
RCTLogWarn(@"RCTPushy -- native sync: %s attempt timed out", type.c_str());
|
|
3045
3059
|
if (isFullAttempt) {
|
|
3046
3060
|
return NO;
|
|
3047
3061
|
}
|
|
@@ -3050,7 +3064,7 @@ static NSTimeInterval PushyCheckRequestTimeout(NSTimeInterval deadlineUptime) {
|
|
|
3050
3064
|
if (resultError == nil) {
|
|
3051
3065
|
return YES;
|
|
3052
3066
|
}
|
|
3053
|
-
RCTLogInfo(@"RCTPushy -- native
|
|
3067
|
+
RCTLogInfo(@"RCTPushy -- native sync: %s attempt failed: %@",
|
|
3054
3068
|
type.c_str(), resultError.localizedDescription);
|
|
3055
3069
|
}
|
|
3056
3070
|
}
|