@onekeyfe/react-native-bundle-update 3.0.30 → 3.0.32
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/src/main/java/com/margelo/nitro/reactnativebundleupdate/ReactNativeBundleUpdate.kt
CHANGED
|
@@ -409,10 +409,12 @@ object BundleUpdateStoreAndroid {
|
|
|
409
409
|
/**
|
|
410
410
|
* Subtype of the most recent calculateSHA256 failure on this thread, or
|
|
411
411
|
* null if the last call succeeded. Surfaces the specific reason —
|
|
412
|
-
* FILE_NOT_FOUND /
|
|
413
|
-
* UNEXPECTED_<class> — so
|
|
414
|
-
*
|
|
415
|
-
* verifyPackage failures)
|
|
412
|
+
* FILE_NOT_FOUND / FILE_DISAPPEARED / FILE_TRUNCATED /
|
|
413
|
+
* PERMISSION_DENIED / OOM / IO_<class> / UNEXPECTED_<class> — so
|
|
414
|
+
* analytics can split the previously opaque "Failed to calculate
|
|
415
|
+
* SHA256" bucket (mixpanel: 91.3 percent of verifyPackage failures)
|
|
416
|
+
* into actionable categories. Keep this list in sync with the
|
|
417
|
+
* lastSHA256Failure.set(...) call sites in calculateSHA256 below.
|
|
416
418
|
*
|
|
417
419
|
* Note: 0-byte files are NOT treated as a failure. They hash to the
|
|
418
420
|
* well-known empty-content SHA-256 and the caller's expected/actual
|
|
@@ -1487,7 +1489,14 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
|
|
|
1487
1489
|
partialBytes = 0L
|
|
1488
1490
|
}
|
|
1489
1491
|
|
|
1490
|
-
|
|
1492
|
+
// Close the response before throwing on a null body — OkHttp
|
|
1493
|
+
// holds connection resources on the response wrapper itself,
|
|
1494
|
+
// and `throw` here exits the function before any byteStream()
|
|
1495
|
+
// consumption would close it for us.
|
|
1496
|
+
val body = response.body ?: run {
|
|
1497
|
+
response.close()
|
|
1498
|
+
throw Exception("Empty response body")
|
|
1499
|
+
}
|
|
1491
1500
|
val contentLength = body.contentLength()
|
|
1492
1501
|
// Total size of the whole resource (not the slice). On 206 prefer
|
|
1493
1502
|
// Content-Range's "/total" tail; fall back to partial+contentLength.
|
|
@@ -1576,8 +1585,16 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
|
|
|
1576
1585
|
// matches the verbatim guarantees documented on
|
|
1577
1586
|
// sanitizeErrorMessageForEvent.
|
|
1578
1587
|
OneKeyLog.error("BundleUpdate", "downloadBundle: failed: ${e.javaClass.simpleName}: ${e.message}")
|
|
1579
|
-
|
|
1580
|
-
|
|
1588
|
+
val sanitized = sanitizeErrorMessageForEvent(e)
|
|
1589
|
+
sendEvent("update/error", message = sanitized)
|
|
1590
|
+
// Rethrow with the same sanitized message so the Promise
|
|
1591
|
+
// rejection surfacing to JS carries no /data/user/<u>/<pkg>/
|
|
1592
|
+
// paths. Without this rewrap, FileNotFoundException etc.
|
|
1593
|
+
// would re-leak via Promise.reject's message channel even
|
|
1594
|
+
// though the event payload was already sanitized. Keep
|
|
1595
|
+
// the original exception as `cause` so OneKeyLog (and any
|
|
1596
|
+
// native crash reporter) still sees the full chain.
|
|
1597
|
+
throw Exception(sanitized, e)
|
|
1581
1598
|
} finally {
|
|
1582
1599
|
isDownloading.set(false)
|
|
1583
1600
|
}
|
|
@@ -315,11 +315,15 @@ public class BundleUpdateStore: NSObject {
|
|
|
315
315
|
|
|
316
316
|
/// Subtype of the most recent calculateSHA256 failure on this thread, or
|
|
317
317
|
/// nil if the last call succeeded. Surfaces FILE_NOT_FOUND /
|
|
318
|
-
/// FILE_DISAPPEARED / IO_<NSError code>
|
|
319
|
-
///
|
|
320
|
-
///
|
|
321
|
-
///
|
|
322
|
-
///
|
|
318
|
+
/// FILE_DISAPPEARED / IO_<NSError code> so analytics can split the
|
|
319
|
+
/// previously opaque "Failed to calculate SHA256" bucket (mixpanel:
|
|
320
|
+
/// 91.3% of verifyPackage failures are Android; iOS shares the
|
|
321
|
+
/// calculator and inherits the same blind spot for its 14 ASC + 2
|
|
322
|
+
/// verifyPackage Promise-destroyed cases). Keep this list in sync
|
|
323
|
+
/// with the setSHA256Failure(...) call sites in calculateSHA256
|
|
324
|
+
/// below — Android's wider taxonomy (FILE_TRUNCATED / OOM /
|
|
325
|
+
/// UNEXPECTED_<class>) does not apply here because the iOS reader
|
|
326
|
+
/// surfaces every disk error as a single `IO_<NSError code>`.
|
|
323
327
|
///
|
|
324
328
|
/// Note: 0-byte files are NOT treated as a failure. They hash to the
|
|
325
329
|
/// well-known empty-content SHA256 and the caller's expected/actual
|
|
@@ -359,9 +363,15 @@ public class BundleUpdateStore: NSObject {
|
|
|
359
363
|
var context = CC_SHA256_CTX()
|
|
360
364
|
CC_SHA256_Init(&context)
|
|
361
365
|
var threwError: Error?
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
366
|
+
// safeRead routes reads through FileHandle.read(upToCount:) on
|
|
367
|
+
// iOS 13.4+ — that variant surfaces disk failures as throwing
|
|
368
|
+
// NSError, which we catch here as Swift `Error`. On pre-13.4
|
|
369
|
+
// OS versions safeRead falls back to readData(ofLength:),
|
|
370
|
+
// which raises NSFileHandleOperationException; Swift cannot
|
|
371
|
+
// catch ObjC NSExceptions via try/catch, so on those legacy
|
|
372
|
+
// versions a read failure will still abort the process. We
|
|
373
|
+
// accept that on the floor since 13.4+ has been the deployment
|
|
374
|
+
// target for years.
|
|
365
375
|
while autoreleasepool(invoking: {
|
|
366
376
|
do {
|
|
367
377
|
let data = try Self.safeRead(fileHandle: fileHandle, length: 8192)
|
|
@@ -1283,9 +1293,14 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
|
|
|
1283
1293
|
private func snapshotResumeDataForBackgrounding() {
|
|
1284
1294
|
guard let session = self.urlSession else { return }
|
|
1285
1295
|
// Read both isDownloading AND activeDownloadFilePath inside the
|
|
1286
|
-
// same stateQueue.sync block
|
|
1287
|
-
//
|
|
1288
|
-
//
|
|
1296
|
+
// same stateQueue.sync block. A concurrent downloadBundle entry
|
|
1297
|
+
// flips isDownloading=true at the start of the Promise body but
|
|
1298
|
+
// only writes activeDownloadFilePath later (after the early
|
|
1299
|
+
// guards / version + URL validation). Without this paired read,
|
|
1300
|
+
// didEnterBackground could observe isDownloading=true while
|
|
1301
|
+
// activeDownloadFilePath is still nil from a previous run, or —
|
|
1302
|
+
// on the unwind via defer — see isDownloading=false alongside a
|
|
1303
|
+
// not-yet-cleared path.
|
|
1289
1304
|
let snapshot: (Bool, String?) = self.stateQueue.sync {
|
|
1290
1305
|
(self.isDownloading, self.activeDownloadFilePath)
|
|
1291
1306
|
}
|
|
@@ -1542,7 +1557,19 @@ class ReactNativeBundleUpdate: HybridReactNativeBundleUpdateSpec {
|
|
|
1542
1557
|
let nsError = error as NSError
|
|
1543
1558
|
OneKeyLog.error("BundleUpdate", "downloadBundle: download failed: \(nsError.domain) \(nsError.code) \(nsError.localizedDescription)")
|
|
1544
1559
|
self.sendEvent(type: "update/error", message: "\(nsError.domain) \(nsError.code)")
|
|
1545
|
-
|
|
1560
|
+
// Rethrow a sanitized NSError. The original `error` is a
|
|
1561
|
+
// URLSession/system error whose `localizedDescription` may
|
|
1562
|
+
// include the temp file path (e.g. NSURLErrorFailingURLString
|
|
1563
|
+
// userInfo, or the ~/Library/.../CFNetworkDownload_*.tmp
|
|
1564
|
+
// path on cancel-with-resume) — Promise rejections surface
|
|
1565
|
+
// that string to JS, which would re-leak the paths the
|
|
1566
|
+
// event-payload sanitization just stripped. Keep the full
|
|
1567
|
+
// detail in OneKeyLog (above) and emit only `domain code`.
|
|
1568
|
+
throw NSError(
|
|
1569
|
+
domain: nsError.domain,
|
|
1570
|
+
code: nsError.code,
|
|
1571
|
+
userInfo: [NSLocalizedDescriptionKey: "Download failed: \(nsError.domain) \(nsError.code)"]
|
|
1572
|
+
)
|
|
1546
1573
|
|
|
1547
1574
|
case .success(let (tempURL, response)):
|
|
1548
1575
|
// Successful completion ⇒ no longer need the resume blob.
|