@onekeyfe/react-native-bundle-update 1.1.42 → 1.1.44
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
|
@@ -248,6 +248,101 @@ object BundleUpdateStoreAndroid {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// Pre-launch pending task processing
|
|
252
|
+
|
|
253
|
+
@Volatile
|
|
254
|
+
private var didProcessPreLaunchTask = false
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Checks MMKV for a pending bundle-switch task and applies it before JS runtime starts.
|
|
258
|
+
* Only handles the happy path (status=pending, bundle exists, env matches).
|
|
259
|
+
* All complex logic (retry, download, error handling) remains in JS layer.
|
|
260
|
+
*/
|
|
261
|
+
fun processPreLaunchPendingTask(context: Context) {
|
|
262
|
+
if (didProcessPreLaunchTask) return
|
|
263
|
+
didProcessPreLaunchTask = true
|
|
264
|
+
try {
|
|
265
|
+
// 1. Read MMKV
|
|
266
|
+
MMKV.initialize(context)
|
|
267
|
+
val mmkv = MMKV.mmkvWithID("onekey-app-setting") ?: return
|
|
268
|
+
val taskJson = mmkv.decodeString("onekey_pending_install_task") ?: return
|
|
269
|
+
val taskObj = JSONObject(taskJson)
|
|
270
|
+
|
|
271
|
+
// 2. Validate task fields
|
|
272
|
+
if (taskObj.optString("status") != "pending") return
|
|
273
|
+
if (taskObj.optString("action") != "switch-bundle") return
|
|
274
|
+
if (taskObj.optString("type") != "jsbundle-switch") return
|
|
275
|
+
|
|
276
|
+
val now = System.currentTimeMillis()
|
|
277
|
+
if (taskObj.optLong("expiresAt", 0) <= now) return
|
|
278
|
+
val nextRetryAt = taskObj.optLong("nextRetryAt", 0)
|
|
279
|
+
if (nextRetryAt > 0 && nextRetryAt > now) return
|
|
280
|
+
|
|
281
|
+
// 3. Verify scheduledEnv matches current state
|
|
282
|
+
val currentAppVersion = getAppVersion(context) ?: return
|
|
283
|
+
if (taskObj.optString("scheduledEnvAppVersion") != currentAppVersion) return
|
|
284
|
+
|
|
285
|
+
val currentBV = getCurrentBundleVersion(context)
|
|
286
|
+
val currentBundleVersionStr = if (currentBV != null) {
|
|
287
|
+
val idx = currentBV.lastIndexOf("-")
|
|
288
|
+
if (idx > 0) currentBV.substring(idx + 1) else getBuiltinBundleVersion(context)
|
|
289
|
+
} else {
|
|
290
|
+
getBuiltinBundleVersion(context)
|
|
291
|
+
}
|
|
292
|
+
if (taskObj.optString("scheduledEnvBundleVersion") != currentBundleVersionStr) return
|
|
293
|
+
|
|
294
|
+
// 4. Extract payload
|
|
295
|
+
val payload = taskObj.optJSONObject("payload") ?: return
|
|
296
|
+
val appVersion = payload.optString("appVersion")
|
|
297
|
+
val bundleVersion = payload.optString("bundleVersion")
|
|
298
|
+
val signature = payload.optString("signature")
|
|
299
|
+
if (appVersion.isEmpty() || bundleVersion.isEmpty() || signature.isEmpty()) return
|
|
300
|
+
if (!isSafeVersionString(appVersion) || !isSafeVersionString(bundleVersion)) return
|
|
301
|
+
|
|
302
|
+
// 5. Verify bundle directory and entry file exist
|
|
303
|
+
val folderName = "$appVersion-$bundleVersion"
|
|
304
|
+
val bundleDirPath = File(getBundleDir(context), folderName)
|
|
305
|
+
if (!bundleDirPath.exists()) return
|
|
306
|
+
val entryFile = File(bundleDirPath, "main.jsbundle.hbc")
|
|
307
|
+
if (!entryFile.exists()) {
|
|
308
|
+
OneKeyLog.warn("BundleUpdate", "processPreLaunchPendingTask: bundle dir exists but entry file missing: ${entryFile.absolutePath}")
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 6. Apply (same as installBundle) — use commit() for synchronous writes
|
|
313
|
+
// to ensure all prefs are persisted atomically before proceeding.
|
|
314
|
+
val bundlePrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
315
|
+
val versionPrefs = context.getSharedPreferences(NATIVE_VERSION_PREFS_NAME, Context.MODE_PRIVATE)
|
|
316
|
+
val currentVersion = bundlePrefs.getString(CURRENT_BUNDLE_VERSION_KEY, "")
|
|
317
|
+
bundlePrefs.edit()
|
|
318
|
+
.putString(CURRENT_BUNDLE_VERSION_KEY, folderName)
|
|
319
|
+
.apply {
|
|
320
|
+
if (!currentVersion.isNullOrEmpty()) {
|
|
321
|
+
remove(currentVersion) // legacy signature key cleanup
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
.commit()
|
|
325
|
+
if (!signature.isNullOrEmpty()) {
|
|
326
|
+
writeSignatureFile(context, folderName, signature)
|
|
327
|
+
}
|
|
328
|
+
versionPrefs.edit()
|
|
329
|
+
.putString("nativeVersion", currentAppVersion)
|
|
330
|
+
.putString("nativeBuildNumber", getBuildNumber(context))
|
|
331
|
+
.commit()
|
|
332
|
+
|
|
333
|
+
// 7. Update MMKV task status → applied_waiting_verify
|
|
334
|
+
// Do NOT set runningStartedAt — a falsy value lets JS skip the
|
|
335
|
+
// 10-minute grace period and verify alignment immediately on boot.
|
|
336
|
+
taskObj.put("status", "applied_waiting_verify")
|
|
337
|
+
taskObj.remove("runningStartedAt")
|
|
338
|
+
mmkv.encode("onekey_pending_install_task", taskObj.toString())
|
|
339
|
+
|
|
340
|
+
OneKeyLog.info("BundleUpdate", "processPreLaunchPendingTask: switched to $folderName")
|
|
341
|
+
} catch (e: Exception) {
|
|
342
|
+
OneKeyLog.error("BundleUpdate", "processPreLaunchPendingTask failed: ${e.message}")
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
251
346
|
fun calculateSHA256(filePath: String): String? {
|
|
252
347
|
return try {
|
|
253
348
|
val digest = MessageDigest.getInstance("SHA-256")
|
|
@@ -536,6 +631,7 @@ object BundleUpdateStoreAndroid {
|
|
|
536
631
|
}
|
|
537
632
|
|
|
538
633
|
fun getCurrentBundleMainJSBundle(context: Context): String? {
|
|
634
|
+
processPreLaunchPendingTask(context)
|
|
539
635
|
return try {
|
|
540
636
|
val currentAppVersion = getAppVersion(context)
|
|
541
637
|
val currentBundleVersion = getCurrentBundleVersion(context) ?: run {
|
|
@@ -202,6 +202,99 @@ public class BundleUpdateStore: NSObject {
|
|
|
202
202
|
Bundle.main.infoDictionary?["BUNDLE_VERSION"] as? String ?? ""
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// MARK: - Pre-launch pending task processing
|
|
206
|
+
|
|
207
|
+
private static var didProcessPreLaunchTask = false
|
|
208
|
+
|
|
209
|
+
/// Checks MMKV for a pending bundle-switch task and applies it before JS runtime starts.
|
|
210
|
+
/// Only handles the happy path (status=pending, bundle exists, env matches).
|
|
211
|
+
/// All complex logic (retry, download, error handling) remains in JS layer.
|
|
212
|
+
public static func processPreLaunchPendingTask() {
|
|
213
|
+
guard !didProcessPreLaunchTask else { return }
|
|
214
|
+
didProcessPreLaunchTask = true
|
|
215
|
+
|
|
216
|
+
do {
|
|
217
|
+
// 1. Read MMKV (same pattern as isDevSettingsEnabled)
|
|
218
|
+
MMKV.initialize(rootDir: nil)
|
|
219
|
+
guard let mmkv = MMKV(mmapID: "onekey-app-setting"),
|
|
220
|
+
let taskJson = mmkv.string(forKey: "onekey_pending_install_task"),
|
|
221
|
+
let data = taskJson.data(using: .utf8),
|
|
222
|
+
let taskDict = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
223
|
+
else { return }
|
|
224
|
+
|
|
225
|
+
// 2. Validate task fields
|
|
226
|
+
guard taskDict["status"] as? String == "pending",
|
|
227
|
+
taskDict["action"] as? String == "switch-bundle",
|
|
228
|
+
taskDict["type"] as? String == "jsbundle-switch"
|
|
229
|
+
else { return }
|
|
230
|
+
|
|
231
|
+
let now = Int64(Date().timeIntervalSince1970 * 1000)
|
|
232
|
+
guard let expiresAtNum = taskDict["expiresAt"] as? NSNumber else { return }
|
|
233
|
+
let expiresAt = expiresAtNum.int64Value
|
|
234
|
+
guard expiresAt > now else { return }
|
|
235
|
+
if let nextRetryAtNum = taskDict["nextRetryAt"] as? NSNumber {
|
|
236
|
+
if nextRetryAtNum.int64Value > now { return }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 3. Verify scheduledEnv matches current state
|
|
240
|
+
let currentAppVersion = getCurrentNativeVersion()
|
|
241
|
+
guard taskDict["scheduledEnvAppVersion"] as? String == currentAppVersion else { return }
|
|
242
|
+
|
|
243
|
+
let currentBundleVersionStr: String
|
|
244
|
+
if let cbv = currentBundleVersion(),
|
|
245
|
+
let dashRange = cbv.range(of: "-", options: .backwards) {
|
|
246
|
+
currentBundleVersionStr = String(cbv[dashRange.upperBound...])
|
|
247
|
+
} else {
|
|
248
|
+
currentBundleVersionStr = getBuiltinBundleVersion()
|
|
249
|
+
}
|
|
250
|
+
guard taskDict["scheduledEnvBundleVersion"] as? String == currentBundleVersionStr else { return }
|
|
251
|
+
|
|
252
|
+
// 4. Extract payload
|
|
253
|
+
guard let payload = taskDict["payload"] as? [String: Any],
|
|
254
|
+
let appVersion = payload["appVersion"] as? String,
|
|
255
|
+
let bundleVersion = payload["bundleVersion"] as? String,
|
|
256
|
+
let signature = payload["signature"] as? String,
|
|
257
|
+
!appVersion.isEmpty, !bundleVersion.isEmpty, !signature.isEmpty,
|
|
258
|
+
appVersion.isSafeVersionString, bundleVersion.isSafeVersionString
|
|
259
|
+
else { return }
|
|
260
|
+
|
|
261
|
+
// 5. Verify bundle directory and entry file exist
|
|
262
|
+
let folderName = "\(appVersion)-\(bundleVersion)"
|
|
263
|
+
let bundleDirPath = (bundleDir() as NSString).appendingPathComponent(folderName)
|
|
264
|
+
guard FileManager.default.fileExists(atPath: bundleDirPath) else { return }
|
|
265
|
+
let entryFilePath = (bundleDirPath as NSString).appendingPathComponent("main.jsbundle.hbc")
|
|
266
|
+
guard FileManager.default.fileExists(atPath: entryFilePath) else {
|
|
267
|
+
OneKeyLog.warn("BundleUpdate", "processPreLaunchPendingTask: bundle dir exists but entry file missing: \(entryFilePath)")
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 6. Apply: set currentBundleVersion (same as installBundle)
|
|
272
|
+
let ud = UserDefaults.standard
|
|
273
|
+
ud.set(folderName, forKey: bundlePrefsKey)
|
|
274
|
+
if !signature.isEmpty {
|
|
275
|
+
writeSignatureFile(folderName, signature: signature)
|
|
276
|
+
}
|
|
277
|
+
setNativeVersion(currentAppVersion)
|
|
278
|
+
setNativeBuildNumber(getCurrentNativeBuildNumber())
|
|
279
|
+
ud.synchronize()
|
|
280
|
+
|
|
281
|
+
// 7. Update MMKV task status → applied_waiting_verify
|
|
282
|
+
// Do NOT set runningStartedAt — a falsy value lets JS skip the
|
|
283
|
+
// 10-minute grace period and verify alignment immediately on boot.
|
|
284
|
+
var updatedTask = taskDict
|
|
285
|
+
updatedTask["status"] = "applied_waiting_verify"
|
|
286
|
+
updatedTask.removeValue(forKey: "runningStartedAt")
|
|
287
|
+
let jsonData = try JSONSerialization.data(withJSONObject: updatedTask)
|
|
288
|
+
if let jsonStr = String(data: jsonData, encoding: .utf8) {
|
|
289
|
+
mmkv.set(jsonStr, forKey: "onekey_pending_install_task")
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
OneKeyLog.info("BundleUpdate", "processPreLaunchPendingTask: switched to \(folderName)")
|
|
293
|
+
} catch {
|
|
294
|
+
OneKeyLog.error("BundleUpdate", "processPreLaunchPendingTask failed: \(error)")
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
205
298
|
public static func getMetadataFilePath(_ currentBundleVersion: String) -> String? {
|
|
206
299
|
let path = (bundleDir() as NSString)
|
|
207
300
|
.appendingPathComponent(currentBundleVersion)
|
|
@@ -406,6 +499,7 @@ public class BundleUpdateStore: NSObject {
|
|
|
406
499
|
}
|
|
407
500
|
|
|
408
501
|
public static func currentBundleMainJSBundle() -> String? {
|
|
502
|
+
processPreLaunchPendingTask()
|
|
409
503
|
guard let currentBundleVer = currentBundleVersion() else {
|
|
410
504
|
OneKeyLog.warn("BundleUpdate", "getJsBundlePath: no currentBundleVersion stored")
|
|
411
505
|
return nil
|